blob: 13041b15429f97fdc0b6ddd1c19c6ff89e6f495a [file] [log] [blame]
Saravana Kannanc85ecf92013-01-21 17:58:35 -08001/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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#include <linux/err.h>
Stephen Boydd86d1f22012-01-24 17:36:34 -080014#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/clk.h>
Matt Wagantall33d01f52012-02-23 23:27:44 -080016#include <mach/clk-provider.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include "clock-voter.h"
18
Stephen Boydd86d1f22012-01-24 17:36:34 -080019static DEFINE_MUTEX(voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020
21/* Aggregate the rate of clocks that are currently on. */
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070022static unsigned long voter_clk_aggregate_rate(const struct clk *parent)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023{
24 struct clk *clk;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070025 unsigned long rate = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026
27 list_for_each_entry(clk, &parent->children, siblings) {
28 struct clk_voter *v = to_clk_voter(clk);
29 if (v->enabled)
Matt Wagantalldd63ac32012-04-05 13:17:31 -070030 rate = max(clk->rate, rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031 }
32 return rate;
33}
34
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070035static int voter_clk_set_rate(struct clk *clk, unsigned long rate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036{
37 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038 struct clk *clkp;
39 struct clk_voter *clkh, *v = to_clk_voter(clk);
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070040 unsigned long cur_rate, new_rate, other_rate = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
Vikram Mulukutlaa1b20072013-02-04 15:07:35 -080042 if (v->is_branch)
43 return 0;
44
Stephen Boydd86d1f22012-01-24 17:36:34 -080045 mutex_lock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
47 if (v->enabled) {
Saravana Kannan7a6532e2012-10-18 20:51:13 -070048 struct clk *parent = clk->parent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049
50 /*
51 * Get the aggregate rate without this clock's vote and update
52 * if the new rate is different than the current rate
53 */
54 list_for_each_entry(clkp, &parent->children, siblings) {
55 clkh = to_clk_voter(clkp);
56 if (clkh->enabled && clkh != v)
Matt Wagantalldd63ac32012-04-05 13:17:31 -070057 other_rate = max(clkp->rate, other_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058 }
59
Matt Wagantalldd63ac32012-04-05 13:17:31 -070060 cur_rate = max(other_rate, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 new_rate = max(other_rate, rate);
62
63 if (new_rate != cur_rate) {
Matt Wagantall8e6126f2011-11-08 13:34:19 -080064 ret = clk_set_rate(parent, new_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065 if (ret)
66 goto unlock;
67 }
68 }
Matt Wagantalldd63ac32012-04-05 13:17:31 -070069 clk->rate = rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070unlock:
Stephen Boydd86d1f22012-01-24 17:36:34 -080071 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072
73 return ret;
74}
75
Stephen Boydd86d1f22012-01-24 17:36:34 -080076static int voter_clk_prepare(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077{
Saravana Kannan4d77a4f2011-09-26 19:02:02 -070078 int ret = 0;
Matt Wagantall9de3bfb2011-11-03 20:13:12 -070079 unsigned long cur_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080 struct clk *parent;
81 struct clk_voter *v = to_clk_voter(clk);
82
Stephen Boydd86d1f22012-01-24 17:36:34 -080083 mutex_lock(&voter_clk_lock);
Saravana Kannan7a6532e2012-10-18 20:51:13 -070084 parent = clk->parent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085
Patrick Daly94517d62013-03-22 14:37:24 -070086 if (v->is_branch) {
87 v->enabled = true;
88 goto out;
89 }
90
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 /*
92 * Increase the rate if this clock is voting for a higher rate
93 * than the current rate.
94 */
95 cur_rate = voter_clk_aggregate_rate(parent);
Matt Wagantalldd63ac32012-04-05 13:17:31 -070096 if (clk->rate > cur_rate) {
97 ret = clk_set_rate(parent, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 if (ret)
99 goto out;
100 }
101 v->enabled = true;
102out:
Stephen Boydd86d1f22012-01-24 17:36:34 -0800103 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104
105 return ret;
106}
107
Stephen Boydd86d1f22012-01-24 17:36:34 -0800108static void voter_clk_unprepare(struct clk *clk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109{
Stephen Boydd86d1f22012-01-24 17:36:34 -0800110 unsigned long cur_rate, new_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 struct clk *parent;
112 struct clk_voter *v = to_clk_voter(clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Vikram Mulukutlaa1b20072013-02-04 15:07:35 -0800114
Stephen Boydd86d1f22012-01-24 17:36:34 -0800115 mutex_lock(&voter_clk_lock);
Saravana Kannan7a6532e2012-10-18 20:51:13 -0700116 parent = clk->parent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117
118 /*
119 * Decrease the rate if this clock was the only one voting for
120 * the highest rate.
121 */
122 v->enabled = false;
Patrick Daly94517d62013-03-22 14:37:24 -0700123 if (v->is_branch)
124 goto out;
125
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126 new_rate = voter_clk_aggregate_rate(parent);
Matt Wagantalldd63ac32012-04-05 13:17:31 -0700127 cur_rate = max(new_rate, clk->rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128
129 if (new_rate < cur_rate)
Matt Wagantall8e6126f2011-11-08 13:34:19 -0800130 clk_set_rate(parent, new_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131
Patrick Daly94517d62013-03-22 14:37:24 -0700132out:
Stephen Boydd86d1f22012-01-24 17:36:34 -0800133 mutex_unlock(&voter_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134}
135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136static int voter_clk_is_enabled(struct clk *clk)
137{
138 struct clk_voter *v = to_clk_voter(clk);
139 return v->enabled;
140}
141
Matt Wagantall9de3bfb2011-11-03 20:13:12 -0700142static long voter_clk_round_rate(struct clk *clk, unsigned long rate)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143{
Saravana Kannan7a6532e2012-10-18 20:51:13 -0700144 return clk_round_rate(clk->parent, rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145}
146
147static bool voter_clk_is_local(struct clk *clk)
148{
149 return true;
150}
151
Matt Wagantallbdd6e902012-05-09 20:48:25 -0700152static enum handoff voter_clk_handoff(struct clk *clk)
Matt Wagantall35e78fc2012-04-05 14:18:44 -0700153{
Saravana Kannanc85ecf92013-01-21 17:58:35 -0800154 if (!clk->rate)
155 return HANDOFF_DISABLED_CLK;
Matt Wagantall35e78fc2012-04-05 14:18:44 -0700156
Saravana Kannanc85ecf92013-01-21 17:58:35 -0800157 /*
158 * Send the default rate to the parent if necessary and update the
159 * software state of the voter clock.
160 */
161 if (voter_clk_prepare(clk) < 0)
162 return HANDOFF_DISABLED_CLK;
163
164 return HANDOFF_ENABLED_CLK;
Matt Wagantall35e78fc2012-04-05 14:18:44 -0700165}
166
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167struct clk_ops clk_ops_voter = {
Stephen Boydd86d1f22012-01-24 17:36:34 -0800168 .prepare = voter_clk_prepare,
169 .unprepare = voter_clk_unprepare,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170 .set_rate = voter_clk_set_rate,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 .is_enabled = voter_clk_is_enabled,
172 .round_rate = voter_clk_round_rate,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 .is_local = voter_clk_is_local,
Matt Wagantallbdd6e902012-05-09 20:48:25 -0700174 .handoff = voter_clk_handoff,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175};