All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rajendra Nayak <rnayak@codeaurora.org>
To: sboyd@codeaurora.org, mturquette@baylibre.com
Cc: linux-clk@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	linux-kernel@vger.kernel.org, tdas@codeaurora.org,
	Rajendra Nayak <rnayak@codeaurora.org>
Subject: [RFC v3 11/11] clk: qcom: Add basic CPU clock driver for msm8996
Date: Thu, 29 Sep 2016 14:05:52 +0530	[thread overview]
Message-ID: <1475138152-859-12-git-send-email-rnayak@codeaurora.org> (raw)
In-Reply-To: <1475138152-859-1-git-send-email-rnayak@codeaurora.org>

This is a skeletal CPU clock driver, which adds support for the
CPU SS primary as well as secondary/alternate PLLs, and the
primary/secondary muxes.

This still has support missing for
1. CBF PLL and mux
2. ACD
3. APM

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
---
 drivers/clk/qcom/Makefile       |   1 +
 drivers/clk/qcom/clk-cpu-8996.c | 408 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 409 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-cpu-8996.c

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 2a25f4e..407668d 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -10,6 +10,7 @@ clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
 clk-qcom-y += reset.o
+clk-qcom-y += clk-cpu-8996.o
 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
 
 obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
new file mode 100644
index 0000000..e690544
--- /dev/null
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2016, 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/cpu.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-pll.h"
+#include "clk-regmap.h"
+#include "clk-regmap-mux.h"
+
+#define VCO(a, b, c) { \
+	.val = a,\
+	.min_freq = b,\
+	.max_freq = c,\
+}
+
+static const struct alpha_pll_config hfpll_config = {
+	.l = 60,
+	.config_ctl_val = 0x200D4828,
+	.config_ctl_hi_val = 0x006,
+	.pre_div_mask = BIT(12),
+	.post_div_mask = 0x3 << 8,
+	.main_output_mask = BIT(0),
+	.early_output_mask = BIT(3),
+};
+
+static struct clk_alpha_pll perfcl_pll = {
+	.offset = 0x80000,
+	.min_rate = 600000000,
+	.max_rate = 3000000000,
+	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
+			| SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "perfcl_pll",
+		.parent_names = (const char *[]){ "xo_board" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static struct clk_alpha_pll pwrcl_pll = {
+	.offset = 0x0,
+	.min_rate = 600000000,
+	.max_rate = 3000000000,
+	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
+			| SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "pwrcl_pll",
+		.parent_names = (const char *[]){ "xo_board" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static const struct pll_vco alt_pll_vco_modes[] = {
+	VCO(3,  250000000,  500000000),
+	VCO(2,  500000000,  750000000),
+	VCO(1,  750000000, 1000000000),
+	VCO(0, 1000000000, 2150400000),
+};
+
+static const struct alpha_pll_config altpll_config = {
+	.l = 16,
+	.vco_val = 0x3 << 20,
+	.vco_mask = 0x3 << 20,
+	.config_ctl_val = 0x4001051B,
+	.post_div_mask = 0x3 << 8,
+	.post_div_val = 0x1,
+	.main_output_mask = BIT(0),
+	.early_output_mask = BIT(3),
+};
+
+static struct clk_alpha_pll perfcl_alt_pll = {
+	.offset = 0x80100,
+	.vco_table = alt_pll_vco_modes,
+	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
+	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_alt_pll",
+		.parent_names = (const char *[]){ "xo_board" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static struct clk_alpha_pll pwrcl_alt_pll = {
+	.offset = 0x100,
+	.vco_table = alt_pll_vco_modes,
+	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
+	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_alt_pll",
+		.parent_names = (const char *[]){ "xo_board" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static struct clk_regmap_mux pwrcl_pmux = {
+	.reg = 0x40,
+	.shift = 0,
+	.width = 2,
+	.table = (u32 []){0, 1, 3},
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_pmux",
+		.parent_names = (const char *[]){
+			"pwrcl_smux",
+			"pwrcl_pll",
+			"pwrcl_alt_pll",
+		},
+		.num_parents = 3,
+		.ops = &clk_regmap_mux_closest_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap_mux pwrcl_smux = {
+	.reg = 0x40,
+	.shift = 2,
+	.width = 2,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_smux",
+		.parent_names = (const char *[]){
+			"xo_board",
+			"pwrcl_pll_main",
+			"sys_apcscbf_clk",
+			"sys_apcsaux_clk",
+		},
+		.num_parents = 4,
+		.ops = &clk_regmap_mux_closest_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap_mux perfcl_pmux = {
+	.reg = 0x80040,
+	.shift = 0,
+	.width = 2,
+	.table = (u32 []){0, 1, 3},
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_pmux",
+		.parent_names = (const char *[]){
+			"perfcl_smux",
+			"perfcl_pll",
+			"perfcl_alt_pll",
+		},
+		.num_parents = 3,
+		.ops = &clk_regmap_mux_closest_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap_mux perfcl_smux = {
+	.reg = 0x80040,
+	.shift = 2,
+	.width = 2,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_smux",
+		.parent_names = (const char *[]){
+			"xo_board",
+			"perfcl_pll_main",
+			"sys_apcscbf_clk",
+			"sys_apcsaux_clk",
+		},
+		.num_parents = 4,
+		.ops = &clk_regmap_mux_closest_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+struct clk_cpu_8996 {
+	struct clk_hw *alt_clk;
+	struct clk_hw *pll;
+	struct clk_regmap clkr;
+};
+
+static inline struct clk_cpu_8996 *to_clk_cpu_8996(struct clk_hw *hw)
+{
+	return container_of(to_clk_regmap(hw), struct clk_cpu_8996, clkr);
+}
+
+static int clk_cpu_8996_set_rate(struct clk_hw *hw, unsigned long rate,
+				 unsigned long prate)
+{
+	int ret;
+	struct clk_cpu_8996 *cpuclk = to_clk_cpu_8996(hw);
+	struct clk *alt_clk, *pll, *parent;
+
+	alt_clk = clk_hw_get_clk(cpuclk->alt_clk);
+	pll = clk_hw_get_clk(cpuclk->pll);
+	parent = clk_hw_get_clk(clk_hw_get_parent(hw));
+
+	/* Switch parent to alt clk */
+	if (cpuclk->alt_clk) {
+		ret = clk_set_parent(parent, alt_clk);
+		if (ret)
+			return ret;
+	}
+
+	/* Set the PLL to new rate */
+	ret = clk_set_rate(pll, rate);
+	if (ret)
+		goto error;
+
+	/* Switch back to primary pll */
+	if (cpuclk->alt_clk) {
+		ret = clk_set_parent(parent, pll);
+		if (ret)
+			goto error;
+	}
+	return 0;
+
+error:
+	if (cpuclk->alt_clk)
+		clk_set_parent(parent, pll);
+
+	return ret;
+}
+
+static unsigned long clk_cpu_8996_recalc_rate(struct clk_hw *hw,
+					      unsigned long prate)
+{
+	return clk_hw_get_rate(clk_hw_get_parent(hw));
+}
+
+static long clk_cpu_8996_round_rate(struct clk_hw *hw, unsigned long rate,
+				    unsigned long *prate)
+{
+	return clk_hw_round_rate(clk_hw_get_parent(hw), rate);
+}
+
+static struct clk_ops clk_cpu_8996_ops = {
+	.set_rate = clk_cpu_8996_set_rate,
+	.recalc_rate = clk_cpu_8996_recalc_rate,
+	.round_rate = clk_cpu_8996_round_rate,
+};
+
+static struct clk_cpu_8996 pwrcl_clk = {
+	.alt_clk = &pwrcl_alt_pll.clkr.hw,
+	.pll = &pwrcl_pll.clkr.hw,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_clk",
+		.parent_names = (const char *[]){ "pwrcl_pmux" },
+		.num_parents = 1,
+		.ops = &clk_cpu_8996_ops,
+	},
+};
+
+static struct clk_cpu_8996 perfcl_clk = {
+	.alt_clk = &perfcl_alt_pll.clkr.hw,
+	.pll = &perfcl_pll.clkr.hw,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_clk",
+		.parent_names = (const char *[]){ "perfcl_pmux" },
+		.num_parents = 1,
+		.ops = &clk_cpu_8996_ops,
+	},
+};
+
+static const struct regmap_config cpu_msm8996_regmap_config = {
+	.reg_bits		= 32,
+	.reg_stride		= 4,
+	.val_bits		= 32,
+	.max_register		= 0x80210,
+	.fast_io		= true,
+	.val_format_endian	= REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct of_device_id match_table[] = {
+	{ .compatible = "qcom,cpu-clk-msm8996" },
+	{}
+};
+
+#define cluster_clk_register(dev, clk, clkr) { \
+	clk = devm_clk_register_regmap(dev, clkr); \
+	if (IS_ERR(clk)) \
+		return PTR_ERR(clk); }
+
+#define cpu_clk_register_fixed(dev, clk, name, pname, flags, m, n) { \
+	clk = clk_register_fixed_factor(dev, name, pname, flags, m, n); \
+	if (IS_ERR(clk)) \
+		return PTR_ERR(clk); }
+
+#define cpu_set_rate(dev, clk, rate) { \
+	if (clk_set_rate(clk, rate)) \
+		dev_err(dev, "Failed to set " #clk " to " #rate "\n"); }
+
+#define cpu_prepare_enable(dev, clk) { \
+	if (clk_prepare_enable(clk)) \
+		dev_err(dev, "Failed to enable " #clk "\n"); }
+
+#define cpu_set_parent(dev, clk, parent) { \
+	if (clk_set_parent(clk, parent)) \
+		dev_err(dev, "Failed to set parent for " #clk "\n"); }
+
+struct clk *pwr_clk, *perf_clk;
+
+static int register_cpu_clocks(struct device *dev, struct regmap *regmap)
+{
+	struct clk *perf_alt_pll, *pwr_alt_pll, *perf_pll, *pwr_pll;
+	struct clk *perf_pmux, *perf_smux, *pwr_pmux, *pwr_smux;
+	struct clk *perf_pll_main, *pwr_pll_main;
+
+	/* register PLLs */
+	cluster_clk_register(dev, perf_pll, &perfcl_pll.clkr);
+	cluster_clk_register(dev, pwr_pll, &pwrcl_pll.clkr);
+	cluster_clk_register(dev, perf_alt_pll, &perfcl_alt_pll.clkr);
+	cluster_clk_register(dev, pwr_alt_pll, &pwrcl_alt_pll.clkr);
+
+	/* register MUXs */
+	cluster_clk_register(dev, perf_pmux, &perfcl_pmux.clkr);
+	cluster_clk_register(dev, perf_smux, &perfcl_smux.clkr);
+	cluster_clk_register(dev, pwr_pmux, &pwrcl_pmux.clkr);
+	cluster_clk_register(dev, pwr_smux, &pwrcl_smux.clkr);
+
+	/* register Fixed clks */
+	cpu_clk_register_fixed(dev, perf_pll_main, "perfcl_pll_main",
+			       "perfcl_pll", CLK_SET_RATE_PARENT, 1, 2);
+	cpu_clk_register_fixed(dev, pwr_pll_main, "pwrcl_pll_main",
+			       "pwrcl_pll", CLK_SET_RATE_PARENT, 1, 2);
+
+	/* Register CPU clks */
+	cluster_clk_register(dev, perf_clk, &perfcl_clk.clkr);
+	cluster_clk_register(dev, pwr_clk, &pwrcl_clk.clkr);
+
+	/* Initialise the PLLs */
+	clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config);
+	clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config);
+	clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
+	clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
+
+	/* Enable all PLLs and alt PLLs */
+	cpu_prepare_enable(dev, perf_pll);
+	cpu_prepare_enable(dev, pwr_pll);
+	cpu_prepare_enable(dev, perf_alt_pll);
+	cpu_prepare_enable(dev, pwr_alt_pll);
+
+	/* Init MUXes with default parents */
+	cpu_set_parent(dev, perf_pmux, perf_pll);
+	cpu_set_parent(dev, pwr_pmux, pwr_pll);
+	cpu_set_parent(dev, perf_smux, perf_pll_main);
+	cpu_set_parent(dev, pwr_smux, pwr_pll_main);
+
+	return 0;
+}
+
+static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
+{
+	int ret;
+	void __iomem *base;
+	struct resource *res;
+	struct clk_onecell_data *data;
+	struct device *dev = &pdev->dev;
+	struct regmap *regmap_cpu;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->clks = devm_kcalloc(dev, 3, sizeof(struct clk *), GFP_KERNEL);
+	if (!data->clks)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap_cpu = devm_regmap_init_mmio(dev, base,
+					   &cpu_msm8996_regmap_config);
+	if (IS_ERR(regmap_cpu))
+		return PTR_ERR(regmap_cpu);
+
+	ret = register_cpu_clocks(dev, regmap_cpu);
+	if (ret)
+		return ret;
+
+	data->clks[0] = pwr_clk;
+	data->clks[1] = perf_clk;
+	data->clk_num = 2;
+
+	return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+}
+
+static struct platform_driver qcom_cpu_clk_msm8996_driver = {
+	.probe = qcom_cpu_clk_msm8996_driver_probe,
+	.driver = {
+		.name = "qcom-cpu-clk-msm8996",
+		.of_match_table = match_table,
+	},
+};
+
+builtin_platform_driver(qcom_cpu_clk_msm8996_driver);
-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation


  parent reply	other threads:[~2016-09-29  8:35 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-29  8:35 [PATCH v3 00/11] clk: qcom: PLL updates Rajendra Nayak
2016-09-29  8:35 ` [PATCH v3 01/11] clk: qcom: Add support for alpha pll hwfsm ops Rajendra Nayak
2016-11-02 21:51   ` Stephen Boyd
2016-09-29  8:35 ` [PATCH v3 02/11] clk: qcom: Add support to initialize alpha plls Rajendra Nayak
2016-11-02 21:51   ` Stephen Boyd
2016-09-29  8:35 ` [PATCH v3 03/11] clk: qcom: handle alpha PLLs with 16bit alpha val registers Rajendra Nayak
2016-11-02 21:51   ` Stephen Boyd
2016-09-29  8:35 ` [PATCH v3 04/11] clk: qcom: Enable FSM mode for votable alpha PLLs Rajendra Nayak
2016-11-02 21:51   ` Stephen Boyd
2016-09-29  8:35 ` [PATCH v3 05/11] clk: qcom: Add .is_enabled ops for clk-alpha-pll Rajendra Nayak
2016-11-02 21:51   ` Stephen Boyd
2016-09-29  8:35 ` [PATCH v3 06/11] clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update Rajendra Nayak
2016-11-02 21:54   ` Stephen Boyd
2016-11-03  8:28     ` Rajendra Nayak
2016-11-03 19:48       ` Stephen Boyd
2016-09-29  8:35 ` [PATCH v3 07/11] clk: qcom: support dynamic update using latched interface Rajendra Nayak
2016-09-29  8:35 ` [PATCH v3 08/11] clk: qcom: mmcc-8996: Add capability flags for some alpha PLLs Rajendra Nayak
2016-09-29  8:35 ` [PATCH v3 09/11] clk: qcom: Add support for table based lookups in clk-regmap-mux Rajendra Nayak
2016-09-29  8:35 ` [PATCH v3 10/11] clk: Add clk_hw_get_clk() helper API to be used by clk providers Rajendra Nayak
2016-11-02 22:22   ` Stephen Boyd
2016-11-03  8:34     ` Rajendra Nayak
2016-11-03 19:46       ` Stephen Boyd
2016-09-29  8:35 ` Rajendra Nayak [this message]
2016-11-02 22:17   ` [RFC v3 11/11] clk: qcom: Add basic CPU clock driver for msm8996 Stephen Boyd
2016-11-03  8:33     ` Rajendra Nayak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1475138152-859-12-git-send-email-rnayak@codeaurora.org \
    --to=rnayak@codeaurora.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=sboyd@codeaurora.org \
    --cc=tdas@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.