All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Brown <davidb@codeaurora.org>
To: David Brown <davidb@codeaurora.org>,
	Daniel Walker <dwalker@fifo99.com>,
	Bryan Huntsman <bryanh@codeaurora.org>,
	Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-msm@vger.kernel.org,
	Stephen Boyd <sboyd@codeaurora.org>,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 09/34] msm: clock: Implement rate voting
Date: Wed,  2 Nov 2011 11:36:06 -0700	[thread overview]
Message-ID: <1320258991-22325-10-git-send-email-davidb@codeaurora.org> (raw)
In-Reply-To: <1320258991-22325-1-git-send-email-davidb@codeaurora.org>

From: Stephen Boyd <sboyd@codeaurora.org>

Some clocks have multiple consumers where each consumer requires
a minimum rate. Implement a sub driver to aggregate
clk_set_rate() calls from each consumer and call clk_set_rate()
on the parent clock when the minimum requested rate of all the
voters changes.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
---
 arch/arm/mach-msm/Makefile      |    1 +
 arch/arm/mach-msm/clock-pcom.h  |    2 +-
 arch/arm/mach-msm/clock-voter.c |  187 +++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-msm/clock-voter.h |   41 +++++++++
 arch/arm/mach-msm/clock.c       |   18 ++---
 arch/arm/mach-msm/clock.h       |    9 ++
 6 files changed, 245 insertions(+), 13 deletions(-)
 create mode 100644 arch/arm/mach-msm/clock-voter.c
 create mode 100644 arch/arm/mach-msm/clock-voter.h

diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 4285dfd..5318553 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -1,5 +1,6 @@
 obj-y += io.o idle.o timer.o
 obj-y += clock.o
+obj-y += clock-voter.o
 obj-$(CONFIG_DEBUG_FS) += clock-debug.o
 
 obj-$(CONFIG_MSM_VIC) += irq-vic.o
diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h
index 955c917..a5ac74d 100644
--- a/arch/arm/mach-msm/clock-pcom.h
+++ b/arch/arm/mach-msm/clock-pcom.h
@@ -145,7 +145,7 @@ static inline struct pcom_clk *to_pcom_clk(struct clk *clk)
 			.ops = &clk_ops_pcom, \
 			.flags = clk_flags, \
 			.dbg_name = #clk_id, \
-			.lock = __SPIN_LOCK_UNLOCKED(clk_name.c), \
+			CLK_INIT(clk_name.c), \
 		}, \
 	}
 
