ipq806x: Add support for IPQ806x chip family
[openwrt/svn-archive/archive.git] / target / linux / ipq806x / patches / 0171-clk-qcom-Add-Krait-clock-controller-driver.patch
diff --git a/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches/0171-clk-qcom-Add-Krait-clock-controller-driver.patch
new file mode 100644 (file)
index 0000000..8fa83df
--- /dev/null
@@ -0,0 +1,420 @@
+From 6912e27d97ba5671e8c2434bed0ebd23fde5e13d Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd@codeaurora.org>
+Date: Wed, 18 Jun 2014 14:29:29 -0700
+Subject: [PATCH 171/182] clk: qcom: Add Krait clock controller driver
+
+The Krait CPU clocks are made up of a primary mux and secondary
+mux for each CPU and the L2, controlled via cp15 accessors. For
+Kraits within KPSSv1 each secondary mux accepts a different aux
+source, but on KPSSv2 each secondary mux accepts the same aux
+source.
+
+Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ drivers/clk/qcom/Kconfig    |    8 +
+ drivers/clk/qcom/Makefile   |    1 +
+ drivers/clk/qcom/krait-cc.c |  364 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 373 insertions(+)
+ create mode 100644 drivers/clk/qcom/krait-cc.c
+
+diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
+index e9e5360..7418108 100644
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -70,6 +70,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
+diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
+index 29b2a45..1b88abe 100644
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -19,3 +19,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
+ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+ obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
++obj-$(CONFIG_KRAITCC) += krait-cc.o
+diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
+new file mode 100644
+index 0000000..90985ea
+--- /dev/null
++++ b/drivers/clk/qcom/krait-cc.c
+@@ -0,0 +1,364 @@
++/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/slab.h>
++
++#include <asm/smp_plat.h>
++
++#include "clk-krait.h"
++
++DEFINE_FIXED_DIV_CLK(acpu_aux, 2, "gpll0_vote");
++
++static u8 sec_mux_map[] = {
++      2,
++      0,
++};
++
++static u8 pri_mux_map[] = {
++      1,
++      2,
++      0,
++};
++
++static int
++krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
++{
++      struct div_clk *div;
++      struct clk_init_data init = {
++              .num_parents = 1,
++              .ops = &clk_ops_div,
++              .flags = CLK_SET_RATE_PARENT,
++      };
++      const char *p_names[1];
++      struct clk *clk;
++
++      div = devm_kzalloc(dev, sizeof(*dev), GFP_KERNEL);
++      if (!div)
++              return -ENOMEM;
++
++      div->data.div = 2;
++      div->data.min_div = 2;
++      div->data.max_div = 2;
++      div->ops = &clk_div_ops_kpss_div2;
++      div->mask = 0x3;
++      div->shift = 6;
++      div->priv = (void *)(id >= 0);
++      div->offset = offset;
++      div->hw.init = &init;
++
++      init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
++      if (!init.name)
++              return -ENOMEM;
++
++      init.parent_names = p_names;
++      p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
++      if (!p_names[0]) {
++              kfree(init.name);
++              return -ENOMEM;
++      }
++
++      clk = devm_clk_register(dev, &div->hw);
++      kfree(p_names[0]);
++      kfree(init.name);
++
++      return PTR_ERR_OR_ZERO(clk);
++}
++
++static int
++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
++                bool unique_aux)
++{
++      struct mux_clk *mux;
++      static const char *sec_mux_list[] = {
++              "acpu_aux",
++              "qsb",
++      };
++      struct clk_init_data init = {
++              .parent_names = sec_mux_list,
++              .num_parents = ARRAY_SIZE(sec_mux_list),
++              .ops = &clk_ops_gen_mux,
++              .flags = CLK_SET_RATE_PARENT,
++      };
++      struct clk *clk;
++
++      mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++      if (!mux)
++              return -ENOMEM;
++
++      mux->offset = offset;
++      mux->priv = (void *)(id >= 0);
++      mux->has_safe_parent = true;
++      mux->safe_sel = 2;
++      mux->ops = &clk_mux_ops_kpss;
++      mux->mask = 0x3;
++      mux->shift = 2;
++      mux->parent_map = sec_mux_map;
++      mux->hw.init = &init;
++
++      init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++      if (!init.name)
++              return -ENOMEM;
++
++      if (unique_aux) {
++              sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
++              if (!sec_mux_list[0]) {
++                      clk = ERR_PTR(-ENOMEM);
++                      goto err_aux;
++              }
++      }
++
++      clk = devm_clk_register(dev, &mux->hw);
++
++      if (unique_aux)
++              kfree(sec_mux_list[0]);
++err_aux:
++      kfree(init.name);
++      return PTR_ERR_OR_ZERO(clk);
++}
++
++static struct clk *
++krait_add_pri_mux(struct device *dev, int id, const char * s, unsigned offset)
++{
++      struct mux_clk *mux;
++      const char *p_names[3];
++      struct clk_init_data init = {
++              .parent_names = p_names,
++              .num_parents = ARRAY_SIZE(p_names),
++              .ops = &clk_ops_gen_mux,
++              .flags = CLK_SET_RATE_PARENT,
++      };
++      struct clk *clk;
++
++      mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
++      if (!mux)
++              return ERR_PTR(-ENOMEM);
++
++      mux->has_safe_parent = true;
++      mux->safe_sel = 0;
++      mux->ops = &clk_mux_ops_kpss;
++      mux->mask = 0x3;
++      mux->shift = 0;
++      mux->offset = offset;
++      mux->priv = (void *)(id >= 0);
++      mux->parent_map = pri_mux_map;
++      mux->hw.init = &init;
++
++      init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
++      if (!init.name)
++              return ERR_PTR(-ENOMEM);
++
++      p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
++      if (!p_names[0]) {
++              clk = ERR_PTR(-ENOMEM);
++              goto err_p0;
++      }
++
++      p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
++      if (!p_names[1]) {
++              clk = ERR_PTR(-ENOMEM);
++              goto err_p1;
++      }
++
++      p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
++      if (!p_names[2]) {
++              clk = ERR_PTR(-ENOMEM);
++              goto err_p2;
++      }
++
++      clk = devm_clk_register(dev, &mux->hw);
++
++      kfree(p_names[2]);
++err_p2:
++      kfree(p_names[1]);
++err_p1:
++      kfree(p_names[0]);
++err_p0:
++      kfree(init.name);
++      return clk;
++}
++
++/* id < 0 for L2, otherwise id == physical CPU number */
++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
++{
++      int ret;
++      unsigned offset;
++      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 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");
+-- 
+1.7.10.4
+