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