Skip to contentSkip to conversation

Krait clocks + Krait CPUfreq

PATCH Superseded Stephen Boyd 6 participants 22 messages Jun 25, 2014 – Sep 10, 2014

Subsystems (6)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover Krait clocks + Krait CPUfreq

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
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#1 lore raw

PATCH 1/12 ARM: Add Krait L2 register accessor functions

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

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

58 lines not on this page · raw message

arch/arm/include/asm/krait-l2-accessors.h new file +20 -0
new file mode 100644
index 000000000000..48fe5527bc01
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
+
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
+extern u32 krait_get_l2_indirect_reg(u32 addr);
+
+#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#2 lore raw

PATCH 2/12 clk: Add safe switch hook

In reply to Stephen Boyd

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

---
 drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-private.h  |  2 ++
 include/linux/clk-provider.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)
drivers/clk/clk.c +46 -7
index 8b73edef151d..5e32fa55032b 100644
@@ -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();
 
include/linux/clk-private.h +2 -0
index efbf70b9fd84..f48684af4d8f 100644
@@ -38,8 +38,10 @@ struct clk {
 	struct clk		**parents;
 	u8			num_parents;
 	u8			new_parent_index;
+	u8			safe_parent_index;
 	unsigned long		rate;
 	unsigned long		new_rate;
+	struct clk		*safe_parent;
 	struct clk		*new_parent;
 	struct clk		*new_child;
 	unsigned long		flags;
include/linux/clk-provider.h +1 -0
index 0c287dbbb144..7485911df4b6 100644
@@ -170,6 +170,7 @@ struct clk_ops {
 					struct clk **best_parent_clk);
 	int		(*set_parent)(struct clk_hw *hw, u8 index);
 	u8		(*get_parent)(struct clk_hw *hw);
+	struct clk	*(*get_safe_parent)(struct clk_hw *hw);
 	int		(*set_rate)(struct clk_hw *hw, unsigned long rate,
 				    unsigned long parent_rate);
 	int		(*set_rate_and_parent)(struct clk_hw *hw,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#3 lore raw

PATCH 2/12 clk: Add safe switch hook

Quoting Stephen Boyd (2014-06-24 17:06:13)

Quoted 8 lines

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).

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

Quoted 143 lines
---
 drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-private.h  |  2 ++
 include/linux/clk-provider.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)
drivers/clk/clk.c +46 -7
index 8b73edef151d..5e32fa55032b 100644
@@ -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();
 
