linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq
@ 2014-06-25  0:06 Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 01/12] ARM: Add Krait L2 register accessor functions Stephen Boyd
                   ` (12 more replies)
  0 siblings, 13 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

These patches provide cpufreq scaling on devices with Krait CPUs.
For now I've only added support for 8960 and 8974. Adding more
chips should be fairly easy. There are still some big TODOs but
I'm posting these patches now to get any early feedback possible.

The first patch has been posted before. It's included here for
completeness. The second patch changes the way rates are changed
in the generic framework so that we can safely switch rates of clocks
while they're being used to clock the CPU. The third patch is 
temporary until I can properly use the CCF code for generic muxes
and dividers. The fourth, fifth and sixth patches are mostly ready to go,
they add HFPLL support. After that, the seventh patch adds support for the
Krait clock ops; basically muxes and dividers that are controlled by cp15
registers. The eighth patch adds a driver to control the muxes in KPSSv1
that provide the aux clock source for the Krait clocks. The ninth patch ties
all the Krait clocks together into a clock provider. The tenth and eleventh
patches are the cpufreq layer that look for a qcom,krait cpu and then register
a cpufreq driver that switches the rate of the CPU clocks between whatever OPP
tables say are available. The final patch ties it all together by adding
all the DTS necessary to get this stuff to probe.

I plan to submit the cpufreq patches directly to the cpufreq maintainers
and the DTS changes through arm-soc, but I've included everything here to
make it easier to pick things up for testing, etc.

TODO:
 * Get rid of clk-generic.c and use the CCF generic code
 * Make cpufreq-krait generic for any systems with one clock per CPU
 * Populate OPPs by reading the qfprom instead of relying on DT
 * Add Krait regulator voltage scaling (not strictly necessary)
 * Document lots of DT bindings

Stephen Boyd (12):
  ARM: Add Krait L2 register accessor functions
  clk: Add safe switch hook
  clk: qcom: Add support for muxes, dividers, and mux dividers
  clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
  clk: qcom: Add HFPLL driver
  clk: qcom: Add MSM8960'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 a cpufreq-krait based on cpufreq-cpu0
  cpufreq: Add module to register cpufreq-krait device
  ARM: dts: qcom: Add necessary DT data for Krait cpufreq

 arch/arm/boot/dts/qcom-msm8960.dtsi       |  49 ++++
 arch/arm/boot/dts/qcom-msm8974.dtsi       | 106 +++++++-
 arch/arm/common/Kconfig                   |   3 +
 arch/arm/common/Makefile                  |   1 +
 arch/arm/common/krait-l2-accessors.c      |  58 +++++
 arch/arm/include/asm/krait-l2-accessors.h |  20 ++
 drivers/clk/clk.c                         |  53 +++-
 drivers/clk/qcom/Kconfig                  |  25 ++
 drivers/clk/qcom/Makefile                 |   6 +
 drivers/clk/qcom/clk-generic.c            | 405 ++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-hfpll.c              | 260 +++++++++++++++++++
 drivers/clk/qcom/clk-hfpll.h              |  54 ++++
 drivers/clk/qcom/clk-krait.c              | 121 +++++++++
 drivers/clk/qcom/clk-krait.h              |  22 ++
 drivers/clk/qcom/gcc-msm8960.c            |  82 ++++++
 drivers/clk/qcom/hfpll.c                  | 110 ++++++++
 drivers/clk/qcom/kpss-xcc.c               | 115 +++++++++
 drivers/clk/qcom/krait-cc.c               | 366 +++++++++++++++++++++++++++
 drivers/cpufreq/Kconfig                   |  13 +
 drivers/cpufreq/Kconfig.arm               |   8 +
 drivers/cpufreq/Makefile                  |   2 +
 drivers/cpufreq/cpufreq-krait.c           | 190 ++++++++++++++
 drivers/cpufreq/qcom-cpufreq.c            |  48 ++++
 include/linux/clk-private.h               |   2 +
 include/linux/clk-provider.h              |   1 +
 include/linux/clk/msm-clk-generic.h       | 208 +++++++++++++++
 26 files changed, 2317 insertions(+), 11 deletions(-)
 create mode 100644 arch/arm/common/krait-l2-accessors.c
 create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
 create mode 100644 drivers/clk/qcom/clk-generic.c
 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/cpufreq-krait.c
 create mode 100644 drivers/cpufreq/qcom-cpufreq.c
 create mode 100644 include/linux/clk/msm-clk-generic.h

-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFC/PATCH 01/12] ARM: Add Krait L2 register accessor functions
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 02/12] clk: Add safe switch hook Stephen Boyd
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 arch/arm/common/Kconfig                   |  3 ++
 arch/arm/common/Makefile                  |  1 +
 arch/arm/common/krait-l2-accessors.c      | 58 +++++++++++++++++++++++++++++++
 arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
 4 files changed, 82 insertions(+)
 create mode 100644 arch/arm/common/krait-l2-accessors.c
 create mode 100644 arch/arm/include/asm/krait-l2-accessors.h

diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index c3a4e9ceba34..9da52dc6260b 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -9,6 +9,9 @@ config DMABOUNCE
 	bool
 	select ZONE_DMA
 
+config KRAIT_L2_ACCESSORS
+	bool
+
 config SHARP_LOCOMO
 	bool
 
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 70b1eff477b3..6b2cddf6e8d0 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -7,6 +7,7 @@ obj-y				+= firmware.o
 obj-$(CONFIG_ICST)		+= icst.o
 obj-$(CONFIG_SA1111)		+= sa1111.o
 obj-$(CONFIG_DMABOUNCE)		+= dmabounce.o
+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
 obj-$(CONFIG_SHARP_LOCOMO)	+= locomo.o
 obj-$(CONFIG_SHARP_PARAM)	+= sharpsl_param.o
 obj-$(CONFIG_SHARP_SCOOP)	+= scoop.o
diff --git a/arch/arm/common/krait-l2-accessors.c b/arch/arm/common/krait-l2-accessors.c
new file mode 100644
index 000000000000..5d514bbc88a6
--- /dev/null
+++ b/arch/arm/common/krait-l2-accessors.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/export.h>
+
+#include <asm/barrier.h>
+#include <asm/krait-l2-accessors.h>
+
+static DEFINE_RAW_SPINLOCK(krait_l2_lock);
+
+void krait_set_l2_indirect_reg(u32 addr, u32 val)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&krait_l2_lock, flags);
+	/*
+	 * Select the L2 window by poking l2cpselr, then write to the window
+	 * via l2cpdr.
+	 */
+	asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
+	isb();
+	asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
+	isb();
+
+	raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
+}
+EXPORT_SYMBOL(krait_set_l2_indirect_reg);
+
+u32 krait_get_l2_indirect_reg(u32 addr)
+{
+	u32 val;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&krait_l2_lock, flags);
+	/*
+	 * Select the L2 window by poking l2cpselr, then read from the window
+	 * via l2cpdr.
+	 */
+	asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
+	isb();
+	asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
+
+	raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
+
+	return val;
+}
+EXPORT_SYMBOL(krait_get_l2_indirect_reg);
diff --git a/arch/arm/include/asm/krait-l2-accessors.h b/arch/arm/include/asm/krait-l2-accessors.h
new file mode 100644
index 000000000000..48fe5527bc01
--- /dev/null
+++ b/arch/arm/include/asm/krait-l2-accessors.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
+
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
+extern u32 krait_get_l2_indirect_reg(u32 addr);
+
+#endif
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 02/12] clk: Add safe switch hook
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 01/12] ARM: Add Krait L2 register accessor functions Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
       [not found]   ` <20140729060529.4906.66639@quantum>
  2014-06-25  0:06 ` [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers Stephen Boyd
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-private.h  |  2 ++
 include/linux/clk-provider.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 8b73edef151d..5e32fa55032b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1367,6 +1367,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;
@@ -1376,6 +1377,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);
@@ -1458,14 +1470,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;
 	}
 
@@ -1507,7 +1547,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) {
@@ -1527,9 +1568,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)
@@ -1603,6 +1641,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();
 
diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
index efbf70b9fd84..f48684af4d8f 100644
--- a/include/linux/clk-private.h
+++ b/include/linux/clk-private.h
@@ -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;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 0c287dbbb144..7485911df4b6 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -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,
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 01/12] ARM: Add Krait L2 register accessor functions Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 02/12] clk: Add safe switch hook Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-07-14 10:06   ` pramod gurav
  2014-06-25  0:06 ` [RFC/PATCH 04/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) Stephen Boyd
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm
  Cc: linux-arm-kernel, linux-kernel, Mike Turquette, Saravana Kannan

The Krait CPU clocks are made up of muxes and dividers with a
handful of sources. Add a set of clk_ops that allow us to
configure these clocks so we can support CPU frequency scaling on
Krait CPUs.

Based on code originally written by Saravana Kannan.

Cc: Saravana Kannan <skannan@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/qcom/Makefile           |   1 +
 drivers/clk/qcom/clk-generic.c      | 405 ++++++++++++++++++++++++++++++++++++
 include/linux/clk/msm-clk-generic.h | 208 ++++++++++++++++++
 3 files changed, 614 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-generic.c
 create mode 100644 include/linux/clk/msm-clk-generic.h

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 689e05bf4f95..8684ff004be6 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -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-generic.o
 clk-qcom-y += reset.o
 
 obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
diff --git a/drivers/clk/qcom/clk-generic.c b/drivers/clk/qcom/clk-generic.c
new file mode 100644
index 000000000000..a0d778ba7394
--- /dev/null
+++ b/drivers/clk/qcom/clk-generic.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/msm-clk-generic.h>
+
+
+/* ==================== Mux clock ==================== */
+
+static int mux_set_parent(struct clk_hw *hw, u8 sel)
+{
+	struct mux_clk *mux = to_mux_clk(hw);
+
+	if (mux->parent_map)
+		sel = mux->parent_map[sel];
+
+	return mux->ops->set_mux_sel(mux, sel);
+}
+
+static u8 mux_get_parent(struct clk_hw *hw)
+{
+	struct mux_clk *mux = to_mux_clk(hw);
+	int num_parents = __clk_get_num_parents(hw->clk);
+	int i;
+	u8 sel;
+
+	sel = mux->ops->get_mux_sel(mux);
+	if (mux->parent_map) {
+		for (i = 0; i < num_parents; i++)
+			if (sel == mux->parent_map[i])
+				return i;
+		WARN(1, "Can't find parent\n");
+		return -EINVAL;
+	}
+
+	return sel;
+}
+
+static int mux_enable(struct clk_hw *hw)
+{
+	struct mux_clk *mux = to_mux_clk(hw);
+	if (mux->ops->enable)
+		return mux->ops->enable(mux);
+	return 0;
+}
+
+static void mux_disable(struct clk_hw *hw)
+{
+	struct mux_clk *mux = to_mux_clk(hw);
+	if (mux->ops->disable)
+		return mux->ops->disable(mux);
+}
+
+static struct clk *mux_get_safe_parent(struct clk_hw *hw)
+{
+	int i;
+	struct mux_clk *mux = to_mux_clk(hw);
+	int num_parents = __clk_get_num_parents(hw->clk);
+
+	if (!mux->has_safe_parent)
+		return NULL;
+
+	i = mux->safe_sel;
+	if (mux->parent_map)
+		for (i = 0; i < num_parents; i++)
+			if (mux->safe_sel == mux->parent_map[i])
+				break;
+
+	return clk_get_parent_by_index(hw->clk, i);
+}
+
+const struct clk_ops clk_ops_gen_mux = {
+	.enable = mux_enable,
+	.disable = mux_disable,
+	.set_parent = mux_set_parent,
+	.get_parent = mux_get_parent,
+	.determine_rate = __clk_mux_determine_rate,
+	.get_safe_parent = mux_get_safe_parent,
+};
+EXPORT_SYMBOL_GPL(clk_ops_gen_mux);
+
+/* ==================== Divider clock ==================== */
+
+static long __div_round_rate(struct div_data *data, unsigned long rate,
+	struct clk *parent, unsigned int *best_div, unsigned long *best_prate,
+	bool set_parent)
+{
+	unsigned int div, min_div, max_div, _best_div = 1;
+	unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate;
+	unsigned int numer;
+
+	rate = max(rate, 1UL);
+
+	min_div = max(data->min_div, 1U);
+	max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate));
+
+	/*
+	 * div values are doubled for half dividers.
+	 * Adjust for that by picking a numer of 2.
+	 */
+	numer = data->is_half_divider ? 2 : 1;
+
+	if (!set_parent) {
+		prate = *best_prate * numer;
+		div = DIV_ROUND_UP(prate, rate);
+		div = clamp(1U, div, max_div);
+		if (best_div)
+			*best_div = div;
+		return mult_frac(*best_prate, numer, div);
+	}
+
+	for (div = min_div; div <= max_div; div++) {
+		req_prate = mult_frac(rate, div, numer);
+		prate = __clk_round_rate(parent, req_prate);
+		if (IS_ERR_VALUE(prate))
+			break;
+
+		actual_rate = mult_frac(prate, numer, div);
+		if (is_better_rate(rate, rrate, actual_rate)) {
+			rrate = actual_rate;
+			_best_div = div;
+			_best_prate = prate;
+		}
+
+		/*
+		 * Trying higher dividers is only going to ask the parent for
+		 * a higher rate. If it can't even output a rate higher than
+		 * the one we request for this divider, the parent is not
+		 * going to be able to output an even higher rate required
+		 * for a higher divider. So, stop trying higher dividers.
+		 */
+		if (actual_rate < rate)
+			break;
+
+		if (rrate <= rate)
+			break;
+	}
+
+	if (!rrate)
+		return -EINVAL;
+	if (best_div)
+		*best_div = _best_div;
+	if (best_prate)
+		*best_prate = _best_prate;
+
+	return rrate;
+}
+
+static long div_round_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long *parent_rate)
+{
+	struct div_clk *d = to_div_clk(hw);
+	bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT;
+
+	return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk),
+				NULL, parent_rate, set_parent);
+}
+
+static int div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long
+			parent_rate)
+{
+	struct div_clk *d = to_div_clk(hw);
+	int div, rc = 0;
+	struct div_data *data = &d->data;
+
+	div = parent_rate / rate;
+	if (div != data->div)
+		rc = d->ops->set_div(d, div);
+	data->div = div;
+
+	return rc;
+}
+
+static int div_enable(struct clk_hw *hw)
+{
+	struct div_clk *d = to_div_clk(hw);
+	if (d->ops && d->ops->enable)
+		return d->ops->enable(d);
+	return 0;
+}
+
+static void div_disable(struct clk_hw *hw)
+{
+	struct div_clk *d = to_div_clk(hw);
+	if (d->ops && d->ops->disable)
+		return d->ops->disable(d);
+}
+
+static unsigned long div_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+	struct div_clk *d = to_div_clk(hw);
+	unsigned int div = d->data.div;
+
+	if (d->ops && d->ops->get_div)
+		div = max(d->ops->get_div(d), 1);
+	div = max(div, 1U);
+
+	if (!d->ops || !d->ops->set_div)
+		d->data.min_div = d->data.max_div = div;
+	d->data.div = div;
+
+	return prate / div;
+}
+
+const struct clk_ops clk_ops_div = {
+	.enable = div_enable,
+	.disable = div_disable,
+	.round_rate = div_round_rate,
+	.set_rate = div_set_rate,
+	.recalc_rate = div_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_ops_div);
+
+/* ==================== Mux_div clock ==================== */
+
+static int mux_div_clk_enable(struct clk_hw *hw)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+
+	if (md->ops->enable)
+		return md->ops->enable(md);
+	return 0;
+}
+
+static void mux_div_clk_disable(struct clk_hw *hw)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+
+	if (md->ops->disable)
+		return md->ops->disable(md);
+}
+
+static long __mux_div_round_rate(struct clk_hw *hw, unsigned long rate,
+	struct clk **best_parent, int *best_div, unsigned long *best_prate)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+	unsigned int i;
+	unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0;
+	struct clk *_best_parent = 0;
+	int num_parents = __clk_get_num_parents(hw->clk);
+	bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT;
+
+	for (i = 0; i < num_parents; i++) {
+		int div;
+		unsigned long prate;
+		struct clk *p = clk_get_parent_by_index(hw->clk, i);
+
+		rrate = __div_round_rate(&md->data, rate, p, &div, &prate,
+				set_parent);
+
+		if (is_better_rate(rate, best, rrate)) {
+			best = rrate;
+			_best_div = div;
+			_best_prate = prate;
+			_best_parent = p;
+		}
+
+		if (rate <= rrate)
+			break;
+	}
+
+	if (best_div)
+		*best_div = _best_div;
+	if (best_prate)
+		*best_prate = _best_prate;
+	if (best_parent)
+		*best_parent = _best_parent;
+
+	if (best)
+		return best;
+	return -EINVAL;
+}
+
+static long mux_div_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long *parent_rate)
+{
+	return __mux_div_round_rate(hw, rate, NULL, NULL, parent_rate);
+}
+
+/* requires enable lock to be held */
+static int __set_src_div(struct mux_div_clk *md, u8 src_sel, u32 div)
+{
+	int rc;
+
+	rc = md->ops->set_src_div(md, src_sel, div);
+	if (!rc) {
+		md->data.div = div;
+		md->src_sel = src_sel;
+	}
+
+	return rc;
+}
+
+/* Must be called after handoff to ensure parent clock rates are initialized */
+static int safe_parent_init_once(struct clk_hw *hw)
+{
+	unsigned long rrate;
+	u32 best_div;
+	struct clk *best_parent;
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+
+	if (IS_ERR(md->safe_parent))
+		return -EINVAL;
+	if (!md->safe_freq || md->safe_parent)
+		return 0;
+
+	rrate = __mux_div_round_rate(hw, md->safe_freq, &best_parent,
+			&best_div, NULL);
+
+	if (rrate == md->safe_freq) {
+		md->safe_div = best_div;
+		md->safe_parent = best_parent;
+	} else {
+		md->safe_parent = ERR_PTR(-EINVAL);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+__mux_div_clk_set_rate_and_parent(struct clk_hw *hw, u8 index, u32 div)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+	int rc;
+
+	rc = safe_parent_init_once(hw);
+	if (rc)
+		return rc;
+
+	return __set_src_div(md, index, div);
+}
+
+static int mux_div_clk_set_rate_and_parent(struct clk_hw *hw,
+		unsigned long rate, unsigned long parent_rate, u8 index)
+{
+	return __mux_div_clk_set_rate_and_parent(hw, index, parent_rate / rate);
+}
+
+static int mux_div_clk_set_rate(struct clk_hw *hw,
+		unsigned long rate, unsigned long parent_rate)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+	return __mux_div_clk_set_rate_and_parent(hw, md->src_sel,
+			parent_rate / rate);
+}
+
+static int mux_div_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+	return __mux_div_clk_set_rate_and_parent(hw, md->parent_map[index],
+			md->data.div);
+}
+
+static u8 mux_div_clk_get_parent(struct clk_hw *hw)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+	int num_parents = __clk_get_num_parents(hw->clk);
+	u32 i, div, sel;
+
+	md->ops->get_src_div(md, &sel, &div);
+	md->src_sel = sel;
+
+	for (i = 0; i < num_parents; i++)
+		if (sel == md->parent_map[i])
+			return i;
+	WARN(1, "Can't find parent\n");
+	return -EINVAL;
+}
+
+static unsigned long
+mux_div_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+	struct mux_div_clk *md = to_mux_div_clk(hw);
+	u32 div, sel;
+
+	md->ops->get_src_div(md, &sel, &div);
+
+	return prate / div;
+}
+
+const struct clk_ops clk_ops_mux_div_clk = {
+	.enable = mux_div_clk_enable,
+	.disable = mux_div_clk_disable,
+	.set_rate_and_parent = mux_div_clk_set_rate_and_parent,
+	.set_rate = mux_div_clk_set_rate,
+	.set_parent = mux_div_clk_set_parent,
+	.round_rate = mux_div_clk_round_rate,
+	.get_parent = mux_div_clk_get_parent,
+	.recalc_rate = mux_div_clk_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_ops_mux_div_clk);
diff --git a/include/linux/clk/msm-clk-generic.h b/include/linux/clk/msm-clk-generic.h
new file mode 100644
index 000000000000..cee38636fe0f
--- /dev/null
+++ b/include/linux/clk/msm-clk-generic.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCOM_CLK_GENERIC_H__
+#define __QCOM_CLK_GENERIC_H__
+
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+
+static inline bool is_better_rate(unsigned long req, unsigned long best,
+				  unsigned long new)
+{
+	if (IS_ERR_VALUE(new))
+		return false;
+
+	return (req <= new && new < best) || (best < req && best < new);
+}
+
+/* ==================== Mux clock ==================== */
+
+struct mux_clk;
+
+struct clk_mux_ops {
+	int (*set_mux_sel)(struct mux_clk *clk, int sel);
+	int (*get_mux_sel)(struct mux_clk *clk);
+
+	/* Optional */
+	bool (*is_enabled)(struct mux_clk *clk);
+	int (*enable)(struct mux_clk *clk);
+	void (*disable)(struct mux_clk *clk);
+};
+
+struct mux_clk {
+	/* Parents in decreasing order of preference for obtaining rates. */
+	u8 		*parent_map;
+	bool		has_safe_parent;
+	u8		safe_sel;
+	const struct clk_mux_ops *ops;
+
+	/* Fields not used by helper function. */
+	void __iomem 	*base;
+	u32		offset;
+	u32		en_offset;
+	int		en_reg;
+	u32		mask;
+	u32		shift;
+	u32		en_mask;
+	void		*priv;
+
+	struct clk_hw	hw;
+};
+
+static inline struct mux_clk *to_mux_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct mux_clk, hw);
+}
+
+extern const struct clk_ops clk_ops_gen_mux;
+
+/* ==================== Divider clock ==================== */
+
+struct div_clk;
+
+struct clk_div_ops {
+	int (*set_div)(struct div_clk *clk, int div);
+	int (*get_div)(struct div_clk *clk);
+	bool (*is_enabled)(struct div_clk *clk);
+	int (*enable)(struct div_clk *clk);
+	void (*disable)(struct div_clk *clk);
+};
+
+struct div_data {
+	unsigned int div;
+	unsigned int min_div;
+	unsigned int max_div;
+	/*
+	 * Indicate whether this divider clock supports half-interger divider.
+	 * If it is, all the min_div and max_div have been doubled. It means
+	 * they are 2*N.
+	 */
+	bool is_half_divider;
+};
+
+struct div_clk {
+	struct div_data data;
+
+	/* Optional */
+	const struct clk_div_ops *ops;
+
+	/* Fields not used by helper function. */
+	void __iomem 	*base;
+	u32		offset;
+	u32		mask;
+	u32		shift;
+	u32		en_mask;
+	void		*priv;
+	struct clk_hw	hw;
+};
+
+static inline struct div_clk *to_div_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct div_clk, hw);
+}
+
+extern const struct clk_ops clk_ops_div;
+
+#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \
+static struct div_clk clk_name = {	\
+	.data = {				\
+		.max_div = _div,		\
+		.min_div = _div,		\
+		.div = _div,			\
+	},					\
+	.hw.init = &(struct clk_init_data){ \
+		.parent_names = (const char *[]){ _parent }, \
+		.num_parents = 1,		\
+		.name = #clk_name,		\
+		.ops = &clk_ops_div,		\
+		.flags = CLK_SET_RATE_PARENT,	\
+	}					\
+}
+
+/* ==================== Mux Div clock ==================== */
+
+struct mux_div_clk;
+
+/*
+ * struct mux_div_ops
+ * the enable and disable ops are optional.
+ */
+
+struct mux_div_ops {
+	int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div);
+	void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div);
+	int (*enable)(struct mux_div_clk *);
+	void (*disable)(struct mux_div_clk *);
+	bool (*is_enabled)(struct mux_div_clk *);
+};
+
+/*
+ * struct mux_div_clk - combined mux/divider clock
+ * @priv
+		parameters needed by ops
+ * @safe_freq
+		when switching rates from A to B, the mux div clock will
+		instead switch from A -> safe_freq -> B. This allows the
+		mux_div clock to change rates while enabled, even if this
+		behavior is not supported by the parent clocks.
+
+		If changing the rate of parent A also causes the rate of
+		parent B to change, then safe_freq must be defined.
+
+		safe_freq is expected to have a source clock which is always
+		on and runs at only one rate.
+ * @parents
+		list of parents and mux indicies
+ * @ops
+		function pointers for hw specific operations
+ * @src_sel
+		the mux index which will be used if the clock is enabled.
+ */
+
+struct mux_div_clk {
+	/* Required parameters */
+	const struct mux_div_ops	*ops;
+	struct div_data			data;
+	u8				*parent_map;
+
+	struct clk_hw			hw;
+
+	/* Internal */
+	u32				src_sel;
+
+	/* Optional parameters */
+	void				*priv;
+	void __iomem			*base;
+	u32				div_mask;
+	u32				div_offset;
+	u32				div_shift;
+	u32				src_mask;
+	u32				src_offset;
+	u32				src_shift;
+	u32				en_mask;
+	u32				en_offset;
+
+	u32				safe_div;
+	struct clk			*safe_parent;
+	unsigned long			safe_freq;
+};
+
+static inline struct mux_div_clk *to_mux_div_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct mux_div_clk, hw);
+}
+
+extern const struct clk_ops clk_ops_mux_div_clk;
+
+#endif
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 04/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (2 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 05/12] clk: qcom: Add HFPLL driver Stephen Boyd
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/qcom/Makefile    |   1 +
 drivers/clk/qcom/clk-hfpll.c | 260 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-hfpll.h |  54 +++++++++
 3 files changed, 315 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-hfpll.c
 create mode 100644 drivers/clk/qcom/clk-hfpll.h

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 8684ff004be6..1a0b561bd105 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o
 clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-generic.o
+clk-qcom-y += clk-hfpll.o
 clk-qcom-y += reset.o
 
 obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c
new file mode 100644
index 000000000000..f8a40a7cdf6f
--- /dev/null
+++ b/drivers/clk/qcom/clk-hfpll.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+#include "clk-regmap.h"
+#include "clk-hfpll.h"
+
+#define PLL_OUTCTRL	BIT(0)
+#define PLL_BYPASSNL	BIT(1)
+#define PLL_RESET_N	BIT(2)
+
+/* Initialize a HFPLL at a given rate and enable it. */
+static void __clk_hfpll_init_once(struct clk_hw *hw)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+
+	if (likely(h->init_done))
+		return;
+
+	/* Configure PLL parameters for integer mode. */
+	if (hd->config_val)
+		regmap_write(regmap, hd->config_reg, hd->config_val);
+	regmap_write(regmap, hd->m_reg, 0);
+	regmap_write(regmap, hd->n_reg, 1);
+
+	if (hd->user_reg) {
+		u32 regval = hd->user_val;
+		unsigned long rate;
+
+		rate = __clk_get_rate(hw->clk);
+
+		/* Pick the right VCO. */
+		if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
+			regval |= hd->user_vco_mask;
+		regmap_write(regmap, hd->user_reg, regval);
+	}
+
+	if (hd->droop_reg)
+		regmap_write(regmap, hd->droop_reg, hd->droop_val);
+
+	h->init_done = true;
+}
+
+static void __clk_hfpll_enable(struct clk_hw *hw)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+	u32 val;
+
+	__clk_hfpll_init_once(hw);
+
+	/* Disable PLL bypass mode. */
+	regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
+
+	/*
+	 * H/W requires a 5us delay between disabling the bypass and
+	 * de-asserting the reset. Delay 10us just to be safe.
+	 */
+	mb();
+	udelay(10);
+
+	/* De-assert active-low PLL reset. */
+	regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
+
+	/* Wait for PLL to lock. */
+	if (hd->status_reg) {
+		do {
+			regmap_read(regmap, hd->status_reg, &val);
+		} while (!(val & BIT(hd->lock_bit)));
+	} else {
+		mb();
+		udelay(60);
+	}
+
+	/* Enable PLL output. */
+	regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
+
+	/* Make sure the enable is done before returning. */
+	mb();
+}
+
+/* Enable an already-configured HFPLL. */
+static int clk_hfpll_enable(struct clk_hw *hw)
+{
+	unsigned long flags;
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+	u32 mode;
+
+	spin_lock_irqsave(&h->lock, flags);
+	regmap_read(regmap, hd->mode_reg, &mode);
+	if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
+		__clk_hfpll_enable(hw);
+	spin_unlock_irqrestore(&h->lock, flags);
+
+	return 0;
+}
+
+static void __clk_hfpll_disable(struct clk_hw *hw)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+
+	/*
+	 * Disable the PLL output, disable test mode, enable the bypass mode,
+	 * and assert the reset.
+	 */
+	regmap_update_bits(regmap, hd->mode_reg,
+			PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
+}
+
+static void clk_hfpll_disable(struct clk_hw *hw)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(&h->lock, flags);
+	__clk_hfpll_disable(hw);
+	spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
+				 unsigned long *parent_rate)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	unsigned long rrate;
+
+	rate = clamp(rate, hd->min_rate, hd->max_rate);
+
+	rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
+	if (rrate > hd->max_rate)
+		rrate -= *parent_rate;
+
+	return rrate;
+}
+
+/*
+ * For optimization reasons, assumes no downstream clocks are actively using
+ * it.
+ */
+static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long parent_rate)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+	unsigned long flags;
+	u32 l_val, val;
+	bool enabled;
+
+	l_val = rate / parent_rate;
+
+	spin_lock_irqsave(&h->lock, flags);
+
+	enabled = __clk_is_enabled(hw->clk);
+	if (enabled)
+		__clk_hfpll_disable(hw);
+
+	/* Pick the right VCO. */
+	if (hd->user_reg && hd->user_vco_mask) {
+		regmap_read(regmap, hd->user_reg, &val);
+		if (rate <= hd->low_vco_max_rate)
+			val &= ~hd->user_vco_mask;
+		else
+			val |= hd->user_vco_mask;
+		regmap_write(regmap, hd->user_reg, val);
+	}
+
+	regmap_write(regmap, hd->l_reg, l_val);
+
+	if (enabled)
+		__clk_hfpll_enable(hw);
+
+	spin_unlock_irqrestore(&h->lock, flags);
+
+	return 0;
+}
+
+static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
+					   unsigned long parent_rate)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+	u32 l_val;
+
+	regmap_read(regmap, hd->l_reg, &l_val);
+
+	return l_val * parent_rate;
+}
+
+static void clk_hfpll_init(struct clk_hw *hw)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+	u32 mode, status;
+
+	regmap_read(regmap, hd->mode_reg, &mode);
+	if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
+		__clk_hfpll_init_once(hw);
+		return;
+	}
+
+	if (hd->status_reg) {
+		regmap_read(regmap, hd->status_reg, &status);
+		if (!(status & BIT(hd->lock_bit))) {
+			WARN(1, "HFPLL %s is ON, but not locked!\n",
+					__clk_get_name(hw->clk));
+			clk_hfpll_disable(hw);
+			__clk_hfpll_init_once(hw);
+		}
+	}
+}
+
+static int hfpll_is_enabled(struct clk_hw *hw)
+{
+	struct clk_hfpll *h = to_clk_hfpll(hw);
+	struct hfpll_data const *hd = h->d;
+	struct regmap *regmap = h->clkr.regmap;
+	u32 mode;
+
+	regmap_read(regmap, hd->mode_reg, &mode);
+	mode &= 0x7;
+	return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
+}
+
+const struct clk_ops clk_ops_hfpll = {
+	.enable = clk_hfpll_enable,
+	.disable = clk_hfpll_disable,
+	.is_enabled = hfpll_is_enabled,
+	.round_rate = clk_hfpll_round_rate,
+	.set_rate = clk_hfpll_set_rate,
+	.recalc_rate = clk_hfpll_recalc_rate,
+	.init = clk_hfpll_init,
+};
+EXPORT_SYMBOL_GPL(clk_ops_hfpll);
diff --git a/drivers/clk/qcom/clk-hfpll.h b/drivers/clk/qcom/clk-hfpll.h
new file mode 100644
index 000000000000..48c18d664f4e
--- /dev/null
+++ b/drivers/clk/qcom/clk-hfpll.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __QCOM_CLK_HFPLL_H__
+#define __QCOM_CLK_HFPLL_H__
+
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include "clk-regmap.h"
+
+struct hfpll_data {
+	u32 mode_reg;
+	u32 l_reg;
+	u32 m_reg;
+	u32 n_reg;
+	u32 user_reg;
+	u32 droop_reg;
+	u32 config_reg;
+	u32 status_reg;
+	u8  lock_bit;
+
+	u32 droop_val;
+	u32 config_val;
+	u32 user_val;
+	u32 user_vco_mask;
+	unsigned long low_vco_max_rate;
+
+	unsigned long min_rate;
+	unsigned long max_rate;
+};
+
+struct clk_hfpll {
+	struct hfpll_data const *d;
+	int init_done;
+
+	struct clk_regmap clkr;
+	spinlock_t lock;
+};
+
+#define to_clk_hfpll(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
+
+extern const struct clk_ops clk_ops_hfpll;
+
+#endif
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 05/12] clk: qcom: Add HFPLL driver
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (3 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 04/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs Stephen Boyd
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 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

diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 7f696b7d4422..425bcfe3c39a 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -45,3 +45,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.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 1a0b561bd105..b733adbbad5a 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -15,3 +15,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
diff --git a/drivers/clk/qcom/hfpll.c b/drivers/clk/qcom/hfpll.c
new file mode 100644
index 000000000000..701a377d35c1
--- /dev/null
+++ b/drivers/clk/qcom/hfpll.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+#include "clk-regmap.h"
+#include "clk-hfpll.h"
+
+static const struct hfpll_data hdata = {
+	.mode_reg = 0x00,
+	.l_reg = 0x04,
+	.m_reg = 0x08,
+	.n_reg = 0x0c,
+	.user_reg = 0x10,
+	.config_reg = 0x14,
+	.config_val = 0x430405d,
+	.status_reg = 0x1c,
+	.lock_bit = 16,
+
+	.user_val = 0x8,
+	.user_vco_mask = 0x100000,
+	.low_vco_max_rate = 1248000000,
+	.min_rate = 537600000UL,
+	.max_rate = 2900000000UL,
+};
+
+static const struct of_device_id qcom_hfpll_match_table[] = {
+	{ .compatible = "qcom,hfpll" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
+
+static struct regmap_config hfpll_regmap_config = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register	= 0x30,
+	.fast_io	= true,
+};
+
+static int qcom_hfpll_probe(struct platform_device *pdev)
+{
+	struct clk *clk;
+	struct resource *res;
+	struct device *dev = &pdev->dev;
+	void __iomem *base;
+	struct regmap *regmap;
+	struct clk_hfpll *h;
+	struct clk_init_data init = {
+		.parent_names = (const char *[]){ "xo" },
+		.num_parents = 1,
+		.ops = &clk_ops_hfpll,
+	};
+
+	h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	if (of_property_read_string_index(dev->of_node, "clock-output-names",
+						  0, &init.name))
+		return -ENODEV;
+
+	h->d = &hdata;
+	h->clkr.hw.init = &init;
+	spin_lock_init(&h->lock);
+
+	clk = devm_clk_register_regmap(&pdev->dev, &h->clkr);
+
+	return PTR_ERR_OR_ZERO(clk);
+}
+
+static struct platform_driver qcom_hfpll_driver = {
+	.probe		= qcom_hfpll_probe,
+	.driver		= {
+		.name	= "qcom-hfpll",
+		.owner	= THIS_MODULE,
+		.of_match_table = qcom_hfpll_match_table,
+	},
+};
+module_platform_driver(qcom_hfpll_driver);
+
+MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-hfpll");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (4 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 05/12] clk: qcom: Add HFPLL driver Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-07-14 10:19   ` pramod gurav
  2014-06-25  0:06 ` [RFC/PATCH 07/12] clk: qcom: Add support for Krait clocks Stephen Boyd
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

Describe the HFPLLs present on MSM8960 devices.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
index f4ffd91901f8..d04fc99541e4 100644
--- a/drivers/clk/qcom/gcc-msm8960.c
+++ b/drivers/clk/qcom/gcc-msm8960.c
@@ -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,84 @@ 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 = 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 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,
@@ -2763,6 +2842,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[] = {
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 07/12] clk: qcom: Add support for Krait clocks
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (5 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 08/12] clk: qcom: Add KPSS ACC/GCC driver Stephen Boyd
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/qcom/Makefile    |   1 +
 drivers/clk/qcom/clk-krait.c | 121 +++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clk-krait.h |  22 ++++++++
 3 files changed, 144 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-krait.c
 create mode 100644 drivers/clk/qcom/clk-krait.h

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index b733adbbad5a..b982d271d7db 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o
 clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-generic.o
+clk-qcom-y += clk-krait.o
 clk-qcom-y += clk-hfpll.o
 clk-qcom-y += reset.o
 
diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
new file mode 100644
index 000000000000..42834269a867
--- /dev/null
+++ b/drivers/clk/qcom/clk-krait.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+#include <asm/krait-l2-accessors.h>
+
+#include "clk-krait.h"
+
+/* Secondary and primary muxes share the same cp15 register */
+static DEFINE_SPINLOCK(kpss_clock_reg_lock);
+
+#define LPL_SHIFT	8
+static void __kpss_mux_set_sel(struct mux_clk *mux, int sel)
+{
+	unsigned long flags;
+	u32 regval;
+
+	spin_lock_irqsave(&kpss_clock_reg_lock, flags);
+	regval = krait_get_l2_indirect_reg(mux->offset);
+	regval &= ~(mux->mask << mux->shift);
+	regval |= (sel & mux->mask) << mux->shift;
+	if (mux->priv) {
+		regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
+		regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
+	}
+	krait_set_l2_indirect_reg(mux->offset, regval);
+	spin_unlock_irqrestore(&kpss_clock_reg_lock, flags);
+
+	/* Wait for switch to complete. */
+	mb();
+	udelay(1);
+}
+
+static int kpss_mux_set_sel(struct mux_clk *mux, int sel)
+{
+	mux->en_mask = sel;
+	/* Don't touch mux if CPU is off as it won't work */
+	if (__clk_is_enabled(mux->hw.clk))
+		__kpss_mux_set_sel(mux, sel);
+	return 0;
+}
+
+static int kpss_mux_get_sel(struct mux_clk *mux)
+{
+	u32 sel;
+
+	sel = krait_get_l2_indirect_reg(mux->offset);
+	sel >>= mux->shift;
+	sel &= mux->mask;
+	mux->en_mask = sel;
+
+	return sel;
+}
+
+static int kpss_mux_enable(struct mux_clk *mux)
+{
+	__kpss_mux_set_sel(mux, mux->en_mask);
+	return 0;
+}
+
+static void kpss_mux_disable(struct mux_clk *mux)
+{
+	__kpss_mux_set_sel(mux, mux->safe_sel);
+}
+
+const struct clk_mux_ops clk_mux_ops_kpss = {
+	.enable = kpss_mux_enable,
+	.disable = kpss_mux_disable,
+	.set_mux_sel = kpss_mux_set_sel,
+	.get_mux_sel = kpss_mux_get_sel,
+};
+EXPORT_SYMBOL_GPL(clk_mux_ops_kpss);
+
+/*
+ * The divider can divide by 2, 4, 6 and 8. But we only really need div-2. So
+ * force it to div-2 during handoff and treat it like a fixed div-2 clock.
+ */
+static int kpss_div2_get_div(struct div_clk *div)
+{
+	unsigned long flags;
+	u32 regval;
+	int val;
+
+	spin_lock_irqsave(&kpss_clock_reg_lock, flags);
+	regval = krait_get_l2_indirect_reg(div->offset);
+	val = (regval >> div->shift) & div->mask;
+	regval &= ~(div->mask << div->shift);
+	if (div->priv)
+		regval &= ~(div->mask << (div->shift + LPL_SHIFT));
+	krait_set_l2_indirect_reg(div->offset, regval);
+	spin_unlock_irqrestore(&kpss_clock_reg_lock, flags);
+
+	val = (val + 1) * 2;
+	WARN(val != 2, "Divider %s was configured to div-%d instead of 2!\n",
+		__clk_get_name(div->hw.clk), val);
+
+	return 2;
+}
+
+const struct clk_div_ops clk_div_ops_kpss_div2 = {
+	.get_div = kpss_div2_get_div,
+};
+EXPORT_SYMBOL_GPL(clk_div_ops_kpss_div2);
diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
new file mode 100644
index 000000000000..9c3eb38be542
--- /dev/null
+++ b/drivers/clk/qcom/clk-krait.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SOC_QCOM_CLOCK_KRAIT_H
+#define __SOC_QCOM_CLOCK_KRAIT_H
+
+#include <linux/clk/msm-clk-generic.h>
+
+extern const struct clk_mux_ops clk_mux_ops_kpss;
+extern const struct clk_div_ops clk_div_ops_kpss_div2;
+
+#endif
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 08/12] clk: qcom: Add KPSS ACC/GCC driver
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (6 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 07/12] clk: qcom: Add support for Krait clocks Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver Stephen Boyd
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/qcom/Kconfig    |   8 +++
 drivers/clk/qcom/Makefile   |   1 +
 drivers/clk/qcom/kpss-xcc.c | 115 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 drivers/clk/qcom/kpss-xcc.c

diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 425bcfe3c39a..247f042dc110 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -53,3 +53,11 @@ 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 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.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index b982d271d7db..bb4666733bd9 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -16,4 +16,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
diff --git a/drivers/clk/qcom/kpss-xcc.c b/drivers/clk/qcom/kpss-xcc.c
new file mode 100644
index 000000000000..1061668d48c0
--- /dev/null
+++ b/drivers/clk/qcom/kpss-xcc.c
@@ -0,0 +1,115 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/msm-clk-generic.h>
+
+static int kpss_xcc_set_mux_sel(struct mux_clk *clk, int sel)
+{
+	writel_relaxed(sel, clk->base + clk->offset);
+	return 0;
+}
+
+static int kpss_xcc_get_mux_sel(struct mux_clk *clk)
+{
+	return readl_relaxed(clk->base + clk->offset);
+}
+
+static const struct clk_mux_ops kpss_xcc_ops = {
+	.set_mux_sel = kpss_xcc_set_mux_sel,
+	.get_mux_sel = kpss_xcc_get_mux_sel,
+};
+
+static const char *aux_parents[] = {
+	"pll8_vote",
+	"pxo",
+};
+
+static u8 aux_parent_map[] = {
+	3,
+	0,
+};
+
+static const struct of_device_id kpss_xcc_match_table[] = {
+	{ .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
+	{ .compatible = "qcom,kpss-gcc" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
+
+static int kpss_xcc_driver_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *id;
+	struct clk *clk;
+	struct resource *res;
+	void __iomem *base;
+	struct mux_clk *mux_clk;
+	struct clk_init_data init = {
+		.parent_names = aux_parents,
+		.num_parents = 2,
+		.ops = &clk_ops_gen_mux,
+	};
+
+	id = of_match_device(kpss_xcc_match_table, &pdev->dev);
+	if (!id)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	mux_clk = devm_kzalloc(&pdev->dev, sizeof(*mux_clk), GFP_KERNEL);
+	if (!mux_clk)
+		return -ENOMEM;
+
+	mux_clk->mask = 0x3;
+	mux_clk->parent_map = aux_parent_map;
+	mux_clk->ops = &kpss_xcc_ops;
+	mux_clk->base = base;
+	mux_clk->hw.init = &init;
+
+	if (id->data) {
+		if (of_property_read_string_index(pdev->dev.of_node,
+					"clock-output-names", 0, &init.name))
+			return -ENODEV;
+		mux_clk->offset = 0x14;
+	} else {
+		init.name = "acpu_l2_aux";
+		mux_clk->offset = 0x28;
+	}
+
+	clk = devm_clk_register(&pdev->dev, &mux_clk->hw);
+
+	return PTR_ERR_OR_ZERO(clk);
+}
+
+static struct platform_driver kpss_xcc_driver = {
+	.probe = kpss_xcc_driver_probe,
+	.driver = {
+		.name = "kpss-xcc",
+		.of_match_table = kpss_xcc_match_table,
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(kpss_xcc_driver);
+
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (7 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 08/12] clk: qcom: Add KPSS ACC/GCC driver Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-07-14 10:25   ` pramod gurav
  2014-06-25  0:06 ` [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 Stephen Boyd
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/clk/qcom/Kconfig    |   9 ++
 drivers/clk/qcom/Makefile   |   1 +
 drivers/clk/qcom/krait-cc.c | 366 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 376 insertions(+)
 create mode 100644 drivers/clk/qcom/krait-cc.c

diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 247f042dc110..6f835e406930 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -61,3 +61,12 @@ config KPSS_XCC
 	  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 KRAITCC
+	tristate "Krait Clock Controller"
+	depends on COMMON_CLK_QCOM
+	depends on ARM
+	select KRAIT_L2_ACCESSORS
+	help
+	  Support for the Krait CPU clocks on Qualcomm devices.
+	  Say Y if you want to support CPU frequency scaling.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index bb4666733bd9..e8c7f31916da 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -18,3 +18,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
diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
new file mode 100644
index 000000000000..4ca9589acfca
--- /dev/null
+++ b/drivers/clk/qcom/krait-cc.c
@@ -0,0 +1,366 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+
+#include <asm/smp_plat.h>
+
+#include "clk-krait.h"
+
+DEFINE_FIXED_DIV_CLK(acpu_aux, 2, "gpll0_vote");
+
+static u8 sec_mux_map[] = {
+	2,
+	0,
+};
+
+static u8 pri_mux_map[] = {
+	1,
+	2,
+	0,
+};
+
+static int
+krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
+{
+	struct div_clk *div;
+	struct clk_init_data init = {
+		.num_parents = 1,
+		.ops = &clk_ops_div,
+		.flags = CLK_SET_RATE_PARENT,
+	};
+	const char *p_names[1];
+	struct clk *clk;
+
+	div = devm_kzalloc(dev, sizeof(*dev), GFP_KERNEL);
+	if (!div)
+		return -ENOMEM;
+
+	div->data.div = 2;
+	div->data.min_div = 2;
+	div->data.max_div = 2;
+	div->ops = &clk_div_ops_kpss_div2;
+	div->mask = 0x3;
+	div->shift = 6;
+	div->priv = (void *)(id >= 0);
+	div->offset = offset;
+	div->hw.init = &init;
+
+	init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
+	if (!init.name)
+		return -ENOMEM;
+
+	init.parent_names = p_names;
+	p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
+	if (!p_names[0]) {
+		kfree(init.name);
+		return -ENOMEM;
+	}
+
+	clk = devm_clk_register(dev, &div->hw);
+	kfree(p_names[0]);
+	kfree(init.name);
+
+	return PTR_ERR_OR_ZERO(clk);
+}
+
+static int
+krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
+		  bool unique_aux)
+{
+	struct mux_clk *mux;
+	static const char *sec_mux_list[] = {
+		"acpu_aux",
+		"qsb",
+	};
+	struct clk_init_data init = {
+		.parent_names = sec_mux_list,
+		.num_parents = ARRAY_SIZE(sec_mux_list),
+		.ops = &clk_ops_gen_mux,
+		.flags = CLK_SET_RATE_PARENT,
+	};
+	struct clk *clk;
+
+	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	mux->offset = offset;
+	mux->priv = (void *)(id >= 0);
+	mux->has_safe_parent = true;
+	mux->safe_sel = 2;
+	mux->ops = &clk_mux_ops_kpss;
+	mux->mask = 0x3;
+	mux->shift = 2;
+	mux->parent_map = sec_mux_map;
+	mux->hw.init = &init;
+
+	init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
+	if (!init.name)
+		return -ENOMEM;
+
+	if (unique_aux) {
+		sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
+		if (!sec_mux_list[0]) {
+			clk = ERR_PTR(-ENOMEM);
+			goto err_aux;
+		}
+		pr_info("Parents are %s %s\n", sec_mux_list[0], sec_mux_list[1]);
+	}
+
+	clk = devm_clk_register(dev, &mux->hw);
+
+	if (unique_aux)
+		kfree(sec_mux_list[0]);
+err_aux:
+	kfree(init.name);
+	return PTR_ERR_OR_ZERO(clk);
+}
+
+static struct clk *
+krait_add_pri_mux(struct device *dev, int id, const char * s, unsigned offset)
+{
+	struct mux_clk *mux;
+	const char *p_names[3];
+	struct clk_init_data init = {
+		.parent_names = p_names,
+		.num_parents = ARRAY_SIZE(p_names),
+		.ops = &clk_ops_gen_mux,
+		.flags = CLK_SET_RATE_PARENT,
+	};
+	struct clk *clk;
+
+	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return ERR_PTR(-ENOMEM);
+
+	mux->has_safe_parent = true;
+	mux->safe_sel = 0;
+	mux->ops = &clk_mux_ops_kpss;
+	mux->mask = 0x3;
+	mux->shift = 0;
+	mux->offset = offset;
+	mux->priv = (void *)(id >= 0);
+	mux->parent_map = pri_mux_map;
+	mux->hw.init = &init;
+
+	init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
+	if (!init.name)
+		return ERR_PTR(-ENOMEM);
+
+	p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
+	if (!p_names[0]) {
+		clk = ERR_PTR(-ENOMEM);
+		goto err_p0;
+	}
+
+	p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
+	if (!p_names[1]) {
+		clk = ERR_PTR(-ENOMEM);
+		goto err_p1;
+	}
+
+	p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
+	if (!p_names[2]) {
+		clk = ERR_PTR(-ENOMEM);
+		goto err_p2;
+	}
+
+	clk = devm_clk_register(dev, &mux->hw);
+
+	kfree(p_names[2]);
+err_p2:
+	kfree(p_names[1]);
+err_p1:
+	kfree(p_names[0]);
+err_p0:
+	kfree(init.name);
+	return clk;
+}
+
+/* id < 0 for L2, otherwise id == physical CPU number */
+static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
+{
+	int ret;
+	unsigned offset;
+	const char *s;
+	struct clk *clk;
+
+	if (id >= 0) {
+		offset = 0x4501 + (0x1000 * id);
+		s = kasprintf(GFP_KERNEL, "%d", id);
+		if (!s)
+			return ERR_PTR(-ENOMEM);
+	} else {
+		offset = 0x500;
+		s = "_l2";
+	}
+
+	ret = krait_add_div(dev, id, s, offset);
+	if (ret) {
+		clk = ERR_PTR(ret);
+		goto err;
+	}
+
+	ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
+	if (ret) {
+		clk = ERR_PTR(ret);
+		goto err;
+	}
+
+	clk = krait_add_pri_mux(dev, id, s, offset);
+err:
+	if (id >= 0)
+		kfree(s);
+
+	return clk;
+}
+
+static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
+{
+	unsigned int idx = clkspec->args[0];
+	struct clk **clks = data;
+
+	if (idx >= 5) {
+		pr_err("%s: invalid clock index %d\n", __func__, idx);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return clks[idx] ? : ERR_PTR(-ENODEV);
+}
+
+static const struct of_device_id krait_cc_match_table[] = {
+	{ .compatible = "qcom,krait-cc-v1", (void *)1UL },
+	{ .compatible = "qcom,krait-cc-v2" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, krait_cc_match_table);
+
+static int krait_cc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *id;
+	unsigned long cur_rate, aux_rate;
+	int i, cpu;
+	struct clk *clk;
+	struct clk **clks;
+	struct clk *l2_pri_mux_clk;
+
+	id = of_match_device(krait_cc_match_table, &pdev->dev);
+	if (!id)
+		return -ENODEV;
+
+	/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
+	clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	if (!id->data) {
+		clk = devm_clk_register(dev, &acpu_aux.hw);
+		if (IS_ERR(clk))
+			return PTR_ERR(clk);
+	}
+
+	/* Krait configurations have at most 4 CPUs and one L2 */
+	clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
+	if (!clks)
+		return -ENOMEM;
+
+	for_each_possible_cpu(i) {
+		cpu = cpu_logical_map(i);
+		clk = krait_add_clks(dev, cpu, id->data);
+		if (IS_ERR(clk))
+			return PTR_ERR(clk);
+		clks[cpu] = clk;
+	}
+
+	l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
+	if (IS_ERR(l2_pri_mux_clk))
+		return PTR_ERR(l2_pri_mux_clk);
+	clks[4] = l2_pri_mux_clk;
+
+	/*
+	 * We don't want the CPU or L2 clocks to be turned off at late init
+	 * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
+	 * refcount of these clocks. Any cpufreq/hotplug manager can assume
+	 * that the clocks have already been prepared and enabled by the time
+	 * they take over.
+	 */
+	for_each_online_cpu(i) {
+		cpu = cpu_logical_map(i);
+		clk_prepare_enable(l2_pri_mux_clk);
+		WARN(clk_prepare_enable(clks[cpu]),
+			"Unable to turn on CPU%d clock", cpu);
+	}
+
+	/*
+	 * Force reinit of HFPLLs and muxes to overwrite any potential
+	 * incorrect configuration of HFPLLs and muxes by the bootloader.
+	 * While at it, also make sure the cores are running at known rates
+	 * and print the current rate.
+	 *
+	 * The clocks are set to aux clock rate first to make sure the
+	 * secondary mux is not sourcing off of QSB. The rate is then set to
+	 * two different rates to force a HFPLL reinit under all
+	 * circumstances.
+	 */
+	cur_rate = clk_get_rate(l2_pri_mux_clk);
+	aux_rate = 384000000;
+	if (cur_rate == 1) {
+		pr_info("L2 @ QSB rate. Forcing new rate.\n");
+		cur_rate = aux_rate;
+	}
+	clk_set_rate(l2_pri_mux_clk, aux_rate);
+	clk_set_rate(l2_pri_mux_clk, 2);
+	clk_set_rate(l2_pri_mux_clk, cur_rate);
+	pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
+	for_each_possible_cpu(i) {
+		cpu = cpu_logical_map(i);
+		clk = clks[cpu];
+		cur_rate = clk_get_rate(clk);
+		if (cur_rate == 1) {
+			pr_info("CPU%d @ QSB rate. Forcing new rate.\n", i);
+			cur_rate = aux_rate;
+		}
+		clk_set_rate(clk, aux_rate);
+		clk_set_rate(clk, 2);
+		clk_set_rate(clk, cur_rate);
+		pr_info("CPU%d @ %lu KHz\n", i, clk_get_rate(clk) / 1000);
+	}
+
+	of_clk_add_provider(dev->of_node, krait_of_get, clks);
+
+	return 0;
+}
+
+static struct platform_driver krait_cc_driver = {
+	.probe = krait_cc_probe,
+	.driver = {
+		.name = "clock-krait",
+		.of_match_table = krait_cc_match_table,
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(krait_cc_driver);
+
+MODULE_DESCRIPTION("Krait CPU Clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (8 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:52   ` Stephen Boyd
  2014-06-25  8:47   ` Viresh Kumar
  2014-06-25  0:06 ` [RFC/PATCH 11/12] cpufreq: Add module to register cpufreq-krait device Stephen Boyd
                   ` (2 subsequent siblings)
  12 siblings, 2 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

Krait processors have individual clocks for each CPU that can
scale independently from one another. cpufreq-cpu0 is fairly
close to this, but assumes that there is only one clock for all
CPUs. Add a driver to support the Krait configuration.

TODO: Merge into cpufreq-cpu0? Or make generic?

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/cpufreq/Kconfig         |  13 +++
 drivers/cpufreq/Makefile        |   1 +
 drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+)
 create mode 100644 drivers/cpufreq/cpufreq-krait.c

diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index ffe350f86bca..e9826c85a777 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -196,6 +196,19 @@ config GENERIC_CPUFREQ_CPU0
 
 	  If in doubt, say N.
 
+config GENERIC_CPUFREQ_KRAIT
+	tristate "Krait cpufreq driver"
+	depends on HAVE_CLK && OF
+	# if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
+	depends on !CPU_THERMAL || THERMAL
+	select PM_OPP
+	help
+	  This adds a generic cpufreq driver for CPU0 frequency management.
+	  It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
+	  systems which share clock and voltage across all CPUs.
+
+	  If in doubt, say N.
+
 menu "x86 CPU frequency scaling drivers"
 depends on X86
 source "drivers/cpufreq/Kconfig.x86"
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 738c8b7b17dc..f5c9f68dcc0f 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE)	+= cpufreq_conservative.o
 obj-$(CONFIG_CPU_FREQ_GOV_COMMON)		+= cpufreq_governor.o
 
 obj-$(CONFIG_GENERIC_CPUFREQ_CPU0)	+= cpufreq-cpu0.o
+obj-$(CONFIG_CPUFREQ_KRAIT)		+= cpufreq-krait.o
 
 ##################################################################################
 # x86 drivers.
diff --git a/drivers/cpufreq/cpufreq-krait.c b/drivers/cpufreq/cpufreq-krait.c
new file mode 100644
index 000000000000..7b38b9c948e5
--- /dev/null
+++ b/drivers/cpufreq/cpufreq-krait.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * The OPP code in function krait_set_target() is reused from
+ * drivers/cpufreq/omap-cpufreq.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+static unsigned int transition_latency;
+
+static struct device *cpu_dev;
+static DEFINE_PER_CPU(struct clk *, krait_cpu_clks);
+static struct cpufreq_frequency_table *freq_table;
+static struct thermal_cooling_device *cdev;
+
+static int krait_set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+	unsigned long volt = 0, volt_old = 0;
+	unsigned int old_freq, new_freq;
+	long freq_Hz, freq_exact;
+	int ret;
+	struct clk *cpu_clk;
+
+	cpu_clk = per_cpu(krait_cpu_clks, policy->cpu);
+
+	freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
+	if (freq_Hz <= 0)
+		freq_Hz = freq_table[index].frequency * 1000;
+
+	freq_exact = freq_Hz;
+	new_freq = freq_Hz / 1000;
+	old_freq = clk_get_rate(cpu_clk) / 1000;
+
+	pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
+		 old_freq / 1000, volt_old ? volt_old / 1000 : -1,
+		 new_freq / 1000, volt ? volt / 1000 : -1);
+
+	ret = clk_set_rate(cpu_clk, freq_exact);
+	if (ret)
+		pr_err("failed to set clock rate: %d\n", ret);
+
+	return ret;
+}
+
+static int krait_cpufreq_init(struct cpufreq_policy *policy)
+{
+	int ret;
+
+	policy->clk = per_cpu(krait_cpu_clks, policy->cpu);
+
+	ret = cpufreq_table_validate_and_show(policy, freq_table);
+	if (ret) {
+		pr_err("%s: invalid frequency table: %d\n", __func__, ret);
+		return ret;
+	}
+
+	policy->cpuinfo.transition_latency = transition_latency;
+
+	return 0;
+}
+
+static struct cpufreq_driver krait_cpufreq_driver = {
+	.flags = CPUFREQ_STICKY,
+	.verify = cpufreq_generic_frequency_table_verify,
+	.target_index = krait_set_target,
+	.get = cpufreq_generic_get,
+	.init = krait_cpufreq_init,
+	.name = "generic_krait",
+	.attr = cpufreq_generic_attr,
+};
+
+static int krait_cpufreq_probe(struct platform_device *pdev)
+{
+	struct device_node *np;
+	int ret;
+	unsigned int cpu;
+	struct device *dev;
+	struct clk *clk;
+
+	cpu_dev = get_cpu_device(0);
+	if (!cpu_dev) {
+		pr_err("failed to get krait device\n");
+		return -ENODEV;
+	}
+
+	np = of_node_get(cpu_dev->of_node);
+	if (!np) {
+		pr_err("failed to find krait node\n");
+		return -ENOENT;
+	}
+
+	for_each_possible_cpu(cpu) {
+		dev = get_cpu_device(cpu);
+		if (!dev) {
+			pr_err("failed to get krait device\n");
+			ret = -ENOENT;
+			goto out_put_node;
+		}
+		per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			goto out_put_node;
+		}
+	}
+
+	ret = of_init_opp_table(cpu_dev);
+	if (ret) {
+		pr_err("failed to init OPP table: %d\n", ret);
+		goto out_put_node;
+	}
+
+	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+	if (ret) {
+		pr_err("failed to init cpufreq table: %d\n", ret);
+		goto out_put_node;
+	}
+
+	if (of_property_read_u32(np, "clock-latency", &transition_latency))
+		transition_latency = CPUFREQ_ETERNAL;
+
+	ret = cpufreq_register_driver(&krait_cpufreq_driver);
+	if (ret) {
+		pr_err("failed register driver: %d\n", ret);
+		goto out_free_table;
+	}
+	of_node_put(np);
+
+	/*
+	 * For now, just loading the cooling device;
+	 * thermal DT code takes care of matching them.
+	 */
+	for_each_possible_cpu(cpu) {
+		dev = get_cpu_device(cpu);
+		np = of_node_get(dev->of_node);
+		if (of_find_property(np, "#cooling-cells", NULL)) {
+			cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu));
+			if (IS_ERR(cdev))
+				pr_err("running cpufreq without cooling device: %ld\n",
+				       PTR_ERR(cdev));
+		}
+		of_node_put(np);
+	}
+
+	return 0;
+
+out_free_table:
+	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+out_put_node:
+	of_node_put(np);
+	return ret;
+}
+
+static int krait_cpufreq_remove(struct platform_device *pdev)
+{
+	cpufreq_cooling_unregister(cdev);
+	cpufreq_unregister_driver(&krait_cpufreq_driver);
+	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+
+	return 0;
+}
+
+static struct platform_driver krait_cpufreq_platdrv = {
+	.driver = {
+		.name	= "cpufreq-krait",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= krait_cpufreq_probe,
+	.remove		= krait_cpufreq_remove,
+};
+module_platform_driver(krait_cpufreq_platdrv);
+
+MODULE_DESCRIPTION("Krait CPUfreq driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 11/12] cpufreq: Add module to register cpufreq-krait device
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (9 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-06-25  0:06 ` [RFC/PATCH 12/12] ARM: dts: qcom: Add necessary DT data for Krait cpufreq Stephen Boyd
  2014-07-15 10:33 ` [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq pramod gurav
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 drivers/cpufreq/Kconfig.arm    |  8 +++++++
 drivers/cpufreq/Makefile       |  1 +
 drivers/cpufreq/qcom-cpufreq.c | 48 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+)
 create mode 100644 drivers/cpufreq/qcom-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index ebac67115009..cbc5643b3ecf 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -128,6 +128,14 @@ config ARM_OMAP2PLUS_CPUFREQ
 	depends on ARCH_OMAP2PLUS
 	default ARCH_OMAP2PLUS
 
+config ARM_QCOM_CPUFREQ
+	tristate "Qualcomm based"
+	depends on ARCH_QCOM
+	help
+	  This adds the CPUFreq driver for Qualcomm SoC based boards.
+
+	  If in doubt, say N.
+
 config ARM_S3C_CPUFREQ
 	bool
 	help
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index f5c9f68dcc0f..6b8917ba4b6e 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -62,6 +62,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
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
new file mode 100644
index 000000000000..71f4387734d0
--- /dev/null
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#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>
+
+static int qcom_cpufreq_driver_init(void)
+{
+	struct platform_device_info devinfo = { .name = "cpufreq-krait", };
+	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;
+	}
+	of_node_put(np);
+
+	pdev = platform_device_register_full(&devinfo);
+
+	return PTR_ERR_OR_ZERO(pdev);
+}
+module_init(qcom_cpufreq_driver_init);
+
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
+MODULE_LICENSE("GPL v2");
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC/PATCH 12/12] ARM: dts: qcom: Add necessary DT data for Krait cpufreq
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (10 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 11/12] cpufreq: Add module to register cpufreq-krait device Stephen Boyd
@ 2014-06-25  0:06 ` Stephen Boyd
  2014-07-15 10:33 ` [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq pramod gurav
  12 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:06 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

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

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
 arch/arm/boot/dts/qcom-msm8960.dtsi |  49 +++++++++++++++++
 arch/arm/boot/dts/qcom-msm8974.dtsi | 106 ++++++++++++++++++++++++++++++++++--
 2 files changed, 151 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
index 5303e53e34dc..94eeca22986f 100644
--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
@@ -23,6 +23,24 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc0>;
 			qcom,saw = <&saw0>;
+			clocks = <&kraitcc 0>;
+			clock-names = "cpu";
+			operating-points = <
+				/* kHz    ignored */
+				 918000 1000000
+				 864000 1000000
+				 810000 1000000
+				 756000 1000000
+				 702000 1000000
+				 648000 1000000
+				 594000 1000000
+				 540000 1000000
+				 486000 1000000
+				 432000 1000000
+				 384000 1000000
+			>;
+			clock-latency = <100000>;
+
 		};
 
 		cpu@1 {
@@ -33,6 +51,24 @@
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc1>;
 			qcom,saw = <&saw1>;
+			clocks = <&kraitcc 1>;
+			clock-names = "cpu";
+			operating-points = <
+				/* kHz    ignored */
+				 918000 1000000
+				 864000 1000000
+				 810000 1000000
+				 756000 1000000
+				 702000 1000000
+				 648000 1000000
+				 594000 1000000
+				 540000 1000000
+				 486000 1000000
+				 432000 1000000
+				 384000 1000000
+			>;
+			clock-latency = <100000>;
+
 		};
 
 		L2: l2-cache {
@@ -47,6 +83,11 @@
 		qcom,no-pc-write;
 	};
 
+	kraitcc: clock-controller {
+		compatible = "qcom,krait-cc-v1";
+		#clock-cells = <1>;
+	};
+
 	soc: soc {
 		#address-cells = <1>;
 		#size-cells = <1>;
@@ -100,11 +141,19 @@
 		acc0: clock-controller@2088000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu0_aux";
 		};
 
 		acc1: clock-controller@2098000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu1_aux";
+		};
+
+		l2cc: clock-controller@2011000 {
+			compatible = "qcom,kpss-gcc";
+			reg = <0x2011000 0x1000>;
+			clock-output-names = "acpu_l2_aux";
 		};
 
 		saw0: regulator@2089000 {
diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index 69dca2aca25a..e607063b527a 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -14,40 +14,104 @@
 		#size-cells = <0>;
 		interrupts = <1 9 0xf04>;
 
-		cpu@0 {
+		cpu0: cpu@0 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
 			reg = <0>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc0>;
+			clocks = <&kraitcc 0>;
+			clock-names = "cpu";
+			operating-points = <
+				/* kHz    ignored */
+				 960000 1000000
+				 883200 1000000
+				 806400 1000000
+				 729600 1000000
+				 652800 1000000
+				 576000 1000000
+				 499200 1000000
+				 422400 1000000
+				 345600 1000000
+				 300000 1000000
+			>;
+			clock-latency = <100000>;
 		};
 
-		cpu@1 {
+		cpu1: cpu@1 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
 			reg = <1>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc1>;
+			clocks = <&kraitcc 1>;
+			clock-names = "cpu";
+			operating-points = <
+				/* kHz    ignored */
+				 960000 1000000
+				 883200 1000000
+				 806400 1000000
+				 729600 1000000
+				 652800 1000000
+				 576000 1000000
+				 499200 1000000
+				 422400 1000000
+				 345600 1000000
+				 300000 1000000
+			>;
+			clock-latency = <100000>;
 		};
 
-		cpu@2 {
+		cpu2: cpu@2 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
 			reg = <2>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc2>;
+			clocks = <&kraitcc 2>;
+			clock-names = "cpu";
+			operating-points = <
+				/* kHz    ignored */
+				 960000 1000000
+				 883200 1000000
+				 806400 1000000
+				 729600 1000000
+				 652800 1000000
+				 576000 1000000
+				 499200 1000000
+				 422400 1000000
+				 345600 1000000
+				 300000 1000000
+			>;
+			clock-latency = <100000>;
 		};
 
-		cpu@3 {
+		cpu3: cpu@3 {
 			compatible = "qcom,krait";
 			enable-method = "qcom,kpss-acc-v2";
 			device_type = "cpu";
 			reg = <3>;
 			next-level-cache = <&L2>;
 			qcom,acc = <&acc3>;
+			clocks = <&kraitcc 3>;
+			clock-names = "cpu";
+			operating-points = <
+				/* kHz    ignored */
+				 960000 1000000
+				 883200 1000000
+				 806400 1000000
+				 729600 1000000
+				 652800 1000000
+				 576000 1000000
+				 499200 1000000
+				 422400 1000000
+				 345600 1000000
+				 300000 1000000
+			>;
+			clock-latency = <100000>;
 		};
 
 		L2: l2-cache {
@@ -71,6 +135,10 @@
 		clock-frequency = <19200000>;
 	};
 
+	kraitcc: clock-controller {
+		compatible = "qcom,krait-cc-v2";
+		#clock-cells = <1>;
+	};
 	soc: soc {
 		#address-cells = <1>;
 		#size-cells = <1>;
@@ -144,6 +212,36 @@
 			};
 		};
 
+		clock-controller@f9016000 {
+			compatible = "qcom,hfpll";
+			reg = <0xf9016000 0x30>;
+			clock-output-names = "hfpll_l2";
+		};
+
+		clock-controller@f908a000 {
+			compatible = "qcom,hfpll";
+			reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
+			clock-output-names = "hfpll0";
+		};
+
+		clock-controller@f909a000 {
+			compatible = "qcom,hfpll";
+			reg = <0xf909a000 0x30>, <0xf900a000 0x30>;
+			clock-output-names = "hfpll1";
+		};
+
+		clock-controller@f90aa000 {
+			compatible = "qcom,hfpll";
+			reg = <0xf90aa000 0x30>, <0xf900a000 0x30>;
+			clock-output-names = "hfpll2";
+		};
+
+		clock-controller@f90ba000 {
+			compatible = "qcom,hfpll";
+			reg = <0xf90ba000 0x30>, <0xf900a000 0x30>;
+			clock-output-names = "hfpll3";
+		};
+
 		saw_l2: regulator@f9012000 {
 			compatible = "qcom,saw2";
 			reg = <0xf9012000 0x1000>;
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
  2014-06-25  0:06 ` [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 Stephen Boyd
@ 2014-06-25  0:52   ` Stephen Boyd
  2014-06-25  8:47   ` Viresh Kumar
  1 sibling, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2014-06-25  0:52 UTC (permalink / raw)
  To: linux-arm-msm; +Cc: linux-arm-kernel, linux-kernel, Mike Turquette

On 06/24/14 17:06, Stephen Boyd wrote:
> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
> index 738c8b7b17dc..f5c9f68dcc0f 100644
> --- a/drivers/cpufreq/Makefile
> +++ b/drivers/cpufreq/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE)	+= cpufreq_conservative.o
>  obj-$(CONFIG_CPU_FREQ_GOV_COMMON)		+= cpufreq_governor.o
>  
>  obj-$(CONFIG_GENERIC_CPUFREQ_CPU0)	+= cpufreq-cpu0.o
> +obj-$(CONFIG_CPUFREQ_KRAIT)		+= cpufreq-krait.o
>

