Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * drivers/clk/at91/sckc.c
  3 *
  4 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License as published by
  8 * the Free Software Foundation; either version 2 of the License, or
  9 * (at your option) any later version.
 10 *
 11 */
 12
 13#include <linux/clk-provider.h>
 14#include <linux/clkdev.h>
 15#include <linux/delay.h>
 16#include <linux/of.h>
 17#include <linux/of_address.h>
 18#include <linux/io.h>
 19
 20#define SLOW_CLOCK_FREQ		32768
 21#define SLOWCK_SW_CYCLES	5
 22#define SLOWCK_SW_TIME_USEC	((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
 23				 SLOW_CLOCK_FREQ)
 24
 25#define	AT91_SCKC_CR			0x00
 26#define		AT91_SCKC_RCEN		(1 << 0)
 27#define		AT91_SCKC_OSC32EN	(1 << 1)
 28#define		AT91_SCKC_OSC32BYP	(1 << 2)
 29#define		AT91_SCKC_OSCSEL	(1 << 3)
 30
 31struct clk_slow_osc {
 32	struct clk_hw hw;
 33	void __iomem *sckcr;
 34	unsigned long startup_usec;
 35};
 36
 37#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
 38
 39struct clk_sama5d4_slow_osc {
 40	struct clk_hw hw;
 41	void __iomem *sckcr;
 42	unsigned long startup_usec;
 43	bool prepared;
 44};
 45
 46#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
 47
 48struct clk_slow_rc_osc {
 49	struct clk_hw hw;
 50	void __iomem *sckcr;
 51	unsigned long frequency;
 52	unsigned long accuracy;
 53	unsigned long startup_usec;
 54};
 55
 56#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
 57
 58struct clk_sam9x5_slow {
 59	struct clk_hw hw;
 60	void __iomem *sckcr;
 61	u8 parent;
 62};
 63
 64#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
 65
 66static int clk_slow_osc_prepare(struct clk_hw *hw)
 67{
 68	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
 69	void __iomem *sckcr = osc->sckcr;
 70	u32 tmp = readl(sckcr);
 71
 72	if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
 73		return 0;
 74
 75	writel(tmp | AT91_SCKC_OSC32EN, sckcr);
 76
 77	usleep_range(osc->startup_usec, osc->startup_usec + 1);
 78
 79	return 0;
 80}
 81
 82static void clk_slow_osc_unprepare(struct clk_hw *hw)
 83{
 84	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
 85	void __iomem *sckcr = osc->sckcr;
 86	u32 tmp = readl(sckcr);
 87
 88	if (tmp & AT91_SCKC_OSC32BYP)
 89		return;
 90
 91	writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
 92}
 93
 94static int clk_slow_osc_is_prepared(struct clk_hw *hw)
 95{
 96	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
 97	void __iomem *sckcr = osc->sckcr;
 98	u32 tmp = readl(sckcr);
 99
100	if (tmp & AT91_SCKC_OSC32BYP)
101		return 1;
102
103	return !!(tmp & AT91_SCKC_OSC32EN);
104}
105
106static const struct clk_ops slow_osc_ops = {
107	.prepare = clk_slow_osc_prepare,
108	.unprepare = clk_slow_osc_unprepare,
109	.is_prepared = clk_slow_osc_is_prepared,
110};
111
112static struct clk_hw * __init
113at91_clk_register_slow_osc(void __iomem *sckcr,
114			   const char *name,
115			   const char *parent_name,
116			   unsigned long startup,
117			   bool bypass)
118{
119	struct clk_slow_osc *osc;
120	struct clk_hw *hw;
121	struct clk_init_data init;
122	int ret;
123
124	if (!sckcr || !name || !parent_name)
125		return ERR_PTR(-EINVAL);
126
127	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
128	if (!osc)
129		return ERR_PTR(-ENOMEM);
130
131	init.name = name;
132	init.ops = &slow_osc_ops;
133	init.parent_names = &parent_name;
134	init.num_parents = 1;
135	init.flags = CLK_IGNORE_UNUSED;
136
137	osc->hw.init = &init;
138	osc->sckcr = sckcr;
139	osc->startup_usec = startup;
140
141	if (bypass)
142		writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
143		       sckcr);
144
145	hw = &osc->hw;
146	ret = clk_hw_register(NULL, &osc->hw);
147	if (ret) {
148		kfree(osc);
149		hw = ERR_PTR(ret);
150	}
151
152	return hw;
153}
154
155static void __init
156of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
157{
158	struct clk_hw *hw;
159	const char *parent_name;
160	const char *name = np->name;
161	u32 startup;
162	bool bypass;
163
164	parent_name = of_clk_get_parent_name(np, 0);
165	of_property_read_string(np, "clock-output-names", &name);
166	of_property_read_u32(np, "atmel,startup-time-usec", &startup);
167	bypass = of_property_read_bool(np, "atmel,osc-bypass");
168
169	hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
170					 bypass);
171	if (IS_ERR(hw))
172		return;
173
174	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
175}
176
177static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
178						 unsigned long parent_rate)
179{
180	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
181
182	return osc->frequency;
183}
184
185static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
186						     unsigned long parent_acc)
187{
188	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
189
190	return osc->accuracy;
191}
192
193static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
194{
195	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
196	void __iomem *sckcr = osc->sckcr;
197
198	writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
199
200	usleep_range(osc->startup_usec, osc->startup_usec + 1);
201
202	return 0;
203}
204
205static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
206{
207	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
208	void __iomem *sckcr = osc->sckcr;
209
210	writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
211}
212
213static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
214{
215	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
216
217	return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
218}
219
220static const struct clk_ops slow_rc_osc_ops = {
221	.prepare = clk_slow_rc_osc_prepare,
222	.unprepare = clk_slow_rc_osc_unprepare,
223	.is_prepared = clk_slow_rc_osc_is_prepared,
224	.recalc_rate = clk_slow_rc_osc_recalc_rate,
225	.recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
226};
227
228static struct clk_hw * __init
229at91_clk_register_slow_rc_osc(void __iomem *sckcr,
230			      const char *name,
231			      unsigned long frequency,
232			      unsigned long accuracy,
233			      unsigned long startup)
234{
235	struct clk_slow_rc_osc *osc;
236	struct clk_hw *hw;
237	struct clk_init_data init;
238	int ret;
239
240	if (!sckcr || !name)
241		return ERR_PTR(-EINVAL);
242
243	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
244	if (!osc)
245		return ERR_PTR(-ENOMEM);
246
247	init.name = name;
248	init.ops = &slow_rc_osc_ops;
249	init.parent_names = NULL;
250	init.num_parents = 0;
251	init.flags = CLK_IGNORE_UNUSED;
252
253	osc->hw.init = &init;
254	osc->sckcr = sckcr;
255	osc->frequency = frequency;
256	osc->accuracy = accuracy;
257	osc->startup_usec = startup;
258
259	hw = &osc->hw;
260	ret = clk_hw_register(NULL, &osc->hw);
261	if (ret) {
262		kfree(osc);
263		hw = ERR_PTR(ret);
264	}
265
266	return hw;
267}
268
269static void __init
270of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
271{
272	struct clk_hw *hw;
273	u32 frequency = 0;
274	u32 accuracy = 0;
275	u32 startup = 0;
276	const char *name = np->name;
277
278	of_property_read_string(np, "clock-output-names", &name);
279	of_property_read_u32(np, "clock-frequency", &frequency);
280	of_property_read_u32(np, "clock-accuracy", &accuracy);
281	of_property_read_u32(np, "atmel,startup-time-usec", &startup);
282
283	hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
284					    startup);
285	if (IS_ERR(hw))
286		return;
287
288	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
289}
290
291static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
292{
293	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
294	void __iomem *sckcr = slowck->sckcr;
295	u32 tmp;
296
297	if (index > 1)
298		return -EINVAL;
299
300	tmp = readl(sckcr);
301
302	if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
303	    (index && (tmp & AT91_SCKC_OSCSEL)))
304		return 0;
305
306	if (index)
307		tmp |= AT91_SCKC_OSCSEL;
308	else
309		tmp &= ~AT91_SCKC_OSCSEL;
310
311	writel(tmp, sckcr);
312
313	usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
314
315	return 0;
316}
317
318static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
319{
320	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
321
322	return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
323}
324
325static const struct clk_ops sam9x5_slow_ops = {
326	.set_parent = clk_sam9x5_slow_set_parent,
327	.get_parent = clk_sam9x5_slow_get_parent,
328};
329
330static struct clk_hw * __init
331at91_clk_register_sam9x5_slow(void __iomem *sckcr,
332			      const char *name,
333			      const char **parent_names,
334			      int num_parents)
335{
336	struct clk_sam9x5_slow *slowck;
337	struct clk_hw *hw;
338	struct clk_init_data init;
339	int ret;
340
341	if (!sckcr || !name || !parent_names || !num_parents)
342		return ERR_PTR(-EINVAL);
343
344	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
345	if (!slowck)
346		return ERR_PTR(-ENOMEM);
347
348	init.name = name;
349	init.ops = &sam9x5_slow_ops;
350	init.parent_names = parent_names;
351	init.num_parents = num_parents;
352	init.flags = 0;
353
354	slowck->hw.init = &init;
355	slowck->sckcr = sckcr;
356	slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
357
358	hw = &slowck->hw;
359	ret = clk_hw_register(NULL, &slowck->hw);
360	if (ret) {
361		kfree(slowck);
362		hw = ERR_PTR(ret);
363	}
364
365	return hw;
366}
367
368static void __init
369of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
370{
371	struct clk_hw *hw;
372	const char *parent_names[2];
373	unsigned int num_parents;
374	const char *name = np->name;
375
376	num_parents = of_clk_get_parent_count(np);
377	if (num_parents == 0 || num_parents > 2)
378		return;
379
380	of_clk_parent_fill(np, parent_names, num_parents);
381
382	of_property_read_string(np, "clock-output-names", &name);
383
384	hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
385					    num_parents);
386	if (IS_ERR(hw))
387		return;
388
389	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
390}
391
392static const struct of_device_id sckc_clk_ids[] __initconst = {
393	/* Slow clock */
394	{
395		.compatible = "atmel,at91sam9x5-clk-slow-osc",
396		.data = of_at91sam9x5_clk_slow_osc_setup,
397	},
398	{
399		.compatible = "atmel,at91sam9x5-clk-slow-rc-osc",
400		.data = of_at91sam9x5_clk_slow_rc_osc_setup,
401	},
402	{
403		.compatible = "atmel,at91sam9x5-clk-slow",
404		.data = of_at91sam9x5_clk_slow_setup,
405	},
406	{ /*sentinel*/ }
407};
408
409static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
410{
411	struct device_node *childnp;
412	void (*clk_setup)(struct device_node *, void __iomem *);
413	const struct of_device_id *clk_id;
414	void __iomem *regbase = of_iomap(np, 0);
415
416	if (!regbase)
417		return;
418
419	for_each_child_of_node(np, childnp) {
420		clk_id = of_match_node(sckc_clk_ids, childnp);
421		if (!clk_id)
422			continue;
423		clk_setup = clk_id->data;
424		clk_setup(childnp, regbase);
425	}
426}
427CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
428	       of_at91sam9x5_sckc_setup);
429
430static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
431{
432	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
433
434	if (osc->prepared)
435		return 0;
436
437	/*
438	 * Assume that if it has already been selected (for example by the
439	 * bootloader), enough time has aready passed.
440	 */
441	if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
442		osc->prepared = true;
443		return 0;
444	}
445
446	usleep_range(osc->startup_usec, osc->startup_usec + 1);
447	osc->prepared = true;
448
449	return 0;
450}
451
452static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
453{
454	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
455
456	return osc->prepared;
457}
458
459static const struct clk_ops sama5d4_slow_osc_ops = {
460	.prepare = clk_sama5d4_slow_osc_prepare,
461	.is_prepared = clk_sama5d4_slow_osc_is_prepared,
462};
463
464static void __init of_sama5d4_sckc_setup(struct device_node *np)
465{
466	void __iomem *regbase = of_iomap(np, 0);
467	struct clk_hw *hw;
468	struct clk_sama5d4_slow_osc *osc;
469	struct clk_init_data init;
470	const char *xtal_name;
471	const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
472	bool bypass;
473	int ret;
474
475	if (!regbase)
476		return;
477
478	hw = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
479						      NULL, 0, 32768,
480						      250000000);
481	if (IS_ERR(hw))
482		return;
483
484	xtal_name = of_clk_get_parent_name(np, 0);
485
486	bypass = of_property_read_bool(np, "atmel,osc-bypass");
487
488	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
489	if (!osc)
490		return;
491
492	init.name = parent_names[1];
493	init.ops = &sama5d4_slow_osc_ops;
494	init.parent_names = &xtal_name;
495	init.num_parents = 1;
496	init.flags = CLK_IGNORE_UNUSED;
497
498	osc->hw.init = &init;
499	osc->sckcr = regbase;
500	osc->startup_usec = 1200000;
501
502	if (bypass)
503		writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
504
505	hw = &osc->hw;
506	ret = clk_hw_register(NULL, &osc->hw);
507	if (ret) {
508		kfree(osc);
509		return;
510	}
511
512	hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
513	if (IS_ERR(hw))
514		return;
515
516	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
517}
518CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
519	       of_sama5d4_sckc_setup);