Krait clocks + Krait CPUfreq
Subsystems (6)
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
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
@@ -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
@@ -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
@@ -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
PATCH 2/12 clk: Add safe switch hook
In reply to Stephen BoydSometimes 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
@@ -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
@@ -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
@@ -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
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
Signed-off-by Stephen Boyd <[email protected]>--- 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@@ -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@@ -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@@ -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
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
Signed-off-by Stephen Boyd <[email protected]>--- 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@@ -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@@ -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@@ -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
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
Signed-off-by Stephen Boyd <[email protected]>--- 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@@ -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
-2if (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@@ -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@@ -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/
PATCH 3/12 clk: qcom: Add support for muxes, dividers, and mux dividers
In reply to Stephen BoydThe 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.
--- 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
@@ -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
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_opsthat 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]>Signed-off-by Stephen Boyd <[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.hdrivers/clk/qcom/Makefile +1
-0@@ -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.odrivers/clk/qcom/clk-generic.c new file +45
-0@@ -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@@ -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
PATCH 4/12 clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
In reply to Stephen BoydHFPLLs 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
@@ -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
PATCH 5/12 clk: qcom: Add HFPLL driver
In reply to Stephen BoydOn 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
@@ -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
@@ -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
PATCH 6/12 clk: qcom: Add MSM8960's HFPLLs
In reply to Stephen BoydDescribe 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
@@ -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
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.
Signed-off-by Stephen Boyd <[email protected]>--- drivers/clk/qcom/gcc-msm8960.c | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+)drivers/clk/qcom/gcc-msm8960.c +1
-0@@ -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
PATCH 7/12 clk: qcom: Add support for Krait clocks
In reply to Stephen BoydThe 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
@@ -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
@@ -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
PATCH 8/12 clk: qcom: Add KPSS ACC/GCC driver
In reply to Stephen BoydThe 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
@@ -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
@@ -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
PATCH 9/12 clk: qcom: Add Krait clock controller driver
In reply to Stephen BoydThe 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
@@ -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
@@ -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
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.
Signed-off-by Stephen Boyd <[email protected]>--- 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.cdrivers/clk/qcom/Kconfig +9
-0@@ -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@@ -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.odrivers/clk/qcom/krait-cc.c new file +117
-0@@ -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
PATCH 10/12 cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
In reply to Stephen BoydKrait 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
@@ -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
@@ -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
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@@ -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
Viresh Kumar
maintainerRe: [RFC/PATCH 10/12] cpufreq: Add a cpufreq-krait based on cpufreq-cpu0
In reply to Stephen BoydQuoted 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..
PATCH 11/12 cpufreq: Add module to register cpufreq-krait device
In reply to Stephen BoydRegister 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
@@ -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
@@ -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
PATCH 12/12 ARM: dts: qcom: Add necessary DT data for Krait cpufreq
In reply to Stephen BoydAdd 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
@@ -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
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
@@ -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