Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  3 *
  4 * This program is free software; you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation; either version 2 of the License, or
  7 * (at your option) any later version.
  8 *
  9 */
 10
 11#include <linux/clk-provider.h>
 12#include <linux/clkdev.h>
 13#include <linux/clk/at91_pmc.h>
 14#include <linux/of.h>
 15#include <linux/mfd/syscon.h>
 16#include <linux/regmap.h>
 17
 18#include "pmc.h"
 19
 20DEFINE_SPINLOCK(pmc_pcr_lock);
 21
 22#define PERIPHERAL_MAX		64
 23
 24#define PERIPHERAL_AT91RM9200	0
 25#define PERIPHERAL_AT91SAM9X5	1
 26
 27#define PERIPHERAL_ID_MIN	2
 28#define PERIPHERAL_ID_MAX	31
 29#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
 30
 31#define PERIPHERAL_RSHIFT_MASK	0x3
 32#define PERIPHERAL_RSHIFT(val)	(((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
 33
 34#define PERIPHERAL_MAX_SHIFT	3
 35
 36struct clk_peripheral {
 37	struct clk_hw hw;
 38	struct regmap *regmap;
 39	u32 id;
 40};
 41
 42#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
 43
 44struct clk_sam9x5_peripheral {
 45	struct clk_hw hw;
 46	struct regmap *regmap;
 47	struct clk_range range;
 48	spinlock_t *lock;
 49	u32 id;
 50	u32 div;
 51	bool auto_div;
 52};
 53
 54#define to_clk_sam9x5_peripheral(hw) \
 55	container_of(hw, struct clk_sam9x5_peripheral, hw)
 56
 57static int clk_peripheral_enable(struct clk_hw *hw)
 58{
 59	struct clk_peripheral *periph = to_clk_peripheral(hw);
 60	int offset = AT91_PMC_PCER;
 61	u32 id = periph->id;
 62
 63	if (id < PERIPHERAL_ID_MIN)
 64		return 0;
 65	if (id > PERIPHERAL_ID_MAX)
 66		offset = AT91_PMC_PCER1;
 67	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
 68
 69	return 0;
 70}
 71
 72static void clk_peripheral_disable(struct clk_hw *hw)
 73{
 74	struct clk_peripheral *periph = to_clk_peripheral(hw);
 75	int offset = AT91_PMC_PCDR;
 76	u32 id = periph->id;
 77
 78	if (id < PERIPHERAL_ID_MIN)
 79		return;
 80	if (id > PERIPHERAL_ID_MAX)
 81		offset = AT91_PMC_PCDR1;
 82	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
 83}
 84
 85static int clk_peripheral_is_enabled(struct clk_hw *hw)
 86{
 87	struct clk_peripheral *periph = to_clk_peripheral(hw);
 88	int offset = AT91_PMC_PCSR;
 89	unsigned int status;
 90	u32 id = periph->id;
 91
 92	if (id < PERIPHERAL_ID_MIN)
 93		return 1;
 94	if (id > PERIPHERAL_ID_MAX)
 95		offset = AT91_PMC_PCSR1;
 96	regmap_read(periph->regmap, offset, &status);
 97
 98	return status & PERIPHERAL_MASK(id) ? 1 : 0;
 99}
