blob: 286b3d000de2cbbdc7a8ec7ffda414ba39496980 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/err.h>
15#include <mach/clk.h>
16
17#include "rpm_resources.h"
18#include "clock.h"
19#include "clock-rpm.h"
20
21static DEFINE_SPINLOCK(rpm_clock_lock);
22
23static int rpm_clk_enable(struct clk *clk)
24{
25 unsigned long flags;
26 struct msm_rpm_iv_pair iv;
27 int rc = 0;
28 struct rpm_clk *r = to_rpm_clk(clk);
29 unsigned this_khz, this_sleep_khz;
30 unsigned peer_khz = 0, peer_sleep_khz = 0;
31 struct rpm_clk *peer = r->peer;
32
33 spin_lock_irqsave(&rpm_clock_lock, flags);
34
35 this_khz = r->last_set_khz;
36 /* Don't send requests to the RPM if the rate has not been set. */
37 if (this_khz == 0)
38 goto out;
39
40 this_sleep_khz = r->last_set_sleep_khz;
41
42 iv.id = r->rpm_clk_id;
43
44 /* Take peer clock's rate into account only if it's enabled. */
45 if (peer->enabled) {
46 peer_khz = peer->last_set_khz;
47 peer_sleep_khz = peer->last_set_sleep_khz;
48 }
49
50 iv.value = max(this_khz, peer_khz);
51 rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1);
52 if (rc)
53 goto out;
54
55 iv.value = max(this_sleep_khz, peer_sleep_khz);
56 rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &iv, 1);
Matt Wagantall735f01a2011-08-12 12:40:28 -070057 if (rc) {
58 iv.value = peer_khz;
59 msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1);
60 }
61
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062out:
63 if (!rc)
64 r->enabled = true;
65
66 spin_unlock_irqrestore(&rpm_clock_lock, flags);
67
68 return rc;
69}
70
71static void rpm_clk_disable(struct clk *clk)
72{
73 unsigned long flags;
74 struct rpm_clk *r = to_rpm_clk(clk);
75
76 spin_lock_irqsave(&rpm_clock_lock, flags);
77
78 if (r->last_set_khz) {
79 struct msm_rpm_iv_pair iv;
80 struct rpm_clk *peer = r->peer;
81 unsigned peer_khz = 0, peer_sleep_khz = 0;
82 int rc;
83
84 iv.id = r->rpm_clk_id;
85
86 /* Take peer clock's rate into account only if it's enabled. */
87 if (peer->enabled) {
88 peer_khz = peer->last_set_khz;
89 peer_sleep_khz = peer->last_set_sleep_khz;
90 }
91
92 iv.value = peer_khz;
93 rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1);
94 if (rc)
95 goto out;
96
97 iv.value = peer_sleep_khz;
98 rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &iv, 1);
99 }
100 r->enabled = false;
101out:
102 spin_unlock_irqrestore(&rpm_clock_lock, flags);
103
104 return;
105}
106
107static void rpm_clk_auto_off(struct clk *clk)
108{
109 /* Not supported */
110}
111
112static int rpm_clk_set_min_rate(struct clk *clk, unsigned rate)
113{
114 unsigned long flags;
115 struct rpm_clk *r = to_rpm_clk(clk);
116 unsigned this_khz, this_sleep_khz;
117 int rc = 0;
118
119 this_khz = DIV_ROUND_UP(rate, 1000);
120
121 spin_lock_irqsave(&rpm_clock_lock, flags);
122
123 /* Ignore duplicate requests. */
124 if (r->last_set_khz == this_khz)
125 goto out;
126
127 /* Active-only clocks don't care what the rate is during sleep. So,
128 * they vote for zero. */
129 if (r->active_only)
130 this_sleep_khz = 0;
131 else
132 this_sleep_khz = this_khz;
133
134 if (r->enabled) {
135 struct msm_rpm_iv_pair iv;
136 struct rpm_clk *peer = r->peer;
137 unsigned peer_khz = 0, peer_sleep_khz = 0;
138
139 iv.id = r->rpm_clk_id;
140
141 /* Take peer clock's rate into account only if it's enabled. */
142 if (peer->enabled) {
143 peer_khz = peer->last_set_khz;
144 peer_sleep_khz = peer->last_set_sleep_khz;
145 }
146
147 iv.value = max(this_khz, peer_khz);
148 rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1);
149 if (rc)
150 goto out;
151
152 iv.value = max(this_sleep_khz, peer_sleep_khz);
153 rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &iv, 1);
154 }
155 if (!rc) {
156 r->last_set_khz = this_khz;
157 r->last_set_sleep_khz = this_sleep_khz;
158 }
159
160out:
161 spin_unlock_irqrestore(&rpm_clock_lock, flags);
162
163 return rc;
164}
165
166static unsigned rpm_clk_get_rate(struct clk *clk)
167{
168 struct rpm_clk *r = to_rpm_clk(clk);
169 struct msm_rpm_iv_pair iv = { r->rpm_status_id };
170 int rc;
171
172 rc = msm_rpm_get_status(&iv, 1);
173 if (rc < 0)
174 return rc;
175 return iv.value * 1000;
176}
177
178static int rpm_clk_is_enabled(struct clk *clk)
179{
180 return !!(rpm_clk_get_rate(clk));
181}
182
183static long rpm_clk_round_rate(struct clk *clk, unsigned rate)
184{
185 /* Not supported. */
186 return rate;
187}
188
189static bool rpm_clk_is_local(struct clk *clk)
190{
191 return false;
192}
193
194struct clk_ops clk_ops_rpm = {
195 .enable = rpm_clk_enable,
196 .disable = rpm_clk_disable,
197 .auto_off = rpm_clk_auto_off,
198 .set_min_rate = rpm_clk_set_min_rate,
199 .get_rate = rpm_clk_get_rate,
200 .is_enabled = rpm_clk_is_enabled,
201 .round_rate = rpm_clk_round_rate,
202 .is_local = rpm_clk_is_local,
203};