include/linux/clk-private.h +2 -0
index efbf70b9fd84..f48684af4d8f 100644
@@ -38,8 +38,10 @@ struct clk {
        struct clk              **parents;
        u8                      num_parents;
        u8                      new_parent_index;
+       u8                      safe_parent_index;
        unsigned long           rate;
        unsigned long           new_rate;
+       struct clk              *safe_parent;
        struct clk              *new_parent;
        struct clk              *new_child;
        unsigned long           flags;
include/linux/clk-provider.h +1 -0
index 0c287dbbb144..7485911df4b6 100644
@@ -170,6 +170,7 @@ struct clk_ops {
                                        struct clk **best_parent_clk);
        int             (*set_parent)(struct clk_hw *hw, u8 index);
        u8              (*get_parent)(struct clk_hw *hw);
+       struct clk      *(*get_safe_parent)(struct clk_hw *hw);
        int             (*set_rate)(struct clk_hw *hw, unsigned long rate,
                                    unsigned long parent_rate);
        int             (*set_rate_and_parent)(struct clk_hw *hw,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#4 lore raw

Re: [RFC/PATCH 02/12] clk: Add safe switch hook

Quoted 10 lines · Mike Turquette

Quoting Stephen Boyd (2014-06-24 17:06:13)

Quoted 8 lines

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.

Quoted 8 lines

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.

Quoted 161 lines

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

Quoted 140 lines
---
 drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-private.h  |  2 ++
 include/linux/clk-provider.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)
drivers/clk/clk.c +46 -7
index 8b73edef151d..5e32fa55032b 100644
@@ -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();
 
include/linux/clk-private.h +2 -0
index efbf70b9fd84..f48684af4d8f 100644
@@ -38,8 +38,10 @@ struct clk {
        struct clk              **parents;
        u8                      num_parents;
        u8                      new_parent_index;
+       u8                      safe_parent_index;
        unsigned long           rate;
        unsigned long           new_rate;
+       struct clk              *safe_parent;
        struct clk              *new_parent;
        struct clk              *new_child;
        unsigned long           flags;
include/linux/clk-provider.h +1 -1
index 0c287dbbb144..7485911df4b6 100644
@@ -170,6 +170,7 @@ struct clk_ops {
                                        struct clk **best_parent_clk);
        int             (*set_parent)(struct clk_hw *hw, u8 index);
        u8              (*get_parent)(struct clk_hw *hw);
+       struct clk      *(*get_safe_parent)(struct clk_hw *hw);
        int             (*set_rate)(struct clk_hw *hw, unsigned long rate,
                                    unsigned long parent_rate);
        int             (*set_rate_and_parent)(struct clk_hw *hw,
--
Quoted 3 lines

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 at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#5 lore raw

Re: [RFC/PATCH 02/12] clk: Add safe switch hook

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.

Quoted 115 lines · Thomas Abraham
Quoted 10 lines · Mike Turquette

Quoting Stephen Boyd (2014-06-24 17:06:13)

Quoted 8 lines

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.

Quoted 8 lines

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.

Quoted 80 lines

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

Quoted 67 lines
---
 drivers/clk/clk.c            | 53 ++++++++++++++++++++++++++++++++++++++------
 include/linux/clk-private.h  |  2 ++
 include/linux/clk-provider.h |  1 +
 3 files changed, 49 insertions(+), 7 deletions(-)
drivers/clk/clk.c +12 -0
index 8b73edef151d..5e32fa55032b 100644
@@ -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);
drivers/clk/clk.c +19 -1
@@ -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,
Quoted 8 lines
Quoted 8 lines

drivers/clk/clk.c out: · line 1503

Quoted 8 lines
drivers/clk/clk.c +8 -0
+                                               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.

Quoted 5 lines
Quoted 5 lines

drivers/clk/clk.c out: · line 1505

Quoted 5 lines
drivers/clk/clk.c +1 -2
        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.

Quoted 78 lines
Quoted 63 lines
Quoted 63 lines
drivers/clk/clk.c +6 -4
+                       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();
 
include/linux/clk-private.h +2 -0
index efbf70b9fd84..f48684af4d8f 100644
@@ -38,8 +38,10 @@ struct clk {
        struct clk              **parents;
        u8                      num_parents;
        u8                      new_parent_index;
+       u8                      safe_parent_index;
        unsigned long           rate;
        unsigned long           new_rate;
+       struct clk              *safe_parent;
        struct clk              *new_parent;
        struct clk              *new_child;
        unsigned long           flags;
include/linux/clk-provider.h +1 -1
index 0c287dbbb144..7485911df4b6 100644
@@ -170,6 +170,7 @@ struct clk_ops {
                                        struct clk **best_parent_clk);
        int             (*set_parent)(struct clk_hw *hw, u8 index);
        u8              (*get_parent)(struct clk_hw *hw);
+       struct clk      *(*get_safe_parent)(struct clk_hw *hw);
        int             (*set_rate)(struct clk_hw *hw, unsigned long rate,
                                    unsigned long parent_rate);
        int             (*set_rate_and_parent)(struct clk_hw *hw,
--
Quoted 8 lines
Quoted 3 lines

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 at 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 at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#6 lore raw

PATCH 3/12 clk: qcom: Add support for muxes, dividers, and mux dividers

In reply to Stephen Boyd

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 <[email protected]>
---
 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
drivers/clk/qcom/Makefile +1 -0
index 689e05bf4f95..8684ff004be6 100644
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
 clk-qcom-y += clk-rcg.o
 clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.o
+clk-qcom-y += clk-generic.o
 clk-qcom-y += reset.o
 
 obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
drivers/clk/qcom/clk-generic.c new file +405 -0

405 lines not on this page · raw message

include/linux/clk/msm-clk-generic.h new file +208 -0

208 lines not on this page · raw message

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

Re: [RFC/PATCH 03/12] clk: qcom: Add support for muxes, dividers, and mux dividers

Hi Stephen,

Quoted 80 lines · Stephen Boyd

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 <[email protected]>
---
 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
drivers/clk/qcom/Makefile +1 -0
index 689e05bf4f95..8684ff004be6 100644
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o
 clk-qcom-y += clk-rcg.o
 clk-qcom-y += clk-rcg2.o
 clk-qcom-y += clk-branch.o
+clk-qcom-y += clk-generic.o
 clk-qcom-y += reset.o
 
 obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
drivers/clk/qcom/clk-generic.c new file +45 -0
new file mode 100644
index 000000000000..a0d778ba7394
@@ -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;

drivers/clk/qcom/clk-generic.c line 55

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

drivers/clk/qcom/clk-generic.c line 63

Quoted 8 lines
drivers/clk/qcom/clk-generic.c new file +8 -0
+       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.

Quoted 117 lines
drivers/clk/qcom/clk-generic.c new file +117 -0
+       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;

drivers/clk/qcom/clk-generic.c line 190

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

drivers/clk/qcom/clk-generic.c line 198

Quoted 8 lines
drivers/clk/qcom/clk-generic.c new file +8 -0
+       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.

Quoted 148 lines
drivers/clk/qcom/clk-generic.c new file +148 -0
+       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);
+}
+

drivers/clk/qcom/clk-generic.c line 356

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

drivers/clk/qcom/clk-generic.c line 363

Quoted 7 lines
drivers/clk/qcom/clk-generic.c new file +7 -0
+       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.

Quoted 83 lines
drivers/clk/qcom/clk-generic.c new file +42 -0
+       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);
include/linux/clk/msm-clk-generic.h new file +35 -0
new file mode 100644
index 000000000000..cee38636fe0f
@@ -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);

