blob: 05265f8af347556a7f0b1b224e090989702e0f48 [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);
70 if (WARN_ON(clk->count == 0))
71 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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167static struct clk_lookup *msm_clocks;
168static unsigned msm_num_clocks;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700169
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170void __init msm_clock_init(struct clk_lookup *clock_tbl, size_t num_clocks)
Brian Swetland600f7cf2008-09-09 11:04:14 -0700171{
172 unsigned n;
173
Stephen Boydbd323442011-02-23 09:37:42 -0800174 for (n = 0; n < num_clocks; n++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 struct clk *clk = clock_tbl[n].clk;
176 struct clk *parent = clk_get_parent(clk);
177 clk_set_parent(clk, parent);
Stephen Boydbd323442011-02-23 09:37:42 -0800178 }
Daniel Walker5e96da52010-05-12 13:43:28 -0700179
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 clkdev_add_table(clock_tbl, num_clocks);
181 msm_clocks = clock_tbl;
182 msm_num_clocks = num_clocks;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700183}
184
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185/*
186 * The bootloader and/or AMSS may have left various clocks enabled.
187 * Disable any clocks that have not been explicitly enabled by a
188 * clk_enable() call and don't have the CLKFLAG_SKIP_AUTO_OFF flag.
Brian Swetland600f7cf2008-09-09 11:04:14 -0700189 */
190static int __init clock_late_init(void)
191{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192 unsigned n;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700193 unsigned long flags;
Brian Swetland600f7cf2008-09-09 11:04:14 -0700194 unsigned count = 0;
195
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 clock_debug_init(msm_clocks, msm_num_clocks);
197 for (n = 0; n < msm_num_clocks; n++) {
198 struct clk *clk = msm_clocks[n].clk;
199
Matt Wagantalld64560fe2011-01-26 16:20:54 -0800200 clock_debug_add(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 if (!(clk->flags & CLKFLAG_SKIP_AUTO_OFF)) {
202 spin_lock_irqsave(&clk->lock, flags);
203 if (!clk->count && clk->ops->auto_off) {
Brian Swetland600f7cf2008-09-09 11:04:14 -0700204 count++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 clk->ops->auto_off(clk);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700206 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 spin_unlock_irqrestore(&clk->lock, flags);
Brian Swetland600f7cf2008-09-09 11:04:14 -0700208 }
209 }
Brian Swetland600f7cf2008-09-09 11:04:14 -0700210 pr_info("clock_late_init() disabled %d unused clocks\n", count);
211 return 0;
212}
Brian Swetland600f7cf2008-09-09 11:04:14 -0700213late_initcall(clock_late_init);