linux/drivers/clk/tegra/clk-periph.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/clk-provider.h>
   8#include <linux/export.h>
   9#include <linux/slab.h>
  10#include <linux/err.h>
  11
  12#include "clk.h"
  13
  14static u8 clk_periph_get_parent(struct clk_hw *hw)
  15{
  16        struct tegra_clk_periph *periph = to_clk_periph(hw);
  17        const struct clk_ops *mux_ops = periph->mux_ops;
  18        struct clk_hw *mux_hw = &periph->mux.hw;
  19
  20        __clk_hw_set_clk(mux_hw, hw);
  21
  22        return mux_ops->get_parent(mux_hw);
  23}
  24
  25static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
  26{
  27        struct tegra_clk_periph *periph = to_clk_periph(hw);
  28        const struct clk_ops *mux_ops = periph->mux_ops;
  29        struct clk_hw *mux_hw = &periph->mux.hw;
  30
  31        __clk_hw_set_clk(mux_hw, hw);
  32
  33        return mux_ops->set_parent(mux_hw, index);
  34}
  35
  36static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
  37                                            unsigned long parent_rate)
  38{
  39        struct tegra_clk_periph *periph = to_clk_periph(hw);
  40        const struct clk_ops *div_ops = periph->div_ops;
  41        struct clk_hw *div_hw = &periph->divider.hw;
  42
  43        __clk_hw_set_clk(div_hw, hw);
  44
  45        return div_ops->recalc_rate(div_hw, parent_rate);
  46}
  47
  48static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
  49                                  unsigned long *prate)
  50{
  51        struct tegra_clk_periph *periph = to_clk_periph(hw);
  52        const struct clk_ops *div_ops = periph->div_ops;
  53        struct clk_hw *div_hw = &periph->divider.hw;
  54
  55        __clk_hw_set_clk(div_hw, hw);
  56
  57        return div_ops->round_rate(div_hw, rate, prate);
  58}
  59
  60static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
  61                               unsigned long parent_rate)
  62{
  63        struct tegra_clk_periph *periph = to_clk_periph(hw);
  64        const struct clk_ops *div_ops = periph->div_ops;
  65        struct clk_hw *div_hw = &periph->divider.hw;
  66
  67        __clk_hw_set_clk(div_hw, hw);
  68
  69        return div_ops->set_rate(div_hw, rate, parent_rate);
  70}
  71
  72static int clk_periph_is_enabled(struct clk_hw *hw)
  73{
  74        struct tegra_clk_periph *periph = to_clk_periph(hw);
  75        const struct clk_ops *gate_ops = periph->gate_ops;
  76        struct clk_hw *gate_hw = &periph->gate.hw;
  77
  78        __clk_hw_set_clk(gate_hw, hw);
  79
  80        return gate_ops->is_enabled(gate_hw);
  81}
  82
  83static int clk_periph_enable(struct clk_hw *hw)
  84{
  85        struct tegra_clk_periph *periph = to_clk_periph(hw);
  86        const struct clk_ops *gate_ops = periph->gate_ops;
  87        struct clk_hw *gate_hw = &periph->gate.hw;
  88
  89        __clk_hw_set_clk(gate_hw, hw);
  90
  91        return gate_ops->enable(gate_hw);
  92}
  93
  94static void clk_periph_disable(struct clk_hw *hw)
  95{
  96        struct tegra_clk_periph *periph = to_clk_periph(hw);
  97        const struct clk_ops *gate_ops = periph->gate_ops;
  98        struct clk_hw *gate_hw = &periph->gate.hw;
  99
 100        gate_ops->disable(gate_hw);
 101}
 102
 103static void clk_periph_restore_context(struct clk_hw *hw)
 104{
 105        struct tegra_clk_periph *periph = to_clk_periph(hw);
 106        const struct clk_ops *div_ops = periph->div_ops;
 107        struct clk_hw *div_hw = &periph->divider.hw;
 108        int parent_id;
 109
 110        parent_id = clk_hw_get_parent_index(hw);
 111        if (WARN_ON(parent_id < 0))
 112                return;
 113
 114        if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
 115                div_ops->restore_context(div_hw);
 116
 117        clk_periph_set_parent(hw, parent_id);
 118}
 119
 120const struct clk_ops tegra_clk_periph_ops = {
 121        .get_parent = clk_periph_get_parent,
 122        .set_parent = clk_periph_set_parent,
 123        .recalc_rate = clk_periph_recalc_rate,
 124        .round_rate = clk_periph_round_rate,
 125        .set_rate = clk_periph_set_rate,
 126        .is_enabled = clk_periph_is_enabled,
 127        .enable = clk_periph_enable,
 128        .disable = clk_periph_disable,
 129        .restore_context = clk_periph_restore_context,
 130};
 131
 132static const struct clk_ops tegra_clk_periph_nodiv_ops = {
 133        .get_parent = clk_periph_get_parent,
 134        .set_parent = clk_periph_set_parent,
 135        .is_enabled = clk_periph_is_enabled,
 136        .enable = clk_periph_enable,
 137        .disable = clk_periph_disable,
 138        .restore_context = clk_periph_restore_context,
 139};
 140
 141static const struct clk_ops tegra_clk_periph_no_gate_ops = {
 142        .get_parent = clk_periph_get_parent,
 143        .set_parent = clk_periph_set_parent,
 144        .recalc_rate = clk_periph_recalc_rate,
 145        .round_rate = clk_periph_round_rate,
 146        .set_rate = clk_periph_set_rate,
 147        .restore_context = clk_periph_restore_context,
 148};
 149
 150static struct clk *_tegra_clk_register_periph(const char *name,
 151                        const char * const *parent_names, int num_parents,
 152                        struct tegra_clk_periph *periph,
 153                        void __iomem *clk_base, u32 offset,
 154                        unsigned long flags)
 155{
 156        struct clk *clk;
 157        struct clk_init_data init;
 158        const struct tegra_clk_periph_regs *bank;
 159        bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
 160
 161        if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
 162                flags |= CLK_SET_RATE_PARENT;
 163                init.ops = &tegra_clk_periph_nodiv_ops;
 164        } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
 165                init.ops = &tegra_clk_periph_no_gate_ops;
 166        else
 167                init.ops = &tegra_clk_periph_ops;
 168
 169        init.name = name;
 170        init.flags = flags;
 171        init.parent_names = parent_names;
 172        init.num_parents = num_parents;
 173
 174        bank = get_reg_bank(periph->gate.clk_num);
 175        if (!bank)
 176                return ERR_PTR(-EINVAL);
 177
 178        /* Data in .init is copied by clk_register(), so stack variable OK */
 179        periph->hw.init = &init;
 180        periph->magic = TEGRA_CLK_PERIPH_MAGIC;
 181        periph->mux.reg = clk_base + offset;
 182        periph->divider.reg = div ? (clk_base + offset) : NULL;
 183        periph->gate.clk_base = clk_base;
 184        periph->gate.regs = bank;
 185        periph->gate.enable_refcnt = periph_clk_enb_refcnt;
 186
 187        clk = clk_register(NULL, &periph->hw);
 188        if (IS_ERR(clk))
 189                return clk;
 190
 191        periph->mux.hw.clk = clk;
 192        periph->divider.hw.clk = div ? clk : NULL;
 193        periph->gate.hw.clk = clk;
 194
 195        return clk;
 196}
 197
 198struct clk *tegra_clk_register_periph(const char *name,
 199                const char * const *parent_names, int num_parents,
 200                struct tegra_clk_periph *periph, void __iomem *clk_base,
 201                u32 offset, unsigned long flags)
 202{
 203        return _tegra_clk_register_periph(name, parent_names, num_parents,
 204                        periph, clk_base, offset, flags);
 205}
 206
 207struct clk *tegra_clk_register_periph_nodiv(const char *name,
 208                const char * const *parent_names, int num_parents,
 209                struct tegra_clk_periph *periph, void __iomem *clk_base,
 210                u32 offset)
 211{
 212        periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
 213        return _tegra_clk_register_periph(name, parent_names, num_parents,
 214                        periph, clk_base, offset, CLK_SET_RATE_PARENT);
 215}
 216
 217struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
 218                                           struct tegra_periph_init_data *init)
 219{
 220        return _tegra_clk_register_periph(init->name, init->p.parent_names,
 221                                          init->num_parents, &init->periph,
 222                                          clk_base, init->offset, init->flags);
 223}
 224