linux/drivers/clk/mediatek/clk-mux.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2018 MediaTek Inc.
   4 * Author: Owen Chen <owen.chen@mediatek.com>
   5 */
   6
   7#include <linux/of.h>
   8#include <linux/of_address.h>
   9#include <linux/slab.h>
  10#include <linux/mfd/syscon.h>
  11
  12#include "clk-mtk.h"
  13#include "clk-mux.h"
  14
  15static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
  16{
  17        return container_of(hw, struct mtk_clk_mux, hw);
  18}
  19
  20static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
  21{
  22        struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
  23        unsigned long flags = 0;
  24
  25        if (mux->lock)
  26                spin_lock_irqsave(mux->lock, flags);
  27        else
  28                __acquire(mux->lock);
  29
  30        regmap_write(mux->regmap, mux->data->clr_ofs,
  31                     BIT(mux->data->gate_shift));
  32
  33        /*
  34         * If the parent has been changed when the clock was disabled, it will
  35         * not be effective yet. Set the update bit to ensure the mux gets
  36         * updated.
  37         */
  38        if (mux->reparent && mux->data->upd_shift >= 0) {
  39                regmap_write(mux->regmap, mux->data->upd_ofs,
  40                             BIT(mux->data->upd_shift));
  41                mux->reparent = false;
  42        }
  43
  44        if (mux->lock)
  45                spin_unlock_irqrestore(mux->lock, flags);
  46        else
  47                __release(mux->lock);
  48
  49        return 0;
  50}
  51
  52static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
  53{
  54        struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
  55
  56        regmap_write(mux->regmap, mux->data->set_ofs,
  57                        BIT(mux->data->gate_shift));
  58}
  59
  60static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
  61{
  62        struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
  63        u32 val;
  64
  65        regmap_read(mux->regmap, mux->data->mux_ofs, &val);
  66
  67        return (val & BIT(mux->data->gate_shift)) == 0;
  68}
  69
  70static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
  71{
  72        struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
  73        u32 mask = GENMASK(mux->data->mux_width - 1, 0);
  74        u32 val;
  75
  76        regmap_read(mux->regmap, mux->data->mux_ofs, &val);
  77        val = (val >> mux->data->mux_shift) & mask;
  78
  79        return val;
  80}
  81
  82static int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index)
  83{
  84        struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
  85        u32 mask = GENMASK(mux->data->mux_width - 1, 0);
  86        u32 val, orig;
  87        unsigned long flags = 0;
  88
  89        if (mux->lock)
  90                spin_lock_irqsave(mux->lock, flags);
  91        else
  92                __acquire(mux->lock);
  93
  94        regmap_read(mux->regmap, mux->data->mux_ofs, &orig);
  95        val = (orig & ~(mask << mux->data->mux_shift))
  96                        | (index << mux->data->mux_shift);
  97
  98        if (val != orig) {
  99                regmap_write(mux->regmap, mux->data->clr_ofs,
 100                                mask << mux->data->mux_shift);
 101                regmap_write(mux->regmap, mux->data->set_ofs,
 102                                index << mux->data->mux_shift);
 103
 104                if (mux->data->upd_shift >= 0) {
 105                        regmap_write(mux->regmap, mux->data->upd_ofs,
 106                                        BIT(mux->data->upd_shift));
 107                        mux->reparent = true;
 108                }
 109        }
 110
 111        if (mux->lock)
 112                spin_unlock_irqrestore(mux->lock, flags);
 113        else
 114                __release(mux->lock);
 115
 116        return 0;
 117}
 118
 119static const struct clk_ops mtk_mux_ops = {
 120        .enable = mtk_clk_mux_enable_setclr,
 121        .disable = mtk_clk_mux_disable_setclr,
 122        .is_enabled = mtk_clk_mux_is_enabled,
 123        .get_parent = mtk_clk_mux_get_parent,
 124        .set_parent = mtk_clk_mux_set_parent_setclr_lock,
 125};
 126
 127static struct clk *mtk_clk_register_mux(const struct mtk_mux *mux,
 128                                 struct regmap *regmap,
 129                                 spinlock_t *lock)
 130{
 131        struct mtk_clk_mux *clk_mux;
 132        struct clk_init_data init = {};
 133        struct clk *clk;
 134
 135        clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL);
 136        if (!clk_mux)
 137                return ERR_PTR(-ENOMEM);
 138
 139        init.name = mux->name;
 140        init.flags = mux->flags | CLK_SET_RATE_PARENT;
 141        init.parent_names = mux->parent_names;
 142        init.num_parents = mux->num_parents;
 143        init.ops = &mtk_mux_ops;
 144
 145        clk_mux->regmap = regmap;
 146        clk_mux->data = mux;
 147        clk_mux->lock = lock;
 148        clk_mux->hw.init = &init;
 149
 150        clk = clk_register(NULL, &clk_mux->hw);
 151        if (IS_ERR(clk)) {
 152                kfree(clk_mux);
 153                return clk;
 154        }
 155
 156        return clk;
 157}
 158
 159int mtk_clk_register_muxes(const struct mtk_mux *muxes,
 160                           int num, struct device_node *node,
 161                           spinlock_t *lock,
 162                           struct clk_onecell_data *clk_data)
 163{
 164        struct regmap *regmap;
 165        struct clk *clk;
 166        int i;
 167
 168        regmap = syscon_node_to_regmap(node);
 169        if (IS_ERR(regmap)) {
 170                pr_err("Cannot find regmap for %pOF: %ld\n", node,
 171                       PTR_ERR(regmap));
 172                return PTR_ERR(regmap);
 173        }
 174
 175        for (i = 0; i < num; i++) {
 176                const struct mtk_mux *mux = &muxes[i];
 177
 178                if (IS_ERR_OR_NULL(clk_data->clks[mux->id])) {
 179                        clk = mtk_clk_register_mux(mux, regmap, lock);
 180
 181                        if (IS_ERR(clk)) {
 182                                pr_err("Failed to register clk %s: %ld\n",
 183                                       mux->name, PTR_ERR(clk));
 184                                continue;
 185                        }
 186
 187                        clk_data->clks[mux->id] = clk;
 188                }
 189        }
 190
 191        return 0;
 192}
 193