Skip to contentSkip to conversation

Krait clocks + Krait CPUfreq

PATCH Superseded Stephen Boyd 5 participants 29 messages Sep 5, 2014 – Oct 8, 2014

Subsystems (5)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 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. Please note that these patches rely on the Krait L2 accessor patch I posted a while back in another series[1]. This also relies on the cpufreq-generic patchset from Viresh[2]. If anything can be picked up right now it would be better to reduce the churn over time as other pieces settle. The series gets progressively more controversial as it goes on, so I hope that things near the beggining will be picked up earlier.

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)
* Document DT bindings
* Use a new efuse/eeprom API instead of hardcoding the location in the driver
* Add some thermal awareness

Stephen Boyd (15):
clk: mux: Add unregistration API
clk: mux: Split out register accessors for reuse
clk: Add __clk_mux_determine_rate_closest
clk: divider: Make generic for usage elsewhere
clk: Add safe switch hook
clk: Avoid sending high rates to downstream clocks during set_rate
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

 arch/arm/boot/dts/qcom-apq8064.dtsi          | 230 +++++++++++++++++
 arch/arm/boot/dts/qcom-msm8960.dtsi          |  49 ++++
 arch/arm/boot/dts/qcom-msm8974.dtsi          | 311 ++++++++++++++++++++++-
 drivers/clk/clk-divider.c                    | 197 +++++++++------
 drivers/clk/clk-mux.c                        |  91 ++++---
 drivers/clk/clk.c                            | 133 +++++++---
 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                     | 110 +++++++++
 drivers/clk/qcom/kpss-xcc.c                  |  94 +++++++
 drivers/clk/qcom/krait-cc.c                  | 357 +++++++++++++++++++++++++++
 drivers/cpufreq/Kconfig.arm                  |   9 +
 drivers/cpufreq/Makefile                     |   1 +
 drivers/cpufreq/qcom-cpufreq.c               | 199 +++++++++++++++
 include/dt-bindings/clock/qcom,gcc-msm8960.h |   2 +
 include/linux/clk-private.h                  |   2 +
 include/linux/clk-provider.h                 |  32 ++-
 23 files changed, 2486 insertions(+), 141 deletions(-)
 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

[1] http://lkml.iu.edu/hypermail/linux/kernel/1404.0/02636.html
[2] git://git.linaro.org/people/viresh.kumar/linux cpufreq/cpu0-krait-v3

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

PATCH v2 1/15 clk: mux: Add unregistration API

Some drivers want to remove clocks they register with clk_unregister(). Unfortunately, calling clk_unregister() directly on the clock returned by clk_register_mux() would result in a memory leak of the clk_mux struct. Add an API to free that allocation and unregister the clock.

---
 drivers/clk/clk-mux.c        | 15 +++++++++++++++
 include/linux/clk-provider.h |  2 ++
 2 files changed, 17 insertions(+)
drivers/clk/clk-mux.c +15 -0
index 4f96ff3ba728..c0cf9db9effa 100644
@@ -177,3 +177,18 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
 				      NULL, lock);
 }
 EXPORT_SYMBOL_GPL(clk_register_mux);
+
+void clk_unregister_mux(struct clk *clk)
+{
+	struct clk_hw *hw;
+	struct clk_mux *mux;
+
+	hw = __clk_get_hw(clk);
+	if (!hw)
+		return;
+
+	mux = to_clk_mux(hw);
+	clk_unregister(clk);
+	kfree(mux);
+}
+EXPORT_SYMBOL_GPL(clk_unregister_mux);
include/linux/clk-provider.h +2 -0
index 411dd7eb2653..f0744b8de50a 100644
@@ -401,6 +401,8 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
 		void __iomem *reg, u8 shift, u32 mask,
 		u8 clk_mux_flags, u32 *table, spinlock_t *lock);
 
+void clk_unregister_mux(struct clk *clk);
+
 void of_fixed_factor_clk_setup(struct device_node *node);
 
 /**
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#2 lore raw

PATCH v2 2/15 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 c0cf9db9effa..54217961ae8a 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 << ffs(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 << ffs(val));
+
+		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 f0744b8de50a..48a92e318542 100644
@@ -376,7 +376,7 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
 struct clk_mux {
 	struct clk_hw	hw;
 	void __iomem	*reg;
-	u32		*table;
+	unsigned int	*table;
 	u32		mask;
 	u8		shift;
 	u8		flags;
@@ -391,6 +391,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,
@@ -399,7 +404,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,
hosted by The Linux Foundation
#3 lore raw

PATCH v2 3/15 clk: Add __clk_mux_determine_rate_closest

In reply to Stephen Boyd

Some clock drivers want to find the closest rate on the input of a mux instead of a rate that's less than or equal to the desired rate. Add a generic mux function to support this.

---
 drivers/clk/clk.c            | 47 +++++++++++++++++++++++++++++++++++---------
 include/linux/clk-provider.h |  9 +++++++--
 2 files changed, 45 insertions(+), 11 deletions(-)
drivers/clk/clk.c +38 -9
index b76fa69b44cb..255ec8c92919 100644
@@ -712,14 +712,20 @@ struct clk *__clk_lookup(const char *name)
 	return NULL;
 }
 
-/*
- * Helper for finding best parent to provide a given frequency. This can be used
- * directly as a determine_rate callback (e.g. for a mux), or from a more
- * complex clock that may combine a mux with other operations.
- */
-long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
-			      unsigned long *best_parent_rate,
-			      struct clk **best_parent_p)
+static bool mux_is_better_rate(unsigned long rate, unsigned long now,
+			   unsigned long best, unsigned long flags)
+{
+	if (flags & CLK_MUX_ROUND_CLOSEST)
+		return abs(now - rate) < abs(best - rate);
+
+	return now <= rate && now > best;
+}
+
+static long
+clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate,
+			     unsigned long *best_parent_rate,
+			     struct clk **best_parent_p,
+			     unsigned long flags)
 {
 	struct clk *clk = hw->clk, *parent, *best_parent = NULL;
 	int i, num_parents;
@@ -747,7 +753,7 @@ long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
 			parent_rate = __clk_round_rate(parent, rate);
 		else
 			parent_rate = __clk_get_rate(parent);
-		if (parent_rate <= rate && parent_rate > best) {
+		if (mux_is_better_rate(rate, parent_rate, best, flags)) {
 			best_parent = parent;
 			best = parent_rate;
 		}
@@ -760,8 +766,31 @@ out:
 
 	return best;
 }
+
+/*
+ * Helper for finding best parent to provide a given frequency. This can be used
+ * directly as a determine_rate callback (e.g. for a mux), or from a more
+ * complex clock that may combine a mux with other operations.
+ */
+long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long *best_parent_rate,
+			      struct clk **best_parent_p)
+{
+	return clk_mux_determine_rate_flags(hw, rate, best_parent_rate,
+					    best_parent_p, 0);
+}
 EXPORT_SYMBOL_GPL(__clk_mux_determine_rate);
 
+long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate,
+			      unsigned long *best_parent_rate,
+			      struct clk **best_parent_p)
+{
+	return clk_mux_determine_rate_flags(hw, rate, best_parent_rate,
+					    best_parent_p,
+					    CLK_MUX_ROUND_CLOSEST);
+}
+EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
+
 /***        clk api        ***/
 
 void __clk_unprepare(struct clk *clk)
