Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * AXI clkgen driver
  3 *
  4 * Copyright 2012-2013 Analog Devices Inc.
  5 *  Author: Lars-Peter Clausen <lars@metafoo.de>
  6 *
  7 * Licensed under the GPL-2.
  8 *
  9 */
 10
 11#include <linux/platform_device.h>
 12#include <linux/clk-provider.h>
 13#include <linux/slab.h>
 14#include <linux/io.h>
 15#include <linux/of.h>
 16#include <linux/module.h>
 17#include <linux/err.h>
 18
 19#define AXI_CLKGEN_V2_REG_RESET		0x40
 20#define AXI_CLKGEN_V2_REG_CLKSEL	0x44
 21#define AXI_CLKGEN_V2_REG_DRP_CNTRL	0x70
 22#define AXI_CLKGEN_V2_REG_DRP_STATUS	0x74
 23
 24#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE	BIT(1)
 25#define AXI_CLKGEN_V2_RESET_ENABLE	BIT(0)
 26
 27#define AXI_CLKGEN_V2_DRP_CNTRL_SEL	BIT(29)
 28#define AXI_CLKGEN_V2_DRP_CNTRL_READ	BIT(28)
 29
 30#define AXI_CLKGEN_V2_DRP_STATUS_BUSY	BIT(16)
 31
 32#define MMCM_REG_CLKOUT0_1	0x08
 33#define MMCM_REG_CLKOUT0_2	0x09
 34#define MMCM_REG_CLK_FB1	0x14
 35#define MMCM_REG_CLK_FB2	0x15
 36#define MMCM_REG_CLK_DIV	0x16
 37#define MMCM_REG_LOCK1		0x18
 38#define MMCM_REG_LOCK2		0x19
 39#define MMCM_REG_LOCK3		0x1a
 40#define MMCM_REG_FILTER1	0x4e
 41#define MMCM_REG_FILTER2	0x4f
 42
 43struct axi_clkgen {
 44	void __iomem *base;
 45	struct clk_hw clk_hw;
 46};
 47
 48static uint32_t axi_clkgen_lookup_filter(unsigned int m)
 49{
 50	switch (m) {
 51	case 0:
 52		return 0x01001990;
 53	case 1:
 54		return 0x01001190;
 55	case 2:
 56		return 0x01009890;
 57	case 3:
 58		return 0x01001890;
 59	case 4:
 60		return 0x01008890;
 61	case 5 ... 8:
 62		return 0x01009090;
 63	case 9 ... 11:
 64		return 0x01000890;
 65	case 12:
 66		return 0x08009090;
 67	case 13 ... 22:
 68		return 0x01001090;
 69	case 23 ... 36:
 70		return 0x01008090;
 71	case 37 ... 46:
 72		return 0x08001090;
 73	default:
 74		return 0x08008090;
 75	}
 76}
 77
 78static const uint32_t axi_clkgen_lock_table[] = {
 79	0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
 80	0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
 81	0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
 82	0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
 83	0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
 84	0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
 85	0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
 86	0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
 87	0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
 88};
 89
 90static uint32_t axi_clkgen_lookup_lock(unsigned int m)
 91{
 92	if (m < ARRAY_SIZE(axi_clkgen_lock_table))
 93		return axi_clkgen_lock_table[m];
 94	return 0x1f1f00fa;
 95}
 96
 97static const unsigned int fpfd_min = 10000;
 98static const unsigned int fpfd_max = 300000;
 99static const unsigned int fvco_min = 600000;
