aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@codeaurora.org>2018-08-14 08:12:26 -0400
committerStephen Boyd <sboyd@kernel.org>2018-10-17 16:14:49 -0400
commit4d7dc77babfef1d6cb8fd825e2f17dc3384c3272 (patch)
tree1833d9730d0523b31bb8bda6d5d0001fca0339f2
parent1f79131bfd512f322c16b58dca581ce39beafab9 (diff)
clk: qcom: Add support for Krait clocks
The Krait clocks are made up of a series of muxes and a divider that choose between a fixed rate clock and dedicated HFPLLs for each CPU. Instead of using mmio accesses to remux parents, the Krait implementation exposes the remux control via cp15 registers. Support these clocks. Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Sricharan R <sricharan@codeaurora.org> Tested-by: Craig Tatlor <ctatlor97@gmail.com> [sboyd@kernel.org: Move hidden config to top outside of the visible qcom config zone so that menuconfig looks nice] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r--drivers/clk/qcom/Kconfig4
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clk-krait.c124
-rw-r--r--drivers/clk/qcom/clk-krait.h37
4 files changed, 166 insertions, 0 deletions
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 21aec1048225..93f1342f1949 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -1,3 +1,7 @@
1config KRAIT_CLOCKS
2 bool
3 select KRAIT_L2_ACCESSORS
4
1config QCOM_GDSC 5config QCOM_GDSC
2 bool 6 bool
3 select PM_GENERIC_DOMAINS if PM 7 select PM_GENERIC_DOMAINS if PM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index f82eeaca278e..506c4cfd775f 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -11,6 +11,7 @@ clk-qcom-y += clk-branch.o
11clk-qcom-y += clk-regmap-divider.o 11clk-qcom-y += clk-regmap-divider.o
12clk-qcom-y += clk-regmap-mux.o 12clk-qcom-y += clk-regmap-mux.o
13clk-qcom-y += clk-regmap-mux-div.o 13clk-qcom-y += clk-regmap-mux-div.o
14clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
14clk-qcom-y += clk-hfpll.o 15clk-qcom-y += clk-hfpll.o
15clk-qcom-y += reset.o 16clk-qcom-y += reset.o
16clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o 17clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
new file mode 100644
index 000000000000..2e417678029f
--- /dev/null
+++ b/drivers/clk/qcom/clk-krait.c
@@ -0,0 +1,124 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018, The Linux Foundation. All rights reserved.
3
4#include <linux/kernel.h>
5#include <linux/module.h>
6#include <linux/init.h>
7#include <linux/io.h>
8#include <linux/delay.h>
9#include <linux/err.h>
10#include <linux/clk-provider.h>
11#include <linux/spinlock.h>
12
13#include <asm/krait-l2-accessors.h>
14
15#include "clk-krait.h"
16
17/* Secondary and primary muxes share the same cp15 register */
18static DEFINE_SPINLOCK(krait_clock_reg_lock);
19
20#define LPL_SHIFT 8
21static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
22{
23 unsigned long flags;
24 u32 regval;
25
26 spin_lock_irqsave(&krait_clock_reg_lock, flags);
27 regval = krait_get_l2_indirect_reg(mux->offset);
28 regval &= ~(mux->mask << mux->shift);
29 regval |= (sel & mux->mask) << mux->shift;
30 if (mux->lpl) {
31 regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
32 regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
33 }
34 krait_set_l2_indirect_reg(mux->offset, regval);
35 spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
36
37 /* Wait for switch to complete. */
38 mb();
39 udelay(1);
40}
41
42static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
43{
44 struct krait_mux_clk *mux = to_krait_mux_clk(hw);
45 u32 sel;
46
47 sel = clk_mux_reindex(index, mux->parent_map, 0);
48 mux->en_mask = sel;
49 /* Don't touch mux if CPU is off as it won't work */
50 if (__clk_is_enabled(hw->clk))
51 __krait_mux_set_sel(mux, sel);
52
53 return 0;
54}
55
56static u8 krait_mux_get_parent(struct clk_hw *hw)
57{
58 struct krait_mux_clk *mux = to_krait_mux_clk(hw);
59 u32 sel;
60
61 sel = krait_get_l2_indirect_reg(mux->offset);
62 sel >>= mux->shift;
63 sel &= mux->mask;
64 mux->en_mask = sel;
65
66 return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
67}
68
69const struct clk_ops krait_mux_clk_ops = {
70 .set_parent = krait_mux_set_parent,
71 .get_parent = krait_mux_get_parent,
72 .determine_rate = __clk_mux_determine_rate_closest,
73};
74EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
75
76/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
77static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
78 unsigned long *parent_rate)
79{
80 *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
81 return DIV_ROUND_UP(*parent_rate, 2);
82}
83
84static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
85 unsigned long parent_rate)
86{
87 struct krait_div2_clk *d = to_krait_div2_clk(hw);
88 unsigned long flags;
89 u32 val;
90 u32 mask = BIT(d->width) - 1;
91
92 if (d->lpl)
93 mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
94
95 spin_lock_irqsave(&krait_clock_reg_lock, flags);
96 val = krait_get_l2_indirect_reg(d->offset);
97 val &= ~mask;
98 krait_set_l2_indirect_reg(d->offset, val);
99 spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
100
101 return 0;
102}
103
104static unsigned long
105krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
106{
107 struct krait_div2_clk *d = to_krait_div2_clk(hw);
108 u32 mask = BIT(d->width) - 1;
109 u32 div;
110
111 div = krait_get_l2_indirect_reg(d->offset);
112 div >>= d->shift;
113 div &= mask;
114 div = (div + 1) * 2;
115
116 return DIV_ROUND_UP(parent_rate, div);
117}
118
119const struct clk_ops krait_div2_clk_ops = {
120 .round_rate = krait_div2_round_rate,
121 .set_rate = krait_div2_set_rate,
122 .recalc_rate = krait_div2_recalc_rate,
123};
124EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
new file mode 100644
index 000000000000..441ba1e18b81
--- /dev/null
+++ b/drivers/clk/qcom/clk-krait.h
@@ -0,0 +1,37 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2
3#ifndef __QCOM_CLK_KRAIT_H
4#define __QCOM_CLK_KRAIT_H
5
6#include <linux/clk-provider.h>
7
8struct krait_mux_clk {
9 unsigned int *parent_map;
10 u32 offset;
11 u32 mask;
12 u32 shift;
13 u32 en_mask;
14 bool lpl;
15
16 struct clk_hw hw;
17 struct notifier_block clk_nb;
18};
19
20#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
21
22extern const struct clk_ops krait_mux_clk_ops;
23
24struct krait_div2_clk {
25 u32 offset;
26 u8 width;
27 u32 shift;
28 bool lpl;
29
30 struct clk_hw hw;
31};
32
33#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
34
35extern const struct clk_ops krait_div2_clk_ops;
36
37#endif