Skip to contentSkip to conversation

Krait clocks + Krait CPUfreq

PATCH In review Stephen Boyd 2 participants 16 messages Mar 21, 2015 – Apr 2, 2015

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

v3 cover Krait clocks + Krait CPUfreq

These patches provide cpufreq scaling on devices with Krait CPUs. In Krait CPU designs there's one PLL and two muxes per CPU, allowing us to switch CPU frequencies independently.

				 secondary
	 +-----+                    +
	 | QSB |-------+------------|\
	 +-----+       |            | |-+
		       |    +-------|/  |
		       |    |       +   |
	 +-----+       |    |           |
	 | PLL |----+-------+           |   primary
	 +-----+    |  |                |     +
		    |  |                +-----|\       +------+
	 +-------+  |  |                      | \      |      |
	 | HFPLL |----------+-----------------|  |-----| CPU0 |
	 +-------+  |  |    |                 |  |     |      |
		    |  |    | +-----+         | /      +------+
		    |  |    +-| / 2 |---------|/
		    |  |      +-----+         +
		    |  |         secondary
		    |  |            +
		    |  +------------|\
		    |               | |-+
		    +---------------|/  |   primary
				    +   |     +
					+-----|\       +------+
	 +-------+                            | \      |      |
	 | HFPLL |----------------------------|  |-----| CPU1 |
	 +-------+          |                 |  |     |      |
			    | +-----+         | /      +------+
			    +-| / 2 |---------|/
			      +-----+         +

To support this in the common clock framework we model the muxes, dividers, and PLLs as different clocks. CPUfreq only interacts with the primary mux (farthest right in the diagram). When CPUfreq sets a rate, the mux code finds the best parent that can provide the rate. Due to the design, QSB and the top PLL are always a fixed rate and thus only support one frequency each. These sources provide the lowest frequencies for the CPUs. The HFPLLs are where we can make the CPU go faster (GHz range). Sometimes we need to run the HFPLL twice as fast and divide it by two to get a particular frequency.

When switching rates we can't leave the CPU clocked by the HFPLL because we need to turn off the output of the PLL when changing its frequency. This means we have to switch over to the secondary mux and use one of the fixed sources. This is why we need something like the safe parent patch.

I plan to submit the DTS changes through arm-soc, but I've included everything here to make it easier to pick things up for testing, etc. If anything can be picked up right now it would be better to reduce the churn over time as other pieces settle. Some things are not done, but I'm posting this now to get it out there and because the clock framework patches needed some rework due to recent changes.

Changes since v2:
* Switched to cpufreq-dt
* Ported over PVS binding
* Stripped out cpu logical map to make modules work
* Dropped patches that merged upstream

Changes since v1:
* Added IPQ and APQ8064 support
* Switched to cpufreq-generic
* Added OPP parsing from DT (need to write binding though)
* New patches to make clk-generic.c go away
* Made mux and divider reusable for non-MMIO devices
* Added a mux_determine_rate_closest (not sure if this is really needed)
* Added unregistration of muxes
* New patch to avoid sending high frequencies down to devices using clocks

TODO:
* Add Krait regulator voltage scaling (not strictly necessary)
* Add some thermal awareness
* Use efuse/eeprom API instead of hardcoding the location in the driver
* Figure out how to express number of CPUs without relying on linux's concept of number of CPUs and logical CPU mapping
* Trim down the probe code for krait-cc so that we just register clocks and don't enable or set rates

Stephen Boyd (13):
ARM: Add Krait L2 register accessor functions
clk: mux: Split out register accessors for reuse
clk: Avoid sending high rates to downstream clocks during set_rate
clk: Add safe switch hook
clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
clk: qcom: Add HFPLL driver
clk: qcom: Add MSM8960/APQ8064's HFPLLs
clk: qcom: Add IPQ806X's HFPLLs
clk: qcom: Add support for Krait clocks
clk: qcom: Add KPSS ACC/GCC driver
clk: qcom: Add Krait clock controller driver
cpufreq: Add module to register cpufreq on Krait CPUs
ARM: dts: qcom: Add necessary DT data for Krait cpufreq

 .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt  |   7 +
 .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt  |  28 ++
 .../devicetree/bindings/arm/msm/qcom,pvs.txt       |  38 +++
 .../devicetree/bindings/clock/qcom,hfpll.txt       |  40 +++
 .../devicetree/bindings/clock/qcom,krait-cc.txt    |  22 ++
 arch/arm/boot/dts/qcom-apq8064.dtsi                | 230 ++++++++++++++
 arch/arm/boot/dts/qcom-msm8960.dtsi                |  49 +++
 arch/arm/boot/dts/qcom-msm8974.dtsi                | 311 +++++++++++++++++-
 arch/arm/common/Kconfig                            |   3 +
 arch/arm/common/Makefile                           |   1 +
 arch/arm/common/krait-l2-accessors.c               |  58 ++++
 arch/arm/include/asm/krait-l2-accessors.h          |  20 ++
 drivers/clk/clk-mux.c                              |  76 +++--
 drivers/clk/clk.c                                  |  95 ++++--
 drivers/clk/qcom/Kconfig                           |  28 ++
 drivers/clk/qcom/Makefile                          |   5 +
 drivers/clk/qcom/clk-hfpll.c                       | 253 +++++++++++++++
 drivers/clk/qcom/clk-hfpll.h                       |  54 ++++
 drivers/clk/qcom/clk-krait.c                       | 166 ++++++++++
 drivers/clk/qcom/clk-krait.h                       |  49 +++
 drivers/clk/qcom/gcc-ipq806x.c                     |  83 +++++
 drivers/clk/qcom/gcc-msm8960.c                     | 172 ++++++++++
 drivers/clk/qcom/hfpll.c                           | 109 +++++++
 drivers/clk/qcom/kpss-xcc.c                        |  95 ++++++
 drivers/clk/qcom/krait-cc.c                        | 352 +++++++++++++++++++++
 drivers/cpufreq/Kconfig.arm                        |   9 +
 drivers/cpufreq/Makefile                           |   1 +
 drivers/cpufreq/qcom-cpufreq.c                     | 204 ++++++++++++
 include/dt-bindings/clock/qcom,gcc-msm8960.h       |   2 +
 include/linux/clk-provider.h                       |  10 +-
 30 files changed, 2515 insertions(+), 55 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
 create mode 100644 arch/arm/common/krait-l2-accessors.c
 create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
 create mode 100644 drivers/clk/qcom/clk-hfpll.c
 create mode 100644 drivers/clk/qcom/clk-hfpll.h
 create mode 100644 drivers/clk/qcom/clk-krait.c
 create mode 100644 drivers/clk/qcom/clk-krait.h
 create mode 100644 drivers/clk/qcom/hfpll.c
 create mode 100644 drivers/clk/qcom/kpss-xcc.c
 create mode 100644 drivers/clk/qcom/krait-cc.c
 create mode 100644 drivers/cpufreq/qcom-cpufreq.c
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#1 lore raw

PATCH v3 1/13 ARM: Add Krait L2 register accessor functions

Krait CPUs have a handful of L2 cache controller registers that live behind a cp15 based indirection register. First you program the indirection register (l2cpselr) to point the L2 'window' register (l2cpdr) at what you want to read/write. Then you read/write the 'window' register to do what you want. The l2cpselr register is not banked per-cpu so we must lock around accesses to it to prevent other CPUs from re-pointing l2cpdr underneath us.