100static const unsigned int fvco_max = 1200000;
101
102static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
103	unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
104{
105	unsigned long d, d_min, d_max, _d_min, _d_max;
106	unsigned long m, m_min, m_max;
107	unsigned long f, dout, best_f, fvco;
108
109	fin /= 1000;
110	fout /= 1000;
111
112	best_f = ULONG_MAX;
113	*best_d = 0;
114	*best_m = 0;
115	*best_dout = 0;
116
117	d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
118	d_max = min_t(unsigned long, fin / fpfd_min, 80);
119
120	m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
121	m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
122
123	for (m = m_min; m <= m_max; m++) {
124		_d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
125		_d_max = min(d_max, fin * m / fvco_min);
126
127		for (d = _d_min; d <= _d_max; d++) {
128			fvco = fin * m / d;
129
130			dout = DIV_ROUND_CLOSEST(fvco, fout);
131			dout = clamp_t(unsigned long, dout, 1, 128);
132			f = fvco / dout;
133			if (abs(f - fout) < abs(best_f - fout)) {
134				best_f = f;
135				*best_d = d;
136				*best_m = m;
137				*best_dout = dout;
138				if (best_f == fout)
139					return;
140			}
141		}
142	}
143}
144
145static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
146	unsigned int *high, unsigned int *edge, unsigned int *nocount)
147{
148	if (divider == 1)
149		*nocount = 1;
150	else
151		*nocount = 0;
152
153	*high = divider / 2;
154	*edge = divider % 2;
155	*low = divider - *high;
156}
157
158static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
159	unsigned int reg, unsigned int val)
160{
161	writel(val, axi_clkgen->base + reg);
162}
163
164static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
165	unsigned int reg, unsigned int *val)
166{
167	*val = readl(axi_clkgen->base + reg);
168}
169
170static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
171{
172	unsigned int timeout = 10000;
173	unsigned int val;
174
175	do {
176		axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
177	} while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
178
179	if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
180		return -EIO;
181
182	return val & 0xffff;
183}
184
185static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
186	unsigned int reg, unsigned int *val)
187{
188	unsigned int reg_val;
189	int ret;
190
191	ret = axi_clkgen_wait_non_busy(axi_clkgen);
192	if (ret < 0)
193		return ret;
194
195	reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
196	reg_val |= (reg << 16);
197
198	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
199
200	ret = axi_clkgen_wait_non_busy(axi_clkgen);
201	if (ret < 0)
202		return ret;
203
204	*val = ret;
205
206	return 0;
207}
208
209static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
210	unsigned int reg, unsigned int val, unsigned int mask)
211{
212	unsigned int reg_val = 0;
213	int ret;
214
215	ret = axi_clkgen_wait_non_busy(axi_clkgen);
216	if (ret < 0)
217		return ret;
218
219	if (mask != 0xffff) {
220		axi_clkgen_mmcm_read(axi_clkgen, reg, &reg_val);
221		reg_val &= ~mask;
222	}
223
224	reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
225
226	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
227
228	return 0;
229}
230
231static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
232	bool enable)
233{
234	unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
235
236	if (enable)
237		val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
238
239	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
240}
241
242static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
243{
244	return container_of(clk_hw, struct axi_clkgen, clk_hw);
245}
246
247static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
248	unsigned long rate, unsigned long parent_rate)
249{
250	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
251	unsigned int d, m, dout;
252	unsigned int nocount;
253	unsigned int high;
254	unsigned int edge;
255	unsigned int low;
256	uint32_t filter;
257	uint32_t lock;
258
259	if (parent_rate == 0 || rate == 0)
260		return -EINVAL;
261
262	axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
263
264	if (d == 0 || dout == 0 || m == 0)
265		return -EINVAL;
266
267	filter = axi_clkgen_lookup_filter(m - 1);
268	lock = axi_clkgen_lookup_lock(m - 1);
269
270	axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
271	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
272		(high << 6) | low, 0xefff);
273	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
274		(edge << 7) | (nocount << 6), 0x03ff);
275
276	axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
277	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
278		(edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
279
280	axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
281	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
282		(high << 6) | low, 0xefff);
283	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
284		(edge << 7) | (nocount << 6), 0x03ff);
285
286	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
287	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
288		(((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
289	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
290		(((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
291	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
292	axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
293
294	return 0;
295}
296
297static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
298	unsigned long *parent_rate)
299{
300	unsigned int d, m, dout;
301
302	axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
303
304	if (d == 0 || dout == 0 || m == 0)
305		return -EINVAL;
306
307	return *parent_rate / d * m / dout;
308}
309
310static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
311	unsigned long parent_rate)
312{
313	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
314	unsigned int d, m, dout;
315	unsigned int reg;
316	unsigned long long tmp;
317
318	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
319	dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
320	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
321	d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
322	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
323	m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
324
325	if (d == 0 || dout == 0)
326		return 0;
327
328	tmp = (unsigned long long)(parent_rate / d) * m;
329	do_div(tmp, dout);
330
331	return min_t(unsigned long long, tmp, ULONG_MAX);
332}
333
334static int axi_clkgen_enable(struct clk_hw *clk_hw)
335{
336	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
337
338	axi_clkgen_mmcm_enable(axi_clkgen, true);
339
340	return 0;
341}
342
343static void axi_clkgen_disable(struct clk_hw *clk_hw)
344{
345	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
346
347	axi_clkgen_mmcm_enable(axi_clkgen, false);
348}
349
350static int axi_clkgen_set_parent(struct clk_hw *clk_hw, u8 index)
351{
352	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
353
354	axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, index);
355
356	return 0;
357}
358
359static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw)
360{
361	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
362	unsigned int parent;
363
364	axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, &parent);
365
366	return parent;
367}
368
369static const struct clk_ops axi_clkgen_ops = {
370	.recalc_rate = axi_clkgen_recalc_rate,
371	.round_rate = axi_clkgen_round_rate,
372	.set_rate = axi_clkgen_set_rate,
373	.enable = axi_clkgen_enable,
374	.disable = axi_clkgen_disable,
375	.set_parent = axi_clkgen_set_parent,
376	.get_parent = axi_clkgen_get_parent,
377};
378
379static const struct of_device_id axi_clkgen_ids[] = {
380	{
381		.compatible = "adi,axi-clkgen-2.00.a",
382	},
383	{ },
384};
385MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
386
387static int axi_clkgen_probe(struct platform_device *pdev)
388{
389	const struct of_device_id *id;
390	struct axi_clkgen *axi_clkgen;
391	struct clk_init_data init;
392	const char *parent_names[2];
393	const char *clk_name;
394	struct resource *mem;
395	struct clk *clk;
396	unsigned int i;
397
398	if (!pdev->dev.of_node)
399		return -ENODEV;
400
401	id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
402	if (!id)
403		return -ENODEV;
404
405	axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
406	if (!axi_clkgen)
407		return -ENOMEM;
408
409	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
410	axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
411	if (IS_ERR(axi_clkgen->base))
412		return PTR_ERR(axi_clkgen->base);
413
414	init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
415	if (init.num_parents < 1 || init.num_parents > 2)
416		return -EINVAL;
417
418	for (i = 0; i < init.num_parents; i++) {
419		parent_names[i] = of_clk_get_parent_name(pdev->dev.of_node, i);
420		if (!parent_names[i])
421			return -EINVAL;
422	}
423
424	clk_name = pdev->dev.of_node->name;
425	of_property_read_string(pdev->dev.of_node, "clock-output-names",
426		&clk_name);
427
428	init.name = clk_name;
429	init.ops = &axi_clkgen_ops;
430	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
431	init.parent_names = parent_names;
432
433	axi_clkgen_mmcm_enable(axi_clkgen, false);
434
435	axi_clkgen->clk_hw.init = &init;
436	clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
437	if (IS_ERR(clk))
438		return PTR_ERR(clk);
439
440	return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
441				    clk);
442}
443
444static int axi_clkgen_remove(struct platform_device *pdev)
445{
446	of_clk_del_provider(pdev->dev.of_node);
447
448	return 0;
449}
450
451static struct platform_driver axi_clkgen_driver = {
452	.driver = {
453		.name = "adi-axi-clkgen",
454		.of_match_table = axi_clkgen_ids,
455	},
456	.probe = axi_clkgen_probe,
457	.remove = axi_clkgen_remove,
458};
459module_platform_driver(axi_clkgen_driver);
460
461MODULE_LICENSE("GPL v2");
462MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
463MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");