blob: c1452407d21a578538d23073eb03cc35fd44addc [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 }
52 }
Brian Swetland600f7cf2008-09-09 11:04:14 -070053 clk->count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054out:
55 spin_unlock_irqrestore(&clk->lock, flags);
56
57 return ret;
Brian Swetland600f7cf2008-09-09 11:04:14 -070058}
59EXPORT_SYMBOL(clk_enable);
60
61void clk_disable(struct clk *clk)
62{
63 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064 struct clk *parent;
65
66 if (!clk)
67 return;
68
69 spin_lock_irqsave(&clk->lock, flags);
Stephen Boydd906b522011-07-26 10:51:41 -070070 if (WARN(clk->count == 0, "%s is unbalanced", clk->dbg_name))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071 goto out;
72 if (clk->count == 1) {
73 if (clk->ops->disable)
74 clk->ops->disable(clk);
75 parent = clk_get_parent(clk);
76 clk_disable(parent);
77 }
Brian Swetland600f7cf2008-09-09 11:04:14 -070078 clk->count--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079out:
80 spin_unlock_irqrestore(&clk->lock, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -070081}
82EXPORT_SYMBOL(clk_disable);
83
Daniel Walker5e96da52010-05-12 13:43:28 -070084int clk_reset(struct clk *clk, enum clk_reset_action action)
85{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086 if (!clk->ops->reset)
87 return -ENOSYS;
88
89 return clk->ops->reset(clk, action);
Daniel Walker5e96da52010-05-12 13:43:28 -070090}
91EXPORT_SYMBOL(clk_reset);
92
Brian Swetland600f7cf2008-09-09 11:04:14 -070093unsigned long clk_get_rate(struct clk *clk)
94{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 if (!clk->ops->get_rate)
96 return 0;
97
98 return clk->ops->get_rate(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -070099}
100EXPORT_SYMBOL(clk_get_rate);
101
102int clk_set_rate(struct clk *clk, unsigned long rate)
103{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 if (!clk->ops->set_rate)
105 return -ENOSYS;
Daniel Walker3a790bb2010-12-13 14:35:10 -0800106
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107 return clk->ops->set_rate(clk, rate);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700108}
109EXPORT_SYMBOL(clk_set_rate);
110
Daniel Walker5e96da52010-05-12 13:43:28 -0700111long clk_round_rate(struct clk *clk, unsigned long rate)
112{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113 if (!clk->ops->round_rate)
114 return -ENOSYS;
115
116 return clk->ops->round_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700117}
118EXPORT_SYMBOL(clk_round_rate);
119
120int clk_set_min_rate(struct clk *clk, unsigned long rate)
121{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 if (!clk->ops->set_min_rate)
123 return -ENOSYS;
124
125 return clk->ops->set_min_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700126}
127EXPORT_SYMBOL(clk_set_min_rate);
128
129int clk_set_max_rate(struct clk *clk, unsigned long rate)
130{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 if (!clk->ops->set_max_rate)
132 return -ENOSYS;
133
134 return clk->ops->set_max_rate(clk, rate);
Daniel Walker5e96da52010-05-12 13:43:28 -0700135}
136EXPORT_SYMBOL(clk_set_max_rate);
137
Brian Swetland600f7cf2008-09-09 11:04:14 -0700138int clk_set_parent(struct clk *clk, struct clk *parent)
139{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 if (!clk->ops->set_parent)
141 return 0;
142
143 return clk->ops->set_parent(clk, parent);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700144}
145EXPORT_SYMBOL(clk_set_parent);
146
147struct clk *clk_get_parent(struct clk *clk)
148{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 if (!clk->ops->get_parent)
150 return NULL;
151
152 return clk->ops->get_parent(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700153}
154EXPORT_SYMBOL(clk_get_parent);
155
156int clk_set_flags(struct clk *clk, unsigned long flags)
157{
158 if (clk == NULL || IS_ERR(clk))
159 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 if (!clk->ops->set_flags)
161 return -ENOSYS;
162
163 return clk->ops->set_flags(clk, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700164}
165EXPORT_SYMBOL(clk_set_flags);
166
Stephen Boydbb600ae2011-08-02 20:11:40 -0700167static struct clock_init_data __initdata *clk_init_data;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700168
Stephen Boydbb600ae2011-08-02 20:11:40 -0700169void __init msm_clock_init(struct clock_init_data *data)
Brian Swetland600f7cf2008-09-09 11:04:14 -0700170{
171 unsigned n;
Stephen Boydbb600ae2011-08-02 20:11:40 -0700172 struct clk_lookup *clock_tbl = data->table;
173 size_t num_clocks = data->size;
174
175 clk_init_data = data;
176 if (clk_init_data->init)
177 clk_init_data->init();
Brian Swetland600f7cf2008-09-09 11:04:14 -0700178
Stephen Boydbd323442011-02-23 09:37:42 -0800179 for (n = 0; n < num_clocks; n++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 struct clk *clk = clock_tbl[n].clk;
181 struct clk *parent = clk_get_parent(clk);
182 clk_set_parent(clk, parent);
Stephen Boydbd323442011-02-23 09:37:42 -0800183 }
Daniel Walker5e96da52010-05-12 13:43:28 -0700184
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 clkdev_add_table(clock_tbl, num_clocks);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700186}
187
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188/*
189 * The bootloader and/or AMSS may have left various clocks enabled.
190 * Disable any clocks that have not been explicitly enabled by a
191 * clk_enable() call and don't have the CLKFLAG_SKIP_AUTO_OFF flag.
Brian Swetland600f7cf2008-09-09 11:04:14 -0700192 */
193static int __init clock_late_init(void)
194{
Stephen Boydbb600ae2011-08-02 20:11:40 -0700195 unsigned n, count = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700196 unsigned long flags;
Stephen Boydbb600ae2011-08-02 20:11:40 -0700197 int ret = 0;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700198
Stephen Boydbb600ae2011-08-02 20:11:40 -0700199 clock_debug_init(clk_init_data);
200 for (n = 0; n < clk_init_data->size; n++) {
201 struct clk *clk = clk_init_data->table[n].clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202
Matt Wagantalld64560fe2011-01-26 16:20:54 -0800203 clock_debug_add(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 if (!(clk->flags & CLKFLAG_SKIP_AUTO_OFF)) {
205 spin_lock_irqsave(&clk->lock, flags);
206 if (!clk->count && clk->ops->auto_off) {
Brian Swetland600f7cf2008-09-09 11:04:14 -0700207 count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 clk->ops->auto_off(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700209 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 spin_unlock_irqrestore(&clk->lock, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700211 }
212 }
Brian Swetland600f7cf2008-09-09 11:04:14 -0700213 pr_info("clock_late_init() disabled %d unused clocks\n", count);
Stephen Boydbb600ae2011-08-02 20:11:40 -0700214 if (clk_init_data->late_init)
215 ret = clk_init_data->late_init();
216 return ret;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700217}
Brian Swetland600f7cf2008-09-09 11:04:14 -0700218late_initcall(clock_late_init);