clk-mux.c (6588B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> 4 * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> 5 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> 6 * 7 * Simple multiplexer clock implementation 8 */ 9 10#include <linux/clk-provider.h> 11#include <linux/device.h> 12#include <linux/module.h> 13#include <linux/slab.h> 14#include <linux/io.h> 15#include <linux/err.h> 16 17/* 18 * DOC: basic adjustable multiplexer clock that cannot gate 19 * 20 * Traits of this clock: 21 * prepare - clk_prepare only ensures that parents are prepared 22 * enable - clk_enable only ensures that parents are enabled 23 * rate - rate is only affected by parent switching. No clk_set_rate support 24 * parent - parent is adjustable through clk_set_parent 25 */ 26 27static inline u32 clk_mux_readl(struct clk_mux *mux) 28{ 29 if (mux->flags & CLK_MUX_BIG_ENDIAN) 30 return ioread32be(mux->reg); 31 32 return readl(mux->reg); 33} 34 35static inline void clk_mux_writel(struct clk_mux *mux, u32 val) 36{ 37 if (mux->flags & CLK_MUX_BIG_ENDIAN) 38 iowrite32be(val, mux->reg); 39 else 40 writel(val, mux->reg); 41} 42 43int clk_mux_val_to_index(struct clk_hw *hw, const u32 *table, unsigned int flags, 44 unsigned int val) 45{ 46 int num_parents = clk_hw_get_num_parents(hw); 47 48 if (table) { 49 int i; 50 51 for (i = 0; i < num_parents; i++) 52 if (table[i] == val) 53 return i; 54 return -EINVAL; 55 } 56 57 if (val && (flags & CLK_MUX_INDEX_BIT)) 58 val = ffs(val) - 1; 59 60 if (val && (flags & CLK_MUX_INDEX_ONE)) 61 val--; 62 63 if (val >= num_parents) 64 return -EINVAL; 65 66 return val; 67} 68EXPORT_SYMBOL_GPL(clk_mux_val_to_index); 69 70unsigned int clk_mux_index_to_val(const u32 *table, unsigned int flags, u8 index) 71{ 72 unsigned int val = index; 73 74 if (table) { 75 val = table[index]; 76 } else { 77 if (flags & CLK_MUX_INDEX_BIT) 78 val = 1 << index; 79 80 if (flags & CLK_MUX_INDEX_ONE) 81 val++; 82 } 83 84 return val; 85} 86EXPORT_SYMBOL_GPL(clk_mux_index_to_val); 87 88static u8 clk_mux_get_parent(struct clk_hw *hw) 89{ 90 struct clk_mux *mux = to_clk_mux(hw); 91 u32 val; 92 93 val = clk_mux_readl(mux) >> mux->shift; 94 val &= mux->mask; 95 96 return clk_mux_val_to_index(hw, mux->table, mux->flags, val); 97} 98 99static int clk_mux_set_parent(struct clk_hw *hw, u8 index) 100{ 101 struct clk_mux *mux = to_clk_mux(hw); 102 u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); 103 unsigned long flags = 0; 104 u32 reg; 105 106 if (mux->lock) 107 spin_lock_irqsave(mux->lock, flags); 108 else 109 __acquire(mux->lock); 110 111 if (mux->flags & CLK_MUX_HIWORD_MASK) { 112 reg = mux->mask << (mux->shift + 16); 113 } else { 114 reg = clk_mux_readl(mux); 115 reg &= ~(mux->mask << mux->shift); 116 } 117 val = val << mux->shift; 118 reg |= val; 119 clk_mux_writel(mux, reg); 120 121 if (mux->lock) 122 spin_unlock_irqrestore(mux->lock, flags); 123 else 124 __release(mux->lock); 125 126 return 0; 127} 128 129static int clk_mux_determine_rate(struct clk_hw *hw, 130 struct clk_rate_request *req) 131{ 132 struct clk_mux *mux = to_clk_mux(hw); 133 134 return clk_mux_determine_rate_flags(hw, req, mux->flags); 135} 136 137const struct clk_ops clk_mux_ops = { 138 .get_parent = clk_mux_get_parent, 139 .set_parent = clk_mux_set_parent, 140 .determine_rate = clk_mux_determine_rate, 141}; 142EXPORT_SYMBOL_GPL(clk_mux_ops); 143 144const struct clk_ops clk_mux_ro_ops = { 145 .get_parent = clk_mux_get_parent, 146}; 147EXPORT_SYMBOL_GPL(clk_mux_ro_ops); 148 149struct clk_hw *__clk_hw_register_mux(struct device *dev, struct device_node *np, 150 const char *name, u8 num_parents, 151 const char * const *parent_names, 152 const struct clk_hw **parent_hws, 153 const struct clk_parent_data *parent_data, 154 unsigned long flags, void __iomem *reg, u8 shift, u32 mask, 155 u8 clk_mux_flags, const u32 *table, spinlock_t *lock) 156{ 157 struct clk_mux *mux; 158 struct clk_hw *hw; 159 struct clk_init_data init = {}; 160 int ret = -EINVAL; 161 162 if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { 163 u8 width = fls(mask) - ffs(mask) + 1; 164 165 if (width + shift > 16) { 166 pr_err("mux value exceeds LOWORD field\n"); 167 return ERR_PTR(-EINVAL); 168 } 169 } 170 171 /* allocate the mux */ 172 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 173 if (!mux) 174 return ERR_PTR(-ENOMEM); 175 176 init.name = name; 177 if (clk_mux_flags & CLK_MUX_READ_ONLY) 178 init.ops = &clk_mux_ro_ops; 179 else 180 init.ops = &clk_mux_ops; 181 init.flags = flags; 182 init.parent_names = parent_names; 183 init.parent_data = parent_data; 184 init.parent_hws = parent_hws; 185 init.num_parents = num_parents; 186 187 /* struct clk_mux assignments */ 188 mux->reg = reg; 189 mux->shift = shift; 190 mux->mask = mask; 191 mux->flags = clk_mux_flags; 192 mux->lock = lock; 193 mux->table = table; 194 mux->hw.init = &init; 195 196 hw = &mux->hw; 197 if (dev || !np) 198 ret = clk_hw_register(dev, hw); 199 else if (np) 200 ret = of_clk_hw_register(np, hw); 201 if (ret) { 202 kfree(mux); 203 hw = ERR_PTR(ret); 204 } 205 206 return hw; 207} 208EXPORT_SYMBOL_GPL(__clk_hw_register_mux); 209 210static void devm_clk_hw_release_mux(struct device *dev, void *res) 211{ 212 clk_hw_unregister_mux(*(struct clk_hw **)res); 213} 214 215struct clk_hw *__devm_clk_hw_register_mux(struct device *dev, struct device_node *np, 216 const char *name, u8 num_parents, 217 const char * const *parent_names, 218 const struct clk_hw **parent_hws, 219 const struct clk_parent_data *parent_data, 220 unsigned long flags, void __iomem *reg, u8 shift, u32 mask, 221 u8 clk_mux_flags, const u32 *table, spinlock_t *lock) 222{ 223 struct clk_hw **ptr, *hw; 224 225 ptr = devres_alloc(devm_clk_hw_release_mux, sizeof(*ptr), GFP_KERNEL); 226 if (!ptr) 227 return ERR_PTR(-ENOMEM); 228 229 hw = __clk_hw_register_mux(dev, np, name, num_parents, parent_names, parent_hws, 230 parent_data, flags, reg, shift, mask, 231 clk_mux_flags, table, lock); 232 233 if (!IS_ERR(hw)) { 234 *ptr = hw; 235 devres_add(dev, ptr); 236 } else { 237 devres_free(ptr); 238 } 239 240 return hw; 241} 242EXPORT_SYMBOL_GPL(__devm_clk_hw_register_mux); 243 244struct clk *clk_register_mux_table(struct device *dev, const char *name, 245 const char * const *parent_names, u8 num_parents, 246 unsigned long flags, void __iomem *reg, u8 shift, u32 mask, 247 u8 clk_mux_flags, const u32 *table, spinlock_t *lock) 248{ 249 struct clk_hw *hw; 250 251 hw = clk_hw_register_mux_table(dev, name, parent_names, 252 num_parents, flags, reg, shift, mask, 253 clk_mux_flags, table, lock); 254 if (IS_ERR(hw)) 255 return ERR_CAST(hw); 256 return hw->clk; 257} 258EXPORT_SYMBOL_GPL(clk_register_mux_table); 259 260void clk_unregister_mux(struct clk *clk) 261{ 262 struct clk_mux *mux; 263 struct clk_hw *hw; 264 265 hw = __clk_get_hw(clk); 266 if (!hw) 267 return; 268 269 mux = to_clk_mux(hw); 270 271 clk_unregister(clk); 272 kfree(mux); 273} 274EXPORT_SYMBOL_GPL(clk_unregister_mux); 275 276void clk_hw_unregister_mux(struct clk_hw *hw) 277{ 278 struct clk_mux *mux; 279 280 mux = to_clk_mux(hw); 281 282 clk_hw_unregister(hw); 283 kfree(mux); 284} 285EXPORT_SYMBOL_GPL(clk_hw_unregister_mux);