linux/drivers/clk/clk-mux.c
<<
>>
Prefs
   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
  26static 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
  34static 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
  42int 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}
  67EXPORT_SYMBOL_GPL(clk_mux_val_to_index);
  68
  69unsigned 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}
  85EXPORT_SYMBOL_GPL(clk_mux_index_to_val);
  86
  87static 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
  98static 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
 128static 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
 136const 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};
 141EXPORT_SYMBOL_GPL(clk_mux_ops);
 142
 143const struct clk_ops clk_mux_ro_ops = {
 144        .get_parent = clk_mux_get_parent,
 145};
 146EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
 147
 148struct 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}
 207EXPORT_SYMBOL_GPL(__clk_hw_register_mux);
 208
 209struct 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}
 223EXPORT_SYMBOL_GPL(clk_register_mux_table);
 224
 225void 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}
 239EXPORT_SYMBOL_GPL(clk_unregister_mux);
 240
 241void 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}
 250EXPORT_SYMBOL_GPL(clk_hw_unregister_mux);
 251