include/linux/clk-provider.h +7 -2
index 48a92e318542..ba97e25c1115 100644
@@ -372,6 +372,8 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
  *	register, and mask of mux bits are in higher 16-bit of this register.
  *	While setting the mux bits, higher 16-bit should also be updated to
  *	indicate changing mux bits.
+ * CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired
+ *	frequency.
  */
 struct clk_mux {
 	struct clk_hw	hw;
@@ -386,7 +388,8 @@ struct clk_mux {
 #define CLK_MUX_INDEX_ONE		BIT(0)
 #define CLK_MUX_INDEX_BIT		BIT(1)
 #define CLK_MUX_HIWORD_MASK		BIT(2)
-#define CLK_MUX_READ_ONLY	BIT(3) /* mux setting cannot be changed */
+#define CLK_MUX_READ_ONLY		BIT(3) /* mux can't be changed */
+#define CLK_MUX_ROUND_CLOSEST		BIT(4)
 
 extern const struct clk_ops clk_mux_ops;
 extern const struct clk_ops clk_mux_ro_ops;
@@ -529,7 +532,9 @@ struct clk *__clk_lookup(const char *name);
 long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
 			      unsigned long *best_parent_rate,
 			      struct clk **best_parent_p);
-
+long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate,
+			      unsigned long *best_parent_rate,
+			      struct clk **best_parent_p);
 /*
  * FIXME clock api without lock protection
  */
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#4 lore raw

PATCH v2 4/15 clk: divider: Make generic for usage elsewhere

In reply to Stephen Boyd

Some devices don't use mmio to interact with dividers. Split out the logic from the register read/write parts so that we can reuse the division logic elsewhere.

---
 drivers/clk/clk-divider.c    | 197 ++++++++++++++++++++++++++-----------------
 include/linux/clk-provider.h |  11 +++
 2 files changed, 132 insertions(+), 76 deletions(-)
drivers/clk/clk-divider.c +121 -76
index 18a9de29df0e..78ecd8af8740 100644
@@ -30,7 +30,7 @@
 
 #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
 
-#define div_mask(d)	((1 << ((d)->width)) - 1)
+#define div_mask(width)	(BIT(width) - 1)
 
 static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
 {
@@ -54,15 +54,16 @@ static unsigned int _get_table_mindiv(const struct clk_div_table *table)
 	return mindiv;
 }
 
-static unsigned int _get_maxdiv(struct clk_divider *divider)
+static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
+				unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ONE_BASED)
-		return div_mask(divider);
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
-		return 1 << div_mask(divider);
-	if (divider->table)
-		return _get_table_maxdiv(divider->table);
-	return div_mask(divider) + 1;
+	if (flags & CLK_DIVIDER_ONE_BASED)
+		return div_mask(width);
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
+		return 1 << div_mask(width);
+	if (table)
+		return _get_table_maxdiv(table);
+	return div_mask(width) + 1;
 }
 
 static unsigned int _get_table_div(const struct clk_div_table *table,
@@ -76,14 +77,15 @@ static unsigned int _get_table_div(const struct clk_div_table *table,
 	return 0;
 }
 
-static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
+static unsigned int _get_div(const struct clk_div_table *table,
+			     unsigned int val, unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ONE_BASED)
+	if (flags & CLK_DIVIDER_ONE_BASED)
 		return val;
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return 1 << val;
-	if (divider->table)
-		return _get_table_div(divider->table, val);
+	if (table)
+		return _get_table_div(table, val);
 	return val + 1;
 }
 
@@ -98,29 +100,28 @@ static unsigned int _get_table_val(const struct clk_div_table *table,
 	return 0;
 }
 
-static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
+static unsigned int _get_val(const struct clk_div_table *table,
+			     unsigned int div, unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ONE_BASED)
+	if (flags & CLK_DIVIDER_ONE_BASED)
 		return div;
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return __ffs(div);
-	if (divider->table)
-		return  _get_table_val(divider->table, div);
+	if (table)
+		return  _get_table_val(table, div);
 	return div - 1;
 }
 
-static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
-		unsigned long parent_rate)
+unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
+				  unsigned int val,
+				  const struct clk_div_table *table,
+				  unsigned long flags)
 {
-	struct clk_divider *divider = to_clk_divider(hw);
-	unsigned int div, val;
-
-	val = clk_readl(divider->reg) >> divider->shift;
-	val &= div_mask(divider);
+	unsigned int div;
 
-	div = _get_div(divider, val);
+	div = _get_div(table, val, flags);
 	if (!div) {
-		WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
+		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			__clk_get_name(hw->clk));
 		return parent_rate;
@@ -128,6 +129,20 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
 
 	return DIV_ROUND_UP(parent_rate, div);
 }
+EXPORT_SYMBOL_GPL(divider_recalc_rate);
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned int val;
+
+	val = clk_readl(divider->reg) >> divider->shift;
+	val &= div_mask(divider->width);
+
+	return divider_recalc_rate(hw, parent_rate, val, divider->table,
+				   divider->flags);
+}
 
 /*
  * The reverse of DIV_ROUND_UP: The maximum number which
@@ -146,12 +161,13 @@ static bool _is_valid_table_div(const struct clk_div_table *table,
 	return false;
 }
 
-static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
+static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
+			  unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return is_power_of_2(div);
-	if (divider->table)
-		return _is_valid_table_div(divider->table, div);
+	if (table)
+		return _is_valid_table_div(table, div);
 	return true;
 }
 
@@ -191,71 +207,76 @@ static int _round_down_table(const struct clk_div_table *table, int div)
 	return down;
 }
 
-static int _div_round_up(struct clk_divider *divider,
-		unsigned long parent_rate, unsigned long rate)
+static int _div_round_up(const struct clk_div_table *table,
+			 unsigned long parent_rate, unsigned long rate,
+			 unsigned long flags)
 {
 	int div = DIV_ROUND_UP(parent_rate, rate);
 
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		div = __roundup_pow_of_two(div);
-	if (divider->table)
-		div = _round_up_table(divider->table, div);
+	if (table)
+		div = _round_up_table(table, div);
 
 	return div;
 }
 
-static int _div_round_closest(struct clk_divider *divider,
-		unsigned long parent_rate, unsigned long rate)
+static int _div_round_closest(const struct clk_div_table *table,
+			      unsigned long parent_rate, unsigned long rate,
+			      unsigned long flags)
 {
 	int up, down, div;
 
 	up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);
 
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
+	if (flags & CLK_DIVIDER_POWER_OF_TWO) {
 		up = __roundup_pow_of_two(div);
 		down = __rounddown_pow_of_two(div);
-	} else if (divider->table) {
-		up = _round_up_table(divider->table, div);
-		down = _round_down_table(divider->table, div);
+	} else if (table) {
+		up = _round_up_table(table, div);
+		down = _round_down_table(table, div);
 	}
 
 	return (up - div) <= (div - down) ? up : down;
 }
 
-static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
-		unsigned long rate)
+static int _div_round(const struct clk_div_table *table,
+		      unsigned long parent_rate, unsigned long rate,
+		      unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
-		return _div_round_closest(divider, parent_rate, rate);
+	if (flags & CLK_DIVIDER_ROUND_CLOSEST)
+		return _div_round_closest(table, parent_rate, rate, flags);
 
-	return _div_round_up(divider, parent_rate, rate);
+	return _div_round_up(table, parent_rate, rate, flags);
 }
 
-static bool _is_best_div(struct clk_divider *divider,
-		unsigned long rate, unsigned long now, unsigned long best)
+static bool _is_best_div(unsigned long rate, unsigned long now,
+			 unsigned long best, unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
+	if (flags & CLK_DIVIDER_ROUND_CLOSEST)
 		return abs(rate - now) < abs(rate - best);
 
 	return now <= rate && now > best;
 }
 
-static int _next_div(struct clk_divider *divider, int div)
+static int _next_div(const struct clk_div_table *table, int div,
+		     unsigned long flags)
 {
 	div++;
 
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return __roundup_pow_of_two(div);
-	if (divider->table)
-		return _round_up_table(divider->table, div);
+	if (table)
+		return _round_up_table(table, div);
 
 	return div;
 }
 
 static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
-		unsigned long *best_parent_rate)
+			       unsigned long *best_parent_rate,
+			       const struct clk_div_table *table, u8 width,
+			       unsigned long flags)
 {
-	struct clk_divider *divider = to_clk_divider(hw);
 	int i, bestdiv = 0;
 	unsigned long parent_rate, best = 0, now, maxdiv;
 	unsigned long parent_rate_saved = *best_parent_rate;
@@ -263,11 +284,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	if (!rate)
 		rate = 1;
 
-	maxdiv = _get_maxdiv(divider);
+	maxdiv = _get_maxdiv(table, width, flags);
 
 	if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
 		parent_rate = *best_parent_rate;
-		bestdiv = _div_round(divider, parent_rate, rate);
+		bestdiv = _div_round(table, parent_rate, rate, flags);
 		bestdiv = bestdiv == 0 ? 1 : bestdiv;
 		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
 		return bestdiv;
@@ -279,8 +300,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	 */
 	maxdiv = min(ULONG_MAX / rate, maxdiv);
 