diff --git a/arch/arm/mach-msm/clock-voter.c b/arch/arm/mach-msm/clock-voter.c
new file mode 100644
index 0000000..6da4384
--- /dev/null
+++ b/arch/arm/mach-msm/clock-voter.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+
+#include "clock.h"
+#include "clock-voter.h"
+
+static DEFINE_SPINLOCK(voter_clk_lock);
+
+/* Aggregate the rate of clocks that are currently on. */
+static unsigned voter_clk_aggregate_rate(const struct clk *parent)
+{
+	struct clk *clk;
+	unsigned rate = 0;
+
+	list_for_each_entry(clk, &parent->children, siblings) {
+		struct clk_voter *v = to_clk_voter(clk);
+		if (v->enabled)
+			rate = max(v->rate, rate);
+	}
+	return rate;
+}
+
+static int voter_clk_set_rate(struct clk *clk, unsigned rate)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct clk *clkp;
+	struct clk_voter *clkh, *v = to_clk_voter(clk);
+	unsigned cur_rate, new_rate, other_rate = 0;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+
+	if (v->enabled) {
+		struct clk *parent = v->parent;
+
+		/*
+		 * Get the aggregate rate without this clock's vote and update
+		 * if the new rate is different than the current rate
+		 */
+		list_for_each_entry(clkp, &parent->children, siblings) {
+			clkh = to_clk_voter(clkp);
+			if (clkh->enabled && clkh != v)
+				other_rate = max(clkh->rate, other_rate);
+		}
+
+		cur_rate = max(other_rate, v->rate);
+		new_rate = max(other_rate, rate);
+
+		if (new_rate != cur_rate) {
+			ret = clk_set_min_rate(parent, new_rate);
+			if (ret)
+				goto unlock;
+		}
+	}
+	v->rate = rate;
+unlock:
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return ret;
+}
+
+static int voter_clk_enable(struct clk *clk)
+{
+	int ret;
+	unsigned long flags;
+	unsigned cur_rate;
+	struct clk *parent;
+	struct clk_voter *v = to_clk_voter(clk);
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	parent = v->parent;
+
+	/*
+	 * Increase the rate if this clock is voting for a higher rate
+	 * than the current rate.
+	 */
+	cur_rate = voter_clk_aggregate_rate(parent);
+	if (v->rate > cur_rate) {
+		ret = clk_set_min_rate(parent, v->rate);
+		if (ret)
+			goto out;
+	}
+	v->enabled = true;
+out:
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return ret;
+}
+
+static void voter_clk_disable(struct clk *clk)
+{
+	unsigned long flags;
+	struct clk *parent;
+	struct clk_voter *v = to_clk_voter(clk);
+	unsigned cur_rate, new_rate;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	parent = v->parent;
+
+	/*
+	 * Decrease the rate if this clock was the only one voting for
+	 * the highest rate.
+	 */
+	v->enabled = false;
+	new_rate = voter_clk_aggregate_rate(parent);
+	cur_rate = max(new_rate, v->rate);
+
+	if (new_rate < cur_rate)
+		clk_set_min_rate(parent, new_rate);
+
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+}
+
+static unsigned voter_clk_get_rate(struct clk *clk)
+{
+	unsigned rate;
+	unsigned long flags;
+	struct clk_voter *v = to_clk_voter(clk);
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	rate = v->rate;
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return rate;
+}
+
+static unsigned voter_clk_is_enabled(struct clk *clk)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return v->enabled;
+}
+
+static long voter_clk_round_rate(struct clk *clk, unsigned rate)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return clk_round_rate(v->parent, rate);
+}
+
+static int voter_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	if (list_empty(&clk->siblings))
+		list_add(&clk->siblings, &parent->children);
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return 0;
+}
+
+static struct clk *voter_clk_get_parent(struct clk *clk)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return v->parent;
+}
+
+static bool voter_clk_is_local(struct clk *clk)
+{
+	return true;
+}
+
+struct clk_ops clk_ops_voter = {
+	.enable = voter_clk_enable,
+	.disable = voter_clk_disable,
+	.set_rate = voter_clk_set_rate,
+	.set_min_rate = voter_clk_set_rate,
+	.get_rate = voter_clk_get_rate,
+	.is_enabled = voter_clk_is_enabled,
+	.round_rate = voter_clk_round_rate,
+	.set_parent = voter_clk_set_parent,
+	.get_parent = voter_clk_get_parent,
+	.is_local = voter_clk_is_local,
+};
diff --git a/arch/arm/mach-msm/clock-voter.h b/arch/arm/mach-msm/clock-voter.h
new file mode 100644
index 0000000..170ba67
--- /dev/null
+++ b/arch/arm/mach-msm/clock-voter.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+
+struct clk_ops;
+extern struct clk_ops clk_ops_voter;
+
+struct clk_voter {
+	bool enabled;
+	unsigned rate;
+	struct clk *parent;
+	struct clk c;
+};
+
+static inline struct clk_voter *to_clk_voter(struct clk *clk)
+{
+	return container_of(clk, struct clk_voter, c);
+}
+
+#define DEFINE_CLK_VOTER(clk_name, _parent) \
+	struct clk_voter clk_name = { \
+		.parent = _parent, \
+		.c = { \
+			.dbg_name = #clk_name, \
+			.ops = &clk_ops_voter, \
+			CLK_INIT(clk_name.c), \
+		}, \
+	}
+
+#endif
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 026bdc0..ad55eaa 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -154,12 +154,6 @@ int clk_set_flags(struct clk *clk, unsigned long flags)
 }
 EXPORT_SYMBOL(clk_set_flags);
 
-/* EBI1 is the only shared clock that several clients want to vote on as of
- * this commit. If this changes in the future, then it might be better to
- * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more
- * generic to support different clocks.
- */
-static struct clk *ebi1_clk;
 static struct clk_lookup *msm_clocks;
 static unsigned msm_num_clocks;
 
