aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorgi Djakov <georgi.djakov@linaro.org>2017-12-05 10:47:01 -0500
committerStephen Boyd <sboyd@codeaurora.org>2018-01-02 13:00:25 -0500
commit81ac38847a1d7fdd74a232cae195ff8f0fb4ab21 (patch)
tree1351c95197c1f8ac96cc05251570a1a2565bf4a3
parent081bfeed5f1b8394d993afa6b0ce20ed3e868960 (diff)
clk: qcom: Add APCS clock controller support
Add a driver for the APCS clock controller. It is part of the APCS hardware block, which among other things implements also a combined mux and half integer divider functionality. It can choose between a fixed-rate clock or the dedicated APCS (A53) PLL. The source and the divider can be set both at the same time. This is required for enabling CPU frequency scaling on MSM8916-based platforms. Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Amit Kucheria <amit.kucheria@linaro.org> [sboyd@codeaurora.org: Include rcg header for parent_map, drop multiple unneeded includes, add COMPILE_TEST to APCS depends, made tristate/modular] Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r--drivers/clk/qcom/Kconfig11
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/apcs-msm8916.c138
3 files changed, 150 insertions, 0 deletions
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 49db9fda6548..769e27353e1b 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -22,6 +22,17 @@ config QCOM_A53PLL
22 Say Y if you want to support higher CPU frequencies on MSM8916 22 Say Y if you want to support higher CPU frequencies on MSM8916
23 devices. 23 devices.
24 24
25config QCOM_CLK_APCS_MSM8916
26 tristate "MSM8916 APCS Clock Controller"
27 depends on COMMON_CLK_QCOM
28 depends on QCOM_APCS_IPC || COMPILE_TEST
29 default ARCH_QCOM
30 help
31 Support for the APCS Clock Controller on msm8916 devices. The
32 APCS is managing the mux and divider which feeds the CPUs.
33 Say Y if you want to support CPU frequency scaling on devices
34 such as msm8916.
35
25config QCOM_CLK_RPM 36config QCOM_CLK_RPM
26 tristate "RPM based Clock Controller" 37 tristate "RPM based Clock Controller"
27 depends on COMMON_CLK_QCOM && MFD_QCOM_RPM 38 depends on COMMON_CLK_QCOM && MFD_QCOM_RPM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 7c51d877f967..0408cebf38d4 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -34,5 +34,6 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
34obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o 34obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
35obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o 35obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
36obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o 36obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o
37obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
37obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o 38obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
38obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o 39obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
diff --git a/drivers/clk/qcom/apcs-msm8916.c b/drivers/clk/qcom/apcs-msm8916.c
new file mode 100644
index 000000000000..246957f1a413
--- /dev/null
+++ b/drivers/clk/qcom/apcs-msm8916.c
@@ -0,0 +1,138 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Qualcomm APCS clock controller driver
4 *
5 * Copyright (c) 2017, Linaro Limited
6 * Author: Georgi Djakov <georgi.djakov@linaro.org>
7 */
8
9#include <linux/clk.h>
10#include <linux/clk-provider.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/slab.h>
14#include <linux/platform_device.h>
15#include <linux/regmap.h>
16
17#include "clk-regmap.h"
18#include "clk-regmap-mux-div.h"
19
20static const u32 gpll0_a53cc_map[] = { 4, 5 };
21
22static const char * const gpll0_a53cc[] = {
23 "gpll0_vote",
24 "a53pll",
25};
26
27/*
28 * We use the notifier function for switching to a temporary safe configuration
29 * (mux and divider), while the A53 PLL is reconfigured.
30 */
31static int a53cc_notifier_cb(struct notifier_block *nb, unsigned long event,
32 void *data)
33{
34 int ret = 0;
35 struct clk_regmap_mux_div *md = container_of(nb,
36 struct clk_regmap_mux_div,
37 clk_nb);
38 if (event == PRE_RATE_CHANGE)
39 /* set the mux and divider to safe frequency (400mhz) */
40 ret = mux_div_set_src_div(md, 4, 3);
41
42 return notifier_from_errno(ret);
43}
44
45static int qcom_apcs_msm8916_clk_probe(struct platform_device *pdev)
46{
47 struct device *dev = &pdev->dev;
48 struct device *parent = dev->parent;
49 struct clk_regmap_mux_div *a53cc;
50 struct regmap *regmap;
51 struct clk_init_data init = { };
52 int ret;
53
54 regmap = dev_get_regmap(parent, NULL);
55 if (IS_ERR(regmap)) {
56 ret = PTR_ERR(regmap);
57 dev_err(dev, "failed to get regmap: %d\n", ret);
58 return ret;
59 }
60
61 a53cc = devm_kzalloc(dev, sizeof(*a53cc), GFP_KERNEL);
62 if (!a53cc)
63 return -ENOMEM;
64
65 init.name = "a53mux";
66 init.parent_names = gpll0_a53cc;
67 init.num_parents = ARRAY_SIZE(gpll0_a53cc);
68 init.ops = &clk_regmap_mux_div_ops;
69 init.flags = CLK_SET_RATE_PARENT;
70
71 a53cc->clkr.hw.init = &init;
72 a53cc->clkr.regmap = regmap;
73 a53cc->reg_offset = 0x50;
74 a53cc->hid_width = 5;
75 a53cc->hid_shift = 0;
76 a53cc->src_width = 3;
77 a53cc->src_shift = 8;
78 a53cc->parent_map = gpll0_a53cc_map;
79
80 a53cc->pclk = devm_clk_get(parent, NULL);
81 if (IS_ERR(a53cc->pclk)) {
82 ret = PTR_ERR(a53cc->pclk);
83 dev_err(dev, "failed to get clk: %d\n", ret);
84 return ret;
85 }
86
87 a53cc->clk_nb.notifier_call = a53cc_notifier_cb;
88 ret = clk_notifier_register(a53cc->pclk, &a53cc->clk_nb);
89 if (ret) {
90 dev_err(dev, "failed to register clock notifier: %d\n", ret);
91 return ret;
92 }
93
94 ret = devm_clk_register_regmap(dev, &a53cc->clkr);
95 if (ret) {
96 dev_err(dev, "failed to register regmap clock: %d\n", ret);
97 goto err;
98 }
99
100 ret = of_clk_add_hw_provider(parent->of_node, of_clk_hw_simple_get,
101 &a53cc->clkr.hw);
102 if (ret) {
103 dev_err(dev, "failed to add clock provider: %d\n", ret);
104 goto err;
105 }
106
107 platform_set_drvdata(pdev, a53cc);
108
109 return 0;
110
111err:
112 clk_notifier_unregister(a53cc->pclk, &a53cc->clk_nb);
113 return ret;
114}
115
116static int qcom_apcs_msm8916_clk_remove(struct platform_device *pdev)
117{
118 struct clk_regmap_mux_div *a53cc = platform_get_drvdata(pdev);
119 struct device *parent = pdev->dev.parent;
120
121 clk_notifier_unregister(a53cc->pclk, &a53cc->clk_nb);
122 of_clk_del_provider(parent->of_node);
123
124 return 0;
125}
126
127static struct platform_driver qcom_apcs_msm8916_clk_driver = {
128 .probe = qcom_apcs_msm8916_clk_probe,
129 .remove = qcom_apcs_msm8916_clk_remove,
130 .driver = {
131 .name = "qcom-apcs-msm8916-clk",
132 },
133};
134module_platform_driver(qcom_apcs_msm8916_clk_driver);
135
136MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
137MODULE_LICENSE("GPL v2");
138MODULE_DESCRIPTION("Qualcomm MSM8916 APCS clock driver");