Cc Mark Rutland <[email protected]>
Cc Russell King <[email protected]>
Cc Courtney Cavin <[email protected]>
---
 arch/arm/common/Kconfig                   |  3 ++
 arch/arm/common/Makefile                  |  1 +
 arch/arm/common/krait-l2-accessors.c      | 58 +++++++++++++++++++++++++++++++
 arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
 4 files changed, 82 insertions(+)
 create mode 100644 arch/arm/common/krait-l2-accessors.c
 create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
arch/arm/common/Kconfig +3 -0
index c3a4e9ceba34..9da52dc6260b 100644
@@ -9,6 +9,9 @@ config DMABOUNCE
 	bool
 	select ZONE_DMA
 
+config KRAIT_L2_ACCESSORS
+	bool
+
 config SHARP_LOCOMO
 	bool
 
arch/arm/common/Makefile +1 -0
index 70b1eff477b3..6b2cddf6e8d0 100644
@@ -7,6 +7,7 @@ obj-y				+= firmware.o
 obj-$(CONFIG_ICST)		+= icst.o
 obj-$(CONFIG_SA1111)		+= sa1111.o
 obj-$(CONFIG_DMABOUNCE)		+= dmabounce.o
+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
 obj-$(CONFIG_SHARP_LOCOMO)	+= locomo.o
 obj-$(CONFIG_SHARP_PARAM)	+= sharpsl_param.o
 obj-$(CONFIG_SHARP_SCOOP)	+= scoop.o
arch/arm/common/krait-l2-accessors.c new file +58 -0

58 lines not on this page · raw message

arch/arm/include/asm/krait-l2-accessors.h new file +20 -0
new file mode 100644
index 000000000000..48fe5527bc01
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. 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 __ASMARM_KRAIT_L2_ACCESSORS_H
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
+
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
+extern u32 krait_get_l2_indirect_reg(u32 addr);
+
+#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#2 lore raw

PATCH v3 2/13 clk: mux: Split out register accessors for reuse

In reply to Stephen Boyd

We want to reuse the logic in clk-mux.c for other clock drivers that don't use readl as register accessors. Fortunately, there really isn't much to the mux code besides the table indirection and quirk flags if you assume any bit shifting and masking has been done already. Pull that logic out into reusable functions that operate on an optional table and some flags so that other drivers can use the same logic.

---
 drivers/clk/clk-mux.c        | 76 +++++++++++++++++++++++++++-----------------
 include/linux/clk-provider.h |  9 ++++--
 2 files changed, 54 insertions(+), 31 deletions(-)
drivers/clk/clk-mux.c +47 -29
index 69a094c3783d..70a05c7f0fb1 100644
@@ -29,35 +29,24 @@
 
 #define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
 
