Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754417AbaGNKZi (ORCPT ); Mon, 14 Jul 2014 06:25:38 -0400 Received: from mail-lb0-f180.google.com ([209.85.217.180]:62238 "EHLO mail-lb0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751718AbaGNKZ2 (ORCPT ); Mon, 14 Jul 2014 06:25:28 -0400 MIME-Version: 1.0 In-Reply-To: <1403654783-7176-10-git-send-email-sboyd@codeaurora.org> References: <1403654783-7176-1-git-send-email-sboyd@codeaurora.org> <1403654783-7176-10-git-send-email-sboyd@codeaurora.org> Date: Mon, 14 Jul 2014 15:55:26 +0530 Message-ID: Subject: Re: [RFC/PATCH 09/12] clk: qcom: Add Krait clock controller driver From: pramod gurav To: Stephen Boyd Cc: linux-arm-msm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Mike Turquette Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, Jun 25, 2014 at 5:36 AM, Stephen Boyd wrote: > 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 > --- > 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 > > diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig > index 247f042dc110..6f835e406930 100644 > --- a/drivers/clk/qcom/Kconfig > +++ b/drivers/clk/qcom/Kconfig > @@ -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. > diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile > index bb4666733bd9..e8c7f31916da 100644 > --- a/drivers/clk/qcom/Makefile > +++ b/drivers/clk/qcom/Makefile > @@ -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 > diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c > new file mode 100644 > index 000000000000..4ca9589acfca > --- /dev/null > +++ b/drivers/clk/qcom/krait-cc.c > @@ -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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#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; > + } > + pr_info("Parents are %s %s\n", sec_mux_list[0], sec_mux_list[1]); line over 80 characters. > + } > + > + 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'. > +{ > + 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@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ -- Thanks and Regards Pramod -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/