-	for (i = 1; i <= maxdiv; i = _next_div(divider, i)) {
-		if (!_is_valid_div(divider, i))
+	for (i = 1; i <= maxdiv; i = _next_div(table, i, flags)) {
+		if (!_is_valid_div(table, i, flags))
 			continue;
 		if (rate * i == parent_rate_saved) {
 			/*
@@ -294,7 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
 				MULT_ROUND_UP(rate, i));
 		now = DIV_ROUND_UP(parent_rate, i);
-		if (_is_best_div(divider, rate, now, best)) {
+		if (_is_best_div(rate, now, best, flags)) {
 			bestdiv = i;
 			best = now;
 			*best_parent_rate = parent_rate;
@@ -302,48 +323,72 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	}
 
 	if (!bestdiv) {
-		bestdiv = _get_maxdiv(divider);
+		bestdiv = _get_maxdiv(table, width, flags);
 		*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
 	}
 
 	return bestdiv;
 }
 
-static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
-				unsigned long *prate)
+long divider_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate, const struct clk_div_table *table,
+			u8 width, unsigned long flags)
 {
 	int div;
-	div = clk_divider_bestdiv(hw, rate, prate);
+
+	div = clk_divider_bestdiv(hw, rate, prate, table, width, flags);
 
 	return DIV_ROUND_UP(*prate, div);
 }
+EXPORT_SYMBOL_GPL(divider_round_rate);
 
-static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
-				unsigned long parent_rate)
+static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
 {
 	struct clk_divider *divider = to_clk_divider(hw);
+
+	return divider_round_rate(hw, rate, prate, divider->table,
+				  divider->width, divider->flags);
+}
+
+int divider_get_val(unsigned long rate, unsigned long parent_rate,
+		    const struct clk_div_table *table, u8 width,
+		    unsigned long flags)
+{
 	unsigned int div, value;
-	unsigned long flags = 0;
-	u32 val;
 
 	div = DIV_ROUND_UP(parent_rate, rate);
 
-	if (!_is_valid_div(divider, div))
+	if (!_is_valid_div(table, div, flags))
 		return -EINVAL;
 
-	value = _get_val(divider, div);
+	value = _get_val(table, div, flags);
+
+	min_t(unsigned int, value, div_mask(width));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(divider_get_val);
+
+static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned int value;
+	unsigned long flags = 0;
+	u32 val;
 
-	if (value > div_mask(divider))
-		value = div_mask(divider);
+	value = divider_get_val(rate, parent_rate, divider->table,
+				divider->width, divider->flags);
 
 	if (divider->lock)
 		spin_lock_irqsave(divider->lock, flags);
 
 	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
-		val = div_mask(divider) << (divider->shift + 16);
+		val = div_mask(divider->width) << (divider->shift + 16);
 	} else {
 		val = clk_readl(divider->reg);
-		val &= ~(div_mask(divider) << divider->shift);
+		val &= ~(div_mask(divider->width) << divider->shift);
 	}
 	val |= value << divider->shift;
 	clk_writel(val, divider->reg);
include/linux/clk-provider.h +11 -0
index ba97e25c1115..32d39613d217 100644
@@ -342,6 +342,17 @@ struct clk_divider {
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;
+
+unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
+		unsigned int val, const struct clk_div_table *table,
+		unsigned long flags);
+long divider_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *prate, const struct clk_div_table *table,
+		u8 width, unsigned long flags);
+int divider_get_val(unsigned long rate, unsigned long parent_rate,
+		const struct clk_div_table *table, u8 width,
+		unsigned long flags);
+
 struct clk *clk_register_divider(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		void __iomem *reg, u8 shift, u8 width,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#5 lore raw

Re: [PATCH v2 04/15] clk: divider: Make generic for usage elsewhere

Quoted 11 lines · Stephen Boyd
drivers/clk/clk-divider.c +5 -2
+
+int divider_get_val(unsigned long rate, unsigned long parent_rate,
+		    const struct clk_div_table *table, u8 width,
+		    unsigned long flags)
+{
 	unsigned int div, value;
-	unsigned long flags = 0;
-	u32 val;
 
 	div = DIV_ROUND_UP(parent_rate, rate);
 

drivers/clk/clk-divider.c static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, · line 369

Quoted 10 lines
drivers/clk/clk-divider.c +6 -2
-	if (!_is_valid_div(divider, div))
+	if (!_is_valid_div(table, div, flags))
 		return -EINVAL;
 
-	value = _get_val(divider, div);
+	value = _get_val(table, div, flags);
+
+	min_t(unsigned int, value, div_mask(width));
+
+	return 0;

This should be return value, not return 0.

Quoted 3 lines
(quoted diff) +2 -0
+}
+EXPORT_SYMBOL_GPL(divider_get_val);
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#6 lore raw

Re: [PATCH v2 04/15] clk: divider: Make generic for usage elsewhere

Hi Stephen,

Just noticed this regression while testing the patch on Arndale board.

https://bugs.linaro.org/show_bug.cgi?id=740

--srini

Quoted 28 lines · Stephen Boyd
Quoted 11 lines · Stephen Boyd
drivers/clk/clk-divider.c +5 -2
+
+int divider_get_val(unsigned long rate, unsigned long parent_rate,
+		    const struct clk_div_table *table, u8 width,
+		    unsigned long flags)
+{
  	unsigned int div, value;
-	unsigned long flags = 0;
-	u32 val;
 
  	div = DIV_ROUND_UP(parent_rate, rate);
 

drivers/clk/clk-divider.c static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, · line 369

Quoted 10 lines
drivers/clk/clk-divider.c +6 -2
-	if (!_is_valid_div(divider, div))
+	if (!_is_valid_div(table, div, flags))
  		return -EINVAL;
 
-	value = _get_val(divider, div);
+	value = _get_val(table, div, flags);
+
+	min_t(unsigned int, value, div_mask(width));
+
+	return 0;

This should be return value, not return 0.

Quoted 3 lines
(quoted diff) +2 -0
+}
+EXPORT_SYMBOL_GPL(divider_get_val);
#7 lore raw

Re: [PATCH v2 04/15] clk: divider: Make generic for usage elsewhere

Quoted 5 lines · Srinivas Kandagatla

Hi Stephen,

Just noticed this regression while testing the patch on Arndale board.

https://bugs.linaro.org/show_bug.cgi?id=740

If you return value it works correctly right?

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#8 lore raw

Re: [PATCH v2 04/15] clk: divider: Make generic for usage elsewhere

Quoted 9 lines · Stephen Boyd
Quoted 5 lines · Srinivas Kandagatla

Hi Stephen,

Just noticed this regression while testing the patch on Arndale board.

https://bugs.linaro.org/show_bug.cgi?id=740

If you return value it works correctly right?

Am really not sure, as this bug was originally reported as part of Linaro tracking kernel.

--srini

#9 lore raw

Re: [PATCH v2 04/15] clk: divider: Make generic for usage elsewhere

In reply to Stephen Boyd
Quoted 9 lines · Stephen Boyd
Quoted 5 lines · Srinivas Kandagatla

Hi Stephen,

Just noticed this regression while testing the patch on Arndale board.

https://bugs.linaro.org/show_bug.cgi?id=740

If you return value it works correctly right?

I think you are right, ..
From last message from bugs.linaro.org:

"
That seems to be it!

This change:

drivers/clk/clk-divider.c +0 -0
@@ -366,7 +366,7 @@ int divider_get_val(unsigned long rate, unsigned long

parent

         min_t(unsigned int, value, div_mask(width));

- return 0;
+ return value;
}
EXPORT_SYMBOL_GPL(divider_get_val);

- makes Arndale to boot; rootfs on mmc mounts OK.
"

thanks,
srini

#10 lore raw

PATCH v2 4/15 clk: divider: Make generic for usage elsewhere

In reply to Stephen Boyd

Some devices don't use mmio to interact with dividers. Split out the logic from the register read/write parts so that we can reuse the division logic elsewhere.

---
 drivers/clk/clk-divider.c    | 197 ++++++++++++++++++++++++++-----------------
 include/linux/clk-provider.h |  11 +++
 2 files changed, 132 insertions(+), 76 deletions(-)
drivers/clk/clk-divider.c +121 -76
index 18a9de29df0e..78ecd8af8740 100644
@@ -30,7 +30,7 @@
 
 #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
 
-#define div_mask(d)	((1 << ((d)->width)) - 1)
+#define div_mask(width)	(BIT(width) - 1)
 
 static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
 {
@@ -54,15 +54,16 @@ static unsigned int _get_table_mindiv(const struct clk_div_table *table)
 	return mindiv;
 }
 
-static unsigned int _get_maxdiv(struct clk_divider *divider)
+static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
+				unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ONE_BASED)
-		return div_mask(divider);
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
-		return 1 << div_mask(divider);
-	if (divider->table)
-		return _get_table_maxdiv(divider->table);
-	return div_mask(divider) + 1;
+	if (flags & CLK_DIVIDER_ONE_BASED)
+		return div_mask(width);
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
+		return 1 << div_mask(width);
+	if (table)
+		return _get_table_maxdiv(table);
+	return div_mask(width) + 1;
 }
 
 static unsigned int _get_table_div(const struct clk_div_table *table,
@@ -76,14 +77,15 @@ static unsigned int _get_table_div(const struct clk_div_table *table,
 	return 0;
 }
 
-static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
+static unsigned int _get_div(const struct clk_div_table *table,
+			     unsigned int val, unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ONE_BASED)
+	if (flags & CLK_DIVIDER_ONE_BASED)
 		return val;
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return 1 << val;
-	if (divider->table)
-		return _get_table_div(divider->table, val);
+	if (table)
+		return _get_table_div(table, val);
 	return val + 1;
 }
 
@@ -98,29 +100,28 @@ static unsigned int _get_table_val(const struct clk_div_table *table,
 	return 0;
 }
 
-static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
+static unsigned int _get_val(const struct clk_div_table *table,
+			     unsigned int div, unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ONE_BASED)
+	if (flags & CLK_DIVIDER_ONE_BASED)
 		return div;
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return __ffs(div);
-	if (divider->table)
-		return  _get_table_val(divider->table, div);
+	if (table)
+		return  _get_table_val(table, div);
 	return div - 1;
 }
 
-static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
-		unsigned long parent_rate)
+unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
+				  unsigned int val,
+				  const struct clk_div_table *table,
+				  unsigned long flags)
 {
-	struct clk_divider *divider = to_clk_divider(hw);
-	unsigned int div, val;
-
-	val = clk_readl(divider->reg) >> divider->shift;
-	val &= div_mask(divider);
+	unsigned int div;
 
-	div = _get_div(divider, val);
+	div = _get_div(table, val, flags);
 	if (!div) {
-		WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
+		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			__clk_get_name(hw->clk));
 		return parent_rate;
@@ -128,6 +129,20 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
 
 	return DIV_ROUND_UP(parent_rate, div);
 }
+EXPORT_SYMBOL_GPL(divider_recalc_rate);
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned int val;
+
+	val = clk_readl(divider->reg) >> divider->shift;
+	val &= div_mask(divider->width);
+
+	return divider_recalc_rate(hw, parent_rate, val, divider->table,
+				   divider->flags);
+}
 
 /*
  * The reverse of DIV_ROUND_UP: The maximum number which
@@ -146,12 +161,13 @@ static bool _is_valid_table_div(const struct clk_div_table *table,
 	return false;
 }
 
-static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
+static bool _is_valid_div(const struct clk_div_table *table, unsigned int div,
+			  unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return is_power_of_2(div);
-	if (divider->table)
-		return _is_valid_table_div(divider->table, div);
+	if (table)
+		return _is_valid_table_div(table, div);
 	return true;
 }
 
@@ -191,71 +207,76 @@ static int _round_down_table(const struct clk_div_table *table, int div)
 	return down;
 }
 
-static int _div_round_up(struct clk_divider *divider,
-		unsigned long parent_rate, unsigned long rate)
+static int _div_round_up(const struct clk_div_table *table,
+			 unsigned long parent_rate, unsigned long rate,
+			 unsigned long flags)
 {
 	int div = DIV_ROUND_UP(parent_rate, rate);
 
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		div = __roundup_pow_of_two(div);
-	if (divider->table)
-		div = _round_up_table(divider->table, div);
+	if (table)
+		div = _round_up_table(table, div);
 
 	return div;
 }
 
-static int _div_round_closest(struct clk_divider *divider,
-		unsigned long parent_rate, unsigned long rate)
+static int _div_round_closest(const struct clk_div_table *table,
+			      unsigned long parent_rate, unsigned long rate,
+			      unsigned long flags)
 {
 	int up, down, div;
 
 	up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);
 
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
+	if (flags & CLK_DIVIDER_POWER_OF_TWO) {
 		up = __roundup_pow_of_two(div);
 		down = __rounddown_pow_of_two(div);
-	} else if (divider->table) {
-		up = _round_up_table(divider->table, div);
-		down = _round_down_table(divider->table, div);
+	} else if (table) {
+		up = _round_up_table(table, div);
+		down = _round_down_table(table, div);
 	}
 
 	return (up - div) <= (div - down) ? up : down;
 }
 
-static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
-		unsigned long rate)
+static int _div_round(const struct clk_div_table *table,
+		      unsigned long parent_rate, unsigned long rate,
+		      unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
-		return _div_round_closest(divider, parent_rate, rate);
+	if (flags & CLK_DIVIDER_ROUND_CLOSEST)
+		return _div_round_closest(table, parent_rate, rate, flags);
 
-	return _div_round_up(divider, parent_rate, rate);
+	return _div_round_up(table, parent_rate, rate, flags);
 }
 
-static bool _is_best_div(struct clk_divider *divider,
-		unsigned long rate, unsigned long now, unsigned long best)
+static bool _is_best_div(unsigned long rate, unsigned long now,
+			 unsigned long best, unsigned long flags)
 {
-	if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
+	if (flags & CLK_DIVIDER_ROUND_CLOSEST)
 		return abs(rate - now) < abs(rate - best);
 
 	return now <= rate && now > best;
 }
 
-static int _next_div(struct clk_divider *divider, int div)
+static int _next_div(const struct clk_div_table *table, int div,
+		     unsigned long flags)
 {
 	div++;
 
-	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return __roundup_pow_of_two(div);
-	if (divider->table)
-		return _round_up_table(divider->table, div);
+	if (table)
+		return _round_up_table(table, div);
 
 	return div;
 }
 
 static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
-		unsigned long *best_parent_rate)
+			       unsigned long *best_parent_rate,
+			       const struct clk_div_table *table, u8 width,
+			       unsigned long flags)
 {
-	struct clk_divider *divider = to_clk_divider(hw);
 	int i, bestdiv = 0;
 	unsigned long parent_rate, best = 0, now, maxdiv;
 	unsigned long parent_rate_saved = *best_parent_rate;
@@ -263,11 +284,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	if (!rate)
 		rate = 1;
 
-	maxdiv = _get_maxdiv(divider);
+	maxdiv = _get_maxdiv(table, width, flags);
 
 	if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
 		parent_rate = *best_parent_rate;
-		bestdiv = _div_round(divider, parent_rate, rate);
+		bestdiv = _div_round(table, parent_rate, rate, flags);
 		bestdiv = bestdiv == 0 ? 1 : bestdiv;
 		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
 		return bestdiv;
@@ -279,8 +300,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	 */
 	maxdiv = min(ULONG_MAX / rate, maxdiv);
 
-	for (i = 1; i <= maxdiv; i = _next_div(divider, i)) {
-		if (!_is_valid_div(divider, i))
+	for (i = 1; i <= maxdiv; i = _next_div(table, i, flags)) {
+		if (!_is_valid_div(table, i, flags))
 			continue;
 		if (rate * i == parent_rate_saved) {
 			/*
@@ -294,7 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
 				MULT_ROUND_UP(rate, i));
 		now = DIV_ROUND_UP(parent_rate, i);
-		if (_is_best_div(divider, rate, now, best)) {
+		if (_is_best_div(rate, now, best, flags)) {
 			bestdiv = i;
 			best = now;
 			*best_parent_rate = parent_rate;
@@ -302,48 +323,72 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	}
 
 	if (!bestdiv) {
-		bestdiv = _get_maxdiv(divider);
+		bestdiv = _get_maxdiv(table, width, flags);
 		*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
 	}
 
 	return bestdiv;
 }
 
-static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
-				unsigned long *prate)
+long divider_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate, const struct clk_div_table *table,
+			u8 width, unsigned long flags)
 {
 	int div;
-	div = clk_divider_bestdiv(hw, rate, prate);
+
+	div = clk_divider_bestdiv(hw, rate, prate, table, width, flags);
 
 	return DIV_ROUND_UP(*prate, div);
 }
+EXPORT_SYMBOL_GPL(divider_round_rate);
 
-static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
-				unsigned long parent_rate)
+static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
 {
 	struct clk_divider *divider = to_clk_divider(hw);
+
+	return divider_round_rate(hw, rate, prate, divider->table,
+				  divider->width, divider->flags);
+}
+
+int divider_get_val(unsigned long rate, unsigned long parent_rate,
+		    const struct clk_div_table *table, u8 width,
+		    unsigned long flags)
+{
 	unsigned int div, value;
-	unsigned long flags = 0;
-	u32 val;
 
 	div = DIV_ROUND_UP(parent_rate, rate);
 
-	if (!_is_valid_div(divider, div))
+	if (!_is_valid_div(table, div, flags))
 		return -EINVAL;
 
-	value = _get_val(divider, div);
+	value = _get_val(table, div, flags);
+
+	min_t(unsigned int, value, div_mask(width));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(divider_get_val);
+
+static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	unsigned int value;
+	unsigned long flags = 0;
+	u32 val;
 
-	if (value > div_mask(divider))
-		value = div_mask(divider);
+	value = divider_get_val(rate, parent_rate, divider->table,
+				divider->width, divider->flags);
 
 	if (divider->lock)
 		spin_lock_irqsave(divider->lock, flags);
 
 	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
-		val = div_mask(divider) << (divider->shift + 16);
+		val = div_mask(divider->width) << (divider->shift + 16);
 	} else {
 		val = clk_readl(divider->reg);
-		val &= ~(div_mask(divider) << divider->shift);
+		val &= ~(div_mask(divider->width) << divider->shift);
 	}
 	val |= value << divider->shift;
 	clk_writel(val, divider->reg);
include/linux/clk-provider.h +11 -0
index 92a00b61370a..5058fb98dbfb 100644
@@ -342,6 +342,17 @@ struct clk_divider {
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;
+
+unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
+		unsigned int val, const struct clk_div_table *table,
+		unsigned long flags);
+long divider_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *prate, const struct clk_div_table *table,
+		u8 width, unsigned long flags);
+int divider_get_val(unsigned long rate, unsigned long parent_rate,
+		const struct clk_div_table *table, u8 width,
+		unsigned long flags);
+
 struct clk *clk_register_divider(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		void __iomem *reg, u8 shift, u8 width,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#11 lore raw

PATCH v2 5/15 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 some 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.

---
 drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-private.h  |  2 ++
 include/linux/clk-provider.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)
drivers/clk/clk.c +46 -7
index 255ec8c92919..b23e2b9c9102 100644
@@ -1368,6 +1368,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
 			     struct clk *new_parent, u8 p_index)
 {
 	struct clk *child;
+	struct clk *parent;
 
 	clk->new_rate = new_rate;
 	clk->new_parent = new_parent;
@@ -1377,6 +1378,17 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
 	if (new_parent && new_parent != clk->parent)
 		new_parent->new_child = clk;
 
+	if (clk->ops->get_safe_parent) {
+		parent = clk->ops->get_safe_parent(clk->hw);
+		if (parent) {
+			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);
@@ -1459,14 +1471,42 @@ out:
 static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
 {
 	struct clk *child, *tmp_clk, *fail_clk = NULL;
+	struct clk *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);
+		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)
+			ret = __clk_notify(clk, event, clk->rate,
+					   clk->new_rate);
+		if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
 			fail_clk = clk;
 	}
 
@@ -1508,7 +1548,8 @@ static void clk_change_rate(struct clk *clk)
 	else if (clk->parent)
 		best_parent_rate = clk->parent->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) {
@@ -1528,9 +1569,6 @@ static void clk_change_rate(struct clk *clk)
 
 	clk->rate = clk_recalc(clk, best_parent_rate);
 
-	if (clk->notifier_count && old_rate != clk->rate)
-		__clk_notify(clk, POST_RATE_CHANGE, 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)
@@ -1604,6 +1642,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
 	/* change the rates */
 	clk_change_rate(top);
 
+	clk_propagate_rate_change(top, POST_RATE_CHANGE);
 out:
 	clk_prepare_unlock();
 
include/linux/clk-private.h +2 -0
index efbf70b9fd84..f48684af4d8f 100644
@@ -38,8 +38,10 @@ struct clk {
 	struct clk		**parents;
 	u8			num_parents;
 	u8			new_parent_index;
+	u8			safe_parent_index;
 	unsigned long		rate;
 	unsigned long		new_rate;
+	struct clk		*safe_parent;
 	struct clk		*new_parent;
 	struct clk		*new_child;
 	unsigned long		flags;
include/linux/clk-provider.h +1 -0
index 32d39613d217..6dec0b306336 100644
@@ -170,6 +170,7 @@ struct clk_ops {
 					struct clk **best_parent_clk);
 	int		(*set_parent)(struct clk_hw *hw, u8 index);
 	u8		(*get_parent)(struct clk_hw *hw);
+	struct clk	*(*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,
hosted by The Linux Foundation
#12 lore raw

PATCH v2 6/15 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 | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)
drivers/clk/clk.c +21 -12
index b23e2b9c9102..5e9afbc3040b 100644
@@ -1533,20 +1533,22 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
  * walk down a subtree and set the new rates notifying the rate
  * change on the way
  */
-static void clk_change_rate(struct clk *clk)
+static void clk_change_rate(struct clk *clk, unsigned long best_parent_rate)
 {
 	struct clk *child;
 	unsigned long old_rate;
-	unsigned long best_parent_rate = 0;
 	bool skip_set_rate = false;
 	struct clk *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 &&
 			!clk->safe_parent) {
@@ -1567,18 +1569,19 @@ static void clk_change_rate(struct clk *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;
 
 	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;
-		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);
 }
 
 /**
@@ -1606,6 +1609,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
 {
 	struct clk *top, *fail_clk;
 	int ret = 0;
+	unsigned long parent_rate;
 
 	if (!clk)
 		return 0;
@@ -1639,8 +1643,13 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
 		goto out;
 	}
 
+	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_propagate_rate_change(top, POST_RATE_CHANGE);
 out:
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#13 lore raw

PATCH v2 7/15 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.

---
 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 783cfb24faa4..f1e54065e4dc 100644
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
 clk-qcom-y += clk-rcg.o
 clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.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,
hosted by The Linux Foundation
#14 lore raw

PATCH v2 8/15 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.

---
 drivers/clk/qcom/Kconfig  |   8 ++++
 drivers/clk/qcom/Makefile |   1 +
 drivers/clk/qcom/hfpll.c  | 110 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+)
 create mode 100644 drivers/clk/qcom/hfpll.c
drivers/clk/qcom/Kconfig +8 -0
index 1107351ed346..3ff19a51a933 100644
@@ -70,3 +70,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 f1e54065e4dc..db39912cffec 100644
@@ -17,3 +17,4 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-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 +110 -0

110 lines not on this page · raw message

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

PATCH v2 9/15 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 007534f7a2d7..e60327baf5db 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,
hosted by The Linux Foundation
#16 lore raw

PATCH v2 10/15 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 4032e510d9aa..353261b12ba1 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,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,
@@ -2232,6 +2312,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,
hosted by The Linux Foundation
#17 lore raw

PATCH v2 11/15 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 3ff19a51a933..2fd07b17e10e 100644
@@ -78,3 +78,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 db39912cffec..c7fda5e9dcc7 100644
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
 clk-qcom-y += clk-rcg.o
 clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.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,
hosted by The Linux Foundation
#18 lore raw

PATCH v2 12/15 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.

---
 drivers/clk/qcom/Kconfig    |  8 ++++
 drivers/clk/qcom/Makefile   |  1 +
 drivers/clk/qcom/kpss-xcc.c | 94 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+)
 create mode 100644 drivers/clk/qcom/kpss-xcc.c
drivers/clk/qcom/Kconfig +8 -0
index 2fd07b17e10e..6bf73773f75a 100644
@@ -79,6 +79,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 c7fda5e9dcc7..d85491bfc053 100644
@@ -18,4 +18,5 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-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 +94 -0

94 lines not on this page · raw message

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

PATCH v2 13/15 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.

---
 drivers/clk/qcom/Kconfig    |   8 +
 drivers/clk/qcom/Makefile   |   1 +
 drivers/clk/qcom/krait-cc.c | 357 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 366 insertions(+)
 create mode 100644 drivers/clk/qcom/krait-cc.c
drivers/clk/qcom/Kconfig +8 -0
index 6bf73773f75a..63285d116013 100644
@@ -87,6 +87,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 d85491bfc053..866f0fe600d9 100644
@@ -20,3 +20,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 +357 -0

357 lines not on this page · raw message

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

PATCH v2 14/15 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.

---
 drivers/cpufreq/Kconfig.arm    |   9 ++
 drivers/cpufreq/Makefile       |   1 +
 drivers/cpufreq/qcom-cpufreq.c | 199 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/cpufreq/qcom-cpufreq.c
drivers/cpufreq/Kconfig.arm +9 -0
index b0b0ca1e9aac..5eb3bbce2f58 100644
@@ -129,6 +129,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 e8efe9b8d55f..b345bca5fa09 100644
@@ -61,6 +61,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ)		+= imx6q-cpufreq.o
 obj-$(CONFIG_ARM_INTEGRATOR)		+= integrator-cpufreq.o
 obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ)	+= kirkwood-cpufreq.o
 obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ)		+= qcom-cpufreq.o
 obj-$(CONFIG_PXA25x)			+= pxa2xx-cpufreq.o
 obj-$(CONFIG_PXA27x)			+= pxa2xx-cpufreq.o
 obj-$(CONFIG_PXA3xx)			+= pxa3xx-cpufreq.o
drivers/cpufreq/qcom-cpufreq.c new file +199 -0

199 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
maintainer Acked-by on v2 14/15
#21 lore raw

Re: [PATCH v2 14/15] cpufreq: Add module to register cpufreq on Krait CPUs

drivers/cpufreq/qcom-cpufreq.c line 1

Quoted 7 lines · Stephen Boyd
drivers/cpufreq/qcom-cpufreq.c new file +1 -0
new file mode 100644
index 000000000000..aa8eb97144b6
@@ -0,0 +1,199 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.

Shouldn't this have Qcom instead?

Quoted 10 lines
drivers/cpufreq/qcom-cpufreq.c new file +10 -0
+ * 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.
+ */
+

drivers/cpufreq/qcom-cpufreq.c line 22

Quoted 10 lines
drivers/cpufreq/qcom-cpufreq.c new file +10 -0
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_opp.h>
+#include <linux/init.h>

Would be good if these can be in alphanumeric order. That helps maintaining them later on..

drivers/cpufreq/qcom-cpufreq.c line 76

Quoted 9 lines
drivers/cpufreq/qcom-cpufreq.c new file +7 -0
+static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
+{
 
+}
+
+static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
+{
 
+}

To be honest, I didn't try to apply much brain on the above routines :)

drivers/cpufreq/qcom-cpufreq.c line 123

Quoted 9 lines
drivers/cpufreq/qcom-cpufreq.c new file +9 -0
+static int __init qcom_cpufreq_populate_opps(void)
+{
+       int len, num_rows, i, k;
+       char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
+       struct device_node *np;
+       struct device *dev;
+       int cpu = 0;
+       int speed, pvs, pvs_ver;
+       int cols;

All the 'int' declarations can be combined in a single line if you would like.

Quoted 64 lines
drivers/cpufreq/qcom-cpufreq.c new file +64 -0
+       np = of_find_node_by_name(NULL, "qcom,pvs");
+       if (!np)
+               pr_warn("Can't find PVS node\n");
+
+       if (of_property_read_bool(np, "qcom,pvs-format-a")) {
+               get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
+               cols = 2;
+       } else {
+               get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
+               cols = 3;
+       }
+
+       snprintf(table_name, sizeof(table_name),
+                       "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
+again:
+       dev = get_cpu_device(cpu);
+       if (!dev)
+               return -ENODEV;
+
+       if (!of_find_property(np, table_name, &len))
+               return -EINVAL;
+
+       len /= sizeof(u32);
+       if (len % cols || len == 0)
+               return -EINVAL;
+
+       num_rows = len / cols;
+
+       for (i = 0, k = 0; i < num_rows; i++) {
+               u32 freq, volt;
+
+               of_property_read_u32_index(np, table_name, k++, &freq);
+               of_property_read_u32_index(np, table_name, k++, &volt);
+               while (k % cols)
+                       k++; /* Skip uA entries if present */
+               if (dev_pm_opp_add(dev, freq, volt))
+                       pr_warn("failed to add OPP %u\n", freq);
+       }
+
+       if (cpu++ < num_possible_cpus())
+               goto again;
+
+       return 0;
+}
+
+static int __init qcom_cpufreq_driver_init(void)
+{
+       struct platform_device_info devinfo = { .name = "cpufreq-generic", };
+       struct device *cpu_dev;
+       struct device_node *np;
+       struct platform_device *pdev;
+
+       cpu_dev = get_cpu_device(0);
+       if (!cpu_dev)
+               return -ENODEV;
+
+       np = of_node_get(cpu_dev->of_node);
+       if (!np)
+               return -ENOENT;
+
+       if (!of_device_is_compatible(np, "qcom,krait")) {
+               of_node_put(np);
+               return -ENODEV;
+       }

drivers/cpufreq/qcom-cpufreq.c line 198

Quoted 10 lines
drivers/cpufreq/qcom-cpufreq.c new file +10 -0
+       of_node_put(np);
+
+       qcom_cpufreq_populate_opps();
+       pdev = platform_device_register_full(&devinfo);
+
+       return PTR_ERR_OR_ZERO(pdev);
+}
+module_init(qcom_cpufreq_driver_init);
+
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");

A module author as well?

Quoted 1 line

+MODULE_LICENSE("GPL v2");

Otherwise mostly good:

#22 lore raw

Re: [PATCH v2 14/15] cpufreq: Add module to register cpufreq on Krait CPUs

Quoted 11 lines · Viresh Kumar

drivers/cpufreq/qcom-cpufreq.c line 1

Quoted 7 lines · Stephen Boyd
drivers/cpufreq/qcom-cpufreq.c new file +1 -0
new file mode 100644
index 000000000000..aa8eb97144b6
@@ -0,0 +1,199 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.

Shouldn't this have Qcom instead?

No. The copyright has been transferred.

Christopher

Signature · 3 lines
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by the Linux Foundation.
#23 lore raw

Re: [PATCH v2 14/15] cpufreq: Add module to register cpufreq on Krait CPUs

In reply to Viresh Kumar
Quoted 16 lines · Viresh Kumar
Quoted 14 lines · Stephen Boyd

+ * 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.
+ */

drivers/cpufreq/qcom-cpufreq.c new file +5 -0
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
Quoted 8 lines

drivers/cpufreq/qcom-cpufreq.c line 22

Quoted 6 lines
drivers/cpufreq/qcom-cpufreq.c new file +6 -0
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_opp.h>
+#include <linux/init.h>

Would be good if these can be in alphanumeric order. That helps maintaining them later on..

Ok.

Quoted 11 lines

drivers/cpufreq/qcom-cpufreq.c line 123

Quoted 10 lines
drivers/cpufreq/qcom-cpufreq.c new file +9 -0
+static int __init qcom_cpufreq_populate_opps(void)
+{
+       int len, num_rows, i, k;
+       char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
+       struct device_node *np;
+       struct device *dev;
+       int cpu = 0;
+       int speed, pvs, pvs_ver;
+       int cols;

All the 'int' declarations can be combined in a single line if you would like.

Sure.

Quoted 3 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");

A module author as well?

How about we use git blame instead?

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
maintainer
#24 lore raw

Re: [PATCH v2 14/15] cpufreq: Add module to register cpufreq on Krait CPUs

Quoted 3 lines · Stephen Boyd
Quoted 1 line

A module author as well?

How about we use git blame instead?

Not very sure but this information is probably present in the compiled module. And so can be accessed without the sourcecode as well..

--
viresh

#25 lore raw

PATCH v2 14/15 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.

---
 drivers/cpufreq/Kconfig.arm    |   9 ++
 drivers/cpufreq/Makefile       |   1 +
 drivers/cpufreq/qcom-cpufreq.c | 199 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/cpufreq/qcom-cpufreq.c
drivers/cpufreq/Kconfig.arm +9 -0
index b0b0ca1e9aac..5eb3bbce2f58 100644
@@ -129,6 +129,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 e8efe9b8d55f..b345bca5fa09 100644
@@ -61,6 +61,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ)		+= imx6q-cpufreq.o
 obj-$(CONFIG_ARM_INTEGRATOR)		+= integrator-cpufreq.o
 obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ)	+= kirkwood-cpufreq.o
 obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ)		+= qcom-cpufreq.o
 obj-$(CONFIG_PXA25x)			+= pxa2xx-cpufreq.o
 obj-$(CONFIG_PXA27x)			+= pxa2xx-cpufreq.o
 obj-$(CONFIG_PXA3xx)			+= pxa3xx-cpufreq.o
drivers/cpufreq/qcom-cpufreq.c new file +199 -0

199 lines not on this page · raw message

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

PATCH v2 15/15 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.

---

We can go faster than this, but I've limited it to the safe frequencies until we have regulator support where we would need to increase the voltages from whatever the bootloader picks before going faster.

 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 92bf793622c3..f01296bdb8f4 100644
@@ -21,6 +21,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc0>;
 			qcom,saw = <&saw0>;
+			clocks = <&kraitcc 0>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		cpu at 1 {
@@ -31,6 +34,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc1>;
 			qcom,saw = <&saw1>;
+			clocks = <&kraitcc 1>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		cpu at 2 {
@@ -41,6 +47,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc2>;
 			qcom,saw = <&saw2>;
+			clocks = <&kraitcc 2>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		cpu at 3 {
@@ -51,6 +60,9 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc3>;
 			qcom,saw = <&saw3>;
+			clocks = <&kraitcc 3>;
+			clock-names = "cpu";
+			clock-latency = <100000>;
 		};
 
 		L2: l2-cache {
@@ -64,6 +76,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>;
@@ -92,21 +312,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,
hosted by The Linux Foundation
#27 lore raw
In reply to Stephen Boyd

Hi Stephen,
Looks like one patch is missing from v1. Is this removed from v2 patchset from some reason?
ARM: Add Krait L2 register accessor functions

But there are references to this in v2 set and build fails while compiling drivers/clk/qcom/clk-krait.c.

Thanks and Regards
Pramod

Quoted 128 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
				    +   |     +
					+-----|\       +------+
	 +-------+                            | \      |      |
	 | 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. Please note that these patches rely on the Krait L2 accessor patch I posted a while back in another series[1]. This also relies on the cpufreq-generic patchset from Viresh[2]. If anything can be picked up right now it would be better to reduce the churn over time as other pieces settle. The series gets progressively more controversial as it goes on, so I hope that things near the beggining will be picked up earlier.

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)
* Document DT bindings
* Use a new efuse/eeprom API instead of hardcoding the location in the driver
* Add some thermal awareness

Stephen Boyd (15):
clk: mux: Add unregistration API
clk: mux: Split out register accessors for reuse
clk: Add __clk_mux_determine_rate_closest
clk: divider: Make generic for usage elsewhere
clk: Add safe switch hook
clk: Avoid sending high rates to downstream clocks during set_rate
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

 arch/arm/boot/dts/qcom-apq8064.dtsi          | 230 +++++++++++++++++
 arch/arm/boot/dts/qcom-msm8960.dtsi          |  49 ++++
 arch/arm/boot/dts/qcom-msm8974.dtsi          | 311 ++++++++++++++++++++++-
 drivers/clk/clk-divider.c                    | 197 +++++++++------
 drivers/clk/clk-mux.c                        |  91 ++++---
 drivers/clk/clk.c                            | 133 +++++++---
 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                     | 110 +++++++++
 drivers/clk/qcom/kpss-xcc.c                  |  94 +++++++
 drivers/clk/qcom/krait-cc.c                  | 357 +++++++++++++++++++++++++++
 drivers/cpufreq/Kconfig.arm                  |   9 +
 drivers/cpufreq/Makefile                     |   1 +
 drivers/cpufreq/qcom-cpufreq.c               | 199 +++++++++++++++
 include/dt-bindings/clock/qcom,gcc-msm8960.h |   2 +
 include/linux/clk-private.h                  |   2 +
 include/linux/clk-provider.h                 |  32 ++-
 23 files changed, 2486 insertions(+), 141 deletions(-)
 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

[1] http://lkml.iu.edu/hypermail/linux/kernel/1404.0/02636.html
[2] git://git.linaro.org/people/viresh.kumar/linux cpufreq/cpu0-krait-v3

#28 lore raw

Sorry Stephen for spaming.
I read the full patch cover letter and found your note. Somehow I missed on that.

Thanks
Pramod

Quoted 146 lines · Pramod Gurav

Hi Stephen,
Looks like one patch is missing from v1. Is this removed from v2 patchset from some reason?
ARM: Add Krait L2 register accessor functions

But there are references to this in v2 set and build fails while compiling drivers/clk/qcom/clk-krait.c.

Thanks and Regards
Pramod

Quoted 125 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
				    +   |     +
					+-----|\       +------+
	 +-------+                            | \      |      |
	 | 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. Please note that these patches rely on the Krait L2 accessor patch I posted a while back in another series[1]. This also relies on the cpufreq-generic patchset from Viresh[2]. If anything can be picked up right now it would be better to reduce the churn over time as other pieces settle. The series gets progressively more controversial as it goes on, so I hope that things near the beggining will be picked up earlier.

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)
* Document DT bindings
* Use a new efuse/eeprom API instead of hardcoding the location in the driver
* Add some thermal awareness

Stephen Boyd (15):
clk: mux: Add unregistration API
clk: mux: Split out register accessors for reuse
clk: Add __clk_mux_determine_rate_closest
clk: divider: Make generic for usage elsewhere
clk: Add safe switch hook
clk: Avoid sending high rates to downstream clocks during set_rate
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

 arch/arm/boot/dts/qcom-apq8064.dtsi          | 230 +++++++++++++++++
 arch/arm/boot/dts/qcom-msm8960.dtsi          |  49 ++++
 arch/arm/boot/dts/qcom-msm8974.dtsi          | 311 ++++++++++++++++++++++-
 drivers/clk/clk-divider.c                    | 197 +++++++++------
 drivers/clk/clk-mux.c                        |  91 ++++---
 drivers/clk/clk.c                            | 133 +++++++---
 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                     | 110 +++++++++
 drivers/clk/qcom/kpss-xcc.c                  |  94 +++++++
 drivers/clk/qcom/krait-cc.c                  | 357 +++++++++++++++++++++++++++
 drivers/cpufreq/Kconfig.arm                  |   9 +
 drivers/cpufreq/Makefile                     |   1 +
 drivers/cpufreq/qcom-cpufreq.c               | 199 +++++++++++++++
 include/dt-bindings/clock/qcom,gcc-msm8960.h |   2 +
 include/linux/clk-private.h                  |   2 +
 include/linux/clk-provider.h                 |  32 ++-
 23 files changed, 2486 insertions(+), 141 deletions(-)
 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
Quoted 3 lines

[1] http://lkml.iu.edu/hypermail/linux/kernel/1404.0/02636.html
[2] git://git.linaro.org/people/viresh.kumar/linux cpufreq/cpu0-krait-v3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/