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 */ |
18 | static DEFINE_SPINLOCK(krait_clock_reg_lock); |
19 | |
20 | #define LPL_SHIFT 8 |
21 | #define SECCLKAGD BIT(4) |
22 | |
23 | static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) |
24 | { |
25 | unsigned long flags; |
26 | u32 regval; |
27 | |
28 | spin_lock_irqsave(&krait_clock_reg_lock, flags); |
29 | |
30 | regval = krait_get_l2_indirect_reg(mux->offset); |
31 | |
32 | /* apq/ipq8064 Errata: disable sec_src clock gating during switch. */ |
33 | if (mux->disable_sec_src_gating) { |
34 | regval |= SECCLKAGD; |
35 | krait_set_l2_indirect_reg(mux->offset, regval); |
36 | } |
37 | |
38 | regval &= ~(mux->mask << mux->shift); |
39 | regval |= (sel & mux->mask) << mux->shift; |
40 | if (mux->lpl) { |
41 | regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); |
42 | regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); |
43 | } |
44 | krait_set_l2_indirect_reg(mux->offset, regval); |
45 | |
46 | /* apq/ipq8064 Errata: re-enabled sec_src clock gating. */ |
47 | if (mux->disable_sec_src_gating) { |
48 | regval &= ~SECCLKAGD; |
49 | krait_set_l2_indirect_reg(mux->offset, regval); |
50 | } |
51 | |
52 | /* Wait for switch to complete. */ |
53 | mb(); |
54 | udelay(1); |
55 | |
56 | /* |
57 | * Unlock now to make sure the mux register is not |
58 | * modified while switching to the new parent. |
59 | */ |
60 | spin_unlock_irqrestore(lock: &krait_clock_reg_lock, flags); |
61 | } |
62 | |
63 | static int krait_mux_set_parent(struct clk_hw *hw, u8 index) |
64 | { |
65 | struct krait_mux_clk *mux = to_krait_mux_clk(hw); |
66 | u32 sel; |
67 | |
68 | sel = clk_mux_index_to_val(table: mux->parent_map, flags: 0, index); |
69 | mux->en_mask = sel; |
70 | /* Don't touch mux if CPU is off as it won't work */ |
71 | if (__clk_is_enabled(clk: hw->clk)) |
72 | __krait_mux_set_sel(mux, sel); |
73 | |
74 | mux->reparent = true; |
75 | |
76 | return 0; |
77 | } |
78 | |
79 | static u8 krait_mux_get_parent(struct clk_hw *hw) |
80 | { |
81 | struct krait_mux_clk *mux = to_krait_mux_clk(hw); |
82 | u32 sel; |
83 | |
84 | sel = krait_get_l2_indirect_reg(mux->offset); |
85 | sel >>= mux->shift; |
86 | sel &= mux->mask; |
87 | mux->en_mask = sel; |
88 | |
89 | return clk_mux_val_to_index(hw, table: mux->parent_map, flags: 0, val: sel); |
90 | } |
91 | |
92 | const struct clk_ops krait_mux_clk_ops = { |
93 | .set_parent = krait_mux_set_parent, |
94 | .get_parent = krait_mux_get_parent, |
95 | .determine_rate = __clk_mux_determine_rate_closest, |
96 | }; |
97 | EXPORT_SYMBOL_GPL(krait_mux_clk_ops); |
98 | |
99 | /* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ |
100 | static int krait_div2_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) |
101 | { |
102 | req->best_parent_rate = clk_hw_round_rate(hw: clk_hw_get_parent(hw), rate: req->rate * 2); |
103 | req->rate = DIV_ROUND_UP(req->best_parent_rate, 2); |
104 | return 0; |
105 | } |
106 | |
107 | static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, |
108 | unsigned long parent_rate) |
109 | { |
110 | struct krait_div2_clk *d = to_krait_div2_clk(hw); |
111 | unsigned long flags; |
112 | u32 val; |
113 | u32 mask = BIT(d->width) - 1; |
114 | |
115 | if (d->lpl) |
116 | mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; |
117 | else |
118 | mask <<= d->shift; |
119 | |
120 | spin_lock_irqsave(&krait_clock_reg_lock, flags); |
121 | val = krait_get_l2_indirect_reg(d->offset); |
122 | val &= ~mask; |
123 | krait_set_l2_indirect_reg(d->offset, val); |
124 | spin_unlock_irqrestore(lock: &krait_clock_reg_lock, flags); |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static unsigned long |
130 | krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
131 | { |
132 | struct krait_div2_clk *d = to_krait_div2_clk(hw); |
133 | u32 mask = BIT(d->width) - 1; |
134 | u32 div; |
135 | |
136 | div = krait_get_l2_indirect_reg(d->offset); |
137 | div >>= d->shift; |
138 | div &= mask; |
139 | div = (div + 1) * 2; |
140 | |
141 | return DIV_ROUND_UP(parent_rate, div); |
142 | } |
143 | |
144 | const struct clk_ops krait_div2_clk_ops = { |
145 | .determine_rate = krait_div2_determine_rate, |
146 | .set_rate = krait_div2_set_rate, |
147 | .recalc_rate = krait_div2_recalc_rate, |
148 | }; |
149 | EXPORT_SYMBOL_GPL(krait_div2_clk_ops); |
150 | |