286 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 implementation8 */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 gate19 *20 * Traits of this clock:21 * prepare - clk_prepare only ensures that parents are prepared22 * enable - clk_enable only ensures that parents are enabled23 * rate - rate is only affected by parent switching. No clk_set_rate support24 * parent - parent is adjustable through clk_set_parent25 */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 else40 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 else109 __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 else124 __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 else180 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);286