@@ -167,13 +161,13 @@ void __init msm_clock_init(struct clk_lookup *clock_tbl, size_t num_clocks)
 {
 	unsigned n;
 
-	clkdev_add_table(clock_tbl, num_clocks);
-
-	for (n = 0; n < num_clocks; n++)
-		spin_lock_init(&clock_tbl[n].clk->lock);
+	for (n = 0; n < num_clocks; n++) {
+		struct clk *clk = clock_tbl[n].clk;
+		struct clk *parent = clk_get_parent(clk);
+		clk_set_parent(clk, parent);
+	}
 
-	ebi1_clk = clk_get(NULL, "ebi1_clk");
-	BUG_ON(ebi1_clk == NULL);
+	clkdev_add_table(clock_tbl, num_clocks);
 
 	msm_clocks = clock_tbl;
 	msm_num_clocks = num_clocks;
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 6a7cbca..6aecd95 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -55,9 +55,18 @@ struct clk {
 	uint32_t flags;
 	struct clk_ops *ops;
 	const char *dbg_name;
+
+	struct list_head children;
+	struct list_head siblings;
+
 	spinlock_t lock;
 };
 
+#define CLK_INIT(name) \
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock), \
+	.children = LIST_HEAD_INIT((name).children), \
+	.siblings = LIST_HEAD_INIT((name).siblings)
+
 #define OFF CLKFLAG_AUTO_OFF
 #define CLK_MIN CLKFLAG_MIN
 #define CLK_MAX CLKFLAG_MAX
-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

WARNING: multiple messages have this Message-ID (diff)
From: davidb@codeaurora.org (David Brown)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 09/34] msm: clock: Implement rate voting
Date: Wed,  2 Nov 2011 11:36:06 -0700	[thread overview]
Message-ID: <1320258991-22325-10-git-send-email-davidb@codeaurora.org> (raw)
In-Reply-To: <1320258991-22325-1-git-send-email-davidb@codeaurora.org>

From: Stephen Boyd <sboyd@codeaurora.org>

Some clocks have multiple consumers where each consumer requires
a minimum rate. Implement a sub driver to aggregate
clk_set_rate() calls from each consumer and call clk_set_rate()
on the parent clock when the minimum requested rate of all the
voters changes.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
---
 arch/arm/mach-msm/Makefile      |    1 +
 arch/arm/mach-msm/clock-pcom.h  |    2 +-
 arch/arm/mach-msm/clock-voter.c |  187 +++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-msm/clock-voter.h |   41 +++++++++
 arch/arm/mach-msm/clock.c       |   18 ++---
 arch/arm/mach-msm/clock.h       |    9 ++
 6 files changed, 245 insertions(+), 13 deletions(-)
 create mode 100644 arch/arm/mach-msm/clock-voter.c
 create mode 100644 arch/arm/mach-msm/clock-voter.h

diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 4285dfd..5318553 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -1,5 +1,6 @@
 obj-y += io.o idle.o timer.o
 obj-y += clock.o
+obj-y += clock-voter.o
 obj-$(CONFIG_DEBUG_FS) += clock-debug.o
 
 obj-$(CONFIG_MSM_VIC) += irq-vic.o
diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h
index 955c917..a5ac74d 100644
--- a/arch/arm/mach-msm/clock-pcom.h
+++ b/arch/arm/mach-msm/clock-pcom.h
@@ -145,7 +145,7 @@ static inline struct pcom_clk *to_pcom_clk(struct clk *clk)
 			.ops = &clk_ops_pcom, \
 			.flags = clk_flags, \
 			.dbg_name = #clk_id, \
-			.lock = __SPIN_LOCK_UNLOCKED(clk_name.c), \
+			CLK_INIT(clk_name.c), \
 		}, \
 	}
 
