ipq806x: fix 3.18 support
[openwrt/staging/lynxis/omap.git] / target / linux / ipq806x / patches-3.18 / 142-clk-qcom-Add-Krait-clock-controller-driver.patch
diff --git a/target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-3.18/142-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644 (file)
index 0000000..98a09ac
--- /dev/null
@@ -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
+@@ -87,6 +87,14 @@ config KPSS_XCC
+         if you want to support CPU frequency scaling on devices such
+         as MSM8960, APQ8064, etc.
++config KRAITCC
++      tristate "Krait Clock Controller"
++      depends on COMMON_CLK_QCOM && ARM
++      select KRAIT_CLOCKS
++      help
++        Support for the Krait CPU clocks on Qualcomm devices.
++        Say Y if you want to support CPU frequency scaling.
++
+ config KRAIT_CLOCKS
+       bool
+       select KRAIT_L2_ACCESSORS
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -20,3 +20,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");