1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Spreadtrum gate clock driver |
4 | // |
5 | // Copyright (C) 2017 Spreadtrum, Inc. |
6 | // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/regmap.h> |
10 | |
11 | #include "gate.h" |
12 | |
13 | static void clk_gate_toggle(const struct sprd_gate *sg, bool en) |
14 | { |
15 | const struct sprd_clk_common *common = &sg->common; |
16 | unsigned int reg; |
17 | bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? true : false; |
18 | |
19 | set ^= en; |
20 | |
21 | regmap_read(map: common->regmap, reg: common->reg, val: ®); |
22 | |
23 | if (set) |
24 | reg |= sg->enable_mask; |
25 | else |
26 | reg &= ~sg->enable_mask; |
27 | |
28 | regmap_write(map: common->regmap, reg: common->reg, val: reg); |
29 | } |
30 | |
31 | static void clk_sc_gate_toggle(const struct sprd_gate *sg, bool en) |
32 | { |
33 | const struct sprd_clk_common *common = &sg->common; |
34 | bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; |
35 | unsigned int offset; |
36 | |
37 | set ^= en; |
38 | |
39 | /* |
40 | * Each set/clear gate clock has three registers: |
41 | * common->reg - base register |
42 | * common->reg + offset - set register |
43 | * common->reg + 2 * offset - clear register |
44 | */ |
45 | offset = set ? sg->sc_offset : sg->sc_offset * 2; |
46 | |
47 | regmap_write(map: common->regmap, reg: common->reg + offset, |
48 | val: sg->enable_mask); |
49 | } |
50 | |
51 | static void sprd_gate_disable(struct clk_hw *hw) |
52 | { |
53 | struct sprd_gate *sg = hw_to_sprd_gate(hw); |
54 | |
55 | clk_gate_toggle(sg, en: false); |
56 | } |
57 | |
58 | static int sprd_gate_enable(struct clk_hw *hw) |
59 | { |
60 | struct sprd_gate *sg = hw_to_sprd_gate(hw); |
61 | |
62 | clk_gate_toggle(sg, en: true); |
63 | |
64 | return 0; |
65 | } |
66 | |
67 | static void sprd_sc_gate_disable(struct clk_hw *hw) |
68 | { |
69 | struct sprd_gate *sg = hw_to_sprd_gate(hw); |
70 | |
71 | clk_sc_gate_toggle(sg, en: false); |
72 | } |
73 | |
74 | static int sprd_sc_gate_enable(struct clk_hw *hw) |
75 | { |
76 | struct sprd_gate *sg = hw_to_sprd_gate(hw); |
77 | |
78 | clk_sc_gate_toggle(sg, en: true); |
79 | |
80 | return 0; |
81 | } |
82 | |
83 | static int sprd_pll_sc_gate_prepare(struct clk_hw *hw) |
84 | { |
85 | struct sprd_gate *sg = hw_to_sprd_gate(hw); |
86 | |
87 | clk_sc_gate_toggle(sg, en: true); |
88 | udelay(sg->udelay); |
89 | |
90 | return 0; |
91 | } |
92 | |
93 | static int sprd_gate_is_enabled(struct clk_hw *hw) |
94 | { |
95 | struct sprd_gate *sg = hw_to_sprd_gate(hw); |
96 | struct sprd_clk_common *common = &sg->common; |
97 | struct clk_hw *parent; |
98 | unsigned int reg; |
99 | |
100 | if (sg->flags & SPRD_GATE_NON_AON) { |
101 | parent = clk_hw_get_parent(hw); |
102 | if (!parent || !clk_hw_is_enabled(hw: parent)) |
103 | return 0; |
104 | } |
105 | |
106 | regmap_read(map: common->regmap, reg: common->reg, val: ®); |
107 | |
108 | if (sg->flags & CLK_GATE_SET_TO_DISABLE) |
109 | reg ^= sg->enable_mask; |
110 | |
111 | reg &= sg->enable_mask; |
112 | |
113 | return reg ? 1 : 0; |
114 | } |
115 | |
116 | const struct clk_ops sprd_gate_ops = { |
117 | .disable = sprd_gate_disable, |
118 | .enable = sprd_gate_enable, |
119 | .is_enabled = sprd_gate_is_enabled, |
120 | }; |
121 | EXPORT_SYMBOL_GPL(sprd_gate_ops); |
122 | |
123 | const struct clk_ops sprd_sc_gate_ops = { |
124 | .disable = sprd_sc_gate_disable, |
125 | .enable = sprd_sc_gate_enable, |
126 | .is_enabled = sprd_gate_is_enabled, |
127 | }; |
128 | EXPORT_SYMBOL_GPL(sprd_sc_gate_ops); |
129 | |
130 | const struct clk_ops sprd_pll_sc_gate_ops = { |
131 | .unprepare = sprd_sc_gate_disable, |
132 | .prepare = sprd_pll_sc_gate_prepare, |
133 | .is_enabled = sprd_gate_is_enabled, |
134 | }; |
135 | EXPORT_SYMBOL_GPL(sprd_pll_sc_gate_ops); |
136 | |