include/linux/clk/msm-clk-generic.h line 45

Quoted 10 lines
include/linux/clk/msm-clk-generic.h new file +10 -0
+
+       /* 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".

include/linux/clk/msm-clk-generic.h line 51

Quoted 6 lines
include/linux/clk/msm-clk-generic.h new file +6 -0
+       bool            has_safe_parent;
+       u8              safe_sel;
+       const struct clk_mux_ops *ops;
+
+       /* Fields not used by helper function. */
+       void __iomem    *base;

Same here.

Quoted 40 lines
include/linux/clk/msm-clk-generic.h new file +40 -0
+       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;

include/linux/clk/msm-clk-generic.h line 101

Quoted 10 lines
include/linux/clk/msm-clk-generic.h new file +10 -0
+};
+
+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.

Quoted 116 lines
include/linux/clk/msm-clk-generic.h new file +107 -1
+       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 at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 2 lines
Thanks and Regards
Pramod
#8 lore raw

PATCH 4/12 clk: qcom: Add support for High-Frequency PLLs (HFPLLs)

In reply to Stephen Boyd

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

---
 drivers/clk/qcom/Makefile    |   1 +
 drivers/clk/qcom/clk-hfpll.c | 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
drivers/clk/qcom/Makefile +1 -0
index 8684ff004be6..1a0b561bd105 100644
@@ -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
drivers/clk/qcom/clk-hfpll.c new file +260 -0

260 lines not on this page · raw message

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

54 lines not on this page · raw message

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

PATCH 5/12 clk: qcom: Add HFPLL driver

In reply to Stephen Boyd

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

---
 drivers/clk/qcom/Kconfig  |   8 ++++
 drivers/clk/qcom/Makefile |   1 +
 drivers/clk/qcom/hfpll.c  | 110 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+)
 create mode 100644 drivers/clk/qcom/hfpll.c
drivers/clk/qcom/Kconfig +8 -0
index 7f696b7d4422..425bcfe3c39a 100644
@@ -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.
drivers/clk/qcom/Makefile +1 -0
index 1a0b561bd105..b733adbbad5a 100644
@@ -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
drivers/clk/qcom/hfpll.c new file +110 -0