diff --git a/arch/arm/mach-msm/clock-voter.c b/arch/arm/mach-msm/clock-voter.c
new file mode 100644
index 0000000..6da4384
--- /dev/null
+++ b/arch/arm/mach-msm/clock-voter.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+
+#include "clock.h"
+#include "clock-voter.h"
+
+static DEFINE_SPINLOCK(voter_clk_lock);
+
+/* Aggregate the rate of clocks that are currently on. */
+static unsigned voter_clk_aggregate_rate(const struct clk *parent)
+{
+	struct clk *clk;
+	unsigned rate = 0;
+
+	list_for_each_entry(clk, &parent->children, siblings) {
+		struct clk_voter *v = to_clk_voter(clk);
+		if (v->enabled)
+			rate = max(v->rate, rate);
+	}
+	return rate;
+}
+
+static int voter_clk_set_rate(struct clk *clk, unsigned rate)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct clk *clkp;
+	struct clk_voter *clkh, *v = to_clk_voter(clk);
+	unsigned cur_rate, new_rate, other_rate = 0;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+
+	if (v->enabled) {
+		struct clk *parent = v->parent;
+
+		/*
+		 * Get the aggregate rate without this clock's vote and update
+		 * if the new rate is different than the current rate
+		 */
+		list_for_each_entry(clkp, &parent->children, siblings) {
+			clkh = to_clk_voter(clkp);
+			if (clkh->enabled && clkh != v)
+				other_rate = max(clkh->rate, other_rate);
+		}
+
+		cur_rate = max(other_rate, v->rate);
+		new_rate = max(other_rate, rate);
+
+		if (new_rate != cur_rate) {
+			ret = clk_set_min_rate(parent, new_rate);
+			if (ret)
+				goto unlock;
+		}
+	}
+	v->rate = rate;
+unlock:
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return ret;
+}
+
+static int voter_clk_enable(struct clk *clk)
+{
+	int ret;
+	unsigned long flags;
+	unsigned cur_rate;
+	struct clk *parent;
+	struct clk_voter *v = to_clk_voter(clk);
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	parent = v->parent;
+
+	/*
+	 * Increase the rate if this clock is voting for a higher rate
+	 * than the current rate.
+	 */
+	cur_rate = voter_clk_aggregate_rate(parent);
+	if (v->rate > cur_rate) {
+		ret = clk_set_min_rate(parent, v->rate);
+		if (ret)
+			goto out;
+	}
+	v->enabled = true;
+out:
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return ret;
+}
+
+static void voter_clk_disable(struct clk *clk)
+{
+	unsigned long flags;
+	struct clk *parent;
+	struct clk_voter *v = to_clk_voter(clk);
+	unsigned cur_rate, new_rate;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	parent = v->parent;
+
+	/*
+	 * Decrease the rate if this clock was the only one voting for
+	 * the highest rate.
+	 */
+	v->enabled = false;
+	new_rate = voter_clk_aggregate_rate(parent);
+	cur_rate = max(new_rate, v->rate);
+
+	if (new_rate < cur_rate)
+		clk_set_min_rate(parent, new_rate);
+
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+}
+
+static unsigned voter_clk_get_rate(struct clk *clk)
+{
+	unsigned rate;
+	unsigned long flags;
+	struct clk_voter *v = to_clk_voter(clk);
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	rate = v->rate;
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return rate;
+}
+
+static unsigned voter_clk_is_enabled(struct clk *clk)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return v->enabled;
+}
+
+static long voter_clk_round_rate(struct clk *clk, unsigned rate)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return clk_round_rate(v->parent, rate);
+}
+
+static int voter_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	if (list_empty(&clk->siblings))
+		list_add(&clk->siblings, &parent->children);
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return 0;
+}
+
+static struct clk *voter_clk_get_parent(struct clk *clk)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return v->parent;
+}
+
+static bool voter_clk_is_local(struct clk *clk)
+{
+	return true;
+}
+
+struct clk_ops clk_ops_voter = {
+	.enable = voter_clk_enable,
+	.disable = voter_clk_disable,
+	.set_rate = voter_clk_set_rate,
+	.set_min_rate = voter_clk_set_rate,
+	.get_rate = voter_clk_get_rate,
+	.is_enabled = voter_clk_is_enabled,
+	.round_rate = voter_clk_round_rate,
+	.set_parent = voter_clk_set_parent,
+	.get_parent = voter_clk_get_parent,
+	.is_local = voter_clk_is_local,
+};
diff --git a/arch/arm/mach-msm/clock-voter.h b/arch/arm/mach-msm/clock-voter.h
new file mode 100644
index 0000000..170ba67
--- /dev/null
+++ b/arch/arm/mach-msm/clock-voter.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+
+struct clk_ops;
+extern struct clk_ops clk_ops_voter;
+
+struct clk_voter {
+	bool enabled;
+	unsigned rate;
+	struct clk *parent;
+	struct clk c;
+};
+
+static inline struct clk_voter *to_clk_voter(struct clk *clk)
+{
+	return container_of(clk, struct clk_voter, c);
+}
+
+#define DEFINE_CLK_VOTER(clk_name, _parent) \
+	struct clk_voter clk_name = { \
+		.parent = _parent, \
+		.c = { \
+			.dbg_name = #clk_name, \
+			.ops = &clk_ops_voter, \
+			CLK_INIT(clk_name.c), \
+		}, \
+	}
+
+#endif
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 026bdc0..ad55eaa 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -154,12 +154,6 @@ int clk_set_flags(struct clk *clk, unsigned long flags)
 }
 EXPORT_SYMBOL(clk_set_flags);
 
