Blame view
drivers/clk/clk-gate.c
4.82 KB
e1bd55e5a clk: Tag basic cl... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
9d9f78ed9 clk: basic clock ... |
2 3 4 5 |
/* * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> * |
9d9f78ed9 clk: basic clock ... |
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
* Gated clock implementation */ #include <linux/clk-provider.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h> #include <linux/string.h> /** * DOC: basic gatable clock which can gate and ungate it's ouput * * Traits of this clock: * prepare - clk_(un)prepare only ensures parent is (un)prepared * enable - clk_enable and clk_disable are functional & control gating * rate - inherits rate from parent. No clk_set_rate support * parent - fixed parent. No clk_set_parent support */ |
d1c8a501e clk: gate: add ex... |
25 26 27 28 |
static inline u32 clk_gate_readl(struct clk_gate *gate) { if (gate->flags & CLK_GATE_BIG_ENDIAN) return ioread32be(gate->reg); |
5834fd75e clk: core: replac... |
29 |
return readl(gate->reg); |
d1c8a501e clk: gate: add ex... |
30 31 32 33 34 35 36 |
} static inline void clk_gate_writel(struct clk_gate *gate, u32 val) { if (gate->flags & CLK_GATE_BIG_ENDIAN) iowrite32be(val, gate->reg); else |
5834fd75e clk: core: replac... |
37 |
writel(val, gate->reg); |
d1c8a501e clk: gate: add ex... |
38 |
} |
fbc42aab5 clk: clk-gate: Cr... |
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/* * It works on following logic: * * For enabling clock, enable = 1 * set2dis = 1 -> clear bit -> set = 0 * set2dis = 0 -> set bit -> set = 1 * * For disabling clock, enable = 0 * set2dis = 1 -> set bit -> set = 1 * set2dis = 0 -> clear bit -> set = 0 * * So, result is always: enable xor set2dis. */ static void clk_gate_endisable(struct clk_hw *hw, int enable) |
9d9f78ed9 clk: basic clock ... |
53 |
{ |
fbc42aab5 clk: clk-gate: Cr... |
54 55 |
struct clk_gate *gate = to_clk_gate(hw); int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; |
3f649ab72 treewide: Remove ... |
56 |
unsigned long flags; |
fbc42aab5 clk: clk-gate: Cr... |
57 58 59 |
u32 reg; set ^= enable; |
9d9f78ed9 clk: basic clock ... |
60 61 62 |
if (gate->lock) spin_lock_irqsave(gate->lock, flags); |
661e2180c clk: basic-type: ... |
63 64 |
else __acquire(gate->lock); |
9d9f78ed9 clk: basic clock ... |
65 |
|
045779942 clk: gate: add CL... |
66 67 68 69 70 |
if (gate->flags & CLK_GATE_HIWORD_MASK) { reg = BIT(gate->bit_idx + 16); if (set) reg |= BIT(gate->bit_idx); } else { |
d1c8a501e clk: gate: add ex... |
71 |
reg = clk_gate_readl(gate); |
045779942 clk: gate: add CL... |
72 73 74 75 76 77 |
if (set) reg |= BIT(gate->bit_idx); else reg &= ~BIT(gate->bit_idx); } |
9d9f78ed9 clk: basic clock ... |
78 |
|
d1c8a501e clk: gate: add ex... |
79 |
clk_gate_writel(gate, reg); |
9d9f78ed9 clk: basic clock ... |
80 81 82 |
if (gate->lock) spin_unlock_irqrestore(gate->lock, flags); |
661e2180c clk: basic-type: ... |
83 84 |
else __release(gate->lock); |
9d9f78ed9 clk: basic clock ... |
85 86 87 88 |
} static int clk_gate_enable(struct clk_hw *hw) { |
fbc42aab5 clk: clk-gate: Cr... |
89 |
clk_gate_endisable(hw, 1); |
9d9f78ed9 clk: basic clock ... |
90 91 92 |
return 0; } |
9d9f78ed9 clk: basic clock ... |
93 94 95 |
static void clk_gate_disable(struct clk_hw *hw) { |
fbc42aab5 clk: clk-gate: Cr... |
96 |
clk_gate_endisable(hw, 0); |
9d9f78ed9 clk: basic clock ... |
97 |
} |
9d9f78ed9 clk: basic clock ... |
98 |
|
0a9c869d5 clk: gate: expose... |
99 |
int clk_gate_is_enabled(struct clk_hw *hw) |
9d9f78ed9 clk: basic clock ... |
100 101 102 |
{ u32 reg; struct clk_gate *gate = to_clk_gate(hw); |
d1c8a501e clk: gate: add ex... |
103 |
reg = clk_gate_readl(gate); |
9d9f78ed9 clk: basic clock ... |
104 105 106 107 108 109 110 111 112 |
/* if a set bit disables this clk, flip it before masking */ if (gate->flags & CLK_GATE_SET_TO_DISABLE) reg ^= BIT(gate->bit_idx); reg &= BIT(gate->bit_idx); return reg ? 1 : 0; } |
0a9c869d5 clk: gate: expose... |
113 |
EXPORT_SYMBOL_GPL(clk_gate_is_enabled); |
9d9f78ed9 clk: basic clock ... |
114 |
|
822c250e1 clk: add "const" ... |
115 |
const struct clk_ops clk_gate_ops = { |
9d9f78ed9 clk: basic clock ... |
116 117 118 119 120 |
.enable = clk_gate_enable, .disable = clk_gate_disable, .is_enabled = clk_gate_is_enabled, }; EXPORT_SYMBOL_GPL(clk_gate_ops); |
194efb6e2 clk: gate: Add su... |
121 122 123 124 125 |
struct clk_hw *__clk_hw_register_gate(struct device *dev, struct device_node *np, const char *name, const char *parent_name, const struct clk_hw *parent_hw, const struct clk_parent_data *parent_data, unsigned long flags, |
9d9f78ed9 clk: basic clock ... |
126 127 128 129 |
void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) { struct clk_gate *gate; |
e270d8cb1 clk: gate: Add hw... |
130 |
struct clk_hw *hw; |
cc819cf8d clk: Zero init cl... |
131 |
struct clk_init_data init = {}; |
194efb6e2 clk: gate: Add su... |
132 |
int ret = -EINVAL; |
9d9f78ed9 clk: basic clock ... |
133 |
|
045779942 clk: gate: add CL... |
134 |
if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { |
2e9dcdae4 clk-gate: fix bit... |
135 |
if (bit_idx > 15) { |
045779942 clk: gate: add CL... |
136 137 138 139 140 |
pr_err("gate bit exceeds LOWORD field "); return ERR_PTR(-EINVAL); } } |
27d545915 clk: basic: impro... |
141 |
/* allocate the gate */ |
d122db7e8 clk: basic-types:... |
142 143 |
gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) |
27d545915 clk: basic: impro... |
144 |
return ERR_PTR(-ENOMEM); |
9d9f78ed9 clk: basic clock ... |
145 |
|
0197b3ea0 clk: Use a separa... |
146 147 |
init.name = name; init.ops = &clk_gate_ops; |
90b6c5c73 clk: Remove CLK_I... |
148 |
init.flags = flags; |
295face99 clk: gate: fix co... |
149 |
init.parent_names = parent_name ? &parent_name : NULL; |
194efb6e2 clk: gate: Add su... |
150 151 152 153 154 155 |
init.parent_hws = parent_hw ? &parent_hw : NULL; init.parent_data = parent_data; if (parent_name || parent_hw || parent_data) init.num_parents = 1; else init.num_parents = 0; |
0197b3ea0 clk: Use a separa... |
156 |
|
9d9f78ed9 clk: basic clock ... |
157 158 159 160 161 |
/* struct clk_gate assignments */ gate->reg = reg; gate->bit_idx = bit_idx; gate->flags = clk_gate_flags; gate->lock = lock; |
0197b3ea0 clk: Use a separa... |
162 |
gate->hw.init = &init; |
9d9f78ed9 clk: basic clock ... |
163 |
|
e270d8cb1 clk: gate: Add hw... |
164 |
hw = &gate->hw; |
194efb6e2 clk: gate: Add su... |
165 166 167 168 |
if (dev || !np) ret = clk_hw_register(dev, hw); else if (np) ret = of_clk_hw_register(np, hw); |
e270d8cb1 clk: gate: Add hw... |
169 |
if (ret) { |
27d545915 clk: basic: impro... |
170 |
kfree(gate); |
e270d8cb1 clk: gate: Add hw... |
171 172 |
hw = ERR_PTR(ret); } |
27d545915 clk: basic: impro... |
173 |
|
e270d8cb1 clk: gate: Add hw... |
174 |
return hw; |
194efb6e2 clk: gate: Add su... |
175 |
|
e270d8cb1 clk: gate: Add hw... |
176 |
} |
194efb6e2 clk: gate: Add su... |
177 |
EXPORT_SYMBOL_GPL(__clk_hw_register_gate); |
e270d8cb1 clk: gate: Add hw... |
178 179 180 181 182 183 184 185 186 187 188 189 190 |
struct clk *clk_register_gate(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) { struct clk_hw *hw; hw = clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx, clk_gate_flags, lock); if (IS_ERR(hw)) return ERR_CAST(hw); return hw->clk; |
9d9f78ed9 clk: basic clock ... |
191 |
} |
5cfe10bb0 clk: export fixed... |
192 |
EXPORT_SYMBOL_GPL(clk_register_gate); |
4e3c021fb clk: Add clk_unre... |
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
void clk_unregister_gate(struct clk *clk) { struct clk_gate *gate; struct clk_hw *hw; hw = __clk_get_hw(clk); if (!hw) return; gate = to_clk_gate(hw); clk_unregister(clk); kfree(gate); } EXPORT_SYMBOL_GPL(clk_unregister_gate); |
e270d8cb1 clk: gate: Add hw... |
209 210 211 212 213 214 215 216 217 218 219 |
void clk_hw_unregister_gate(struct clk_hw *hw) { struct clk_gate *gate; gate = to_clk_gate(hw); clk_hw_unregister(hw); kfree(gate); } EXPORT_SYMBOL_GPL(clk_hw_unregister_gate); |