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