blob: e6cc2336f2d3dcfc8c993a048928678accdd897e [file] [log] [blame]
Brian Swetland600f7cf2008-09-09 11:04:14 -07001/* arch/arm/mach-msm/clock.c
2 *
3 * Copyright (C) 2007 Google, Inc.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
Brian Swetland600f7cf2008-09-09 11:04:14 -07005 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
Brian Swetland600f7cf2008-09-09 11:04:14 -070017#include <linux/kernel.h>
Brian Swetland600f7cf2008-09-09 11:04:14 -070018#include <linux/err.h>
Brian Swetland600f7cf2008-09-09 11:04:14 -070019#include <linux/spinlock.h>
Stephen Boydbd323442011-02-23 09:37:42 -080020#include <linux/string.h>
21#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <linux/clk.h>
Stephen Boydbd323442011-02-23 09:37:42 -080023#include <linux/clkdev.h>
Brian Swetland600f7cf2008-09-09 11:04:14 -070024
25#include "clock.h"
Brian Swetland600f7cf2008-09-09 11:04:14 -070026
Brian Swetland600f7cf2008-09-09 11:04:14 -070027/*
Brian Swetland600f7cf2008-09-09 11:04:14 -070028 * Standard clock functions defined in include/linux/clk.h
29 */
Brian Swetland600f7cf2008-09-09 11:04:14 -070030int clk_enable(struct clk *clk)
31{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032 int ret = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -070033 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034 struct clk *parent;
35
36 if (!clk)
37 return 0;
38
39 spin_lock_irqsave(&clk->lock, flags);
40 if (clk->count == 0) {
41 parent = clk_get_parent(clk);
42 ret = clk_enable(parent);
43 if (ret)
44 goto out;
Stephen Boyd7fa26742011-08-11 23:22:29 -070045 ret = clk_enable(clk->depends);
46 if (ret) {
47 clk_disable(parent);
48 goto out;
49 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
51 if (clk->ops->enable)
52 ret = clk->ops->enable(clk);
53 if (ret) {
Stephen Boyd7fa26742011-08-11 23:22:29 -070054 clk_disable(clk->depends);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055 clk_disable(parent);
56 goto out;
57 }
Matt Wagantall14dc2af2011-08-12 13:16:06 -070058 } else if (clk->flags & CLKFLAG_HANDOFF_RATE) {
59 /*
60 * The clock was already enabled by handoff code so there is no
61 * need to enable it again here. Clearing the handoff flag will
62 * prevent the lateinit handoff code from disabling the clock if
63 * a client driver still has it enabled.
64 */
65 clk->flags &= ~CLKFLAG_HANDOFF_RATE;
66 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067 }
Brian Swetland600f7cf2008-09-09 11:04:14 -070068 clk->count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069out:
70 spin_unlock_irqrestore(&clk->lock, flags);
71
72 return ret;
Brian Swetland600f7cf2008-09-09 11:04:14 -070073}
74EXPORT_SYMBOL(clk_enable);
75
76void clk_disable(struct clk *clk)
77{
78 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079 struct clk *parent;
80
81 if (!clk)
82 return;
83
84 spin_lock_irqsave(&clk->lock, flags);
Stephen Boydd906b522011-07-26 10:51:41 -070085 if (WARN(clk->count == 0, "%s is unbalanced", clk->dbg_name))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086 goto out;
87 if (clk->count == 1) {
88 if (clk->ops->disable)
89 clk->ops->disable(clk);
Stephen Boyd7fa26742011-08-11 23:22:29 -070090 clk_disable(clk->depends);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 parent = clk_get_parent(clk);
92 clk_disable(parent);
93 }
Brian Swetland600f7cf2008-09-09 11:04:14 -070094 clk->count--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095out:
96 spin_unlock_irqrestore(&clk->lock, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -070097}
98EXPORT_SYMBOL(clk_disable);
99
Daniel Walker5e96da52010-05-12 13:43:28 -0700100int clk_reset(struct clk *clk, enum clk_reset_action action)
101{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102 if (!clk->ops->reset)
103 return -ENOSYS;
104
105 return clk->ops->reset(clk, action);
Daniel Walker5e96da52010-05-12 13:43:28 -0700106}
107EXPORT_SYMBOL(clk_reset);
108
Brian Swetland600f7cf2008-09-09 11:04:14 -0700109unsigned long clk_get_rate(struct clk *clk)
110{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 if (!clk->ops->get_rate)
112 return 0;
113
114 return clk->ops->get_rate(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700115}
116EXPORT_SYMBOL(clk_get_rate);
117
118int clk_set_rate(struct clk *clk, unsigned long rate)
119{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120 if (!clk->ops->set_rate)
121 return -ENOSYS;
Daniel Walker3a790bb2010-12-13 14:35:10 -0800122
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123 return clk->ops->set_rate(clk, rate);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700124}
125EXPORT_SYMBOL(clk_set_rate);
126
Daniel Walker5e96da52010-05-12 13:43:28 -0700127long clk_round_rate(struct clk *clk, unsigned long rate)
128{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129 if (!clk->ops->round_rate)
130 return -ENOSYS;
131
132 return clk->ops->round_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700133}
134EXPORT_SYMBOL(clk_round_rate);
135
136int clk_set_min_rate(struct clk *clk, unsigned long rate)
137{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138 if (!clk->ops->set_min_rate)
139 return -ENOSYS;
140
141 return clk->ops->set_min_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700142}
143EXPORT_SYMBOL(clk_set_min_rate);
144
145int clk_set_max_rate(struct clk *clk, unsigned long rate)
146{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 if (!clk->ops->set_max_rate)
148 return -ENOSYS;
149
150 return clk->ops->set_max_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700151}
152EXPORT_SYMBOL(clk_set_max_rate);
153
Brian Swetland600f7cf2008-09-09 11:04:14 -0700154int clk_set_parent(struct clk *clk, struct clk *parent)
155{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 if (!clk->ops->set_parent)
157 return 0;
158
159 return clk->ops->set_parent(clk, parent);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700160}
161EXPORT_SYMBOL(clk_set_parent);
162
163struct clk *clk_get_parent(struct clk *clk)
164{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 if (!clk->ops->get_parent)
166 return NULL;
167
168 return clk->ops->get_parent(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700169}
170EXPORT_SYMBOL(clk_get_parent);
171
172int clk_set_flags(struct clk *clk, unsigned long flags)
173{
174 if (clk == NULL || IS_ERR(clk))
175 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176 if (!clk->ops->set_flags)
177 return -ENOSYS;
178
179 return clk->ops->set_flags(clk, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700180}
181EXPORT_SYMBOL(clk_set_flags);
182
Stephen Boydbb600ae2011-08-02 20:11:40 -0700183static struct clock_init_data __initdata *clk_init_data;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700184
Stephen Boydbb600ae2011-08-02 20:11:40 -0700185void __init msm_clock_init(struct clock_init_data *data)
Brian Swetland600f7cf2008-09-09 11:04:14 -0700186{
187 unsigned n;
Stephen Boyd94625ef2011-07-12 17:06:01 -0700188 struct clk_lookup *clock_tbl;
189 size_t num_clocks;
Stephen Boydbb600ae2011-08-02 20:11:40 -0700190
191 clk_init_data = data;
192 if (clk_init_data->init)
193 clk_init_data->init();
Brian Swetland600f7cf2008-09-09 11:04:14 -0700194
Stephen Boyd94625ef2011-07-12 17:06:01 -0700195 clock_tbl = data->table;
196 num_clocks = data->size;
197
Stephen Boydbd323442011-02-23 09:37:42 -0800198 for (n = 0; n < num_clocks; n++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 struct clk *clk = clock_tbl[n].clk;
200 struct clk *parent = clk_get_parent(clk);
201 clk_set_parent(clk, parent);
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700202 if (clk->ops->handoff)
203 clk->ops->handoff(clk);
Stephen Boydbd323442011-02-23 09:37:42 -0800204 }
Daniel Walker5e96da52010-05-12 13:43:28 -0700205
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 clkdev_add_table(clock_tbl, num_clocks);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700207}
208
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209/*
210 * The bootloader and/or AMSS may have left various clocks enabled.
211 * Disable any clocks that have not been explicitly enabled by a
212 * clk_enable() call and don't have the CLKFLAG_SKIP_AUTO_OFF flag.
Brian Swetland600f7cf2008-09-09 11:04:14 -0700213 */
214static int __init clock_late_init(void)
215{
Stephen Boydbb600ae2011-08-02 20:11:40 -0700216 unsigned n, count = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700217 unsigned long flags;
Stephen Boydbb600ae2011-08-02 20:11:40 -0700218 int ret = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700219
Stephen Boydbb600ae2011-08-02 20:11:40 -0700220 clock_debug_init(clk_init_data);
221 for (n = 0; n < clk_init_data->size; n++) {
222 struct clk *clk = clk_init_data->table[n].clk;
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700223 bool handoff = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224
Matt Wagantalld64560fe2011-01-26 16:20:54 -0800225 clock_debug_add(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 if (!(clk->flags & CLKFLAG_SKIP_AUTO_OFF)) {
227 spin_lock_irqsave(&clk->lock, flags);
228 if (!clk->count && clk->ops->auto_off) {
Brian Swetland600f7cf2008-09-09 11:04:14 -0700229 count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 clk->ops->auto_off(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700231 }
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700232 if (clk->flags & CLKFLAG_HANDOFF_RATE) {
233 clk->flags &= ~CLKFLAG_HANDOFF_RATE;
234 handoff = true;
235 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236 spin_unlock_irqrestore(&clk->lock, flags);
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700237 /*
238 * Calling clk_disable() outside the lock is safe since
239 * it doesn't need to be atomic with the flag change.
240 */
241 if (handoff)
242 clk_disable(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700243 }
244 }
Brian Swetland600f7cf2008-09-09 11:04:14 -0700245 pr_info("clock_late_init() disabled %d unused clocks\n", count);
Stephen Boydbb600ae2011-08-02 20:11:40 -0700246 if (clk_init_data->late_init)
247 ret = clk_init_data->late_init();
248 return ret;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700249}
Brian Swetland600f7cf2008-09-09 11:04:14 -0700250late_initcall(clock_late_init);