-/* EBI1 is the only shared clock that several clients want to vote on as of
- * this commit. If this changes in the future, then it might be better to
- * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more
- * generic to support different clocks.
- */
-static struct clk *ebi1_clk;
 static struct clk_lookup *msm_clocks;
 static unsigned msm_num_clocks;
 
@@ -167,13 +161,13 @@ void __init msm_clock_init(struct clk_lookup *clock_tbl, size_t num_clocks)
 {
 	unsigned n;
 
-	clkdev_add_table(clock_tbl, num_clocks);
-
-	for (n = 0; n < num_clocks; n++)
-		spin_lock_init(&clock_tbl[n].clk->lock);
+	for (n = 0; n < num_clocks; n++) {
+		struct clk *clk = clock_tbl[n].clk;
+		struct clk *parent = clk_get_parent(clk);
+		clk_set_parent(clk, parent);
+	}
 
-	ebi1_clk = clk_get(NULL, "ebi1_clk");
-	BUG_ON(ebi1_clk == NULL);
+	clkdev_add_table(clock_tbl, num_clocks);
 
 	msm_clocks = clock_tbl;
 	msm_num_clocks = num_clocks;
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 6a7cbca..6aecd95 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -55,9 +55,18 @@ struct clk {
 	uint32_t flags;
 	struct clk_ops *ops;
 	const char *dbg_name;
+
+	struct list_head children;
+	struct list_head siblings;
+
 	spinlock_t lock;
 };
 
+#define CLK_INIT(name) \
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock), \
+	.children = LIST_HEAD_INIT((name).children), \
+	.siblings = LIST_HEAD_INIT((name).siblings)
+
 #define OFF CLKFLAG_AUTO_OFF
 #define CLK_MIN CLKFLAG_MIN
 #define CLK_MAX CLKFLAG_MAX
-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

WARNING: multiple messages have this Message-ID (diff)
From: David Brown <davidb@codeaurora.org>
To: David Brown <davidb@codeaurora.org>,
	Daniel Walker <dwalker@fifo99.com>,
	Bryan Huntsman <bryanh@codeaurora.org>,
	Russell King <linux@arm.linux.org.uk>
Cc: Stephen Boyd <sboyd@codeaurora.org>,
	linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 09/34] msm: clock: Implement rate voting
Date: Wed,  2 Nov 2011 11:36:06 -0700	[thread overview]
Message-ID: <1320258991-22325-10-git-send-email-davidb@codeaurora.org> (raw)
In-Reply-To: <1320258991-22325-1-git-send-email-davidb@codeaurora.org>

From: Stephen Boyd <sboyd@codeaurora.org>

Some clocks have multiple consumers where each consumer requires
a minimum rate. Implement a sub driver to aggregate
clk_set_rate() calls from each consumer and call clk_set_rate()
on the parent clock when the minimum requested rate of all the
voters changes.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
---
 arch/arm/mach-msm/Makefile      |    1 +
 arch/arm/mach-msm/clock-pcom.h  |    2 +-
 arch/arm/mach-msm/clock-voter.c |  187 +++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-msm/clock-voter.h |   41 +++++++++
 arch/arm/mach-msm/clock.c       |   18 ++---
 arch/arm/mach-msm/clock.h       |    9 ++
 6 files changed, 245 insertions(+), 13 deletions(-)
 create mode 100644 arch/arm/mach-msm/clock-voter.c
 create mode 100644 arch/arm/mach-msm/clock-voter.h

diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 4285dfd..5318553 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -1,5 +1,6 @@
 obj-y += io.o idle.o timer.o
 obj-y += clock.o
+obj-y += clock-voter.o
 obj-$(CONFIG_DEBUG_FS) += clock-debug.o
 
 obj-$(CONFIG_MSM_VIC) += irq-vic.o
diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h
index 955c917..a5ac74d 100644
--- a/arch/arm/mach-msm/clock-pcom.h
+++ b/arch/arm/mach-msm/clock-pcom.h
@@ -145,7 +145,7 @@ static inline struct pcom_clk *to_pcom_clk(struct clk *clk)
 			.ops = &clk_ops_pcom, \
 			.flags = clk_flags, \
 			.dbg_name = #clk_id, \
-			.lock = __SPIN_LOCK_UNLOCKED(clk_name.c), \
+			CLK_INIT(clk_name.c), \
 		}, \
 	}
 
diff --git a/arch/arm/mach-msm/clock-voter.c b/arch/arm/mach-msm/clock-voter.c
new file mode 100644
index 0000000..6da4384
--- /dev/null
+++ b/arch/arm/mach-msm/clock-voter.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+
+#include "clock.h"
+#include "clock-voter.h"
+
+static DEFINE_SPINLOCK(voter_clk_lock);
+
+/* Aggregate the rate of clocks that are currently on. */
+static unsigned voter_clk_aggregate_rate(const struct clk *parent)
+{
+	struct clk *clk;
+	unsigned rate = 0;
+
+	list_for_each_entry(clk, &parent->children, siblings) {
+		struct clk_voter *v = to_clk_voter(clk);
+		if (v->enabled)
+			rate = max(v->rate, rate);
+	}
+	return rate;
+}
+
+static int voter_clk_set_rate(struct clk *clk, unsigned rate)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct clk *clkp;
+	struct clk_voter *clkh, *v = to_clk_voter(clk);
+	unsigned cur_rate, new_rate, other_rate = 0;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+
+	if (v->enabled) {
+		struct clk *parent = v->parent;
+
+		/*
+		 * Get the aggregate rate without this clock's vote and update
+		 * if the new rate is different than the current rate
+		 */
+		list_for_each_entry(clkp, &parent->children, siblings) {
+			clkh = to_clk_voter(clkp);
+			if (clkh->enabled && clkh != v)
+				other_rate = max(clkh->rate, other_rate);
+		}
+
+		cur_rate = max(other_rate, v->rate);
+		new_rate = max(other_rate, rate);
+
+		if (new_rate != cur_rate) {
+			ret = clk_set_min_rate(parent, new_rate);
+			if (ret)
+				goto unlock;
+		}
+	}
+	v->rate = rate;
+unlock:
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return ret;
+}
+
+static int voter_clk_enable(struct clk *clk)
+{
+	int ret;
+	unsigned long flags;
+	unsigned cur_rate;
+	struct clk *parent;
+	struct clk_voter *v = to_clk_voter(clk);
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	parent = v->parent;
+
+	/*
+	 * Increase the rate if this clock is voting for a higher rate
+	 * than the current rate.
+	 */
+	cur_rate = voter_clk_aggregate_rate(parent);
+	if (v->rate > cur_rate) {
+		ret = clk_set_min_rate(parent, v->rate);
+		if (ret)
+			goto out;
+	}
+	v->enabled = true;
+out:
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return ret;
+}
+
+static void voter_clk_disable(struct clk *clk)
+{
+	unsigned long flags;
+	struct clk *parent;
+	struct clk_voter *v = to_clk_voter(clk);
+	unsigned cur_rate, new_rate;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	parent = v->parent;
+
+	/*
+	 * Decrease the rate if this clock was the only one voting for
+	 * the highest rate.
+	 */
+	v->enabled = false;
+	new_rate = voter_clk_aggregate_rate(parent);
+	cur_rate = max(new_rate, v->rate);
+
+	if (new_rate < cur_rate)
+		clk_set_min_rate(parent, new_rate);
+
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+}
+
+static unsigned voter_clk_get_rate(struct clk *clk)
+{
+	unsigned rate;
+	unsigned long flags;
+	struct clk_voter *v = to_clk_voter(clk);
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	rate = v->rate;
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return rate;
+}
+
+static unsigned voter_clk_is_enabled(struct clk *clk)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return v->enabled;
+}
+
+static long voter_clk_round_rate(struct clk *clk, unsigned rate)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return clk_round_rate(v->parent, rate);
+}
+
+static int voter_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&voter_clk_lock, flags);
+	if (list_empty(&clk->siblings))
+		list_add(&clk->siblings, &parent->children);
+	spin_unlock_irqrestore(&voter_clk_lock, flags);
+
+	return 0;
+}
+
+static struct clk *voter_clk_get_parent(struct clk *clk)
+{
+	struct clk_voter *v = to_clk_voter(clk);
+	return v->parent;
+}
+
+static bool voter_clk_is_local(struct clk *clk)
+{
+	return true;
+}
+
+struct clk_ops clk_ops_voter = {
+	.enable = voter_clk_enable,
+	.disable = voter_clk_disable,
+	.set_rate = voter_clk_set_rate,
+	.set_min_rate = voter_clk_set_rate,
+	.get_rate = voter_clk_get_rate,
+	.is_enabled = voter_clk_is_enabled,
+	.round_rate = voter_clk_round_rate,
+	.set_parent = voter_clk_set_parent,
+	.get_parent = voter_clk_get_parent,
+	.is_local = voter_clk_is_local,
+};
diff --git a/arch/arm/mach-msm/clock-voter.h b/arch/arm/mach-msm/clock-voter.h
new file mode 100644
index 0000000..170ba67
--- /dev/null
+++ b/arch/arm/mach-msm/clock-voter.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+
+struct clk_ops;
+extern struct clk_ops clk_ops_voter;
+
+struct clk_voter {
+	bool enabled;
+	unsigned rate;
+	struct clk *parent;
+	struct clk c;
+};
+
+static inline struct clk_voter *to_clk_voter(struct clk *clk)
+{
+	return container_of(clk, struct clk_voter, c);
+}
+
+#define DEFINE_CLK_VOTER(clk_name, _parent) \
+	struct clk_voter clk_name = { \
+		.parent = _parent, \
+		.c = { \
+			.dbg_name = #clk_name, \
+			.ops = &clk_ops_voter, \
+			CLK_INIT(clk_name.c), \
+		}, \
+	}
+
+#endif
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 026bdc0..ad55eaa 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -154,12 +154,6 @@ int clk_set_flags(struct clk *clk, unsigned long flags)
 }
 EXPORT_SYMBOL(clk_set_flags);
 