100
101static const struct clk_ops peripheral_ops = {
102	.enable = clk_peripheral_enable,
103	.disable = clk_peripheral_disable,
104	.is_enabled = clk_peripheral_is_enabled,
105};
106
107static struct clk * __init
108at91_clk_register_peripheral(struct regmap *regmap, const char *name,
109			     const char *parent_name, u32 id)
110{
111	struct clk_peripheral *periph;
112	struct clk *clk = NULL;
113	struct clk_init_data init;
114
115	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
116		return ERR_PTR(-EINVAL);
117
118	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
119	if (!periph)
120		return ERR_PTR(-ENOMEM);
121
122	init.name = name;
123	init.ops = &peripheral_ops;
124	init.parent_names = (parent_name ? &parent_name : NULL);
125	init.num_parents = (parent_name ? 1 : 0);
126	init.flags = 0;
127
128	periph->id = id;
129	periph->hw.init = &init;
130	periph->regmap = regmap;
131
132	clk = clk_register(NULL, &periph->hw);
133	if (IS_ERR(clk))
134		kfree(periph);
135
136	return clk;
137}
138
139static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
140{
141	struct clk_hw *parent;
142	unsigned long parent_rate;
143	int shift = 0;
144
145	if (!periph->auto_div)
146		return;
147
148	if (periph->range.max) {
149		parent = clk_hw_get_parent_by_index(&periph->hw, 0);
150		parent_rate = clk_hw_get_rate(parent);
151		if (!parent_rate)
152			return;
153
154		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
155			if (parent_rate >> shift <= periph->range.max)
156				break;
157		}
158	}
159
160	periph->auto_div = false;
161	periph->div = shift;
162}
163
164static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
165{
166	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
167	unsigned long flags;
168
169	if (periph->id < PERIPHERAL_ID_MIN)
170		return 0;
171
172	spin_lock_irqsave(periph->lock, flags);
173	regmap_write(periph->regmap, AT91_PMC_PCR,
174		     (periph->id & AT91_PMC_PCR_PID_MASK));
175	regmap_update_bits(periph->regmap, AT91_PMC_PCR,
176			   AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
177			   AT91_PMC_PCR_EN,
178			   AT91_PMC_PCR_DIV(periph->div) |
179			   AT91_PMC_PCR_CMD |
180			   AT91_PMC_PCR_EN);
181	spin_unlock_irqrestore(periph->lock, flags);
182
183	return 0;
184}
185
186static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
187{
188	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
189	unsigned long flags;
190
191	if (periph->id < PERIPHERAL_ID_MIN)
192		return;
193
194	spin_lock_irqsave(periph->lock, flags);
195	regmap_write(periph->regmap, AT91_PMC_PCR,
196		     (periph->id & AT91_PMC_PCR_PID_MASK));
197	regmap_update_bits(periph->regmap, AT91_PMC_PCR,
198			   AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
199			   AT91_PMC_PCR_CMD);
200	spin_unlock_irqrestore(periph->lock, flags);
201}
202
203static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
204{
205	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
206	unsigned long flags;
207	unsigned int status;
208
209	if (periph->id < PERIPHERAL_ID_MIN)
210		return 1;
211
212	spin_lock_irqsave(periph->lock, flags);
213	regmap_write(periph->regmap, AT91_PMC_PCR,
214		     (periph->id & AT91_PMC_PCR_PID_MASK));
215	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
216	spin_unlock_irqrestore(periph->lock, flags);
217
218	return status & AT91_PMC_PCR_EN ? 1 : 0;
219}
220
221static unsigned long
222clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
223				  unsigned long parent_rate)
224{
225	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
226	unsigned long flags;
227	unsigned int status;
228
229	if (periph->id < PERIPHERAL_ID_MIN)
230		return parent_rate;
231
232	spin_lock_irqsave(periph->lock, flags);
233	regmap_write(periph->regmap, AT91_PMC_PCR,
234		     (periph->id & AT91_PMC_PCR_PID_MASK));
235	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
236	spin_unlock_irqrestore(periph->lock, flags);
237
238	if (status & AT91_PMC_PCR_EN) {
239		periph->div = PERIPHERAL_RSHIFT(status);
240		periph->auto_div = false;
241	} else {
242		clk_sam9x5_peripheral_autodiv(periph);
243	}
244
245	return parent_rate >> periph->div;
246}
247
248static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
249					     unsigned long rate,
250					     unsigned long *parent_rate)
251{
252	int shift = 0;
253	unsigned long best_rate;
254	unsigned long best_diff;
255	unsigned long cur_rate = *parent_rate;
256	unsigned long cur_diff;
257	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
258
259	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
260		return *parent_rate;
261
262	if (periph->range.max) {
263		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
264			cur_rate = *parent_rate >> shift;
265			if (cur_rate <= periph->range.max)
266				break;
267		}
268	}
269
270	if (rate >= cur_rate)
271		return cur_rate;
272
273	best_diff = cur_rate - rate;
274	best_rate = cur_rate;
275	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
276		cur_rate = *parent_rate >> shift;
277		if (cur_rate < rate)
278			cur_diff = rate - cur_rate;
279		else
280			cur_diff = cur_rate - rate;
281
282		if (cur_diff < best_diff) {
283			best_diff = cur_diff;
284			best_rate = cur_rate;
285		}
286
287		if (!best_diff || cur_rate < rate)
288			break;
289	}
290
291	return best_rate;
292}
293
294static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
295					  unsigned long rate,
296					  unsigned long parent_rate)
297{
298	int shift;
299	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
300	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
301		if (parent_rate == rate)
302			return 0;
303		else
304			return -EINVAL;
305	}
306
307	if (periph->range.max && rate > periph->range.max)
308		return -EINVAL;
309
310	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
311		if (parent_rate >> shift == rate) {
312			periph->auto_div = false;
313			periph->div = shift;
314			return 0;
315		}
316	}
317
318	return -EINVAL;
319}
320
321static const struct clk_ops sam9x5_peripheral_ops = {
322	.enable = clk_sam9x5_peripheral_enable,
323	.disable = clk_sam9x5_peripheral_disable,
324	.is_enabled = clk_sam9x5_peripheral_is_enabled,
325	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
326	.round_rate = clk_sam9x5_peripheral_round_rate,
327	.set_rate = clk_sam9x5_peripheral_set_rate,
328};
329
330static struct clk * __init
331at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
332				    const char *name, const char *parent_name,
333				    u32 id, const struct clk_range *range)
334{
335	struct clk_sam9x5_peripheral *periph;
336	struct clk *clk = NULL;
337	struct clk_init_data init;
338
339	if (!name || !parent_name)
340		return ERR_PTR(-EINVAL);
341
342	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
343	if (!periph)
344		return ERR_PTR(-ENOMEM);
345
346	init.name = name;
347	init.ops = &sam9x5_peripheral_ops;
348	init.parent_names = (parent_name ? &parent_name : NULL);
349	init.num_parents = (parent_name ? 1 : 0);
350	init.flags = 0;
351
352	periph->id = id;
353	periph->hw.init = &init;
354	periph->div = 0;
355	periph->regmap = regmap;
356	periph->lock = lock;
357	periph->auto_div = true;
358	periph->range = *range;
359
360	clk = clk_register(NULL, &periph->hw);
361	if (IS_ERR(clk))
362		kfree(periph);
363	else
364		clk_sam9x5_peripheral_autodiv(periph);
365
366	return clk;
367}
368
369static void __init
370of_at91_clk_periph_setup(struct device_node *np, u8 type)
371{
372	int num;
373	u32 id;
374	struct clk *clk;
375	const char *parent_name;
376	const char *name;
377	struct device_node *periphclknp;
378	struct regmap *regmap;
379
380	parent_name = of_clk_get_parent_name(np, 0);
381	if (!parent_name)
382		return;
383
384	num = of_get_child_count(np);
385	if (!num || num > PERIPHERAL_MAX)
386		return;
387
388	regmap = syscon_node_to_regmap(of_get_parent(np));
389	if (IS_ERR(regmap))
390		return;
391
392	for_each_child_of_node(np, periphclknp) {
393		if (of_property_read_u32(periphclknp, "reg", &id))
394			continue;
395
396		if (id >= PERIPHERAL_MAX)
397			continue;
398
399		if (of_property_read_string(np, "clock-output-names", &name))
400			name = periphclknp->name;
401
402		if (type == PERIPHERAL_AT91RM9200) {
403			clk = at91_clk_register_peripheral(regmap, name,
404							   parent_name, id);
405		} else {
406			struct clk_range range = CLK_RANGE(0, 0);
407
408			of_at91_get_clk_range(periphclknp,
409					      "atmel,clk-output-range",
410					      &range);
411
412			clk = at91_clk_register_sam9x5_peripheral(regmap,
413								  &pmc_pcr_lock,
414								  name,
415								  parent_name,
416								  id, &range);
417		}
418
419		if (IS_ERR(clk))
420			continue;
421
422		of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
423	}
424}
425
426static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
427{
428	of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
429}
430CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
431	       of_at91rm9200_clk_periph_setup);
432
433static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
434{
435	of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
436}
437CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
438	       of_at91sam9x5_clk_periph_setup);
439