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/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, 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(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, u32 *table, spinlock_t *lock)
 156{
 157        struct clk_mux *mux;
 158        struct clk_hw *hw;
 159        struct clk_init_data init = {};
 160        u8 width = 0;
 161        int ret = -EINVAL;
 162
 163        if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
 164                width = fls(mask) - ffs(mask) + 1;
 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, 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, 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