Hmph, last minute name changes are bad. This should be
CONFIG_GENERIC_CPUFREQ_KRAIT.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
  2014-06-25  0:06 ` [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 Stephen Boyd
  2014-06-25  0:52   ` Stephen Boyd
@ 2014-06-25  8:47   ` Viresh Kumar
  1 sibling, 0 replies; 21+ messages in thread
From: Viresh Kumar @ 2014-06-25  8:47 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-arm-msm, Mike Turquette, linux-kernel, linux-arm-kernel,
	Rafael J. Wysocki, Linux PM list

On Wed, Jun 25, 2014 at 5:36 AM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> Krait processors have individual clocks for each CPU that can
> scale independently from one another. cpufreq-cpu0 is fairly
> close to this, but assumes that there is only one clock for all
> CPUs. Add a driver to support the Krait configuration.

You missed both cpufreq maintainers and PM-list in cc :(

> TODO: Merge into cpufreq-cpu0? Or make generic?

Haven't gone into minute details of this driver but have tried
what looks to be the way ahead.

As you also mentioned merging with cpufreq-cpu0 might be
worth and I have tried extending cpufreq-cpu0's support
for all CPUs..

It should work for Krait. I have tested it for Exynos dual-A15
and it didn't explode :)

Links aren't available yet but you can find that in your
mailbox with this subject:

[PATCH 0/2] cpufreq: cpu0: Extend support beyond CPU0

Cc'd everybody from this thread..

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers
  2014-06-25  0:06 ` [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers Stephen Boyd
@ 2014-07-14 10:06   ` pramod gurav
  0 siblings, 0 replies; 21+ messages in thread
From: pramod gurav @ 2014-07-14 10:06 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-arm-msm, linux-arm-kernel, linux-kernel, Mike Turquette,
	Saravana Kannan

Hi Stephen,

On Wed, Jun 25, 2014 at 5:36 AM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> The Krait CPU clocks are made up of muxes and dividers with a
> handful of sources. Add a set of clk_ops that allow us to
> configure these clocks so we can support CPU frequency scaling on
> Krait CPUs.
>
> Based on code originally written by Saravana Kannan.
>
> Cc: Saravana Kannan <skannan@codeaurora.org>
> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  drivers/clk/qcom/Makefile           |   1 +
>  drivers/clk/qcom/clk-generic.c      | 405 ++++++++++++++++++++++++++++++++++++
>  include/linux/clk/msm-clk-generic.h | 208 ++++++++++++++++++
>  3 files changed, 614 insertions(+)
>  create mode 100644 drivers/clk/qcom/clk-generic.c
>  create mode 100644 include/linux/clk/msm-clk-generic.h
>
> diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
> index 689e05bf4f95..8684ff004be6 100644
> --- a/drivers/clk/qcom/Makefile
> +++ b/drivers/clk/qcom/Makefile
> @@ -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-generic.o
>  clk-qcom-y += reset.o
>
>  obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
> diff --git a/drivers/clk/qcom/clk-generic.c b/drivers/clk/qcom/clk-generic.c
> new file mode 100644
> index 000000000000..a0d778ba7394
> --- /dev/null
> +++ b/drivers/clk/qcom/clk-generic.c
> @@ -0,0 +1,405 @@
> +/*
> + * Copyright (c) 2014, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/export.h>
> +#include <linux/bug.h>
> +#include <linux/err.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clk/msm-clk-generic.h>
> +
> +
> +/* ==================== Mux clock ==================== */
> +
> +static int mux_set_parent(struct clk_hw *hw, u8 sel)
> +{
> +       struct mux_clk *mux = to_mux_clk(hw);
> +
> +       if (mux->parent_map)
> +               sel = mux->parent_map[sel];
> +
> +       return mux->ops->set_mux_sel(mux, sel);
> +}
> +
> +static u8 mux_get_parent(struct clk_hw *hw)
> +{
> +       struct mux_clk *mux = to_mux_clk(hw);
> +       int num_parents = __clk_get_num_parents(hw->clk);
> +       int i;
> +       u8 sel;
> +
> +       sel = mux->ops->get_mux_sel(mux);
> +       if (mux->parent_map) {
> +               for (i = 0; i < num_parents; i++)
> +                       if (sel == mux->parent_map[i])
> +                               return i;
> +               WARN(1, "Can't find parent\n");
> +               return -EINVAL;
> +       }
> +
> +       return sel;
> +}
> +
> +static int mux_enable(struct clk_hw *hw)
> +{
> +       struct mux_clk *mux = to_mux_clk(hw);

Missing a blank line after declarations.

> +       if (mux->ops->enable)
> +               return mux->ops->enable(mux);
> +       return 0;
> +}
> +
> +static void mux_disable(struct clk_hw *hw)
> +{
> +       struct mux_clk *mux = to_mux_clk(hw);

Same here.

> +       if (mux->ops->disable)
> +               return mux->ops->disable(mux);
> +}
> +
> +static struct clk *mux_get_safe_parent(struct clk_hw *hw)
> +{
> +       int i;
> +       struct mux_clk *mux = to_mux_clk(hw);
> +       int num_parents = __clk_get_num_parents(hw->clk);
> +
> +       if (!mux->has_safe_parent)
> +               return NULL;
> +
> +       i = mux->safe_sel;
> +       if (mux->parent_map)
> +               for (i = 0; i < num_parents; i++)
> +                       if (mux->safe_sel == mux->parent_map[i])
> +                               break;
> +
> +       return clk_get_parent_by_index(hw->clk, i);
> +}
> +
> +const struct clk_ops clk_ops_gen_mux = {
> +       .enable = mux_enable,
> +       .disable = mux_disable,
> +       .set_parent = mux_set_parent,
> +       .get_parent = mux_get_parent,
> +       .determine_rate = __clk_mux_determine_rate,
> +       .get_safe_parent = mux_get_safe_parent,
> +};
> +EXPORT_SYMBOL_GPL(clk_ops_gen_mux);
> +
> +/* ==================== Divider clock ==================== */
> +
> +static long __div_round_rate(struct div_data *data, unsigned long rate,
> +       struct clk *parent, unsigned int *best_div, unsigned long *best_prate,
> +       bool set_parent)
> +{
> +       unsigned int div, min_div, max_div, _best_div = 1;
> +       unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate;
> +       unsigned int numer;
> +
> +       rate = max(rate, 1UL);
> +
> +       min_div = max(data->min_div, 1U);
> +       max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate));
> +
> +       /*
> +        * div values are doubled for half dividers.
> +        * Adjust for that by picking a numer of 2.
> +        */
> +       numer = data->is_half_divider ? 2 : 1;
> +
> +       if (!set_parent) {
> +               prate = *best_prate * numer;
> +               div = DIV_ROUND_UP(prate, rate);
> +               div = clamp(1U, div, max_div);
> +               if (best_div)
> +                       *best_div = div;
> +               return mult_frac(*best_prate, numer, div);
> +       }
> +
> +       for (div = min_div; div <= max_div; div++) {
> +               req_prate = mult_frac(rate, div, numer);
> +               prate = __clk_round_rate(parent, req_prate);
> +               if (IS_ERR_VALUE(prate))
> +                       break;
> +
> +               actual_rate = mult_frac(prate, numer, div);
> +               if (is_better_rate(rate, rrate, actual_rate)) {
> +                       rrate = actual_rate;
> +                       _best_div = div;
> +                       _best_prate = prate;
> +               }
> +
> +               /*
> +                * Trying higher dividers is only going to ask the parent for
> +                * a higher rate. If it can't even output a rate higher than
> +                * the one we request for this divider, the parent is not
> +                * going to be able to output an even higher rate required
> +                * for a higher divider. So, stop trying higher dividers.
> +                */
> +               if (actual_rate < rate)
> +                       break;
> +
> +               if (rrate <= rate)
> +                       break;
> +       }
> +
> +       if (!rrate)
> +               return -EINVAL;
> +       if (best_div)
> +               *best_div = _best_div;
> +       if (best_prate)
> +               *best_prate = _best_prate;
> +
> +       return rrate;
> +}
> +
> +static long div_round_rate(struct clk_hw *hw, unsigned long rate,
> +                          unsigned long *parent_rate)
> +{
> +       struct div_clk *d = to_div_clk(hw);
> +       bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT;
> +
> +       return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk),
> +                               NULL, parent_rate, set_parent);
> +}
> +
> +static int div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long
> +                       parent_rate)
> +{
> +       struct div_clk *d = to_div_clk(hw);
> +       int div, rc = 0;
> +       struct div_data *data = &d->data;
> +
> +       div = parent_rate / rate;
> +       if (div != data->div)
> +               rc = d->ops->set_div(d, div);
> +       data->div = div;
> +
> +       return rc;
> +}
> +
> +static int div_enable(struct clk_hw *hw)
> +{
> +       struct div_clk *d = to_div_clk(hw);

Missing a black line here.

> +       if (d->ops && d->ops->enable)
> +               return d->ops->enable(d);
> +       return 0;
> +}
> +
> +static void div_disable(struct clk_hw *hw)
> +{
> +       struct div_clk *d = to_div_clk(hw);

Here as well.

> +       if (d->ops && d->ops->disable)
> +               return d->ops->disable(d);
> +}
> +
> +static unsigned long div_recalc_rate(struct clk_hw *hw, unsigned long prate)
> +{
> +       struct div_clk *d = to_div_clk(hw);
> +       unsigned int div = d->data.div;
> +
> +       if (d->ops && d->ops->get_div)
> +               div = max(d->ops->get_div(d), 1);
> +       div = max(div, 1U);
> +
> +       if (!d->ops || !d->ops->set_div)
> +               d->data.min_div = d->data.max_div = div;
> +       d->data.div = div;
> +
> +       return prate / div;
> +}
> +
> +const struct clk_ops clk_ops_div = {
> +       .enable = div_enable,
> +       .disable = div_disable,
> +       .round_rate = div_round_rate,
> +       .set_rate = div_set_rate,
> +       .recalc_rate = div_recalc_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_ops_div);
> +
> +/* ==================== Mux_div clock ==================== */
> +
> +static int mux_div_clk_enable(struct clk_hw *hw)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +
> +       if (md->ops->enable)
> +               return md->ops->enable(md);
> +       return 0;
> +}
> +
> +static void mux_div_clk_disable(struct clk_hw *hw)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +
> +       if (md->ops->disable)
> +               return md->ops->disable(md);
> +}
> +
> +static long __mux_div_round_rate(struct clk_hw *hw, unsigned long rate,
> +       struct clk **best_parent, int *best_div, unsigned long *best_prate)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +       unsigned int i;
> +       unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0;
> +       struct clk *_best_parent = 0;
> +       int num_parents = __clk_get_num_parents(hw->clk);
> +       bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT;
> +
> +       for (i = 0; i < num_parents; i++) {
> +               int div;
> +               unsigned long prate;
> +               struct clk *p = clk_get_parent_by_index(hw->clk, i);
> +
> +               rrate = __div_round_rate(&md->data, rate, p, &div, &prate,
> +                               set_parent);
> +
> +               if (is_better_rate(rate, best, rrate)) {
> +                       best = rrate;
> +                       _best_div = div;
> +                       _best_prate = prate;
> +                       _best_parent = p;
> +               }
> +
> +               if (rate <= rrate)
> +                       break;
> +       }
> +
> +       if (best_div)
> +               *best_div = _best_div;
> +       if (best_prate)
> +               *best_prate = _best_prate;
> +       if (best_parent)
> +               *best_parent = _best_parent;
> +
> +       if (best)
> +               return best;
> +       return -EINVAL;
> +}
> +
> +static long mux_div_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                  unsigned long *parent_rate)
> +{
> +       return __mux_div_round_rate(hw, rate, NULL, NULL, parent_rate);
> +}
> +
> +/* requires enable lock to be held */
> +static int __set_src_div(struct mux_div_clk *md, u8 src_sel, u32 div)
> +{
> +       int rc;
> +
> +       rc = md->ops->set_src_div(md, src_sel, div);
> +       if (!rc) {
> +               md->data.div = div;
> +               md->src_sel = src_sel;
> +       }
> +
> +       return rc;
> +}
> +
> +/* Must be called after handoff to ensure parent clock rates are initialized */
> +static int safe_parent_init_once(struct clk_hw *hw)
> +{
> +       unsigned long rrate;
> +       u32 best_div;
> +       struct clk *best_parent;
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +
> +       if (IS_ERR(md->safe_parent))
> +               return -EINVAL;
> +       if (!md->safe_freq || md->safe_parent)
> +               return 0;
> +
> +       rrate = __mux_div_round_rate(hw, md->safe_freq, &best_parent,
> +                       &best_div, NULL);
> +
> +       if (rrate == md->safe_freq) {
> +               md->safe_div = best_div;
> +               md->safe_parent = best_parent;
> +       } else {
> +               md->safe_parent = ERR_PTR(-EINVAL);
> +               return -EINVAL;
> +       }
> +       return 0;
> +}
> +
> +static int
> +__mux_div_clk_set_rate_and_parent(struct clk_hw *hw, u8 index, u32 div)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +       int rc;
> +
> +       rc = safe_parent_init_once(hw);
> +       if (rc)
> +               return rc;
> +
> +       return __set_src_div(md, index, div);
> +}
> +
> +static int mux_div_clk_set_rate_and_parent(struct clk_hw *hw,
> +               unsigned long rate, unsigned long parent_rate, u8 index)
> +{
> +       return __mux_div_clk_set_rate_and_parent(hw, index, parent_rate / rate);
> +}
> +
> +static int mux_div_clk_set_rate(struct clk_hw *hw,
> +               unsigned long rate, unsigned long parent_rate)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);

Missing a blank line.

> +       return __mux_div_clk_set_rate_and_parent(hw, md->src_sel,
> +                       parent_rate / rate);
> +}
> +
> +static int mux_div_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);

Same here.

> +       return __mux_div_clk_set_rate_and_parent(hw, md->parent_map[index],
> +                       md->data.div);
> +}
> +
> +static u8 mux_div_clk_get_parent(struct clk_hw *hw)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +       int num_parents = __clk_get_num_parents(hw->clk);
> +       u32 i, div, sel;
> +
> +       md->ops->get_src_div(md, &sel, &div);
> +       md->src_sel = sel;
> +
> +       for (i = 0; i < num_parents; i++)
> +               if (sel == md->parent_map[i])
> +                       return i;
> +       WARN(1, "Can't find parent\n");
> +       return -EINVAL;
> +}
> +
> +static unsigned long
> +mux_div_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
> +{
> +       struct mux_div_clk *md = to_mux_div_clk(hw);
> +       u32 div, sel;
> +
> +       md->ops->get_src_div(md, &sel, &div);
> +
> +       return prate / div;
> +}
> +
> +const struct clk_ops clk_ops_mux_div_clk = {
> +       .enable = mux_div_clk_enable,
> +       .disable = mux_div_clk_disable,
> +       .set_rate_and_parent = mux_div_clk_set_rate_and_parent,
> +       .set_rate = mux_div_clk_set_rate,
> +       .set_parent = mux_div_clk_set_parent,
> +       .round_rate = mux_div_clk_round_rate,
> +       .get_parent = mux_div_clk_get_parent,
> +       .recalc_rate = mux_div_clk_recalc_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_ops_mux_div_clk);
> diff --git a/include/linux/clk/msm-clk-generic.h b/include/linux/clk/msm-clk-generic.h
> new file mode 100644
> index 000000000000..cee38636fe0f
> --- /dev/null
> +++ b/include/linux/clk/msm-clk-generic.h
> @@ -0,0 +1,208 @@
> +/*
> + * Copyright (c) 2014, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __QCOM_CLK_GENERIC_H__
> +#define __QCOM_CLK_GENERIC_H__
> +
> +#include <linux/err.h>
> +#include <linux/clk-provider.h>
> +
> +static inline bool is_better_rate(unsigned long req, unsigned long best,
> +                                 unsigned long new)
> +{
> +       if (IS_ERR_VALUE(new))
> +               return false;
> +
> +       return (req <= new && new < best) || (best < req && best < new);
> +}
> +
> +/* ==================== Mux clock ==================== */
> +
> +struct mux_clk;
> +
> +struct clk_mux_ops {
> +       int (*set_mux_sel)(struct mux_clk *clk, int sel);
> +       int (*get_mux_sel)(struct mux_clk *clk);
> +
> +       /* Optional */
> +       bool (*is_enabled)(struct mux_clk *clk);
> +       int (*enable)(struct mux_clk *clk);
> +       void (*disable)(struct mux_clk *clk);
> +};
> +
> +struct mux_clk {
> +       /* Parents in decreasing order of preference for obtaining rates. */
> +       u8              *parent_map;

Checkpatch fails here with "no space before tabs".

> +       bool            has_safe_parent;
> +       u8              safe_sel;
> +       const struct clk_mux_ops *ops;
> +
> +       /* Fields not used by helper function. */
> +       void __iomem    *base;
Same here.

> +       u32             offset;
> +       u32             en_offset;
> +       int             en_reg;
> +       u32             mask;
> +       u32             shift;
> +       u32             en_mask;
> +       void            *priv;
> +
> +       struct clk_hw   hw;
> +};
> +
> +static inline struct mux_clk *to_mux_clk(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct mux_clk, hw);
> +}
> +
> +extern const struct clk_ops clk_ops_gen_mux;
> +
> +/* ==================== Divider clock ==================== */
> +
> +struct div_clk;
> +
> +struct clk_div_ops {
> +       int (*set_div)(struct div_clk *clk, int div);
> +       int (*get_div)(struct div_clk *clk);
> +       bool (*is_enabled)(struct div_clk *clk);
> +       int (*enable)(struct div_clk *clk);
> +       void (*disable)(struct div_clk *clk);
> +};
> +
> +struct div_data {
> +       unsigned int div;
> +       unsigned int min_div;
> +       unsigned int max_div;
> +       /*
> +        * Indicate whether this divider clock supports half-interger divider.
> +        * If it is, all the min_div and max_div have been doubled. It means
> +        * they are 2*N.
> +        */
> +       bool is_half_divider;
> +};
> +
> +struct div_clk {
> +       struct div_data data;
> +
> +       /* Optional */
> +       const struct clk_div_ops *ops;
> +
> +       /* Fields not used by helper function. */
> +       void __iomem    *base;
Same here.
> +       u32             offset;
> +       u32             mask;
> +       u32             shift;
> +       u32             en_mask;
> +       void            *priv;
> +       struct clk_hw   hw;
> +};
> +
> +static inline struct div_clk *to_div_clk(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct div_clk, hw);
> +}
> +
> +extern const struct clk_ops clk_ops_div;
> +
> +#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \
> +static struct div_clk clk_name = {     \
> +       .data = {                               \
> +               .max_div = _div,                \
> +               .min_div = _div,                \
> +               .div = _div,                    \
> +       },                                      \
> +       .hw.init = &(struct clk_init_data){ \
> +               .parent_names = (const char *[]){ _parent }, \
> +               .num_parents = 1,               \
> +               .name = #clk_name,              \
> +               .ops = &clk_ops_div,            \
> +               .flags = CLK_SET_RATE_PARENT,   \
> +       }                                       \
> +}
> +
> +/* ==================== Mux Div clock ==================== */
> +
> +struct mux_div_clk;
> +
> +/*
> + * struct mux_div_ops
> + * the enable and disable ops are optional.
> + */
> +
> +struct mux_div_ops {
> +       int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div);
> +       void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div);
> +       int (*enable)(struct mux_div_clk *);
> +       void (*disable)(struct mux_div_clk *);
> +       bool (*is_enabled)(struct mux_div_clk *);
> +};
> +
> +/*
> + * struct mux_div_clk - combined mux/divider clock
> + * @priv
> +               parameters needed by ops
> + * @safe_freq
> +               when switching rates from A to B, the mux div clock will
> +               instead switch from A -> safe_freq -> B. This allows the
> +               mux_div clock to change rates while enabled, even if this
> +               behavior is not supported by the parent clocks.
> +
> +               If changing the rate of parent A also causes the rate of
> +               parent B to change, then safe_freq must be defined.
> +
> +               safe_freq is expected to have a source clock which is always
> +               on and runs at only one rate.
> + * @parents
> +               list of parents and mux indicies
> + * @ops
> +               function pointers for hw specific operations
> + * @src_sel
> +               the mux index which will be used if the clock is enabled.
> + */
> +
> +struct mux_div_clk {
> +       /* Required parameters */
> +       const struct mux_div_ops        *ops;
> +       struct div_data                 data;
> +       u8                              *parent_map;
> +
> +       struct clk_hw                   hw;
> +
> +       /* Internal */
> +       u32                             src_sel;
> +
> +       /* Optional parameters */
> +       void                            *priv;
> +       void __iomem                    *base;
> +       u32                             div_mask;
> +       u32                             div_offset;
> +       u32                             div_shift;
> +       u32                             src_mask;
> +       u32                             src_offset;
> +       u32                             src_shift;
> +       u32                             en_mask;
> +       u32                             en_offset;
> +
> +       u32                             safe_div;
> +       struct clk                      *safe_parent;
> +       unsigned long                   safe_freq;
> +};
> +
> +static inline struct mux_div_clk *to_mux_div_clk(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct mux_div_clk, hw);
> +}
> +
> +extern const struct clk_ops clk_ops_mux_div_clk;
> +
> +#endif
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
Thanks and Regards
Pramod

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs
  2014-06-25  0:06 ` [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs Stephen Boyd
@ 2014-07-14 10:19   ` pramod gurav
  0 siblings, 0 replies; 21+ messages in thread
From: pramod gurav @ 2014-07-14 10:19 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-arm-msm, linux-arm-kernel, linux-kernel, Mike Turquette

Hi Stephen,



On Wed, Jun 25, 2014 at 5:36 AM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> Describe the HFPLLs present on MSM8960 devices.
>
> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 82 insertions(+)
>
> diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
> index f4ffd91901f8..d04fc99541e4 100644
> --- a/drivers/clk/qcom/gcc-msm8960.c
> +++ b/drivers/clk/qcom/gcc-msm8960.c
> @@ -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,84 @@ 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,

Needs tabs at the beginning as indent. Checkpatch fails at above three lines.

> +        .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 = 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,

Needs tabs and the beginning as indent. Checkpatch fails at above three lines.

> +       .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 = 0x3400,
> +       .l_reg = 0x3408,
> +       .m_reg = 0x340c,
> +       .n_reg = 0x3410,
> +       .config_reg = 0x3404,
> +       .status_reg = 0x341c,
> +        .config_val = 0x7845c665,
> +        .droop_reg = 0x3414,

Same at above three lines.

> +        .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,
> @@ -2763,6 +2842,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[] = {
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
Thanks and Regards
Pramod

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver
  2014-06-25  0:06 ` [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver Stephen Boyd
@ 2014-07-14 10:25   ` pramod gurav
  0 siblings, 0 replies; 21+ messages in thread
From: pramod gurav @ 2014-07-14 10:25 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-arm-msm, linux-arm-kernel, linux-kernel, Mike Turquette

On Wed, Jun 25, 2014 at 5:36 AM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> 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.
>
> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  drivers/clk/qcom/Kconfig    |   9 ++
>  drivers/clk/qcom/Makefile   |   1 +
>  drivers/clk/qcom/krait-cc.c | 366 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 376 insertions(+)
>  create mode 100644 drivers/clk/qcom/krait-cc.c
>
> diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
> index 247f042dc110..6f835e406930 100644
> --- a/drivers/clk/qcom/Kconfig
> +++ b/drivers/clk/qcom/Kconfig
> @@ -61,3 +61,12 @@ config KPSS_XCC
>           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 KRAITCC
> +       tristate "Krait Clock Controller"
> +       depends on COMMON_CLK_QCOM
> +       depends on ARM
> +       select KRAIT_L2_ACCESSORS
> +       help
> +         Support for the Krait CPU clocks on Qualcomm devices.
> +         Say Y if you want to support CPU frequency scaling.
> diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
> index bb4666733bd9..e8c7f31916da 100644
> --- a/drivers/clk/qcom/Makefile
> +++ b/drivers/clk/qcom/Makefile
> @@ -18,3 +18,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
> diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
> new file mode 100644
> index 000000000000..4ca9589acfca
> --- /dev/null
> +++ b/drivers/clk/qcom/krait-cc.c
> @@ -0,0 +1,366 @@
> +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +
> +#include <asm/smp_plat.h>
> +
> +#include "clk-krait.h"
> +
> +DEFINE_FIXED_DIV_CLK(acpu_aux, 2, "gpll0_vote");
> +
> +static u8 sec_mux_map[] = {
> +       2,
> +       0,
> +};
> +
> +static u8 pri_mux_map[] = {
> +       1,
> +       2,
> +       0,
> +};
> +
> +static int
> +krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
> +{
> +       struct div_clk *div;
> +       struct clk_init_data init = {
> +               .num_parents = 1,
> +               .ops = &clk_ops_div,
> +               .flags = CLK_SET_RATE_PARENT,
> +       };
> +       const char *p_names[1];
> +       struct clk *clk;
> +
> +       div = devm_kzalloc(dev, sizeof(*dev), GFP_KERNEL);
> +       if (!div)
> +               return -ENOMEM;
> +
> +       div->data.div = 2;
> +       div->data.min_div = 2;
> +       div->data.max_div = 2;
> +       div->ops = &clk_div_ops_kpss_div2;
> +       div->mask = 0x3;
> +       div->shift = 6;
> +       div->priv = (void *)(id >= 0);
> +       div->offset = offset;
> +       div->hw.init = &init;
> +
> +       init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
> +       if (!init.name)
> +               return -ENOMEM;
> +
> +       init.parent_names = p_names;
> +       p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
> +       if (!p_names[0]) {
> +               kfree(init.name);
> +               return -ENOMEM;
> +       }
> +
> +       clk = devm_clk_register(dev, &div->hw);
> +       kfree(p_names[0]);
> +       kfree(init.name);
> +
> +       return PTR_ERR_OR_ZERO(clk);
> +}
> +
> +static int
> +krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
> +                 bool unique_aux)
> +{
> +       struct mux_clk *mux;
> +       static const char *sec_mux_list[] = {
> +               "acpu_aux",
> +               "qsb",
> +       };
> +       struct clk_init_data init = {
> +               .parent_names = sec_mux_list,
> +               .num_parents = ARRAY_SIZE(sec_mux_list),
> +               .ops = &clk_ops_gen_mux,
> +               .flags = CLK_SET_RATE_PARENT,
> +       };
> +       struct clk *clk;
> +
> +       mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
> +       if (!mux)
> +               return -ENOMEM;
> +
> +       mux->offset = offset;
> +       mux->priv = (void *)(id >= 0);
> +       mux->has_safe_parent = true;
> +       mux->safe_sel = 2;
> +       mux->ops = &clk_mux_ops_kpss;
> +       mux->mask = 0x3;
> +       mux->shift = 2;
> +       mux->parent_map = sec_mux_map;
> +       mux->hw.init = &init;
> +
> +       init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
> +       if (!init.name)
> +               return -ENOMEM;
> +
> +       if (unique_aux) {
> +               sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
> +               if (!sec_mux_list[0]) {
> +                       clk = ERR_PTR(-ENOMEM);
> +                       goto err_aux;
> +               }
> +               pr_info("Parents are %s %s\n", sec_mux_list[0], sec_mux_list[1]);
line over 80 characters.

> +       }
> +
> +       clk = devm_clk_register(dev, &mux->hw);
> +
> +       if (unique_aux)
> +               kfree(sec_mux_list[0]);
> +err_aux:
> +       kfree(init.name);
> +       return PTR_ERR_OR_ZERO(clk);
> +}
> +
> +static struct clk *
> +krait_add_pri_mux(struct device *dev, int id, const char * s, unsigned offset)

Extra space before 's'.

> +{
> +       struct mux_clk *mux;
> +       const char *p_names[3];
> +       struct clk_init_data init = {
> +               .parent_names = p_names,
> +               .num_parents = ARRAY_SIZE(p_names),
> +               .ops = &clk_ops_gen_mux,
> +               .flags = CLK_SET_RATE_PARENT,
> +       };
> +       struct clk *clk;
> +
> +       mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
> +       if (!mux)
> +               return ERR_PTR(-ENOMEM);
> +
> +       mux->has_safe_parent = true;
> +       mux->safe_sel = 0;
> +       mux->ops = &clk_mux_ops_kpss;
> +       mux->mask = 0x3;
> +       mux->shift = 0;
> +       mux->offset = offset;
> +       mux->priv = (void *)(id >= 0);
> +       mux->parent_map = pri_mux_map;
> +       mux->hw.init = &init;
> +
> +       init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
> +       if (!init.name)
> +               return ERR_PTR(-ENOMEM);
> +
> +       p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
> +       if (!p_names[0]) {
> +               clk = ERR_PTR(-ENOMEM);
> +               goto err_p0;
> +       }
> +
> +       p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
> +       if (!p_names[1]) {
> +               clk = ERR_PTR(-ENOMEM);
> +               goto err_p1;
> +       }
> +
> +       p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
> +       if (!p_names[2]) {
> +               clk = ERR_PTR(-ENOMEM);
> +               goto err_p2;
> +       }
> +
> +       clk = devm_clk_register(dev, &mux->hw);
> +
> +       kfree(p_names[2]);
> +err_p2:
> +       kfree(p_names[1]);
> +err_p1:
> +       kfree(p_names[0]);
> +err_p0:
> +       kfree(init.name);
> +       return clk;
> +}
> +
> +/* id < 0 for L2, otherwise id == physical CPU number */
> +static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
> +{
> +       int ret;
> +       unsigned offset;
> +       const char *s;
> +       struct clk *clk;
> +
> +       if (id >= 0) {
> +               offset = 0x4501 + (0x1000 * id);
> +               s = kasprintf(GFP_KERNEL, "%d", id);
> +               if (!s)
> +                       return ERR_PTR(-ENOMEM);
> +       } else {
> +               offset = 0x500;
> +               s = "_l2";
> +       }
> +
> +       ret = krait_add_div(dev, id, s, offset);
> +       if (ret) {
> +               clk = ERR_PTR(ret);
> +               goto err;
> +       }
> +
> +       ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
> +       if (ret) {
> +               clk = ERR_PTR(ret);
> +               goto err;
> +       }
> +
> +       clk = krait_add_pri_mux(dev, id, s, offset);
> +err:
> +       if (id >= 0)
> +               kfree(s);
> +
> +       return clk;
> +}
> +
> +static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
> +{
> +       unsigned int idx = clkspec->args[0];
> +       struct clk **clks = data;
> +
> +       if (idx >= 5) {
> +               pr_err("%s: invalid clock index %d\n", __func__, idx);
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       return clks[idx] ? : ERR_PTR(-ENODEV);
> +}
> +
> +static const struct of_device_id krait_cc_match_table[] = {
> +       { .compatible = "qcom,krait-cc-v1", (void *)1UL },
> +       { .compatible = "qcom,krait-cc-v2" },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, krait_cc_match_table);
> +
> +static int krait_cc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       const struct of_device_id *id;
> +       unsigned long cur_rate, aux_rate;
> +       int i, cpu;
> +       struct clk *clk;
> +       struct clk **clks;
> +       struct clk *l2_pri_mux_clk;
> +
> +       id = of_match_device(krait_cc_match_table, &pdev->dev);
> +       if (!id)
> +               return -ENODEV;
> +
> +       /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
> +       clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
> +       if (IS_ERR(clk))
> +               return PTR_ERR(clk);
> +
> +       if (!id->data) {
> +               clk = devm_clk_register(dev, &acpu_aux.hw);
> +               if (IS_ERR(clk))
> +                       return PTR_ERR(clk);
> +       }
> +
> +       /* Krait configurations have at most 4 CPUs and one L2 */
> +       clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
> +       if (!clks)
> +               return -ENOMEM;
> +
> +       for_each_possible_cpu(i) {
> +               cpu = cpu_logical_map(i);
> +               clk = krait_add_clks(dev, cpu, id->data);
> +               if (IS_ERR(clk))
> +                       return PTR_ERR(clk);
> +               clks[cpu] = clk;
> +       }
> +
> +       l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
> +       if (IS_ERR(l2_pri_mux_clk))
> +               return PTR_ERR(l2_pri_mux_clk);
> +       clks[4] = l2_pri_mux_clk;
> +
> +       /*
> +        * We don't want the CPU or L2 clocks to be turned off at late init
> +        * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
> +        * refcount of these clocks. Any cpufreq/hotplug manager can assume
> +        * that the clocks have already been prepared and enabled by the time
> +        * they take over.
> +        */
> +       for_each_online_cpu(i) {
> +               cpu = cpu_logical_map(i);
> +               clk_prepare_enable(l2_pri_mux_clk);
> +               WARN(clk_prepare_enable(clks[cpu]),
> +                       "Unable to turn on CPU%d clock", cpu);
> +       }
> +
> +       /*
> +        * Force reinit of HFPLLs and muxes to overwrite any potential
> +        * incorrect configuration of HFPLLs and muxes by the bootloader.
> +        * While at it, also make sure the cores are running at known rates
> +        * and print the current rate.
> +        *
> +        * The clocks are set to aux clock rate first to make sure the
> +        * secondary mux is not sourcing off of QSB. The rate is then set to
> +        * two different rates to force a HFPLL reinit under all
> +        * circumstances.
> +        */
> +       cur_rate = clk_get_rate(l2_pri_mux_clk);
> +       aux_rate = 384000000;
> +       if (cur_rate == 1) {
> +               pr_info("L2 @ QSB rate. Forcing new rate.\n");
> +               cur_rate = aux_rate;
> +       }
> +       clk_set_rate(l2_pri_mux_clk, aux_rate);
> +       clk_set_rate(l2_pri_mux_clk, 2);
> +       clk_set_rate(l2_pri_mux_clk, cur_rate);
> +       pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
> +       for_each_possible_cpu(i) {
> +               cpu = cpu_logical_map(i);
> +               clk = clks[cpu];
> +               cur_rate = clk_get_rate(clk);
> +               if (cur_rate == 1) {
> +                       pr_info("CPU%d @ QSB rate. Forcing new rate.\n", i);
> +                       cur_rate = aux_rate;
> +               }
> +               clk_set_rate(clk, aux_rate);
> +               clk_set_rate(clk, 2);
> +               clk_set_rate(clk, cur_rate);
> +               pr_info("CPU%d @ %lu KHz\n", i, clk_get_rate(clk) / 1000);
> +       }
> +
> +       of_clk_add_provider(dev->of_node, krait_of_get, clks);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver krait_cc_driver = {
> +       .probe = krait_cc_probe,
> +       .driver = {
> +               .name = "clock-krait",
> +               .of_match_table = krait_cc_match_table,
> +               .owner = THIS_MODULE,
> +       },
> +};
> +module_platform_driver(krait_cc_driver);
> +
> +MODULE_DESCRIPTION("Krait CPU Clock Driver");
> +MODULE_LICENSE("GPL v2");
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
Thanks and Regards
Pramod

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq
  2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
                   ` (11 preceding siblings ...)
  2014-06-25  0:06 ` [RFC/PATCH 12/12] ARM: dts: qcom: Add necessary DT data for Krait cpufreq Stephen Boyd
@ 2014-07-15 10:33 ` pramod gurav
  12 siblings, 0 replies; 21+ messages in thread
From: pramod gurav @ 2014-07-15 10:33 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: linux-arm-msm, linux-arm-kernel, linux-kernel, Mike Turquette

Hi Stephen,

On Wed, Jun 25, 2014 at 5:36 AM, Stephen Boyd <sboyd@codeaurora.org> wrote:

>
> Stephen Boyd (12):
>   ARM: Add Krait L2 register accessor functions
>   clk: Add safe switch hook
>   clk: qcom: Add support for muxes, dividers, and mux dividers
>   clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
>   clk: qcom: Add HFPLL driver
>   clk: qcom: Add MSM8960'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 a cpufreq-krait based on cpufreq-cpu0
>   cpufreq: Add module to register cpufreq-krait device
>   ARM: dts: qcom: Add necessary DT data for Krait cpufreq
>

I have applied all these patches on 3.16-rc3. Compiled with below
minor change in drivers/cpufreq/Makefile to get cpufreq-krait.c
compiled:

diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 6b8917b..8afbea4 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE)       +=
cpufreq_conservative.o
 obj-$(CONFIG_CPU_FREQ_GOV_COMMON)              += cpufreq_governor.o

 obj-$(CONFIG_GENERIC_CPUFREQ_CPU0)     += cpufreq-cpu0.o
-obj-$(CONFIG_CPUFREQ_KRAIT)            += cpufreq-krait.o
+obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT)    += cpufreq-krait.o

 ##################################################################################
 # x86 drivers.

Tested cpufreq by enabling all governors in kernel. Tests done on
DB8074 board and Seen the frequency scaling as expected.

>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



-- 
Thanks and Regards
Pramod

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 02/12] clk: Add safe switch hook
       [not found]   ` <20140729060529.4906.66639@quantum>
@ 2014-07-29  8:59     ` Thomas Abraham
  2014-09-10  1:44       ` dbasehore .
  0 siblings, 1 reply; 21+ messages in thread
From: Thomas Abraham @ 2014-07-29  8:59 UTC (permalink / raw)
  To: Mike Turquette
  Cc: Stephen Boyd, linux-arm-msm, linux-kernel, linux-arm-kernel

On Tue, Jul 29, 2014 at 11:35 AM, Mike Turquette <mturquette@linaro.org> wrote:
> Quoting Stephen Boyd (2014-06-24 17:06:13)
>> 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.
>
> Adding Thomas to Cc.
>
> Thomas,
>
> Does this generic hook help you out at all with your CPU frequency
> transitions? I remember in my discussions with Chander K. that you have
> something like safe dividers, safe parents and safe voltages to take
> into account (but I might be misremembering some of that).

Hi Mike,

Samsung CPU clock implementation divides the alternate parent clock to
lower permissible speeds when the old clock rate of the primary parent
(PLL) is lower than the speed of the alternate parent. This step has
to be completed prior to hook gets called.

So though this hook takes care of re-parenting the clock, which is one
of the steps handled in Samsung CPU clock, it is not a alternative to
the current implementation of the Samsung CPU clock.

Thanks,
Thomas.

>
> Stephen,
>
> For reference, recent patches from Samsung to introduce cpu clocks[1]
> which I am not wild about, but the generic infrastructure isn't really
> there yet in the framework core to manage complex, pre-defined,
> multi-clock transitions gracefully.
>
> Regards,
> Mike
>
> [1] http://www.spinics.net/lists/arm-kernel/msg351137.html
>
>>
>> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
>> ---
>>  drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
>>  include/linux/clk-private.h  |  2 ++
>>  include/linux/clk-provider.h |  1 +
>>  3 files changed, 49 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
>> index 8b73edef151d..5e32fa55032b 100644
>> --- a/drivers/clk/clk.c
>> +++ b/drivers/clk/clk.c
>> @@ -1367,6 +1367,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;
>> @@ -1376,6 +1377,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);
>> @@ -1458,14 +1470,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;
>>         }
>>
>> @@ -1507,7 +1547,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) {
>> @@ -1527,9 +1568,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)
>> @@ -1603,6 +1641,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();
>>
>> diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
>> index efbf70b9fd84..f48684af4d8f 100644
>> --- a/include/linux/clk-private.h
>> +++ b/include/linux/clk-private.h
>> @@ -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;
>> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
>> index 0c287dbbb144..7485911df4b6 100644
>> --- a/include/linux/clk-provider.h
>> +++ b/include/linux/clk-provider.h
>> @@ -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,
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
>> hosted by The Linux Foundation
>>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC/PATCH 02/12] clk: Add safe switch hook
  2014-07-29  8:59     ` Thomas Abraham
