summaryrefslogtreecommitdiff
path: root/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch')
-rw-r--r--target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch435
1 files changed, 435 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644
index 0000000..59fc44f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch
@@ -0,0 +1,435 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver
+From: Stephen Boyd <sboyd@codeaurora.org>
+X-Patchwork-Id: 6063121
+Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org>
+To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
+Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
+ linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+ Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org>
+Date: Fri, 20 Mar 2015 23:45:30 -0700
+
+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.
+
+Cc: <devicetree@vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+
+---
+.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
+ drivers/clk/qcom/Kconfig | 8 +
+ drivers/clk/qcom/Makefile | 1 +
+ drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++
+ 4 files changed, 383 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
+ create mode 100644 drivers/clk/qcom/krait-cc.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
+@@ -0,0 +1,22 @@
++Krait Clock Controller
++
++PROPERTIES
++
++- compatible:
++ Usage: required
++ Value type: <string>
++ Definition: must be one of:
++ "qcom,krait-cc-v1"
++ "qcom,krait-cc-v2"
++
++- #clock-cells:
++ Usage: required
++ Value type: <u32>
++ Definition: must be 1
++
++Example:
++
++ kraitcc: clock-controller {
++ compatible = "qcom,krait-cc-v1";
++ #clock-cells = <1>;
++ };
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -114,6 +114,14 @@ config KPSS_XCC
+ if you want to support CPU frequency scaling on devices such
+ as MSM8960, APQ8064, etc.
+
++config KRAITCC
++ tristate "Krait Clock Controller"
++ depends on COMMON_CLK_QCOM && ARM
++ select KRAIT_CLOCKS
++ help
++ Support for the Krait CPU clocks on Qualcomm devices.
++ Say Y if you want to support CPU frequency scaling.
++
+ config KRAIT_CLOCKS
+ bool
+ select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8
+ 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
+--- /dev/null
++++ b/drivers/clk/qcom/krait-cc.c
+@@ -0,0 +1,352 @@
++/* Copyright (c) 2013-2015, 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 "clk-krait.h"
++
++static unsigned int sec_mux_map[] = {
++ 2,
++ 0,
++};
++
++static unsigned int pri_mux_map[] = {
++ 1,
++ 2,
++ 0,
++};
++
++static int
++krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
++{
++ struct krait_div2_clk *div;
++ struct clk_init_data init = {
++ .num_parents = 1,
++ .ops = &krait_div2_clk_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ const char *p_names[1];
++ struct clk *clk;
++
++ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
++ if (!div)
++ return -ENOMEM;
++
++ div->width = 2;
++ div->shift = 6;
++ div->lpl = 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 krait_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 = &krait_mux_clk_ops,
++ .flags = CLK_SET_RATE_PARENT,
++ };
++ struct clk *clk;
++
++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++ if (!mux)
++ return -ENOMEM;
++
++ mux->offset = offset;
++ mux->lpl = id >= 0;
++ mux->has_safe_parent = true;
++ mux->safe_sel = 2;
++ mux->mask = 0x3;
++ mux->shift = 2;
++ mux->parent_map = sec_mux_map;
++ mux->hw.init = &init;
++
++ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++ if (!init.name)
++ return -ENOMEM;
++
++ if (unique_aux) {
++ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
++ if (!sec_mux_list[0]) {
++ clk = ERR_PTR(-ENOMEM);
++ goto err_aux;
++ }
++ }
++
++ clk = devm_clk_register(dev, &mux->hw);
++
++ if (unique_aux)
++ kfree(sec_mux_list[0]);
++err_aux:
++ kfree(init.name);
++ return PTR_ERR_OR_ZERO(clk);
++}
++
++static struct clk *
++krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset)
++{
++ struct krait_mux_clk *mux;
++ const char *p_names[3];
++ struct clk_init_data init = {
++ .parent_names = p_names,
++ .num_parents = ARRAY_SIZE(p_names),
++ .ops = &krait_mux_clk_ops,
++ .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->mask = 0x3;
++ mux->shift = 0;
++ mux->offset = offset;
++ mux->lpl = 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;
++ void *p = NULL;
++ const char *s;
++ struct clk *clk;
++
++ if (id >= 0) {
++ offset = 0x4501 + (0x1000 * id);
++ s = p = 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:
++ kfree(p);
++ 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 cpu;
++ struct clk *clk;
++ struct clk **clks;
++ struct clk *l2_pri_mux_clk;
++
++ id = of_match_device(krait_cc_match_table, 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 = clk_register_fixed_factor(dev, "acpu_aux",
++ "gpll0_vote", 0, 1, 2);
++ 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(cpu) {
++ 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(cpu) {
++ 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(cpu) {
++ clk = clks[cpu];
++ cur_rate = clk_get_rate(clk);
++ if (cur_rate == 1) {
++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
++ 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", cpu, 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 = "krait-cc",
++ .of_match_table = krait_cc_match_table,
++ },
++};
++module_platform_driver(krait_cc_driver);
++
++MODULE_DESCRIPTION("Krait CPU Clock Driver");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:krait-cc");