[go: nahoru, domu]

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