-static u8 clk_mux_get_parent(struct clk_hw *hw)
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
+				unsigned int *table, unsigned long flags)
 {
-	struct clk_mux *mux = to_clk_mux(hw);
 	int num_parents = __clk_get_num_parents(hw->clk);
-	u32 val;
 
-	/*
-	 * FIXME need a mux-specific flag to determine if val is bitwise or numeric
-	 * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
-	 * to 0x7 (index starts at one)
-	 * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
-	 * val = 0x4 really means "bit 2, index starts at bit 0"
-	 */
-	val = clk_readl(mux->reg) >> mux->shift;
-	val &= mux->mask;
-
-	if (mux->table) {
+	if (table) {
 		int i;
 
 		for (i = 0; i < num_parents; i++)
-			if (mux->table[i] == val)
+			if (table[i] == val)
 				return i;
 		return -EINVAL;
 	}
 
-	if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+	if (val && (flags & CLK_MUX_INDEX_BIT))
 		val = ffs(val) - 1;
 
-	if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+	if (val && (flags & CLK_MUX_INDEX_ONE))
 		val--;
 
 	if (val >= num_parents)
@@ -65,24 +54,53 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
 
 	return val;
 }
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
 
-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+static u8 _clk_mux_get_parent(struct clk_hw *hw)
 {
 	struct clk_mux *mux = to_clk_mux(hw);
 	u32 val;
-	unsigned long flags = 0;
 
-	if (mux->table)
-		index = mux->table[index];
+	/*
+	 * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+	 * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+	 * to 0x7 (index starts at one)
+	 * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+	 * val = 0x4 really means "bit 2, index starts at bit 0"
+	 */
+	val = clk_readl(mux->reg) >> mux->shift;
+	val &= mux->mask;
+
+	return clk_mux_get_parent(hw, val, mux->table, mux->flags);
+}
 
-	else {
-		if (mux->flags & CLK_MUX_INDEX_BIT)
-			index = 1 << index;
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
+			     unsigned long flags)
+{
+	unsigned int val = index;
 
-		if (mux->flags & CLK_MUX_INDEX_ONE)
-			index++;
+	if (table) {
+		val = table[val];
+	} else {
+		if (flags & CLK_MUX_INDEX_BIT)
+			val = 1 << index;
+
+		if (flags & CLK_MUX_INDEX_ONE)
+			val++;
 	}
 
+	return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_reindex);
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_mux *mux = to_clk_mux(hw);
+	u32 val;
+	unsigned long flags = 0;
+
+	index = clk_mux_reindex(index, mux->table, mux->flags);
+
 	if (mux->lock)
 		spin_lock_irqsave(mux->lock, flags);
 
@@ -102,21 +120,21 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
 }
 
 const struct clk_ops clk_mux_ops = {
-	.get_parent = clk_mux_get_parent,
+	.get_parent = _clk_mux_get_parent,
 	.set_parent = clk_mux_set_parent,
 	.determine_rate = __clk_mux_determine_rate,
 };
 EXPORT_SYMBOL_GPL(clk_mux_ops);
 
 const struct clk_ops clk_mux_ro_ops = {
-	.get_parent = clk_mux_get_parent,
+	.get_parent = _clk_mux_get_parent,
 };
 EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
 
 struct clk *clk_register_mux_table(struct device *dev, const char *name,
 		const char **parent_names, u8 num_parents, unsigned long flags,
 		void __iomem *reg, u8 shift, u32 mask,
-		u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+		u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
 {
 	struct clk_mux *mux;
 	struct clk *clk;
include/linux/clk-provider.h +7 -2
index 5591ea71a8d1..f7a6f60f8e0c 100644
@@ -409,7 +409,7 @@ void clk_unregister_divider(struct clk *clk);
 struct clk_mux {
 	struct clk_hw	hw;
 	void __iomem	*reg;
-	u32		*table;
+	unsigned int	*table;
 	u32		mask;
 	u8		shift;
 	u8		flags;
@@ -425,6 +425,11 @@ struct clk_mux {
 extern const struct clk_ops clk_mux_ops;
 extern const struct clk_ops clk_mux_ro_ops;
 
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
+				unsigned int *table, unsigned long flags);
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
+			     unsigned long flags);
+
 struct clk *clk_register_mux(struct device *dev, const char *name,
 		const char **parent_names, u8 num_parents, unsigned long flags,
 		void __iomem *reg, u8 shift, u8 width,
@@ -433,7 +438,7 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
 struct clk *clk_register_mux_table(struct device *dev, const char *name,
 		const char **parent_names, u8 num_parents, unsigned long flags,
 		void __iomem *reg, u8 shift, u32 mask,
-		u8 clk_mux_flags, u32 *table, spinlock_t *lock);
+		u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
 
 void clk_unregister_mux(struct clk *clk);
 
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#3 lore raw

PATCH v3 3/13 clk: Avoid sending high rates to downstream clocks during set_rate

In reply to Stephen Boyd

If a clock is on and we call clk_set_rate() on it we may get into a situation where the clock temporarily increases in rate dramatically while we walk the tree and call .set_rate() ops. For example, consider a case where a PLL feeds into a divider. Initially the divider is set to divide by 1 and the PLL is running fairly slow (100MHz). The downstream consumer of the divider output can only handle rates =< 400 MHz, but the divider can only choose between divisors of 1 and 4.

 +-----+   +----------------+
 | PLL |-->| div 1 or div 4 |---> consumer device
 +-----+   +----------------+

To achieve a rate of 400MHz on the output of the divider, we would have to set the rate of the PLL to 1.6 GHz and then divide it by 4. The current code would set the PLL to 1.6GHz first while the divider is still set to 1, thus causing the downstream consumer of the clock to receive a few clock cycles of 1.6GHz clock (far beyond it's maximum acceptable rate). We should be changing the divider first before increasing the PLL rate to avoid this problem.

Therefore, set the rate of any child clocks that are increasing in rate from their current rate so that they can increase their dividers if necessary. We assume that there isn't such a thing as minimum rate requirements.

---
 drivers/clk/clk.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)
drivers/clk/clk.c +22 -12
index eb0152961d3c..0712bea649c1 100644
@@ -1689,21 +1689,24 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *clk,
  * walk down a subtree and set the new rates notifying the rate
  * change on the way
  */
-static void clk_change_rate(struct clk_core *clk)
+static void
+clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate)
 {
 	struct clk_core *child;
 	struct hlist_node *tmp;
 	unsigned long old_rate;
-	unsigned long best_parent_rate = 0;
 	bool skip_set_rate = false;
 	struct clk_core *old_parent;
 
-	old_rate = clk->rate;
+	hlist_for_each_entry(child, &clk->children, child_node) {
+		/* Skip children who will be reparented to another clock */
+		if (child->new_parent && child->new_parent != clk)
+			continue;
+		if (child->new_rate > child->rate)
+			clk_change_rate(child, clk->new_rate);
+	}
 
-	if (clk->new_parent)
-		best_parent_rate = clk->new_parent->rate;
-	else if (clk->parent)
-		best_parent_rate = clk->parent->rate;
+	old_rate = clk->rate;
 
 	if (clk->new_parent && clk->new_parent != clk->parent) {
 		old_parent = __clk_set_parent_before(clk, clk->new_parent);
@@ -1723,7 +1726,7 @@ static void clk_change_rate(struct clk_core *clk)
 	if (!skip_set_rate && clk->ops->set_rate)
 		clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
 
-	clk->rate = clk_recalc(clk, best_parent_rate);
+	clk->rate = clk->new_rate;
 
 	if (clk->notifier_count && old_rate != clk->rate)
 		__clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
@@ -1736,12 +1739,13 @@ static void clk_change_rate(struct clk_core *clk)
 		/* Skip children who will be reparented to another clock */
 		if (child->new_parent && child->new_parent != clk)
 			continue;
-		clk_change_rate(child);
+		if (child->new_rate != child->rate)
+			clk_change_rate(child, clk->new_rate);
 	}
 
 	/* handle the new child who might not be in clk->children yet */
-	if (clk->new_child)
-		clk_change_rate(clk->new_child);
+	if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate)
+		clk_change_rate(clk->new_child, clk->new_rate);
 }
 
 static int clk_core_set_rate_nolock(struct clk_core *clk,
@@ -1750,6 +1754,7 @@ static int clk_core_set_rate_nolock(struct clk_core *clk,
 	struct clk_core *top, *fail_clk;
 	unsigned long rate = req_rate;
 	int ret = 0;
+	unsigned long parent_rate;
 
 	if (!clk)
 		return 0;
@@ -1775,8 +1780,13 @@ static int clk_core_set_rate_nolock(struct clk_core *clk,
 		return -EBUSY;
 	}
 
+	if (top->parent)
+		parent_rate = top->parent->rate;
+	else
+		parent_rate = 0;
+
 	/* change the rates */
-	clk_change_rate(top);
+	clk_change_rate(top, parent_rate);
 
 	clk->req_rate = req_rate;
 
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#4 lore raw

PATCH v3 4/13 clk: Add safe switch hook

In reply to Stephen Boyd

Sometimes clocks can't accept their parent source turning off while the source is reprogrammed to a different rate. Most notably CPU clocks require a way to switch away from the current PLL they're running on, reprogram that PLL to a new rate, and then switch back to the PLL with the new rate once they're done. Add a hook that drivers can implement allowing them to return a 'safe parent' that they can switch their parent to while the upstream source is reprogrammed to support this.

---

This patch is good enough for Krait, but soon I'll need to support a "safe rate" where we ask a clock what rate it needs to be running at to be sure it's within voltage constraints. Right now safe parent handles that problem on Krait, but on other platforms it won't work.

 drivers/clk/clk.c            | 61 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-provider.h |  1 +
 2 files changed, 54 insertions(+), 8 deletions(-)
drivers/clk/clk.c +53 -8
index 0712bea649c1..ead015a8e047 100644
@@ -56,9 +56,12 @@ struct clk_core {
 	struct clk_core		**parents;
 	u8			num_parents;
 	u8			new_parent_index;
+	u8			safe_parent_index;
 	unsigned long		rate;
 	unsigned long		req_rate;
+	unsigned long		old_rate;
 	unsigned long		new_rate;
+	struct clk_core		*safe_parent;
 	struct clk_core		*new_parent;
 	struct clk_core		*new_child;
 	unsigned long		flags;
@@ -1550,7 +1553,8 @@ out:
 static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate,
 			     struct clk_core *new_parent, u8 p_index)
 {
-	struct clk_core *child;
+	struct clk_core *child, *parent;
+	struct clk_hw *parent_hw;
 
 	clk->new_rate = new_rate;
 	clk->new_parent = new_parent;
@@ -1560,6 +1564,18 @@ static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate,
 	if (new_parent && new_parent != clk->parent)
 		new_parent->new_child = clk;
 
+	if (clk->ops->get_safe_parent) {
+		parent_hw = clk->ops->get_safe_parent(clk->hw);
+		if (parent_hw) {
+			parent = parent_hw->core;
+			p_index = clk_fetch_parent_index(clk, parent);
+			clk->safe_parent_index = p_index;
+			clk->safe_parent = parent;
+		}
+	} else {
+		clk->safe_parent = NULL;
+	}
+
 	hlist_for_each_entry(child, &clk->children, child_node) {
 		child->new_rate = clk_recalc(child, new_rate);
 		clk_calc_subtree(child, child->new_rate, NULL, 0);
@@ -1655,14 +1671,43 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *clk,
 						  unsigned long event)
 {
 	struct clk_core *child, *tmp_clk, *fail_clk = NULL;
+	struct clk_core *old_parent;
 	int ret = NOTIFY_DONE;
 
-	if (clk->rate == clk->new_rate)
+	if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE)
 		return NULL;
 
+	switch (event) {
+	case PRE_RATE_CHANGE:
+		if (clk->safe_parent)
+			clk->ops->set_parent(clk->hw, clk->safe_parent_index);
+		clk->old_rate = clk->rate;
+		break;
+	case POST_RATE_CHANGE:
+		if (clk->safe_parent) {
+			old_parent = __clk_set_parent_before(clk,
+							     clk->new_parent);
+			if (clk->ops->set_rate_and_parent) {
+				clk->ops->set_rate_and_parent(clk->hw,
+						clk->new_rate,
+						clk->new_parent ?
+						clk->new_parent->rate : 0,
+						clk->new_parent_index);
+			} else if (clk->ops->set_parent) {
+				clk->ops->set_parent(clk->hw,
+						clk->new_parent_index);
+			}
+			__clk_set_parent_after(clk, clk->new_parent,
+					       old_parent);
+		}
+		break;
+	}
+
 	if (clk->notifier_count) {
-		ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
-		if (ret & NOTIFY_STOP_MASK)
+		if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate)
+			ret = __clk_notify(clk, event, clk->old_rate,
+					   clk->new_rate);
+		if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
 			fail_clk = clk;
 	}
 
@@ -1708,7 +1753,8 @@ clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate)
 
 	old_rate = clk->rate;
 
-	if (clk->new_parent && clk->new_parent != clk->parent) {
+	if (clk->new_parent && clk->new_parent != clk->parent &&
+			!clk->safe_parent) {
 		old_parent = __clk_set_parent_before(clk, clk->new_parent);
 
 		if (clk->ops->set_rate_and_parent) {
@@ -1728,9 +1774,6 @@ clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate)
 
 	clk->rate = clk->new_rate;
 
-	if (clk->notifier_count && old_rate != clk->rate)
-		__clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
-
 	/*
 	 * Use safe iteration, as change_rate can actually swap parents
 	 * for certain clock types.
@@ -1790,6 +1833,8 @@ static int clk_core_set_rate_nolock(struct clk_core *clk,
 
 	clk->req_rate = req_rate;
 
+	clk_propagate_rate_change(top, POST_RATE_CHANGE);
+
 	return ret;
 }
 
include/linux/clk-provider.h +1 -0
index f7a6f60f8e0c..bc0f6faaeda4 100644
@@ -183,6 +183,7 @@ struct clk_ops {
 					  struct clk_hw **best_parent_hw);
 	int		(*set_parent)(struct clk_hw *hw, u8 index);
 	u8		(*get_parent)(struct clk_hw *hw);
+	struct clk_hw	*(*get_safe_parent)(struct clk_hw *hw);
 	int		(*set_rate)(struct clk_hw *hw, unsigned long rate,
 				    unsigned long parent_rate);
 	int		(*set_rate_and_parent)(struct clk_hw *hw,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#5 lore raw

PATCH v3 5/13 clk: qcom: Add support for High-Frequency PLLs (HFPLLs)

In reply to Stephen Boyd

HFPLLs are the main frequency source for Krait CPU clocks. Add support for changing the rate of these PLLs.

---

I'd really like to get rid of __clk_hfpll_init_once() if possible...

 drivers/clk/qcom/Makefile    |   1 +
 drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-hfpll.h |  54 +++++++++
 3 files changed, 308 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-hfpll.c
 create mode 100644 drivers/clk/qcom/clk-hfpll.h
drivers/clk/qcom/Makefile +1 -0
index 617826469595..953f792d2133 100644
@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
+clk-qcom-y += clk-hfpll.o
 clk-qcom-y += reset.o
 
 obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
drivers/clk/qcom/clk-hfpll.c new file +253 -0

253 lines not on this page · raw message

drivers/clk/qcom/clk-hfpll.h new file +54 -0

54 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#6 lore raw

PATCH v3 6/13 clk: qcom: Add HFPLL driver

In reply to Stephen Boyd

On some devices (MSM8974 for example), the HFPLLs are instantiated within the Krait processor subsystem as separate register regions. Add a driver for these PLLs so that we can provide HFPLL clocks for use by the system.

---
 .../devicetree/bindings/clock/qcom,hfpll.txt       |  40 ++++++++
 drivers/clk/qcom/Kconfig                           |   8 ++
 drivers/clk/qcom/Makefile                          |   1 +
 drivers/clk/qcom/hfpll.c                           | 109 +++++++++++++++++++++
 4 files changed, 158 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
 create mode 100644 drivers/clk/qcom/hfpll.c
Documentation/devicetree/bindings/clock/qcom,hfpll.txt new file +40 -0
new file mode 100644
index 000000000000..fee92bb30344
@@ -0,0 +1,40 @@
+High-Frequency PLL (HFPLL)
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be "qcom,hfpll"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: address and size of HPLL registers. An optional second
+		    element specifies the address and size of the alias
+		    register region.
+
+- clock-output-names:
+	Usage: required
+	Value type: <string>
+	Definition: Name of the PLL. Typically hfpllX where X is a CPU number
+		    starting at 0. Otherwise hfpll_Y where Y is more specific
+		    such as "l2".
+
+Example:
+
+1) An HFPLL for the L2 cache.
+
+	clock-controller at f9016000 {
+		compatible = "qcom,hfpll";
+		reg = <0xf9016000 0x30>;
+		clock-output-names = "hfpll_l2";
+	};
+
+2) An HFPLL for CPU0. This HFPLL has the alias register region.
+
+	clock-controller at f908a000 {
+		compatible = "qcom,hfpll";
+		reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
+		clock-output-names = "hfpll0";
+	};
drivers/clk/qcom/Kconfig +8 -0
index 0d7ab52b7ab0..d421fd195114 100644
@@ -88,3 +88,11 @@ config MSM_MMCC_8974
 	  Support for the multimedia clock controller on msm8974 devices.
 	  Say Y if you want to support multimedia devices such as display,
 	  graphics, video encode/decode, camera, etc.
+
+config QCOM_HFPLL
+	tristate "High-Frequency PLL (HFPLL) Clock Controller"
+	depends on COMMON_CLK_QCOM
+	help
+	  Support for the high-frequency PLLs present on Qualcomm devices.
+	  Say Y if you want to support CPU frequency scaling on devices
+	  such as MSM8974, APQ8084, etc.
drivers/clk/qcom/Makefile +1 -0
index 953f792d2133..b1076179fb17 100644
@@ -21,3 +21,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
 obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
 obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
 obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
drivers/clk/qcom/hfpll.c new file +109 -0

109 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#7 lore raw

PATCH v3 7/13 clk: qcom: Add MSM8960/APQ8064's HFPLLs

In reply to Stephen Boyd

Describe the HFPLLs present on MSM8960 and APQ8064 devices.

---
 drivers/clk/qcom/gcc-msm8960.c               | 172 +++++++++++++++++++++++++++
 include/dt-bindings/clock/qcom,gcc-msm8960.h |   2 +
 2 files changed, 174 insertions(+)
drivers/clk/qcom/gcc-msm8960.c +172 -0
index b0b562b9ce0e..0cd3e26f9995 100644
@@ -30,6 +30,7 @@
 #include "clk-pll.h"
 #include "clk-rcg.h"
 #include "clk-branch.h"
+#include "clk-hfpll.h"
 #include "reset.h"
 
 static struct clk_pll pll3 = {
@@ -75,6 +76,164 @@ static struct clk_regmap pll8_vote = {
 	},
 };
 
+static struct hfpll_data hfpll0_data = {
+	.mode_reg = 0x3200,
+	.l_reg = 0x3208,
+	.m_reg = 0x320c,
+	.n_reg = 0x3210,
+	.config_reg = 0x3204,
+	.status_reg = 0x321c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3214,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll0 = {
+	.d = &hfpll0_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll0",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
+};
+
+static struct hfpll_data hfpll1_8064_data = {
+	.mode_reg = 0x3240,
+	.l_reg = 0x3248,
+	.m_reg = 0x324c,
+	.n_reg = 0x3250,
+	.config_reg = 0x3244,
+	.status_reg = 0x325c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3254,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct hfpll_data hfpll1_data = {
+	.mode_reg = 0x3300,
+	.l_reg = 0x3308,
+	.m_reg = 0x330c,
+	.n_reg = 0x3310,
+	.config_reg = 0x3304,
+	.status_reg = 0x331c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3314,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll1 = {
+	.d = &hfpll1_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll1",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
+};
+
+static struct hfpll_data hfpll2_data = {
+	.mode_reg = 0x3280,
+	.l_reg = 0x3288,
+	.m_reg = 0x328c,
+	.n_reg = 0x3290,
+	.config_reg = 0x3284,
+	.status_reg = 0x329c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3294,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll2 = {
+	.d = &hfpll2_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll2",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll2.lock),
+};
+
+static struct hfpll_data hfpll3_data = {
+	.mode_reg = 0x32c0,
+	.l_reg = 0x32c8,
+	.m_reg = 0x32cc,
+	.n_reg = 0x32d0,
+	.config_reg = 0x32c4,
+	.status_reg = 0x32dc,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x32d4,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll3 = {
+	.d = &hfpll3_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll3",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll3.lock),
+};
+
+static struct hfpll_data hfpll_l2_8064_data = {
+	.mode_reg = 0x3300,
+	.l_reg = 0x3308,
+	.m_reg = 0x330c,
+	.n_reg = 0x3310,
+	.config_reg = 0x3304,
+	.status_reg = 0x331c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3314,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct hfpll_data hfpll_l2_data = {
+	.mode_reg = 0x3400,
+	.l_reg = 0x3408,
+	.m_reg = 0x340c,
+	.n_reg = 0x3410,
+	.config_reg = 0x3404,
+	.status_reg = 0x341c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3414,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll_l2 = {
+	.d = &hfpll_l2_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll_l2",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
+};
+
 static struct clk_pll pll14 = {
 	.l_reg = 0x31c4,
 	.m_reg = 0x31c8,
@@ -3140,6 +3299,9 @@ static struct clk_regmap *gcc_msm8960_clks[] = {
 	[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
 	[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
 	[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
+	[PLL9] = &hfpll0.clkr,
+	[PLL10] = &hfpll1.clkr,
+	[PLL12] = &hfpll_l2.clkr,
 };
 
 static const struct qcom_reset_map gcc_msm8960_resets[] = {
@@ -3350,6 +3512,11 @@ static struct clk_regmap *gcc_apq8064_clks[] = {
 	[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
 	[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
 	[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
+	[PLL9] = &hfpll0.clkr,
+	[PLL10] = &hfpll1.clkr,
+	[PLL12] = &hfpll_l2.clkr,
+	[PLL16] = &hfpll2.clkr,
+	[PLL17] = &hfpll3.clkr,
 };
 
 static const struct qcom_reset_map gcc_apq8064_resets[] = {
@@ -3496,6 +3663,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
 	if (!match)
 		return -EINVAL;
 
+	if (match->data == &gcc_apq8064_desc) {
+		hfpll1.d = &hfpll1_8064_data;
+		hfpll_l2.d = &hfpll_l2_8064_data;
+	}
+
 	/* Temporary until RPM clocks supported */
 	clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
 	if (IS_ERR(clk))
include/dt-bindings/clock/qcom,gcc-msm8960.h +2 -0
index 7d20eedfee98..e02742fc81cc 100644
@@ -319,5 +319,7 @@
 #define CE3_SRC					303
 #define CE3_CORE_CLK				304
 #define CE3_H_CLK				305
+#define PLL16					306
+#define PLL17					307
 
 #endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#8 lore raw

PATCH v3 8/13 clk: qcom: Add IPQ806X's HFPLLs

In reply to Stephen Boyd

Describe the HFPLLs present on IPQ806X devices.

---
 drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
drivers/clk/qcom/gcc-ipq806x.c +83 -0
index cbdc31dea7f4..5a03e91f1edc 100644
@@ -30,6 +30,7 @@
 #include "clk-pll.h"
 #include "clk-rcg.h"
 #include "clk-branch.h"
+#include "clk-hfpll.h"
 #include "reset.h"
 
 static struct clk_pll pll0 = {
@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = {
 	},
 };
 
+static struct hfpll_data hfpll0_data = {
+	.mode_reg = 0x3200,
+	.l_reg = 0x3208,
+	.m_reg = 0x320c,
+	.n_reg = 0x3210,
+	.config_reg = 0x3204,
+	.status_reg = 0x321c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3214,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll0 = {
+	.d = &hfpll0_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll0",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
+};
+
+static struct hfpll_data hfpll1_data = {
+	.mode_reg = 0x3240,
+	.l_reg = 0x3248,
+	.m_reg = 0x324c,
+	.n_reg = 0x3250,
+	.config_reg = 0x3244,
+	.status_reg = 0x325c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3314,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll1 = {
+	.d = &hfpll1_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll1",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
+};
+
+static struct hfpll_data hfpll_l2_data = {
+	.mode_reg = 0x3300,
+	.l_reg = 0x3308,
+	.m_reg = 0x330c,
+	.n_reg = 0x3310,
+	.config_reg = 0x3304,
+	.status_reg = 0x331c,
+	.config_val = 0x7845c665,
+	.droop_reg = 0x3314,
+	.droop_val = 0x0108c000,
+	.min_rate = 600000000UL,
+	.max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll_l2 = {
+	.d = &hfpll_l2_data,
+	.clkr.hw.init = &(struct clk_init_data){
+		.parent_names = (const char *[]){ "pxo" },
+		.num_parents = 1,
+		.name = "hfpll_l2",
+		.ops = &clk_ops_hfpll,
+		.flags = CLK_IGNORE_UNUSED,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
+};
+
+
 static struct clk_pll pll14 = {
 	.l_reg = 0x31c4,
 	.m_reg = 0x31c8,
@@ -2273,6 +2353,9 @@ static struct clk_regmap *gcc_ipq806x_clks[] = {
 	[USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
 	[USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
 	[USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr,
+	[PLL9] = &hfpll0.clkr,
+	[PLL10] = &hfpll1.clkr,
+	[PLL12] = &hfpll_l2.clkr,
 };
 
 static const struct qcom_reset_map gcc_ipq806x_resets[] = {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#9 lore raw

PATCH v3 9/13 clk: qcom: Add support for Krait clocks

In reply to Stephen Boyd

The Krait clocks are made up of a series of muxes and a divider that choose between a fixed rate clock and dedicated HFPLLs for each CPU. Instead of using mmio accesses to remux parents, the Krait implementation exposes the remux control via cp15 registers. Support these clocks.

---
 drivers/clk/qcom/Kconfig     |   4 ++
 drivers/clk/qcom/Makefile    |   1 +
 drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-krait.h |  49 +++++++++++++
 4 files changed, 220 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-krait.c
 create mode 100644 drivers/clk/qcom/clk-krait.h
drivers/clk/qcom/Kconfig +4 -0
index d421fd195114..7b82b3c4e259 100644
@@ -96,3 +96,7 @@ config QCOM_HFPLL
 	  Support for the high-frequency PLLs present on Qualcomm devices.
 	  Say Y if you want to support CPU frequency scaling on devices
 	  such as MSM8974, APQ8084, etc.
+
+config KRAIT_CLOCKS
+	bool
+	select KRAIT_L2_ACCESSORS
drivers/clk/qcom/Makefile +1 -0
index b1076179fb17..f8d7cfaee4a2 100644
@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
+clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
 clk-qcom-y += clk-hfpll.o
 clk-qcom-y += reset.o
 
drivers/clk/qcom/clk-krait.c new file +166 -0

166 lines not on this page · raw message

drivers/clk/qcom/clk-krait.h new file +49 -0

49 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#10 lore raw

PATCH v3 10/13 clk: qcom: Add KPSS ACC/GCC driver

In reply to Stephen Boyd

The ACC and GCC regions present in KPSSv1 contain registers to control clocks and power to each Krait CPU and L2. For CPUfreq purposes probe these devices and expose a mux clock that chooses between PXO and PLL8.

---
 .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt  |  7 ++
 .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt  | 28 +++++++
 drivers/clk/qcom/Kconfig                           |  8 ++
 drivers/clk/qcom/Makefile                          |  1 +
 drivers/clk/qcom/kpss-xcc.c                        | 95 ++++++++++++++++++++++
 5 files changed, 139 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
 create mode 100644 drivers/clk/qcom/kpss-xcc.c
Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +7 -0
index 1333db9acfee..382a574a5c55 100644
@@ -21,10 +21,17 @@ PROPERTIES
 		    the register region. An optional second element specifies
 		    the base address and size of the alias register region.
 
+- clock-output-names:
+	Usage: optional
+	Value type: <string>
+	Definition: Name of the output clock. Typically acpuX_aux where X is a
+		    CPU number starting at 0.
+
 Example:
 
 	clock-controller at 2088000 {
 		compatible = "qcom,kpss-acc-v2";
 		reg = <0x02088000 0x1000>,
 		      <0x02008000 0x1000>;
+		clock-output-names = "acpu0_aux";
 	};
Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt new file +28 -0
new file mode 100644
index 000000000000..d1e12f16a28c
@@ -0,0 +1,28 @@
+Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: should be one of:
+			"qcom,kpss-gcc"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: base address and size of the register region
+
+- clock-output-names:
+	Usage: required
+	Value type: <string>
+	Definition: Name of the output clock. Typically acpu_l2_aux indicating
+		    an L2 cache auxiliary clock.
+
+Example:
+
+	l2cc: clock-controller at 2011000 {
+		compatible = "qcom,kpss-gcc";
+		reg = <0x2011000 0x1000>;
+		clock-output-names = "acpu_l2_aux";
+	};
drivers/clk/qcom/Kconfig +8 -0
index 7b82b3c4e259..92eb883533d5 100644
@@ -97,6 +97,14 @@ config QCOM_HFPLL
 	  Say Y if you want to support CPU frequency scaling on devices
 	  such as MSM8974, APQ8084, etc.
 
+config KPSS_XCC
+	tristate "KPSS Clock Controller"
+	depends on COMMON_CLK_QCOM
+	help
+	  Support for the Krait ACC and GCC clock controllers. Say Y
+	  if you want to support CPU frequency scaling on devices such
+	  as MSM8960, APQ8064, etc.
+
 config KRAIT_CLOCKS
 	bool
 	select KRAIT_L2_ACCESSORS
drivers/clk/qcom/Makefile +1 -0
index f8d7cfaee4a2..6327dd623773 100644
@@ -22,4 +22,5 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
 obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
 obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
 obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
 obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
drivers/clk/qcom/kpss-xcc.c new file +95 -0

95 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#11 lore raw

PATCH v3 11/13 clk: qcom: Add Krait clock controller driver

In reply to Stephen Boyd

The Krait CPU clocks are made up of a primary mux and secondary mux for each CPU and the L2, controlled via cp15 accessors. For Kraits within KPSSv1 each secondary mux accepts a different aux source, but on KPSSv2 each secondary mux accepts the same aux source.

---
 .../devicetree/bindings/clock/qcom,krait-cc.txt    |  22 ++
 drivers/clk/qcom/Kconfig                           |   8 +
 drivers/clk/qcom/Makefile                          |   1 +
 drivers/clk/qcom/krait-cc.c                        | 352 +++++++++++++++++++++
 4 files changed, 383 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
 create mode 100644 drivers/clk/qcom/krait-cc.c
Documentation/devicetree/bindings/clock/qcom,krait-cc.txt new file +22 -0
new file mode 100644
index 000000000000..874138f88ec6
@@ -0,0 +1,22 @@
+Krait Clock Controller
+
+PROPERTIES
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+			"qcom,krait-cc-v1"
+			"qcom,krait-cc-v2"
+
+- #clock-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 1
+
+Example:
+
+	kraitcc: clock-controller {
+		compatible = "qcom,krait-cc-v1";
+		#clock-cells = <1>;
+	};
drivers/clk/qcom/Kconfig +8 -0
index 92eb883533d5..6de898c366ff 100644
@@ -105,6 +105,14 @@ config KPSS_XCC
 	  if you want to support CPU frequency scaling on devices such
 	  as MSM8960, APQ8064, etc.
 
+config KRAITCC
+	tristate "Krait Clock Controller"
+	depends on COMMON_CLK_QCOM && ARM
+	select KRAIT_CLOCKS
+	help
+	  Support for the Krait CPU clocks on Qualcomm devices.
+	  Say Y if you want to support CPU frequency scaling.
+
 config KRAIT_CLOCKS
 	bool
 	select KRAIT_L2_ACCESSORS
drivers/clk/qcom/Makefile +1 -0
index 6327dd623773..2477ce687208 100644
@@ -24,3 +24,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
 obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
 obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
 obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+obj-$(CONFIG_KRAITCC) += krait-cc.o
drivers/clk/qcom/krait-cc.c new file +352 -0

352 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#12 lore raw

PATCH v3 12/13 cpufreq: Add module to register cpufreq on Krait CPUs

In reply to Stephen Boyd

Register a cpufreq-generic device whenever we detect that a "qcom,krait" compatible CPU is present in DT.

---
 .../devicetree/bindings/arm/msm/qcom,pvs.txt       |  38 ++++
 drivers/cpufreq/Kconfig.arm                        |   9 +
 drivers/cpufreq/Makefile                           |   1 +
 drivers/cpufreq/qcom-cpufreq.c                     | 204 +++++++++++++++++++++
 4 files changed, 252 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
 create mode 100644 drivers/cpufreq/qcom-cpufreq.c
Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt new file +38 -0
new file mode 100644
index 000000000000..e7cb10426a3b
@@ -0,0 +1,38 @@
+Qualcomm Process Voltage Scaling Tables
+
+The node name is required to be "qcom,pvs". There shall only be one
+such node present in the root of the tree.
+
+PROPERTIES
+
+- qcom,pvs-format-a or qcom,pvs-format-b:
+	Usage: required
+	Value type: <empty>
+	Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
+		    If qcom,pvs-format-a is used the table is two columns
+		    (frequency and voltage in that order). If qcom,pvs-format-b 		    is used the table is three columns (frequency, voltage,
+		    and current in that order).
+
+- qcom,speedX-pvsY-bin-vZ:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
+		    and version Z.
+Example:
+
+	qcom,pvs {
+		qcom,pvs-format-a;
+		qcom,speed0-pvs0-bin-v0 =
+			<  384000000  950000 >,
+			<  486000000  975000 >,
+			<  594000000 1000000 >,
+			<  702000000 1025000 >,
+			<  810000000 1075000 >,
+			<  918000000 1100000 >,
+			< 1026000000 1125000 >,
+			< 1134000000 1175000 >,
+			< 1242000000 1200000 >,
+			< 1350000000 1225000 >,
+			< 1458000000 1237500 >,
+			< 1512000000 1250000 >;
+	};
drivers/cpufreq/Kconfig.arm +9 -0
index 1b06fc4640e2..3829d5521dfc 100644
@@ -137,6 +137,15 @@ config ARM_OMAP2PLUS_CPUFREQ
 	depends on ARCH_OMAP2PLUS
 	default ARCH_OMAP2PLUS
 
+config ARM_QCOM_CPUFREQ
+	tristate "Qualcomm based"
+	depends on ARCH_QCOM
+	select PM_OPP
+	help
+	  This adds the CPUFreq driver for Qualcomm SoC based boards.
+
+	  If in doubt, say N.
+
 config ARM_S3C_CPUFREQ
 	bool
 	help
drivers/cpufreq/Makefile +1 -0
index 82a1821471fd..be95ed8d6873 100644
@@ -65,6 +65,7 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ)	+= kirkwood-cpufreq.o
 obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o
 obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)	+= pxa2xx-cpufreq.o
 obj-$(CONFIG_PXA3xx)			+= pxa3xx-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ)		+= qcom-cpufreq.o
 obj-$(CONFIG_ARM_S3C24XX_CPUFREQ)	+= s3c24xx-cpufreq.o
 obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
 obj-$(CONFIG_ARM_S3C2410_CPUFREQ)	+= s3c2410-cpufreq.o
drivers/cpufreq/qcom-cpufreq.c new file +204 -0

204 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#13 lore raw

PATCH v3 13/13 ARM: dts: qcom: Add necessary DT data for Krait cpufreq

In reply to Stephen Boyd

Add the necessary DT nodes and data so we can probe the cpufreq driver on MSM devices with Krait CPUs.

---
 arch/arm/boot/dts/qcom-apq8064.dtsi | 230 ++++++++++++++++++++++++++
 arch/arm/boot/dts/qcom-msm8960.dtsi |  49 ++++++
 arch/arm/boot/dts/qcom-msm8974.dtsi | 311 +++++++++++++++++++++++++++++++++++-
 3 files changed, 586 insertions(+), 4 deletions(-)
arch/arm/boot/dts/qcom-apq8064.dtsi +230 -0
index b3154c071652..a97cbec75abe 100644
@@ -23,6 +23,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc0>;
 			qcom,saw = <&saw0>;
+			clocks = <&kraitcc 0>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		cpu at 1 {
@@ -33,6 +36,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc1>;
 			qcom,saw = <&saw1>;
+			clocks = <&kraitcc 1>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		cpu at 2 {
@@ -43,6 +49,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc2>;
 			qcom,saw = <&saw2>;
+			clocks = <&kraitcc 2>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		cpu at 3 {
@@ -53,6 +62,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc3>;
 			qcom,saw = <&saw3>;
+			clocks = <&kraitcc 3>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		L2: l2-cache {
@@ -66,6 +78,214 @@
 		interrupts = <1 10 0x304>;
 	};
 
+	qcom,pvs {
+		qcom,pvs-format-a;
+		qcom,speed0-pvs0-bin-v0 =
+			< 384000000 950000  >,
+			< 486000000 975000  >,
+			< 594000000 1000000  >,
+			< 702000000 1025000  >,
+			< 810000000 1075000  >,
+			< 918000000 1100000  >;
+
+		qcom,speed0-pvs1-bin-v0 =
+			< 384000000 900000  >,
+			< 486000000 925000  >,
+			< 594000000 950000  >,
+			< 702000000 975000  >,
+			< 810000000 1025000  >,
+			< 918000000 1050000  >;
+
+		qcom,speed0-pvs3-bin-v0 =
+			< 384000000 850000  >,
+			< 486000000 875000  >,
+			< 594000000 900000  >,
+			< 702000000 925000  >,
+			< 810000000 975000  >,
+			< 918000000 1000000  >;
+
+		qcom,speed0-pvs4-bin-v0 =
+			< 384000000 850000  >,
+			< 486000000 875000  >,
+			< 594000000 900000  >,
+			< 702000000 925000  >,
+			< 810000000 962500  >,
+			< 918000000 975000  >;
+
+		qcom,speed1-pvs0-bin-v0 =
+			< 384000000 950000  >,
+			< 486000000 950000  >,
+			< 594000000 950000  >,
+			< 702000000 962500  >,
+			< 810000000 1000000  >,
+			< 918000000 1025000  >;
+
+		qcom,speed1-pvs1-bin-v0 =
+			< 384000000 950000  >,
+			< 486000000 950000  >,
+			< 594000000 950000  >,
+			< 702000000 962500  >,
+			< 810000000 975000  >,
+			< 918000000 1000000  >;
+
+		qcom,speed1-pvs2-bin-v0 =
+			< 384000000 925000  >,
+			< 486000000 925000  >,
+			< 594000000 925000  >,
+			< 702000000 925000  >,
+			< 810000000 937500  >,
+			< 918000000 950000  >;
+
+		qcom,speed1-pvs3-bin-v0 =
+			< 384000000 900000  >,
+			< 486000000 900000  >,
+			< 594000000 900000  >,
+			< 702000000 900000  >,
+			< 810000000 900000  >,
+			< 918000000 925000  >;
+
+		qcom,speed1-pvs4-bin-v0 =
+			< 384000000 875000  >,
+			< 486000000 875000  >,
+			< 594000000 875000  >,
+			< 702000000 875000  >,
+			< 810000000 887500  >,
+			< 918000000 900000  >;
+
+		qcom,speed1-pvs5-bin-v0 =
+			< 384000000 875000  >,
+			< 486000000 875000  >,
+			< 594000000 875000  >,
+			< 702000000 875000  >,
+			< 810000000 887500  >,
+			< 918000000 900000  >;
+
+		qcom,speed1-pvs6-bin-v0 =
+			< 384000000 875000  >,
+			< 486000000 875000  >,
+			< 594000000 875000  >,
+			< 702000000 875000  >,
+			< 810000000 887500  >,
+			< 918000000 900000  >;
+
+		qcom,speed2-pvs0-bin-v0 =
+			< 384000000 950000  >,
+			< 486000000 950000  >,
+			< 594000000 950000  >,
+			< 702000000 950000  >,
+			< 810000000 962500  >,
+			< 918000000 975000  >;
+
+		qcom,speed2-pvs1-bin-v0 =
+			< 384000000 925000  >,
+			< 486000000 925000  >,
+			< 594000000 925000  >,
+			< 702000000 925000  >,
+			< 810000000 937500  >,
+			< 918000000 950000  >;
+
+		qcom,speed2-pvs2-bin-v0 =
+			< 384000000 900000  >,
+			< 486000000 900000  >,
+			< 594000000 900000  >,
+			< 702000000 900000  >,
+			< 810000000 912500  >,
+			< 918000000 925000  >;
+
+		qcom,speed2-pvs3-bin-v0 =
+			< 384000000 900000  >,
+			< 486000000 900000  >,
+			< 594000000 900000  >,
+			< 702000000 900000  >,
+			< 810000000 900000  >,
+			< 918000000 912500  >;
+
+		qcom,speed2-pvs4-bin-v0 =
+			< 384000000 875000  >,
+			< 486000000 875000  >,
+			< 594000000 875000  >,
+			< 702000000 875000  >,
+			< 810000000 887500  >,
+			< 918000000 900000  >;
+
+		qcom,speed2-pvs5-bin-v0 =
+			< 384000000 875000  >,
+			< 486000000 875000  >,
+			< 594000000 875000  >,
+			< 702000000 875000  >,
+			< 810000000 887500  >,
+			< 918000000 900000  >;
+
+		qcom,speed2-pvs6-bin-v0 =
+			< 384000000 875000  >,
+			< 486000000 875000  >,
+			< 594000000 875000  >,
+			< 702000000 875000  >,
+			< 810000000 887500  >,
+			< 918000000 900000  >;
+
+		qcom,speed14-pvs0-bin-v0 =
+			< 384000000 950000 >,
+			< 486000000 950000 >,
+			< 594000000 950000 >,
+			< 702000000 962500 >,
+			< 810000000 1000000 >,
+			< 918000000 1025000 >;
+
+		qcom,speed14-pvs1-bin-v0 =
+			< 384000000 950000 >,
+			< 486000000 950000 >,
+			< 594000000 950000 >,
+			< 702000000 962500 >,
+			< 810000000 975000 >,
+			< 918000000 1000000 >;
+
+		qcom,speed14-pvs2-bin-v0 =
+			< 384000000 925000 >,
+			< 486000000 925000 >,
+			< 594000000 925000 >,
+			< 702000000 925000 >,
+			< 810000000 937500 >,
+			< 918000000 950000 >;
+
+		qcom,speed14-pvs3-bin-v0 =
+			< 384000000 900000 >,
+			< 486000000 900000 >,
+			< 594000000 900000 >,
+			< 702000000 900000 >,
+			< 810000000 900000 >,
+			< 918000000 925000 >;
+
+		qcom,speed14-pvs4-bin-v0 =
+			< 384000000 875000 >,
+			< 486000000 875000 >,
+			< 594000000 875000 >,
+			< 702000000 875000 >,
+			< 810000000 887500 >,
+			< 918000000 900000 >;
+
+		qcom,speed14-pvs5-bin-v0 =
+			< 384000000 875000 >,
+			< 486000000 875000 >,
+			< 594000000 875000 >,
+			< 702000000 875000 >,
+			< 810000000 887500 >,
+			< 918000000 900000 >;
+
+		qcom,speed14-pvs6-bin-v0 =
+			< 384000000 875000 >,
+			< 486000000 875000 >,
+			< 594000000 875000 >,
+			< 702000000 875000 >,
+			< 810000000 887500 >,
+			< 918000000 900000 >;
+	};
+
+	kraitcc: clock-controller {
+		compatible = "qcom,krait-cc-v1";
+		#clock-cells = <1>;
+	};
+
 	soc: soc {
 		#address-cells = <1>;
 		#size-cells = <1>;
@@ -122,21 +342,31 @@
 		acc0: clock-controller at 2088000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu0_aux";
 		};
 
 		acc1: clock-controller at 2098000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu1_aux";
 		};
 
 		acc2: clock-controller at 20a8000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x020a8000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu2_aux";
 		};
 
 		acc3: clock-controller at 20b8000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x020b8000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu3_aux";
+		};
+
+		l2cc: clock-controller at 2011000 {
+			compatible = "qcom,kpss-gcc";
+			reg = <0x2011000 0x1000>;
+			clock-output-names = "acpu_l2_aux";
 		};
 
 		saw0: regulator at 2089000 {
arch/arm/boot/dts/qcom-msm8960.dtsi +49 -0

78 lines not on this page · raw message

arch/arm/boot/dts/qcom-msm8974.dtsi +307 -4

359 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#14 lore raw
In reply to Stephen Boyd

Hi!

Quoted 25 lines · Stephen Boyd

These patches provide cpufreq scaling on devices with Krait CPUs. In Krait CPU designs there's one PLL and two muxes per CPU, allowing us to switch CPU frequencies independently.

				 secondary
	 +-----+                    +
	 | QSB |-------+------------|\
	 +-----+       |            | |-+
		       |    +-------|/  |
		       |    |       +   |
	 +-----+       |    |           |
	 | PLL |----+-------+           |   primary
	 +-----+    |  |                |     +
		    |  |                +-----|\       +------+
	 +-------+  |  |                      | \      |      |
	 | HFPLL |----------+-----------------|  |-----| CPU0 |
	 +-------+  |  |    |                 |  |     |      |
		    |  |    | +-----+         | /      +------+
		    |  |    +-| / 2 |---------|/
		    |  |      +-----+         +
		    |  |         secondary
		    |  |            +
		    |  +------------|\
		    |               | |-+
		    +---------------|/  |   primary
Quoted 8 lines
				    +   |     +
					+-----|\       +------+
	 +-------+          v                 | \      |      |
	 | HFPLL |----------------------------|  |-----| CPU1 |
	 +-------+          |                 |  |     |      |
			    | +-----+         | /      +------+
			    +-| / 2 |---------|/
			      +-----+         +

Nice ascii art :-). There should be "+" at place marked by "v" :-).

Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#15 lore raw
In reply to Stephen Boyd
Quoted 25 lines · Stephen Boyd

These patches provide cpufreq scaling on devices with Krait CPUs. In Krait CPU designs there's one PLL and two muxes per CPU, allowing us to switch CPU frequencies independently.

				 secondary
	 +-----+                    +
	 | QSB |-------+------------|\
	 +-----+       |            | |-+
		       |    +-------|/  |
		       |    |       +   |
	 +-----+       |    |           |
	 | PLL |----+-------+           |   primary
	 +-----+    |  |                |     +
		    |  |                +-----|\       +------+
	 +-------+  |  |                      | \      |      |
	 | HFPLL |----------+-----------------|  |-----| CPU0 |
	 +-------+  |  |    |                 |  |     |      |
		    |  |    | +-----+         | /      +------+
		    |  |    +-| / 2 |---------|/
		    |  |      +-----+         +
		    |  |         secondary
		    |  |            +
		    |  +------------|\
		    |               | |-+
		    +---------------|/  |   primary
Quoted 8 lines
				    +   |     +
					+-----|\       +------+
	 +-------+                            | \      |      |
	 | HFPLL |----------------------------|  |-----| CPU1 |
	 +-------+          |                 |  |     |      |
			    | +-----+         | /      +------+
			    +-| / 2 |---------|/
			      +-----+         +

And actually this picture should go to Documentation/ somewhere...

Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html