blob: a29e9cd032dd344883b82a174e6fab8b9e938e52 [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;
45
46 if (clk->ops->enable)
47 ret = clk->ops->enable(clk);
48 if (ret) {
49 clk_disable(parent);
50 goto out;
51 }
Matt Wagantall14dc2af2011-08-12 13:16:06 -070052 } else if (clk->flags & CLKFLAG_HANDOFF_RATE) {
53 /*
54 * The clock was already enabled by handoff code so there is no
55 * need to enable it again here. Clearing the handoff flag will
56 * prevent the lateinit handoff code from disabling the clock if
57 * a client driver still has it enabled.
58 */
59 clk->flags &= ~CLKFLAG_HANDOFF_RATE;
60 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 }
Brian Swetland600f7cf2008-09-09 11:04:14 -070062 clk->count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063out:
64 spin_unlock_irqrestore(&clk->lock, flags);
65
66 return ret;
Brian Swetland600f7cf2008-09-09 11:04:14 -070067}
68EXPORT_SYMBOL(clk_enable);
69
70void clk_disable(struct clk *clk)
71{
72 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073 struct clk *parent;
74
75 if (!clk)
76 return;
77
78 spin_lock_irqsave(&clk->lock, flags);
Stephen Boydd906b522011-07-26 10:51:41 -070079 if (WARN(clk->count == 0, "%s is unbalanced", clk->dbg_name))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080 goto out;
81 if (clk->count == 1) {
82 if (clk->ops->disable)
83 clk->ops->disable(clk);
84 parent = clk_get_parent(clk);
85 clk_disable(parent);
86 }
Brian Swetland600f7cf2008-09-09 11:04:14 -070087 clk->count--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088out:
89 spin_unlock_irqrestore(&clk->lock, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -070090}
91EXPORT_SYMBOL(clk_disable);
92
Daniel Walker5e96da52010-05-12 13:43:28 -070093int clk_reset(struct clk *clk, enum clk_reset_action action)
94{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 if (!clk->ops->reset)
96 return -ENOSYS;
97
98 return clk->ops->reset(clk, action);
Daniel Walker5e96da52010-05-12 13:43:28 -070099}
100EXPORT_SYMBOL(clk_reset);
101
Brian Swetland600f7cf2008-09-09 11:04:14 -0700102unsigned long clk_get_rate(struct clk *clk)
103{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 if (!clk->ops->get_rate)
105 return 0;
106
107 return clk->ops->get_rate(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700108}
109EXPORT_SYMBOL(clk_get_rate);
110
111int clk_set_rate(struct clk *clk, unsigned long rate)
112{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113 if (!clk->ops->set_rate)
114 return -ENOSYS;
Daniel Walker3a790bb2010-12-13 14:35:10 -0800115
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 return clk->ops->set_rate(clk, rate);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700117}
118EXPORT_SYMBOL(clk_set_rate);
119
Daniel Walker5e96da52010-05-12 13:43:28 -0700120long clk_round_rate(struct clk *clk, unsigned long rate)
121{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 if (!clk->ops->round_rate)
123 return -ENOSYS;
124
125 return clk->ops->round_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700126}
127EXPORT_SYMBOL(clk_round_rate);
128
129int clk_set_min_rate(struct clk *clk, unsigned long rate)
130{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 if (!clk->ops->set_min_rate)
132 return -ENOSYS;
133
134 return clk->ops->set_min_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700135}
136EXPORT_SYMBOL(clk_set_min_rate);
137
138int clk_set_max_rate(struct clk *clk, unsigned long rate)
139{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 if (!clk->ops->set_max_rate)
141 return -ENOSYS;
142
143 return clk->ops->set_max_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700144}
145EXPORT_SYMBOL(clk_set_max_rate);
146
Brian Swetland600f7cf2008-09-09 11:04:14 -0700147int clk_set_parent(struct clk *clk, struct clk *parent)
148{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 if (!clk->ops->set_parent)
150 return 0;
151
152 return clk->ops->set_parent(clk, parent);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700153}
154EXPORT_SYMBOL(clk_set_parent);
155
156struct clk *clk_get_parent(struct clk *clk)
157{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158 if (!clk->ops->get_parent)
159 return NULL;
160
161 return clk->ops->get_parent(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700162}
163EXPORT_SYMBOL(clk_get_parent);
164
165int clk_set_flags(struct clk *clk, unsigned long flags)
166{
167 if (clk == NULL || IS_ERR(clk))
168 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 if (!clk->ops->set_flags)
170 return -ENOSYS;
171
172 return clk->ops->set_flags(clk, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700173}
174EXPORT_SYMBOL(clk_set_flags);
175
Stephen Boydbb600ae2011-08-02 20:11:40 -0700176static struct clock_init_data __initdata *clk_init_data;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700177
Stephen Boydbb600ae2011-08-02 20:11:40 -0700178void __init msm_clock_init(struct clock_init_data *data)
Brian Swetland600f7cf2008-09-09 11:04:14 -0700179{
180 unsigned n;
Stephen Boydbb600ae2011-08-02 20:11:40 -0700181 struct clk_lookup *clock_tbl = data->table;
182 size_t num_clocks = data->size;
183
184 clk_init_data = data;
185 if (clk_init_data->init)
186 clk_init_data->init();
Brian Swetland600f7cf2008-09-09 11:04:14 -0700187
Stephen Boydbd323442011-02-23 09:37:42 -0800188 for (n = 0; n < num_clocks; n++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 struct clk *clk = clock_tbl[n].clk;
190 struct clk *parent = clk_get_parent(clk);
191 clk_set_parent(clk, parent);
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700192 if (clk->ops->handoff)
193 clk->ops->handoff(clk);
Stephen Boydbd323442011-02-23 09:37:42 -0800194 }
Daniel Walker5e96da52010-05-12 13:43:28 -0700195
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 clkdev_add_table(clock_tbl, num_clocks);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700197}
198
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199/*
200 * The bootloader and/or AMSS may have left various clocks enabled.
201 * Disable any clocks that have not been explicitly enabled by a
202 * clk_enable() call and don't have the CLKFLAG_SKIP_AUTO_OFF flag.
Brian Swetland600f7cf2008-09-09 11:04:14 -0700203 */
204static int __init clock_late_init(void)
205{
Stephen Boydbb600ae2011-08-02 20:11:40 -0700206 unsigned n, count = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700207 unsigned long flags;
Stephen Boydbb600ae2011-08-02 20:11:40 -0700208 int ret = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700209
Stephen Boydbb600ae2011-08-02 20:11:40 -0700210 clock_debug_init(clk_init_data);
211 for (n = 0; n < clk_init_data->size; n++) {
212 struct clk *clk = clk_init_data->table[n].clk;
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700213 bool handoff = false;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214
Matt Wagantalld64560fe2011-01-26 16:20:54 -0800215 clock_debug_add(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 if (!(clk->flags & CLKFLAG_SKIP_AUTO_OFF)) {
217 spin_lock_irqsave(&clk->lock, flags);
218 if (!clk->count && clk->ops->auto_off) {
Brian Swetland600f7cf2008-09-09 11:04:14 -0700219 count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220 clk->ops->auto_off(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700221 }
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700222 if (clk->flags & CLKFLAG_HANDOFF_RATE) {
223 clk->flags &= ~CLKFLAG_HANDOFF_RATE;
224 handoff = true;
225 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 spin_unlock_irqrestore(&clk->lock, flags);
Matt Wagantall14dc2af2011-08-12 13:16:06 -0700227 /*
228 * Calling clk_disable() outside the lock is safe since
229 * it doesn't need to be atomic with the flag change.
230 */
231 if (handoff)
232 clk_disable(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700233 }
234 }
Brian Swetland600f7cf2008-09-09 11:04:14 -0700235 pr_info("clock_late_init() disabled %d unused clocks\n", count);
Stephen Boydbb600ae2011-08-02 20:11:40 -0700236 if (clk_init_data->late_init)
237 ret = clk_init_data->late_init();
238 return ret;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700239}
Brian Swetland600f7cf2008-09-09 11:04:14 -0700240late_initcall(clock_late_init);