110 lines not on this page · raw message

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

PATCH 6/12 clk: qcom: Add MSM8960's HFPLLs

In reply to Stephen Boyd

Describe the HFPLLs present on MSM8960 devices.

---
 drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
drivers/clk/qcom/gcc-msm8960.c +82 -0
index f4ffd91901f8..d04fc99541e4 100644
@@ -30,6 +30,7 @@
 #include "clk-pll.h"
 #include "clk-rcg.h"
 #include "clk-branch.h"
+#include "clk-hfpll.h"
 #include "reset.h"
 
 static struct clk_pll pll3 = {
@@ -75,6 +76,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[] = {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#11 lore raw

Re: [RFC/PATCH 06/12] clk: qcom: Add MSM8960's HFPLLs

Hi Stephen,

Quoted 22 lines · Stephen Boyd

Describe the HFPLLs present on MSM8960 devices.

---
 drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
drivers/clk/qcom/gcc-msm8960.c +1 -0
index f4ffd91901f8..d04fc99541e4 100644
@@ -30,6 +30,7 @@
 #include "clk-pll.h"
 #include "clk-rcg.h"
 #include "clk-branch.h"
+#include "clk-hfpll.h"
 #include "reset.h"
 
 static struct clk_pll pll3 = {
@@ -75,6 +76,84 @@ static struct clk_regmap pll8_vote = {
        },
 };

drivers/clk/qcom/gcc-msm8960.c static struct clk_regmap pll8_vote = { · line 87

Quoted 10 lines
drivers/clk/qcom/gcc-msm8960.c +9 -0
 
+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.

Quoted 17 lines
drivers/clk/qcom/gcc-msm8960.c +17 -0
+        .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),
+};
+

drivers/clk/qcom/gcc-msm8960.c static struct clk_regmap pll8_vote = { · line 114

Quoted 10 lines
drivers/clk/qcom/gcc-msm8960.c +10 -0
+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.

Quoted 15 lines
drivers/clk/qcom/gcc-msm8960.c +15 -0
+       .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),
+};

drivers/clk/qcom/gcc-msm8960.c static struct clk_regmap pll8_vote = { · line 139

Quoted 10 lines
drivers/clk/qcom/gcc-msm8960.c +10 -0
+
+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.

Quoted 39 lines
drivers/clk/qcom/gcc-msm8960.c +20 -1
+        .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 at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 2 lines
Thanks and Regards
Pramod
#12 lore raw

PATCH 7/12 clk: qcom: Add support for Krait clocks

In reply to Stephen Boyd

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

---
 drivers/clk/qcom/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
drivers/clk/qcom/Makefile +1 -0
index b733adbbad5a..b982d271d7db 100644
@@ -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
 
drivers/clk/qcom/clk-krait.c new file +121 -0

121 lines not on this page · raw message

drivers/clk/qcom/clk-krait.h new file +22 -0
new file mode 100644
index 000000000000..9c3eb38be542
@@ -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
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
#13 lore raw

PATCH 8/12 clk: qcom: Add KPSS ACC/GCC driver

In reply to Stephen Boyd

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

---
 drivers/clk/qcom/Kconfig    |   8 +++
 drivers/clk/qcom/Makefile   |   1 +
 drivers/clk/qcom/kpss-xcc.c | 115 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 drivers/clk/qcom/kpss-xcc.c
drivers/clk/qcom/Kconfig +8 -0
index 425bcfe3c39a..247f042dc110 100644
@@ -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.
drivers/clk/qcom/Makefile +1 -0
index b982d271d7db..bb4666733bd9 100644
@@ -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
drivers/clk/qcom/kpss-xcc.c new file +115 -0

115 lines not on this page · raw message

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

PATCH 9/12 clk: qcom: Add Krait clock controller driver

In reply to Stephen Boyd

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

---
 drivers/clk/qcom/Kconfig    |   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
drivers/clk/qcom/Kconfig +9 -0
index 247f042dc110..6f835e406930 100644
@@ -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.
drivers/clk/qcom/Makefile +1 -0
index bb4666733bd9..e8c7f31916da 100644
@@ -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
drivers/clk/qcom/krait-cc.c new file +366 -0

366 lines not on this page · raw message

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

Re: [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver

Quoted 163 lines · Stephen Boyd

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

---
 drivers/clk/qcom/Kconfig    |   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
drivers/clk/qcom/Kconfig +9 -0
index 247f042dc110..6f835e406930 100644
@@ -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.
drivers/clk/qcom/Makefile +1 -0
index bb4666733bd9..e8c7f31916da 100644
@@ -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
drivers/clk/qcom/krait-cc.c new file +117 -0
new file mode 100644
index 000000000000..4ca9589acfca
@@ -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);

drivers/clk/qcom/krait-cc.c line 127

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

drivers/clk/qcom/krait-cc.c line 140

Quoted 13 lines
drivers/clk/qcom/krait-cc.c new file +13 -0
+       }
+
+       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'.

