Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/dlm
[pandora-kernel.git] / arch / arm / mach-bcmring / clock.c
1 /*****************************************************************************
2 * Copyright 2001 - 2009 Broadcom Corporation.  All rights reserved.
3 *
4 * Unless you and Broadcom execute a separate written software license
5 * agreement governing use of this software, this software is licensed to you
6 * under the terms of the GNU General Public License version 2, available at
7 * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8 *
9 * Notwithstanding the above, under no circumstances may you combine this
10 * software in any way with any other Broadcom software provided under a
11 * license other than the GPL, without Broadcom's express prior written
12 * consent.
13 *****************************************************************************/
14
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/device.h>
18 #include <linux/list.h>
19 #include <linux/errno.h>
20 #include <linux/err.h>
21 #include <linux/string.h>
22 #include <linux/clk.h>
23 #include <linux/spinlock.h>
24 #include <mach/csp/hw_cfg.h>
25 #include <mach/csp/chipcHw_def.h>
26 #include <mach/csp/chipcHw_reg.h>
27 #include <mach/csp/chipcHw_inline.h>
28
29 #include <asm/clkdev.h>
30
31 #include "clock.h"
32
33 #define clk_is_primary(x)       ((x)->type & CLK_TYPE_PRIMARY)
34 #define clk_is_pll1(x)          ((x)->type & CLK_TYPE_PLL1)
35 #define clk_is_pll2(x)          ((x)->type & CLK_TYPE_PLL2)
36 #define clk_is_programmable(x)  ((x)->type & CLK_TYPE_PROGRAMMABLE)
37 #define clk_is_bypassable(x)    ((x)->type & CLK_TYPE_BYPASSABLE)
38
39 #define clk_is_using_xtal(x)    ((x)->mode & CLK_MODE_XTAL)
40
41 static DEFINE_SPINLOCK(clk_lock);
42
43 static void __clk_enable(struct clk *clk)
44 {
45         if (!clk)
46                 return;
47
48         /* enable parent clock first */
49         if (clk->parent)
50                 __clk_enable(clk->parent);
51
52         if (clk->use_cnt++ == 0) {
53                 if (clk_is_pll1(clk)) { /* PLL1 */
54                         chipcHw_pll1Enable(clk->rate_hz, 0);
55                 } else if (clk_is_pll2(clk)) {  /* PLL2 */
56                         chipcHw_pll2Enable(clk->rate_hz);
57                 } else if (clk_is_using_xtal(clk)) {    /* source is crystal */
58                         if (!clk_is_primary(clk))
59                                 chipcHw_bypassClockEnable(clk->csp_id);
60                 } else {        /* source is PLL */
61                         chipcHw_setClockEnable(clk->csp_id);
62                 }
63         }
64 }
65
66 int clk_enable(struct clk *clk)
67 {
68         unsigned long flags;
69
70         if (!clk)
71                 return -EINVAL;
72
73         spin_lock_irqsave(&clk_lock, flags);
74         __clk_enable(clk);
75         spin_unlock_irqrestore(&clk_lock, flags);
76
77         return 0;
78 }
79 EXPORT_SYMBOL(clk_enable);
80
81 static void __clk_disable(struct clk *clk)
82 {
83         if (!clk)
84                 return;
85
86         BUG_ON(clk->use_cnt == 0);
87
88         if (--clk->use_cnt == 0) {
89                 if (clk_is_pll1(clk)) { /* PLL1 */
90                         chipcHw_pll1Disable();
91                 } else if (clk_is_pll2(clk)) {  /* PLL2 */
92                         chipcHw_pll2Disable();
93                 } else if (clk_is_using_xtal(clk)) {    /* source is crystal */
94                         if (!clk_is_primary(clk))
95                                 chipcHw_bypassClockDisable(clk->csp_id);
96                 } else {        /* source is PLL */
97                         chipcHw_setClockDisable(clk->csp_id);
98                 }
99         }
100
101         if (clk->parent)
102                 __clk_disable(clk->parent);
103 }
104
105 void clk_disable(struct clk *clk)
106 {
107         unsigned long flags;
108
109         if (!clk)
110                 return;
111
112         spin_lock_irqsave(&clk_lock, flags);
113         __clk_disable(clk);
114         spin_unlock_irqrestore(&clk_lock, flags);
115 }
116 EXPORT_SYMBOL(clk_disable);
117
118 unsigned long clk_get_rate(struct clk *clk)
119 {
120         if (!clk)
121                 return 0;
122
123         return clk->rate_hz;
124 }
125 EXPORT_SYMBOL(clk_get_rate);
126
127 long clk_round_rate(struct clk *clk, unsigned long rate)
128 {
129         unsigned long flags;
130         unsigned long actual;
131         unsigned long rate_hz;
132
133         if (!clk)
134                 return -EINVAL;
135
136         if (!clk_is_programmable(clk))
137                 return -EINVAL;
138
139         if (clk->use_cnt)
140                 return -EBUSY;
141
142         spin_lock_irqsave(&clk_lock, flags);
143         actual = clk->parent->rate_hz;
144         rate_hz = min(actual, rate);
145         spin_unlock_irqrestore(&clk_lock, flags);
146
147         return rate_hz;
148 }
149 EXPORT_SYMBOL(clk_round_rate);
150
151 int clk_set_rate(struct clk *clk, unsigned long rate)
152 {
153         unsigned long flags;
154         unsigned long actual;
155         unsigned long rate_hz;
156
157         if (!clk)
158                 return -EINVAL;
159
160         if (!clk_is_programmable(clk))
161                 return -EINVAL;
162
163         if (clk->use_cnt)
164                 return -EBUSY;
165
166         spin_lock_irqsave(&clk_lock, flags);
167         actual = clk->parent->rate_hz;
168         rate_hz = min(actual, rate);
169         rate_hz = chipcHw_setClockFrequency(clk->csp_id, rate_hz);
170         clk->rate_hz = rate_hz;
171         spin_unlock_irqrestore(&clk_lock, flags);
172
173         return 0;
174 }
175 EXPORT_SYMBOL(clk_set_rate);
176
177 struct clk *clk_get_parent(struct clk *clk)
178 {
179         if (!clk)
180                 return NULL;
181
182         return clk->parent;
183 }
184 EXPORT_SYMBOL(clk_get_parent);
185
186 int clk_set_parent(struct clk *clk, struct clk *parent)
187 {
188         unsigned long flags;
189         struct clk *old_parent;
190
191         if (!clk || !parent)
192                 return -EINVAL;
193
194         if (!clk_is_primary(parent) || !clk_is_bypassable(clk))
195                 return -EINVAL;
196
197         /* if more than one user, parent is not allowed */
198         if (clk->use_cnt > 1)
199                 return -EBUSY;
200
201         if (clk->parent == parent)
202                 return 0;
203
204         spin_lock_irqsave(&clk_lock, flags);
205         old_parent = clk->parent;
206         clk->parent = parent;
207         if (clk_is_using_xtal(parent))
208                 clk->mode |= CLK_MODE_XTAL;
209         else
210                 clk->mode &= (~CLK_MODE_XTAL);
211
212         /* if clock is active */
213         if (clk->use_cnt != 0) {
214                 clk->use_cnt--;
215                 /* enable clock with the new parent */
216                 __clk_enable(clk);
217                 /* disable the old parent */
218                 __clk_disable(old_parent);
219         }
220         spin_unlock_irqrestore(&clk_lock, flags);
221
222         return 0;
223 }
224 EXPORT_SYMBOL(clk_set_parent);