Linux ARM-MSM sub-architecture
 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.

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

Thread overview: 37+ 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 ` [RFC PATCH 01/34] msm: clock-pcom: Mark functions static 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 19:45   ` Russell King - ARM Linux
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 ` [RFC PATCH 04/34] msm: clock: Support one lock per clock 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 ` [RFC PATCH 06/34] msm: clock: Support clk_[s|g]et_parent() clk_ops 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 ` [RFC PATCH 08/34] msm: clock: Enable/disable parent clocks generically David Brown
2011-11-02 18:36 ` David Brown [this message]
2011-11-02 18:36 ` [RFC PATCH 10/34] msm: clock-pcom: Add pbus specific clock ops David Brown
2011-11-02 18:36 ` [RFC PATCH 11/34] msm: Migrate to clock rate voting 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 ` [RFC PATCH 13/34] msm: clock-debug: Implement a default is_enabled() 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 ` [RFC PATCH 15/34] msm: clock: Add local clock control framework 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 ` [RFC PATCH 17/34] msm: clock: Add 7x30 local clock support 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 ` [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 ` [RFC PATCH 21/34] msm: clock: Add debugfs interface to measure clock rates 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 ` [RFC PATCH 23/34] msm: Unify iomap for clock regions David Brown
2011-11-02 18:36 ` [RFC PATCH 24/34] msm: clock: Support dummy clocks 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 ` [RFC PATCH 27/34] msm-8x60: Add serial support David Brown
2011-11-02 18:36 ` [RFC PATCH 28/34] msm: clock: Invert CLKFLAG_AUTO_OFF 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 ` [RFC PATCH 30/34] msm: clock: Add EBI1 voter clocks for ADM on SoCs without them 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 ` [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 ` [RFC PATCH 33/34] ARM: msm: fix names of UART clocks David Brown
2011-11-02 18:36 ` [RFC PATCH 34/34] msm_serial: fix clock rate on DMA-based uarts 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox