blob: 3e1cbb941c16f1c6ad8df20c0f058d7f36d77f8e [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 Boyd2c2875f2012-01-24 17:36:34 -080015#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070016#include <linux/clk.h>
17
18#include "clock.h"
19#include "clock-voter.h"
20
Stephen Boyd2c2875f2012-01-24 17:36:34 -080021static DEFINE_MUTEX(voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022
23/* Aggregate the rate of clocks that are currently on. */
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070024static unsigned long voter_clk_aggregate_rate(const struct clk *parent)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025{
26 struct clk *clk;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070027 unsigned long rate = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028
29 list_for_each_entry(clk, &parent->children, siblings) {
30 struct clk_voter *v = to_clk_voter(clk);
31 if (v->enabled)
Matt Wagantalldd63ac32012-04-05 13:17:31 -070032 rate = max(clk->rate, rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033 }
34 return rate;
35}
36
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070037static int voter_clk_set_rate(struct clk *clk, unsigned long rate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038{
39 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040 struct clk *clkp;
41 struct clk_voter *clkh, *v = to_clk_voter(clk);
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070042 unsigned long cur_rate, new_rate, other_rate = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
Stephen Boyd2c2875f2012-01-24 17:36:34 -080044 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
46 if (v->enabled) {
47 struct clk *parent = v->parent;
48
49 /*
50 * Get the aggregate rate without this clock's vote and update
51 * if the new rate is different than the current rate
52 */
53 list_for_each_entry(clkp, &parent->children, siblings) {
54 clkh = to_clk_voter(clkp);
55 if (clkh->enabled && clkh != v)
Matt Wagantalldd63ac32012-04-05 13:17:31 -070056 other_rate = max(clkp->rate, other_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057 }
58
Matt Wagantalldd63ac32012-04-05 13:17:31 -070059 cur_rate = max(other_rate, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060 new_rate = max(other_rate, rate);
61
62 if (new_rate != cur_rate) {
Matt Wagantall8e6126f2011-11-08 13:34:19 -080063 ret = clk_set_rate(parent, new_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064 if (ret)
65 goto unlock;
66 }
67 }
Matt Wagantalldd63ac32012-04-05 13:17:31 -070068 clk->rate = rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069unlock:
Stephen Boyd2c2875f2012-01-24 17:36:34 -080070 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071
72 return ret;
73}
74
Stephen Boyd2c2875f2012-01-24 17:36:34 -080075static int voter_clk_prepare(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076{
Saravana Kannan4d77a4f2011-09-26 19:02:02 -070077 int ret = 0;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070078 unsigned long cur_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079 struct clk *parent;
80 struct clk_voter *v = to_clk_voter(clk);
81
Stephen Boyd2c2875f2012-01-24 17:36:34 -080082 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083 parent = v->parent;
84
85 /*
86 * Increase the rate if this clock is voting for a higher rate
87 * than the current rate.
88 */
89 cur_rate = voter_clk_aggregate_rate(parent);
Matt Wagantalldd63ac32012-04-05 13:17:31 -070090 if (clk->rate > cur_rate) {
91 ret = clk_set_rate(parent, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092 if (ret)
93 goto out;
94 }
95 v->enabled = true;
96out:
Stephen Boyd2c2875f2012-01-24 17:36:34 -080097 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098
99 return ret;
100}
101
Stephen Boyd2c2875f2012-01-24 17:36:34 -0800102static void voter_clk_unprepare(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103{
Stephen Boyd2c2875f2012-01-24 17:36:34 -0800104 unsigned long cur_rate, new_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 struct clk *parent;
106 struct clk_voter *v = to_clk_voter(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107
Stephen Boyd2c2875f2012-01-24 17:36:34 -0800108 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 parent = v->parent;
110
111 /*
112 * Decrease the rate if this clock was the only one voting for
113 * the highest rate.
114 */
115 v->enabled = false;
116 new_rate = voter_clk_aggregate_rate(parent);
Matt Wagantalldd63ac32012-04-05 13:17:31 -0700117 cur_rate = max(new_rate, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118
119 if (new_rate < cur_rate)
Matt Wagantall8e6126f2011-11-08 13:34:19 -0800120 clk_set_rate(parent, new_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121
Stephen Boyd2c2875f2012-01-24 17:36:34 -0800122 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123}
124
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125static int voter_clk_is_enabled(struct clk *clk)
126{
127 struct clk_voter *v = to_clk_voter(clk);
128 return v->enabled;
129}
130
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700131static long voter_clk_round_rate(struct clk *clk, unsigned long rate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132{
133 struct clk_voter *v = to_clk_voter(clk);
134 return clk_round_rate(v->parent, rate);
135}
136
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137static struct clk *voter_clk_get_parent(struct clk *clk)
138{
139 struct clk_voter *v = to_clk_voter(clk);
140 return v->parent;
141}
142
143static bool voter_clk_is_local(struct clk *clk)
144{
145 return true;
146}
147
Matt Wagantallbdd6e902012-05-09 20:48:25 -0700148static enum handoff voter_clk_handoff(struct clk *clk)
Matt Wagantall35e78fc2012-04-05 14:18:44 -0700149{
150 /* Apply default rate vote */
151 if (clk->rate)
152 return HANDOFF_ENABLED_CLK;
153
154 return HANDOFF_DISABLED_CLK;
155}
156
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157struct clk_ops clk_ops_voter = {
Stephen Boyd2c2875f2012-01-24 17:36:34 -0800158 .prepare = voter_clk_prepare,
159 .unprepare = voter_clk_unprepare,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 .set_rate = voter_clk_set_rate,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 .is_enabled = voter_clk_is_enabled,
162 .round_rate = voter_clk_round_rate,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 .get_parent = voter_clk_get_parent,
164 .is_local = voter_clk_is_local,
Matt Wagantallbdd6e902012-05-09 20:48:25 -0700165 .handoff = voter_clk_handoff,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166};