Blame view
drivers/clk/clk-mux.c
5.47 KB
e1bd55e5a clk: Tag basic cl... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
9d9f78ed9 clk: basic clock ... |
2 3 4 5 6 |
/* * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> * |
9d9f78ed9 clk: basic clock ... |
7 8 |
* Simple multiplexer clock implementation */ |
9d9f78ed9 clk: basic clock ... |
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <linux/clk-provider.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h> /* * DOC: basic adjustable multiplexer clock that cannot gate * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared * enable - clk_enable only ensures that parents are enabled * rate - rate is only affected by parent switching. No clk_set_rate support * parent - parent is adjustable through clk_set_parent */ |
3a7275196 clk: mux: add exp... |
24 25 26 27 |
static inline u32 clk_mux_readl(struct clk_mux *mux) { if (mux->flags & CLK_MUX_BIG_ENDIAN) return ioread32be(mux->reg); |
5834fd75e clk: core: replac... |
28 |
return readl(mux->reg); |
3a7275196 clk: mux: add exp... |
29 30 31 32 33 34 35 |
} static inline void clk_mux_writel(struct clk_mux *mux, u32 val) { if (mux->flags & CLK_MUX_BIG_ENDIAN) iowrite32be(val, mux->reg); else |
5834fd75e clk: core: replac... |
36 |
writel(val, mux->reg); |
3a7275196 clk: mux: add exp... |
37 |
} |
77deb66d2 clk: mux: add hel... |
38 39 |
int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags, unsigned int val) |
9d9f78ed9 clk: basic clock ... |
40 |
{ |
497295afb clk: Replace __cl... |
41 |
int num_parents = clk_hw_get_num_parents(hw); |
9d9f78ed9 clk: basic clock ... |
42 |
|
77deb66d2 clk: mux: add hel... |
43 |
if (table) { |
ce4f3313b clk: add table lo... |
44 45 46 |
int i; for (i = 0; i < num_parents; i++) |
77deb66d2 clk: mux: add hel... |
47 |
if (table[i] == val) |
ce4f3313b clk: add table lo... |
48 49 50 |
return i; return -EINVAL; } |
9d9f78ed9 clk: basic clock ... |
51 |
|
77deb66d2 clk: mux: add hel... |
52 |
if (val && (flags & CLK_MUX_INDEX_BIT)) |
9d9f78ed9 clk: basic clock ... |
53 |
val = ffs(val) - 1; |
77deb66d2 clk: mux: add hel... |
54 |
if (val && (flags & CLK_MUX_INDEX_ONE)) |
9d9f78ed9 clk: basic clock ... |
55 |
val--; |
ce4f3313b clk: add table lo... |
56 |
if (val >= num_parents) |
9d9f78ed9 clk: basic clock ... |
57 58 59 60 |
return -EINVAL; return val; } |
77deb66d2 clk: mux: add hel... |
61 |
EXPORT_SYMBOL_GPL(clk_mux_val_to_index); |
9d9f78ed9 clk: basic clock ... |
62 |
|
77deb66d2 clk: mux: add hel... |
63 |
unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index) |
9d9f78ed9 clk: basic clock ... |
64 |
{ |
77deb66d2 clk: mux: add hel... |
65 |
unsigned int val = index; |
9d9f78ed9 clk: basic clock ... |
66 |
|
77deb66d2 clk: mux: add hel... |
67 68 |
if (table) { val = table[index]; |
3837bd277 clk: fix codying ... |
69 |
} else { |
77deb66d2 clk: mux: add hel... |
70 71 |
if (flags & CLK_MUX_INDEX_BIT) val = 1 << index; |
ce4f3313b clk: add table lo... |
72 |
|
77deb66d2 clk: mux: add hel... |
73 74 |
if (flags & CLK_MUX_INDEX_ONE) val++; |
ce4f3313b clk: add table lo... |
75 |
} |
9d9f78ed9 clk: basic clock ... |
76 |
|
77deb66d2 clk: mux: add hel... |
77 78 79 80 81 82 83 84 |
return val; } EXPORT_SYMBOL_GPL(clk_mux_index_to_val); static u8 clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *mux = to_clk_mux(hw); u32 val; |
3a7275196 clk: mux: add exp... |
85 |
val = clk_mux_readl(mux) >> mux->shift; |
77deb66d2 clk: mux: add hel... |
86 87 88 89 90 91 92 93 94 95 96 |
val &= mux->mask; return clk_mux_val_to_index(hw, mux->table, mux->flags, val); } static int clk_mux_set_parent(struct clk_hw *hw, u8 index) { struct clk_mux *mux = to_clk_mux(hw); u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); unsigned long flags = 0; u32 reg; |
9d9f78ed9 clk: basic clock ... |
97 98 |
if (mux->lock) spin_lock_irqsave(mux->lock, flags); |
661e2180c clk: basic-type: ... |
99 100 |
else __acquire(mux->lock); |
9d9f78ed9 clk: basic clock ... |
101 |
|
ba492e900 clk: mux: add CLK... |
102 |
if (mux->flags & CLK_MUX_HIWORD_MASK) { |
77deb66d2 clk: mux: add hel... |
103 |
reg = mux->mask << (mux->shift + 16); |
ba492e900 clk: mux: add CLK... |
104 |
} else { |
3a7275196 clk: mux: add exp... |
105 |
reg = clk_mux_readl(mux); |
77deb66d2 clk: mux: add hel... |
106 |
reg &= ~(mux->mask << mux->shift); |
ba492e900 clk: mux: add CLK... |
107 |
} |
77deb66d2 clk: mux: add hel... |
108 109 |
val = val << mux->shift; reg |= val; |
3a7275196 clk: mux: add exp... |
110 |
clk_mux_writel(mux, reg); |
9d9f78ed9 clk: basic clock ... |
111 112 113 |
if (mux->lock) spin_unlock_irqrestore(mux->lock, flags); |
661e2180c clk: basic-type: ... |
114 115 |
else __release(mux->lock); |
9d9f78ed9 clk: basic clock ... |
116 117 118 |
return 0; } |
9d9f78ed9 clk: basic clock ... |
119 |
|
4ad69b80e clk: honor CLK_MU... |
120 121 122 123 124 125 126 |
static int clk_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct clk_mux *mux = to_clk_mux(hw); return clk_mux_determine_rate_flags(hw, req, mux->flags); } |
822c250e1 clk: add "const" ... |
127 |
const struct clk_ops clk_mux_ops = { |
9d9f78ed9 clk: basic clock ... |
128 129 |
.get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent, |
4ad69b80e clk: honor CLK_MU... |
130 |
.determine_rate = clk_mux_determine_rate, |
9d9f78ed9 clk: basic clock ... |
131 132 |
}; EXPORT_SYMBOL_GPL(clk_mux_ops); |
c57acd14a clk: mux: Add sup... |
133 134 135 136 |
const struct clk_ops clk_mux_ro_ops = { .get_parent = clk_mux_get_parent, }; EXPORT_SYMBOL_GPL(clk_mux_ro_ops); |
9611b3aac clk: mux: Add sup... |
137 138 139 140 141 142 |
struct clk_hw *__clk_hw_register_mux(struct device *dev, struct device_node *np, const char *name, u8 num_parents, const char * const *parent_names, const struct clk_hw **parent_hws, const struct clk_parent_data *parent_data, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, |
ce4f3313b clk: add table lo... |
143 |
u8 clk_mux_flags, u32 *table, spinlock_t *lock) |
9d9f78ed9 clk: basic clock ... |
144 145 |
{ struct clk_mux *mux; |
264b31719 clk: mux: Add hw ... |
146 |
struct clk_hw *hw; |
cc819cf8d clk: Zero init cl... |
147 |
struct clk_init_data init = {}; |
ba492e900 clk: mux: add CLK... |
148 |
u8 width = 0; |
9611b3aac clk: mux: Add sup... |
149 |
int ret = -EINVAL; |
ba492e900 clk: mux: add CLK... |
150 151 152 153 154 155 156 157 158 |
if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { width = fls(mask) - ffs(mask) + 1; if (width + shift > 16) { pr_err("mux value exceeds LOWORD field "); return ERR_PTR(-EINVAL); } } |
9d9f78ed9 clk: basic clock ... |
159 |
|
27d545915 clk: basic: impro... |
160 |
/* allocate the mux */ |
1e28733e3 clk: clk-mux: Imp... |
161 |
mux = kzalloc(sizeof(*mux), GFP_KERNEL); |
0b910402c clk: clk-mux: Del... |
162 |
if (!mux) |
9d9f78ed9 clk: basic clock ... |
163 |
return ERR_PTR(-ENOMEM); |
9d9f78ed9 clk: basic clock ... |
164 |
|
0197b3ea0 clk: Use a separa... |
165 |
init.name = name; |
c57acd14a clk: mux: Add sup... |
166 167 168 169 |
if (clk_mux_flags & CLK_MUX_READ_ONLY) init.ops = &clk_mux_ro_ops; else init.ops = &clk_mux_ops; |
90b6c5c73 clk: Remove CLK_I... |
170 |
init.flags = flags; |
0197b3ea0 clk: Use a separa... |
171 |
init.parent_names = parent_names; |
9611b3aac clk: mux: Add sup... |
172 173 |
init.parent_data = parent_data; init.parent_hws = parent_hws; |
0197b3ea0 clk: Use a separa... |
174 |
init.num_parents = num_parents; |
9d9f78ed9 clk: basic clock ... |
175 176 177 |
/* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; |
ce4f3313b clk: add table lo... |
178 |
mux->mask = mask; |
9d9f78ed9 clk: basic clock ... |
179 180 |
mux->flags = clk_mux_flags; mux->lock = lock; |
ce4f3313b clk: add table lo... |
181 |
mux->table = table; |
31df9db99 clk: mux: assign ... |
182 |
mux->hw.init = &init; |
9d9f78ed9 clk: basic clock ... |
183 |
|
264b31719 clk: mux: Add hw ... |
184 |
hw = &mux->hw; |
9611b3aac clk: mux: Add sup... |
185 186 187 188 |
if (dev || !np) ret = clk_hw_register(dev, hw); else if (np) ret = of_clk_hw_register(np, hw); |
264b31719 clk: mux: Add hw ... |
189 |
if (ret) { |
27d545915 clk: basic: impro... |
190 |
kfree(mux); |
264b31719 clk: mux: Add hw ... |
191 192 |
hw = ERR_PTR(ret); } |
27d545915 clk: basic: impro... |
193 |
|
264b31719 clk: mux: Add hw ... |
194 195 |
return hw; } |
9611b3aac clk: mux: Add sup... |
196 |
EXPORT_SYMBOL_GPL(__clk_hw_register_mux); |
264b31719 clk: mux: Add hw ... |
197 198 199 |
struct clk *clk_register_mux_table(struct device *dev, const char *name, const char * const *parent_names, u8 num_parents, |
9611b3aac clk: mux: Add sup... |
200 |
unsigned long flags, void __iomem *reg, u8 shift, u32 mask, |
264b31719 clk: mux: Add hw ... |
201 202 203 |
u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_hw *hw; |
9611b3aac clk: mux: Add sup... |
204 205 206 |
hw = clk_hw_register_mux_table(dev, name, parent_names, num_parents, flags, reg, shift, mask, clk_mux_flags, table, lock); |
264b31719 clk: mux: Add hw ... |
207 208 209 |
if (IS_ERR(hw)) return ERR_CAST(hw); return hw->clk; |
9d9f78ed9 clk: basic clock ... |
210 |
} |
5cfe10bb0 clk: export fixed... |
211 |
EXPORT_SYMBOL_GPL(clk_register_mux_table); |
ce4f3313b clk: add table lo... |
212 |
|
4e3c021fb clk: Add clk_unre... |
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
void clk_unregister_mux(struct clk *clk) { struct clk_mux *mux; struct clk_hw *hw; hw = __clk_get_hw(clk); if (!hw) return; mux = to_clk_mux(hw); clk_unregister(clk); kfree(mux); } EXPORT_SYMBOL_GPL(clk_unregister_mux); |
264b31719 clk: mux: Add hw ... |
228 229 230 231 232 233 234 235 236 237 238 |
void clk_hw_unregister_mux(struct clk_hw *hw) { struct clk_mux *mux; mux = to_clk_mux(hw); clk_hw_unregister(hw); kfree(mux); } EXPORT_SYMBOL_GPL(clk_hw_unregister_mux); |