Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

Need help to get security updates for your Linux BSP?
Loading...
Note: File does not exist in v3.1.
  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_hw * __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_init_data init;
113	struct clk_hw *hw;
114	int ret;
115
116	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
117		return ERR_PTR(-EINVAL);
118
119	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
120	if (!periph)
121		return ERR_PTR(-ENOMEM);
122
123	init.name = name;
124	init.ops = &peripheral_ops;
125	init.parent_names = (parent_name ? &parent_name : NULL);
126	init.num_parents = (parent_name ? 1 : 0);
127	init.flags = 0;
128
129	periph->id = id;
130	periph->hw.init = &init;
131	periph->regmap = regmap;
132
133	hw = &periph->hw;
134	ret = clk_hw_register(NULL, &periph->hw);
135	if (ret) {
136		kfree(periph);
137		hw = ERR_PTR(ret);
138	}
139
140	return hw;
141}
142
143static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
144{
145	struct clk_hw *parent;
146	unsigned long parent_rate;
147	int shift = 0;
148
149	if (!periph->auto_div)
150		return;
151
152	if (periph->range.max) {
153		parent = clk_hw_get_parent_by_index(&periph->hw, 0);
154		parent_rate = clk_hw_get_rate(parent);
155		if (!parent_rate)
156			return;
157
158		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
159			if (parent_rate >> shift <= periph->range.max)
160				break;
161		}
162	}
163
164	periph->auto_div = false;
165	periph->div = shift;
166}
167
168static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
169{
170	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
171	unsigned long flags;
172
173	if (periph->id < PERIPHERAL_ID_MIN)
174		return 0;
175
176	spin_lock_irqsave(periph->lock, flags);
177	regmap_write(periph->regmap, AT91_PMC_PCR,
178		     (periph->id & AT91_PMC_PCR_PID_MASK));
179	regmap_update_bits(periph->regmap, AT91_PMC_PCR,
180			   AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
181			   AT91_PMC_PCR_EN,
182			   AT91_PMC_PCR_DIV(periph->div) |
183			   AT91_PMC_PCR_CMD |
184			   AT91_PMC_PCR_EN);
185	spin_unlock_irqrestore(periph->lock, flags);
186
187	return 0;
188}
189
190static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
191{
192	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
193	unsigned long flags;
194
195	if (periph->id < PERIPHERAL_ID_MIN)
196		return;
197
198	spin_lock_irqsave(periph->lock, flags);
199	regmap_write(periph->regmap, AT91_PMC_PCR,
200		     (periph->id & AT91_PMC_PCR_PID_MASK));
201	regmap_update_bits(periph->regmap, AT91_PMC_PCR,
202			   AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
203			   AT91_PMC_PCR_CMD);
204	spin_unlock_irqrestore(periph->lock, flags);
205}
206
207static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
208{
209	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
210	unsigned long flags;
211	unsigned int status;
212
213	if (periph->id < PERIPHERAL_ID_MIN)
214		return 1;
215
216	spin_lock_irqsave(periph->lock, flags);
217	regmap_write(periph->regmap, AT91_PMC_PCR,
218		     (periph->id & AT91_PMC_PCR_PID_MASK));
219	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
220	spin_unlock_irqrestore(periph->lock, flags);
221
222	return status & AT91_PMC_PCR_EN ? 1 : 0;
223}
224
225static unsigned long
226clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
227				  unsigned long parent_rate)
228{
229	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
230	unsigned long flags;
231	unsigned int status;
232
233	if (periph->id < PERIPHERAL_ID_MIN)
234		return parent_rate;
235
236	spin_lock_irqsave(periph->lock, flags);
237	regmap_write(periph->regmap, AT91_PMC_PCR,
238		     (periph->id & AT91_PMC_PCR_PID_MASK));
239	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
240	spin_unlock_irqrestore(periph->lock, flags);
241
242	if (status & AT91_PMC_PCR_EN) {
243		periph->div = PERIPHERAL_RSHIFT(status);
244		periph->auto_div = false;
245	} else {
246		clk_sam9x5_peripheral_autodiv(periph);
247	}
248
249	return parent_rate >> periph->div;
250}
251
252static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
253					     unsigned long rate,
254					     unsigned long *parent_rate)
255{
256	int shift = 0;
257	unsigned long best_rate;
258	unsigned long best_diff;
259	unsigned long cur_rate = *parent_rate;
260	unsigned long cur_diff;
261	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
262
263	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
264		return *parent_rate;
265
266	if (periph->range.max) {
267		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
268			cur_rate = *parent_rate >> shift;
269			if (cur_rate <= periph->range.max)
270				break;
271		}
272	}
273
274	if (rate >= cur_rate)
275		return cur_rate;
276
277	best_diff = cur_rate - rate;
278	best_rate = cur_rate;
279	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
280		cur_rate = *parent_rate >> shift;
281		if (cur_rate < rate)
282			cur_diff = rate - cur_rate;
283		else
284			cur_diff = cur_rate - rate;
285
286		if (cur_diff < best_diff) {
287			best_diff = cur_diff;
288			best_rate = cur_rate;
289		}
290
291		if (!best_diff || cur_rate < rate)
292			break;
293	}
294
295	return best_rate;
296}
297
298static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
299					  unsigned long rate,
300					  unsigned long parent_rate)
301{
302	int shift;
303	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
304	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
305		if (parent_rate == rate)
306			return 0;
307		else
308			return -EINVAL;
309	}
310
311	if (periph->range.max && rate > periph->range.max)
312		return -EINVAL;
313
314	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
315		if (parent_rate >> shift == rate) {
316			periph->auto_div = false;
317			periph->div = shift;
318			return 0;
319		}
320	}
321
322	return -EINVAL;
323}
324
325static const struct clk_ops sam9x5_peripheral_ops = {
326	.enable = clk_sam9x5_peripheral_enable,
327	.disable = clk_sam9x5_peripheral_disable,
328	.is_enabled = clk_sam9x5_peripheral_is_enabled,
329	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
330	.round_rate = clk_sam9x5_peripheral_round_rate,
331	.set_rate = clk_sam9x5_peripheral_set_rate,
332};
333
334static struct clk_hw * __init
335at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
336				    const char *name, const char *parent_name,
337				    u32 id, const struct clk_range *range)
338{
339	struct clk_sam9x5_peripheral *periph;
340	struct clk_init_data init;
341	struct clk_hw *hw;
342	int ret;
343
344	if (!name || !parent_name)
345		return ERR_PTR(-EINVAL);
346
347	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
348	if (!periph)
349		return ERR_PTR(-ENOMEM);
350
351	init.name = name;
352	init.ops = &sam9x5_peripheral_ops;
353	init.parent_names = (parent_name ? &parent_name : NULL);
354	init.num_parents = (parent_name ? 1 : 0);
355	init.flags = 0;
356
357	periph->id = id;
358	periph->hw.init = &init;
359	periph->div = 0;
360	periph->regmap = regmap;
361	periph->lock = lock;
362	periph->auto_div = true;
363	periph->range = *range;
364
365	hw = &periph->hw;
366	ret = clk_hw_register(NULL, &periph->hw);
367	if (ret) {
368		kfree(periph);
369		hw = ERR_PTR(ret);
370	} else
371		clk_sam9x5_peripheral_autodiv(periph);
372
373	return hw;
374}
375
376static void __init
377of_at91_clk_periph_setup(struct device_node *np, u8 type)
378{
379	int num;
380	u32 id;
381	struct clk_hw *hw;
382	const char *parent_name;
383	const char *name;
384	struct device_node *periphclknp;
385	struct regmap *regmap;
386
387	parent_name = of_clk_get_parent_name(np, 0);
388	if (!parent_name)
389		return;
390
391	num = of_get_child_count(np);
392	if (!num || num > PERIPHERAL_MAX)
393		return;
394
395	regmap = syscon_node_to_regmap(of_get_parent(np));
396	if (IS_ERR(regmap))
397		return;
398
399	for_each_child_of_node(np, periphclknp) {
400		if (of_property_read_u32(periphclknp, "reg", &id))
401			continue;
402
403		if (id >= PERIPHERAL_MAX)
404			continue;
405
406		if (of_property_read_string(np, "clock-output-names", &name))
407			name = periphclknp->name;
408
409		if (type == PERIPHERAL_AT91RM9200) {
410			hw = at91_clk_register_peripheral(regmap, name,
411							   parent_name, id);
412		} else {
413			struct clk_range range = CLK_RANGE(0, 0);
414
415			of_at91_get_clk_range(periphclknp,
416					      "atmel,clk-output-range",
417					      &range);
418
419			hw = at91_clk_register_sam9x5_peripheral(regmap,
420								  &pmc_pcr_lock,
421								  name,
422								  parent_name,
423								  id, &range);
424		}
425
426		if (IS_ERR(hw))
427			continue;
428
429		of_clk_add_hw_provider(periphclknp, of_clk_hw_simple_get, hw);
430	}
431}
432
433static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
434{
435	of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
436}
437CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
438	       of_at91rm9200_clk_periph_setup);
439
440static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
441{
442	of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
443}
444CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
445	       of_at91sam9x5_clk_periph_setup);
446