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_disable_unused(struct clk_hw *hw)
 104{
 105        struct tegra_clk_periph *periph = to_clk_periph(hw);
 106        const struct clk_ops *gate_ops = periph->gate_ops;
 107        struct clk_hw *gate_hw = &periph->gate.hw;
 108
 109        gate_ops->disable_unused(gate_hw);
 110}
 111
 112static void clk_periph_restore_context(struct clk_hw *hw)
 113{
 114        struct tegra_clk_periph *periph = to_clk_periph(hw);
 115        const struct clk_ops *div_ops = periph->div_ops;
 116        struct clk_hw *div_hw = &periph->divider.hw;
 117        int parent_id;
 118
 119        parent_id = clk_hw_get_parent_index(hw);
 120        if (WARN_ON(parent_id < 0))
 121                return;
 122
 123        if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
 124                div_ops->restore_context(div_hw);
 125
 126        clk_periph_set_parent(hw, parent_id);
 127}
 128
 129const struct clk_ops tegra_clk_periph_ops = {
 130        .get_parent = clk_periph_get_parent,
 131        .set_parent = clk_periph_set_parent,
 132        .recalc_rate = clk_periph_recalc_rate,
 133        .round_rate = clk_periph_round_rate,
 134        .set_rate = clk_periph_set_rate,
 135        .is_enabled = clk_periph_is_enabled,
 136        .enable = clk_periph_enable,
 137        .disable = clk_periph_disable,
 138        .disable_unused = clk_periph_disable_unused,
 139        .restore_context = clk_periph_restore_context,
 140};
 141
 142static const struct clk_ops tegra_clk_periph_nodiv_ops = {
 143        .get_parent = clk_periph_get_parent,
 144        .set_parent = clk_periph_set_parent,
 145        .is_enabled = clk_periph_is_enabled,
 146        .enable = clk_periph_enable,
 147        .disable = clk_periph_disable,
 148        .disable_unused = clk_periph_disable_unused,
 149        .restore_context = clk_periph_restore_context,
 150};
 151
 152static const struct clk_ops tegra_clk_periph_no_gate_ops = {
 153        .get_parent = clk_periph_get_parent,
 154        .set_parent = clk_periph_set_parent,
 155        .recalc_rate = clk_periph_recalc_rate,
 156        .round_rate = clk_periph_round_rate,
 157        .set_rate = clk_periph_set_rate,
 158        .restore_context = clk_periph_restore_context,
 159};
 160
 161static struct clk *_tegra_clk_register_periph(const char *name,
 162                        const char * const *parent_names, int num_parents,
 163                        struct tegra_clk_periph *periph,
 164                        void __iomem *clk_base, u32 offset,
 165                        unsigned long flags)
 166{
 167        struct clk *clk;
 168        struct clk_init_data init;
 169        const struct tegra_clk_periph_regs *bank;
 170        bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
 171
 172        if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
 173                flags |= CLK_SET_RATE_PARENT;
 174                init.ops = &tegra_clk_periph_nodiv_ops;
 175        } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
 176                init.ops = &tegra_clk_periph_no_gate_ops;
 177        else
 178                init.ops = &tegra_clk_periph_ops;
 179
 180        init.name = name;
 181        init.flags = flags;
 182        init.parent_names = parent_names;
 183        init.num_parents = num_parents;
 184
 185        bank = get_reg_bank(periph->gate.clk_num);
 186        if (!bank)
 187                return ERR_PTR(-EINVAL);
 188
 189        /* Data in .init is copied by clk_register(), so stack variable OK */
 190        periph->hw.init = &init;
 191        periph->magic = TEGRA_CLK_PERIPH_MAGIC;
 192        periph->mux.reg = clk_base + offset;
 193        periph->divider.reg = div ? (clk_base + offset) : NULL;
 194        periph->gate.clk_base = clk_base;
 195        periph->gate.regs = bank;
 196        periph->gate.enable_refcnt = periph_clk_enb_refcnt;
 197
 198        clk = clk_register(NULL, &periph->hw);
 199        if (IS_ERR(clk))
 200                return clk;
 201
 202        periph->mux.hw.clk = clk;
 203        periph->divider.hw.clk = div ? clk : NULL;
 204        periph->gate.hw.clk = clk;
 205
 206        return clk;
 207}
 208
 209struct clk *tegra_clk_register_periph(const char *name,
 210                const char * const *parent_names, int num_parents,
 211                struct tegra_clk_periph *periph, void __iomem *clk_base,
 212                u32 offset, unsigned long flags)
 213{
 214        return _tegra_clk_register_periph(name, parent_names, num_parents,
 215                        periph, clk_base, offset, flags);
 216}
 217
 218struct clk *tegra_clk_register_periph_nodiv(const char *name,
 219                const char * const *parent_names, int num_parents,
 220                struct tegra_clk_periph *periph, void __iomem *clk_base,
 221                u32 offset)
 222{
 223        periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
 224        return _tegra_clk_register_periph(name, parent_names, num_parents,
 225                        periph, clk_base, offset, CLK_SET_RATE_PARENT);
 226}
 227
 228struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
 229                                           struct tegra_periph_init_data *init)
 230{
 231        return _tegra_clk_register_periph(init->name, init->p.parent_names,
 232                                          init->num_parents, &init->periph,
 233                                          clk_base, init->offset, init->flags);
 234}
 235