1/*
2 * Clock management for AT32AP CPUs
3 *
4 * Copyright (C) 2006 Atmel Corporation
5 *
6 * Based on arch/arm/mach-at91/clock.c
7 *   Copyright (C) 2005 David Brownell
8 *   Copyright (C) 2005 Ivan Kokshaysky
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14#include <linux/clk.h>
15#include <linux/err.h>
16#include <linux/device.h>
17#include <linux/string.h>
18
19#include "clock.h"
20
21static DEFINE_SPINLOCK(clk_lock);
22
23struct clk *clk_get(struct device *dev, const char *id)
24{
25	int i;
26
27	for (i = 0; i < at32_nr_clocks; i++) {
28		struct clk *clk = at32_clock_list[i];
29
30		if (clk->dev == dev && strcmp(id, clk->name) == 0)
31			return clk;
32	}
33
34	return ERR_PTR(-ENOENT);
35}
36EXPORT_SYMBOL(clk_get);
37
38void clk_put(struct clk *clk)
39{
40	/* clocks are static for now, we can't free them */
41}
42EXPORT_SYMBOL(clk_put);
43
44static void __clk_enable(struct clk *clk)
45{
46	if (clk->parent)
47		__clk_enable(clk->parent);
48	if (clk->users++ == 0 && clk->mode)
49		clk->mode(clk, 1);
50}
51
52int clk_enable(struct clk *clk)
53{
54	unsigned long flags;
55
56	spin_lock_irqsave(&clk_lock, flags);
57	__clk_enable(clk);
58	spin_unlock_irqrestore(&clk_lock, flags);
59
60	return 0;
61}
62EXPORT_SYMBOL(clk_enable);
63
64static void __clk_disable(struct clk *clk)
65{
66	if (clk->users == 0) {
67		printk(KERN_ERR "%s: mismatched disable\n", clk->name);
68		WARN_ON(1);
69		return;
70	}
71
72	if (--clk->users == 0 && clk->mode)
73		clk->mode(clk, 0);
74	if (clk->parent)
75		__clk_disable(clk->parent);
76}
77
78void clk_disable(struct clk *clk)
79{
80	unsigned long flags;
81
82	spin_lock_irqsave(&clk_lock, flags);
83	__clk_disable(clk);
84	spin_unlock_irqrestore(&clk_lock, flags);
85}
86EXPORT_SYMBOL(clk_disable);
87
88unsigned long clk_get_rate(struct clk *clk)
89{
90	unsigned long flags;
91	unsigned long rate;
92
93	spin_lock_irqsave(&clk_lock, flags);
94	rate = clk->get_rate(clk);
95	spin_unlock_irqrestore(&clk_lock, flags);
96
97	return rate;
98}
99EXPORT_SYMBOL(clk_get_rate);
100
101long clk_round_rate(struct clk *clk, unsigned long rate)
102{
103	unsigned long flags, actual_rate;
104
105	if (!clk->set_rate)
106		return -ENOSYS;
107
108	spin_lock_irqsave(&clk_lock, flags);
109	actual_rate = clk->set_rate(clk, rate, 0);
110	spin_unlock_irqrestore(&clk_lock, flags);
111
112	return actual_rate;
113}
114EXPORT_SYMBOL(clk_round_rate);
115
116int clk_set_rate(struct clk *clk, unsigned long rate)
117{
118	unsigned long flags;
119	long ret;
120
121	if (!clk->set_rate)
122		return -ENOSYS;
123
124	spin_lock_irqsave(&clk_lock, flags);
125	ret = clk->set_rate(clk, rate, 1);
126	spin_unlock_irqrestore(&clk_lock, flags);
127
128	return (ret < 0) ? ret : 0;
129}
130EXPORT_SYMBOL(clk_set_rate);
131
132int clk_set_parent(struct clk *clk, struct clk *parent)
133{
134	unsigned long flags;
135	int ret;
136
137	if (!clk->set_parent)
138		return -ENOSYS;
139
140	spin_lock_irqsave(&clk_lock, flags);
141	ret = clk->set_parent(clk, parent);
142	spin_unlock_irqrestore(&clk_lock, flags);
143
144	return ret;
145}
146EXPORT_SYMBOL(clk_set_parent);
147
148struct clk *clk_get_parent(struct clk *clk)
149{
150	return clk->parent;
151}
152EXPORT_SYMBOL(clk_get_parent);
153