blob: fa170bf40dbec810f53955cf1907c58619eea149 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/err.h>
Stephen Boydd86d1f22012-01-24 17:36:34 -080015#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070016#include <linux/clk.h>
Matt Wagantall33d01f52012-02-23 23:27:44 -080017#include <mach/clk-provider.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include "clock-voter.h"
19
Stephen Boydd86d1f22012-01-24 17:36:34 -080020static DEFINE_MUTEX(voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070021
22/* Aggregate the rate of clocks that are currently on. */
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070023static unsigned long voter_clk_aggregate_rate(const struct clk *parent)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024{
25 struct clk *clk;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070026 unsigned long rate = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
28 list_for_each_entry(clk, &parent->children, siblings) {
29 struct clk_voter *v = to_clk_voter(clk);
30 if (v->enabled)
Matt Wagantalldd63ac32012-04-05 13:17:31 -070031 rate = max(clk->rate, rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032 }
33 return rate;
34}
35
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070036static int voter_clk_set_rate(struct clk *clk, unsigned long rate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037{
38 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039 struct clk *clkp;
40 struct clk_voter *clkh, *v = to_clk_voter(clk);
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070041 unsigned long cur_rate, new_rate, other_rate = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042
Stephen Boydd86d1f22012-01-24 17:36:34 -080043 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044
45 if (v->enabled) {
46 struct clk *parent = v->parent;
47
48 /*
49 * Get the aggregate rate without this clock's vote and update
50 * if the new rate is different than the current rate
51 */
52 list_for_each_entry(clkp, &parent->children, siblings) {
53 clkh = to_clk_voter(clkp);
54 if (clkh->enabled && clkh != v)
Matt Wagantalldd63ac32012-04-05 13:17:31 -070055 other_rate = max(clkp->rate, other_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056 }
57
Matt Wagantalldd63ac32012-04-05 13:17:31 -070058 cur_rate = max(other_rate, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059 new_rate = max(other_rate, rate);
60
61 if (new_rate != cur_rate) {
Matt Wagantall8e6126f2011-11-08 13:34:19 -080062 ret = clk_set_rate(parent, new_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063 if (ret)
64 goto unlock;
65 }
66 }
Matt Wagantalldd63ac32012-04-05 13:17:31 -070067 clk->rate = rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068unlock:
Stephen Boydd86d1f22012-01-24 17:36:34 -080069 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070
71 return ret;
72}
73
Stephen Boydd86d1f22012-01-24 17:36:34 -080074static int voter_clk_prepare(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075{
Saravana Kannan4d77a4f2011-09-26 19:02:02 -070076 int ret = 0;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070077 unsigned long cur_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070078 struct clk *parent;
79 struct clk_voter *v = to_clk_voter(clk);
80
Stephen Boydd86d1f22012-01-24 17:36:34 -080081 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082 parent = v->parent;
83
84 /*
85 * Increase the rate if this clock is voting for a higher rate
86 * than the current rate.
87 */
88 cur_rate = voter_clk_aggregate_rate(parent);
Matt Wagantalldd63ac32012-04-05 13:17:31 -070089 if (clk->rate > cur_rate) {
90 ret = clk_set_rate(parent, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 if (ret)
92 goto out;
93 }
94 v->enabled = true;
95out:
Stephen Boydd86d1f22012-01-24 17:36:34 -080096 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097
98 return ret;
99}
100
Stephen Boydd86d1f22012-01-24 17:36:34 -0800101static void voter_clk_unprepare(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102{
Stephen Boydd86d1f22012-01-24 17:36:34 -0800103 unsigned long cur_rate, new_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 struct clk *parent;
105 struct clk_voter *v = to_clk_voter(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106
Stephen Boydd86d1f22012-01-24 17:36:34 -0800107 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108 parent = v->parent;
109
110 /*
111 * Decrease the rate if this clock was the only one voting for
112 * the highest rate.
113 */
114 v->enabled = false;
115 new_rate = voter_clk_aggregate_rate(parent);
Matt Wagantalldd63ac32012-04-05 13:17:31 -0700116 cur_rate = max(new_rate, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117
118 if (new_rate < cur_rate)
Matt Wagantall8e6126f2011-11-08 13:34:19 -0800119 clk_set_rate(parent, new_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120
Stephen Boydd86d1f22012-01-24 17:36:34 -0800121 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122}
123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124static int voter_clk_is_enabled(struct clk *clk)
125{
126 struct clk_voter *v = to_clk_voter(clk);
127 return v->enabled;
128}
129
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700130static long voter_clk_round_rate(struct clk *clk, unsigned long rate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131{
132 struct clk_voter *v = to_clk_voter(clk);
133 return clk_round_rate(v->parent, rate);
134}
135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136static struct clk *voter_clk_get_parent(struct clk *clk)
137{
138 struct clk_voter *v = to_clk_voter(clk);
139 return v->parent;
140}
141
142static bool voter_clk_is_local(struct clk *clk)
143{
144 return true;
145}
146
Matt Wagantallbdd6e902012-05-09 20:48:25 -0700147static enum handoff voter_clk_handoff(struct clk *clk)
Matt Wagantall35e78fc2012-04-05 14:18:44 -0700148{
149 /* Apply default rate vote */
150 if (clk->rate)
151 return HANDOFF_ENABLED_CLK;
152
153 return HANDOFF_DISABLED_CLK;
154}
155
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156struct clk_ops clk_ops_voter = {
Stephen Boydd86d1f22012-01-24 17:36:34 -0800157 .prepare = voter_clk_prepare,
158 .unprepare = voter_clk_unprepare,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 .set_rate = voter_clk_set_rate,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 .is_enabled = voter_clk_is_enabled,
161 .round_rate = voter_clk_round_rate,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 .get_parent = voter_clk_get_parent,
163 .is_local = voter_clk_is_local,
Matt Wagantallbdd6e902012-05-09 20:48:25 -0700164 .handoff = voter_clk_handoff,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165};