Quoted 235 lines
drivers/clk/qcom/krait-cc.c new file +226 -1
+{
+       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 at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 2 lines
Thanks and Regards
Pramod
#16 lore raw

PATCH 10/12 cpufreq: Add a cpufreq-krait based on cpufreq-cpu0

In reply to Stephen Boyd

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?

---
 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
drivers/cpufreq/Kconfig +13 -0
index ffe350f86bca..e9826c85a777 100644
@@ -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"
drivers/cpufreq/Makefile +1 -0
index 738c8b7b17dc..f5c9f68dcc0f 100644
@@ -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.
drivers/cpufreq/cpufreq-krait.c new file +190 -0

190 lines not on this page · raw message

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

Re: [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0

drivers/cpufreq/Makefile obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o · line 18

Quoted 10 lines · Stephen Boyd
drivers/cpufreq/Makefile +1 -0
index 738c8b7b17dc..f5c9f68dcc0f 100644
@@ -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.

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

Re: [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0

In reply to Stephen Boyd
Quoted 4 lines · Stephen Boyd

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 :(

Quoted 1 line

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..

#19 lore raw

PATCH 11/12 cpufreq: Add module to register cpufreq-krait device

In reply to Stephen Boyd

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

---
 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
drivers/cpufreq/Kconfig.arm +8 -0
index ebac67115009..cbc5643b3ecf 100644
@@ -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
drivers/cpufreq/Makefile +1 -0
index f5c9f68dcc0f..6b8917ba4b6e 100644
@@ -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
drivers/cpufreq/qcom-cpufreq.c new file +48 -0

48 lines not on this page · raw message

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

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

In reply to Stephen Boyd

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

---
 arch/arm/boot/dts/qcom-msm8960.dtsi |  49 +++++++++++++++++
 arch/arm/boot/dts/qcom-msm8974.dtsi | 106 ++++++++++++++++++++++++++++++++++--
 2 files changed, 151 insertions(+), 4 deletions(-)
arch/arm/boot/dts/qcom-msm8960.dtsi +49 -0
index 5303e53e34dc..94eeca22986f 100644
@@ -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 at 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 at 2088000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu0_aux";
 		};
 
 		acc1: clock-controller at 2098000 {
 			compatible = "qcom,kpss-acc-v1";
 			reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+			clock-output-names = "acpu1_aux";
+		};
+
+		l2cc: clock-controller at 2011000 {
+			compatible = "qcom,kpss-gcc";
+			reg = <0x2011000 0x1000>;
+			clock-output-names = "acpu_l2_aux";
 		};
 
 		saw0: regulator at 2089000 {
arch/arm/boot/dts/qcom-msm8974.dtsi +102 -4

154 lines not on this page · raw message

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

Hi Stephen,

Quoted 15 lines · Stephen Boyd

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:

drivers/cpufreq/Makefile +0 -0
index 6b8917b..8afbea4 100644
@@ -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.

Quoted 10 lines

--
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 at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 2 lines
Thanks and Regards
Pramod