Commit | Line | Data |
---|---|---|
4d7dc77b SB |
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 | static 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 | ||
42 | static 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 | ||
bb5c4a85 | 47 | sel = clk_mux_index_to_val(mux->parent_map, 0, index); |
4d7dc77b SB |
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 | ||
77612720 S |
53 | mux->reparent = true; |
54 | ||
4d7dc77b SB |
55 | return 0; |
56 | } | |
57 | ||
58 | static u8 krait_mux_get_parent(struct clk_hw *hw) | |
59 | { | |
60 | struct krait_mux_clk *mux = to_krait_mux_clk(hw); | |
61 | u32 sel; | |
62 | ||
63 | sel = krait_get_l2_indirect_reg(mux->offset); | |
64 | sel >>= mux->shift; | |
65 | sel &= mux->mask; | |
66 | mux->en_mask = sel; | |
67 | ||
bb5c4a85 | 68 | return clk_mux_val_to_index(hw, mux->parent_map, 0, sel); |
4d7dc77b SB |
69 | } |
70 | ||
71 | const struct clk_ops krait_mux_clk_ops = { | |
72 | .set_parent = krait_mux_set_parent, | |
73 | .get_parent = krait_mux_get_parent, | |
74 | .determine_rate = __clk_mux_determine_rate_closest, | |
75 | }; | |
76 | EXPORT_SYMBOL_GPL(krait_mux_clk_ops); | |
77 | ||
78 | /* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ | |
79 | static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, | |
80 | unsigned long *parent_rate) | |
81 | { | |
82 | *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); | |
83 | return DIV_ROUND_UP(*parent_rate, 2); | |
84 | } | |
85 | ||
86 | static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, | |
87 | unsigned long parent_rate) | |
88 | { | |
89 | struct krait_div2_clk *d = to_krait_div2_clk(hw); | |
90 | unsigned long flags; | |
91 | u32 val; | |
92 | u32 mask = BIT(d->width) - 1; | |
93 | ||
94 | if (d->lpl) | |
95 | mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; | |
96 | ||
97 | spin_lock_irqsave(&krait_clock_reg_lock, flags); | |
98 | val = krait_get_l2_indirect_reg(d->offset); | |
99 | val &= ~mask; | |
100 | krait_set_l2_indirect_reg(d->offset, val); | |
101 | spin_unlock_irqrestore(&krait_clock_reg_lock, flags); | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static unsigned long | |
107 | krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | |
108 | { | |
109 | struct krait_div2_clk *d = to_krait_div2_clk(hw); | |
110 | u32 mask = BIT(d->width) - 1; | |
111 | u32 div; | |
112 | ||
113 | div = krait_get_l2_indirect_reg(d->offset); | |
114 | div >>= d->shift; | |
115 | div &= mask; | |
116 | div = (div + 1) * 2; | |
117 | ||
118 | return DIV_ROUND_UP(parent_rate, div); | |
119 | } | |
120 | ||
121 | const struct clk_ops krait_div2_clk_ops = { | |
122 | .round_rate = krait_div2_round_rate, | |
123 | .set_rate = krait_div2_set_rate, | |
124 | .recalc_rate = krait_div2_recalc_rate, | |
125 | }; | |
126 | EXPORT_SYMBOL_GPL(krait_div2_clk_ops); |