@ 2014-09-10  1:44       ` dbasehore .
  0 siblings, 0 replies; 21+ messages in thread
From: dbasehore . @ 2014-09-10  1:44 UTC (permalink / raw)
  To: Thomas Abraham
  Cc: Mike Turquette, Stephen Boyd, linux-arm-msm, linux-kernel,
	linux-arm-kernel

Based on what Thomas pointed out and my own thoughts, as long as we don't
plan on changing the rate of the safe parent, but changing our own dividers, we
should be able to use set_rate_and_parent to solve the issue on Samsung's
SoCs. If we can think of a reason that we might need to change the rate of the
safe parent, this would have to be setup to make a recursive call to
clk_set_rate.

On Tue, Jul 29, 2014 at 1:59 AM, Thomas Abraham <ta.omasab@gmail.com> wrote:
>
> On Tue, Jul 29, 2014 at 11:35 AM, Mike Turquette <mturquette@linaro.org> wrote:
> > Quoting Stephen Boyd (2014-06-24 17:06:13)
> >> 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.
> >
> > Adding Thomas to Cc.
> >
> > Thomas,
> >
> > Does this generic hook help you out at all with your CPU frequency
> > transitions? I remember in my discussions with Chander K. that you have
> > something like safe dividers, safe parents and safe voltages to take
> > into account (but I might be misremembering some of that).
>
> Hi Mike,
>
> Samsung CPU clock implementation divides the alternate parent clock to
> lower permissible speeds when the old clock rate of the primary parent
> (PLL) is lower than the speed of the alternate parent. This step has
> to be completed prior to hook gets called.
>
> So though this hook takes care of re-parenting the clock, which is one
> of the steps handled in Samsung CPU clock, it is not a alternative to
> the current implementation of the Samsung CPU clock.
>
> Thanks,
> Thomas.
>
> >
> > Stephen,
> >
> > For reference, recent patches from Samsung to introduce cpu clocks[1]
> > which I am not wild about, but the generic infrastructure isn't really
> > there yet in the framework core to manage complex, pre-defined,
> > multi-clock transitions gracefully.
> >
> > Regards,
> > Mike
> >
> > [1] http://www.spinics.net/lists/arm-kernel/msg351137.html
> >
> >>
> >> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> >> ---
> >>  drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
> >>  include/linux/clk-private.h  |  2 ++
> >>  include/linux/clk-provider.h |  1 +
> >>  3 files changed, 49 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> >> index 8b73edef151d..5e32fa55032b 100644
> >> --- a/drivers/clk/clk.c
> >> +++ b/drivers/clk/clk.c
> >> @@ -1367,6 +1367,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;
> >> @@ -1376,6 +1377,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);
> >> @@ -1458,14 +1470,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;
> >> +       }
> >> +

It might be less confusing to break out setting the safe parent into
it's own function.

>
> >>         if (clk->notifier_count) {
> >> -               ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
> >> -               if (ret & NOTIFY_STOP_MASK)
> >> +               if (event != POST_RATE_CHANGE)

It seems like a bad merge happened here. This will prevent any
POST_RATE_CHANGE events from getting sent.

>
> >> +                       ret = __clk_notify(clk, event, clk->rate,
> >> +                                          clk->new_rate);
> >> +               if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
> >>                         fail_clk = clk;
> >>         }
> >>
> >> @@ -1507,7 +1547,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) {
> >> @@ -1527,9 +1568,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)
> >> @@ -1603,6 +1641,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();
> >>
> >> diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
> >> index efbf70b9fd84..f48684af4d8f 100644
> >> --- a/include/linux/clk-private.h
> >> +++ b/include/linux/clk-private.h
> >> @@ -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;
> >> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> >> index 0c287dbbb144..7485911df4b6 100644
> >> --- a/include/linux/clk-provider.h
> >> +++ b/include/linux/clk-provider.h
> >> @@ -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,
> >> --
> >> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> >> hosted by The Linux Foundation
> >>
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2014-09-10  1:44 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-25  0:06 [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 01/12] ARM: Add Krait L2 register accessor functions Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 02/12] clk: Add safe switch hook Stephen Boyd
     [not found]   ` <20140729060529.4906.66639@quantum>
2014-07-29  8:59     ` Thomas Abraham
2014-09-10  1:44       ` dbasehore .
2014-06-25  0:06 ` [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers Stephen Boyd
2014-07-14 10:06   ` pramod gurav
2014-06-25  0:06 ` [RFC/PATCH 04/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 05/12] clk: qcom: Add HFPLL driver Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs Stephen Boyd
2014-07-14 10:19   ` pramod gurav
2014-06-25  0:06 ` [RFC/PATCH 07/12] clk: qcom: Add support for Krait clocks Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 08/12] clk: qcom: Add KPSS ACC/GCC driver Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver Stephen Boyd
2014-07-14 10:25   ` pramod gurav
2014-06-25  0:06 ` [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 Stephen Boyd
2014-06-25  0:52   ` Stephen Boyd
2014-06-25  8:47   ` Viresh Kumar
2014-06-25  0:06 ` [RFC/PATCH 11/12] cpufreq: Add module to register cpufreq-krait device Stephen Boyd
2014-06-25  0:06 ` [RFC/PATCH 12/12] ARM: dts: qcom: Add necessary DT data for Krait cpufreq Stephen Boyd
2014-07-15 10:33 ` [RFC/PATCH 00/12] Krait clocks + Krait CPUfreq pramod gurav

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).