-/* EBI1 is the only shared clock that several clients want to vote on as of
- * this commit. If this changes in the future, then it might be better to
- * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more
- * generic to support different clocks.
- */
-static struct clk *ebi1_clk;
 static struct clk_lookup *msm_clocks;
 static unsigned msm_num_clocks;
 
@@ -167,13 +161,13 @@ void __init msm_clock_init(struct clk_lookup *clock_tbl, size_t num_clocks)
 {
 	unsigned n;
 
-	clkdev_add_table(clock_tbl, num_clocks);
-
-	for (n = 0; n < num_clocks; n++)
-		spin_lock_init(&clock_tbl[n].clk->lock);
+	for (n = 0; n < num_clocks; n++) {
+		struct clk *clk = clock_tbl[n].clk;
+		struct clk *parent = clk_get_parent(clk);
+		clk_set_parent(clk, parent);
+	}
 
-	ebi1_clk = clk_get(NULL, "ebi1_clk");
-	BUG_ON(ebi1_clk == NULL);
+	clkdev_add_table(clock_tbl, num_clocks);
 
 	msm_clocks = clock_tbl;
 	msm_num_clocks = num_clocks;
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 6a7cbca..6aecd95 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -55,9 +55,18 @@ struct clk {
 	uint32_t flags;
 	struct clk_ops *ops;
 	const char *dbg_name;
+
+	struct list_head children;
+	struct list_head siblings;
+
 	spinlock_t lock;
 };
 
+#define CLK_INIT(name) \
+	.lock = __SPIN_LOCK_UNLOCKED((name).lock), \
+	.children = LIST_HEAD_INIT((name).children), \
+	.siblings = LIST_HEAD_INIT((name).siblings)
+
 #define OFF CLKFLAG_AUTO_OFF
 #define CLK_MIN CLKFLAG_MIN
 #define CLK_MAX CLKFLAG_MAX
-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


  parent reply	other threads:[~2011-11-02 18:36 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-11-02 18:35 [RFC PATCH 00/34] msm: msm8660 and msm8960 clock support David Brown
2011-11-02 18:35 ` David Brown
2011-11-02 18:35 ` [RFC PATCH 01/34] msm: clock-pcom: Mark functions static David Brown
2011-11-02 18:35   ` David Brown
2011-11-02 18:35 ` [RFC PATCH 02/34] msm: clock: Always use an array to iterate over clocks David Brown
2011-11-02 18:35   ` David Brown
2011-11-02 18:35   ` David Brown
2011-11-02 19:45   ` Russell King - ARM Linux
2011-11-02 19:45     ` Russell King - ARM Linux
2011-11-02 21:34     ` Stephen Boyd
2011-11-02 21:34       ` Stephen Boyd
2011-11-02 18:36 ` [RFC PATCH 03/34] msm: clock: Pass struct clk to the clk_ops David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 04/34] msm: clock: Support one lock per clock David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 05/34] msm: clock-pcom: Introduce a struct pcom_clk David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 06/34] msm: clock: Support clk_[s|g]et_parent() clk_ops David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 07/34] msm: clock-debug: Use clk_enable()/clk_disable() directly David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 08/34] msm: clock: Enable/disable parent clocks generically David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` David Brown [this message]
2011-11-02 18:36   ` [RFC PATCH 09/34] msm: clock: Implement rate voting David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 10/34] msm: clock-pcom: Add pbus specific clock ops David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 11/34] msm: Migrate to clock rate voting David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 12/34] msm: clock: Make most clk_*() operations optional David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 13/34] msm: clock-debug: Implement a default is_enabled() David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 14/34] msm: proc_comm: Add CLKCTL_RPC_SRC_REQUEST David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 15/34] msm: clock: Add local clock control framework David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 16/34] msm: clock-pcom: Expose pc_clk_reset David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 17/34] msm: clock: Add 7x30 local clock support David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 18/34] msm: clock-local: Add support for 8x60 clock types David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 19/34] msm: clock: Add 8x60 clock support David Brown
2011-11-02 18:36 ` [RFC PATCH 20/34] msm: clock: Add list_rate debugfs nodes for locally-controlled clocks David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 21/34] msm: clock: Add debugfs interface to measure clock rates David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 22/34] msm: clock-8x60: Support measurement of CPU and L2 clocks David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 23/34] msm: Unify iomap for clock regions David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 24/34] msm: clock: Support dummy clocks David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 25/34] msm: clock: Add 8960 clock support David Brown
2011-11-02 18:36 ` [RFC PATCH 26/34] msm: 8660: Add FLUID support David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 27/34] msm-8x60: Add serial support David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 28/34] msm: clock: Invert CLKFLAG_AUTO_OFF David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 29/34] msm: clock: Expand CLK_MIN, CLK_MAX and CLK_MINMAX macros David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 30/34] msm: clock: Add EBI1 voter clocks for ADM on SoCs without them David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 31/34] msm: clock: Remove msm_clk_soc_init() David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 32/34] msm: clock-8x60: Add local control of vpe_axi_clk and vpe_axi_clk David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 33/34] ARM: msm: fix names of UART clocks David Brown
2011-11-02 18:36   ` David Brown
2011-11-02 18:36 ` [RFC PATCH 34/34] msm_serial: fix clock rate on DMA-based uarts David Brown
2011-11-02 18:36   ` David Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1320258991-22325-10-git-send-email-davidb@codeaurora.org \
    --to=davidb@codeaurora.org \
    --cc=bryanh@codeaurora.org \
    --cc=dwalker@fifo99.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@arm.linux.org.uk \
    --cc=sboyd@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.