linux/drivers/clk/tegra/clk-periph.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms and conditions of the GNU General Public License,
   6 * version 2, as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  15 */
  16
  17#include <linux/clk.h>
  18#include <linux/clk-provider.h>
  19#include <linux/slab.h>
  20#include <linux/err.h>
  21
  22#include "clk.h"
  23
  24static u8 clk_periph_get_parent(struct clk_hw *hw)
  25{
  26        struct tegra_clk_periph *periph = to_clk_periph(hw);
  27        const struct clk_ops *mux_ops = periph->mux_ops;
  28        struct clk_hw *mux_hw = &periph->mux.hw;
  29
  30        mux_hw->clk = hw->clk;
  31
  32        return mux_ops->get_parent(mux_hw);
  33}
  34
  35static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
  36{
  37        struct tegra_clk_periph *periph = to_clk_periph(hw);
  38        const struct clk_ops *mux_ops = periph->mux_ops;
  39        struct clk_hw *mux_hw = &periph->mux.hw;
  40
  41        mux_hw->clk = hw->clk;
  42
  43        return mux_ops->set_parent(mux_hw, index);
  44}
  45
  46static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
  47                                            unsigned long parent_rate)
  48{
  49        struct tegra_clk_periph *periph = to_clk_periph(hw);
  50        const struct clk_ops *div_ops = periph->div_ops;
  51        struct clk_hw *div_hw = &periph->divider.hw;
  52
  53        div_hw->clk = hw->clk;
  54
  55        return div_ops->recalc_rate(div_hw, parent_rate);
  56}
  57
  58static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
  59                                  unsigned long *prate)
  60{
  61        struct tegra_clk_periph *periph = to_clk_periph(hw);
  62        const struct clk_ops *div_ops = periph->div_ops;
  63        struct clk_hw *div_hw = &periph->divider.hw;
  64
  65        div_hw->clk = hw->clk;
  66
  67        return div_ops->round_rate(div_hw, rate, prate);
  68}
  69
  70static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
  71                               unsigned long parent_rate)
  72{
  73        struct tegra_clk_periph *periph = to_clk_periph(hw);
  74        const struct clk_ops *div_ops = periph->div_ops;
  75        struct clk_hw *div_hw = &periph->divider.hw;
  76
  77        div_hw->clk = hw->clk;
  78
  79        return div_ops->set_rate(div_hw, rate, parent_rate);
  80}
  81
  82static int clk_periph_is_enabled(struct clk_hw *hw)
  83{
  84        struct tegra_clk_periph *periph = to_clk_periph(hw);
  85        const struct clk_ops *gate_ops = periph->gate_ops;
  86        struct clk_hw *gate_hw = &periph->gate.hw;
  87
  88        gate_hw->clk = hw->clk;
  89
  90        return gate_ops->is_enabled(gate_hw);
  91}
  92
  93static int clk_periph_enable(struct clk_hw *hw)
  94{
  95        struct tegra_clk_periph *periph = to_clk_periph(hw);
  96        const struct clk_ops *gate_ops = periph->gate_ops;
  97        struct clk_hw *gate_hw = &periph->gate.hw;
  98
  99        gate_hw->clk = hw->clk;
 100
 101        return gate_ops->enable(gate_hw);
 102}
 103
 104static void clk_periph_disable(struct clk_hw *hw)
 105{
 106        struct tegra_clk_periph *periph = to_clk_periph(hw);
 107        const struct clk_ops *gate_ops = periph->gate_ops;
 108        struct clk_hw *gate_hw = &periph->gate.hw;
 109
 110        gate_ops->disable(gate_hw);
 111}
 112
 113void tegra_periph_reset_deassert(struct clk *c)
 114{
 115        struct clk_hw *hw = __clk_get_hw(c);
 116        struct tegra_clk_periph *periph = to_clk_periph(hw);
 117        struct tegra_clk_periph_gate *gate;
 118
 119        if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
 120                gate = to_clk_periph_gate(hw);
 121                if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
 122                        WARN_ON(1);
 123                        return;
 124                }
 125        } else {
 126                gate = &periph->gate;
 127        }
 128
 129        tegra_periph_reset(gate, 0);
 130}
 131
 132void tegra_periph_reset_assert(struct clk *c)
 133{
 134        struct clk_hw *hw = __clk_get_hw(c);
 135        struct tegra_clk_periph *periph = to_clk_periph(hw);
 136        struct tegra_clk_periph_gate *gate;
 137
 138        if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
 139                gate = to_clk_periph_gate(hw);
 140                if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
 141                        WARN_ON(1);
 142                        return;
 143                }
 144        } else {
 145                gate = &periph->gate;
 146        }
 147
 148        tegra_periph_reset(gate, 1);
 149}
 150
 151const struct clk_ops tegra_clk_periph_ops = {
 152        .get_parent = clk_periph_get_parent,
 153        .set_parent = clk_periph_set_parent,
 154        .recalc_rate = clk_periph_recalc_rate,
 155        .round_rate = clk_periph_round_rate,
 156        .set_rate = clk_periph_set_rate,
 157        .is_enabled = clk_periph_is_enabled,
 158        .enable = clk_periph_enable,
 159        .disable = clk_periph_disable,
 160};
 161
 162const struct clk_ops tegra_clk_periph_nodiv_ops = {
 163        .get_parent = clk_periph_get_parent,
 164        .set_parent = clk_periph_set_parent,
 165        .is_enabled = clk_periph_is_enabled,
 166        .enable = clk_periph_enable,
 167        .disable = clk_periph_disable,
 168};
 169
 170static struct clk *_tegra_clk_register_periph(const char *name,
 171                        const char **parent_names, int num_parents,
 172                        struct tegra_clk_periph *periph,
 173                        void __iomem *clk_base, u32 offset, bool div)
 174{
 175        struct clk *clk;
 176        struct clk_init_data init;
 177
 178        init.name = name;
 179        init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
 180        init.flags = div ? 0 : CLK_SET_RATE_PARENT;
 181        init.parent_names = parent_names;
 182        init.num_parents = num_parents;
 183
 184        /* Data in .init is copied by clk_register(), so stack variable OK */
 185        periph->hw.init = &init;
 186        periph->magic = TEGRA_CLK_PERIPH_MAGIC;
 187        periph->mux.reg = clk_base + offset;
 188        periph->divider.reg = div ? (clk_base + offset) : NULL;
 189        periph->gate.clk_base = clk_base;
 190
 191        clk = clk_register(NULL, &periph->hw);
 192        if (IS_ERR(clk))
 193                return clk;
 194
 195        periph->mux.hw.clk = clk;
 196        periph->divider.hw.clk = div ? clk : NULL;
 197        periph->gate.hw.clk = clk;
 198
 199        return clk;
 200}
 201
 202struct clk *tegra_clk_register_periph(const char *name,
 203                const char **parent_names, int num_parents,
 204                struct tegra_clk_periph *periph, void __iomem *clk_base,
 205                u32 offset)
 206{
 207        return _tegra_clk_register_periph(name, parent_names, num_parents,
 208                        periph, clk_base, offset, true);
 209}
 210
 211struct clk *tegra_clk_register_periph_nodiv(const char *name,
 212                const char **parent_names, int num_parents,
 213                struct tegra_clk_periph *periph, void __iomem *clk_base,
 214                u32 offset)
 215{
 216        return _tegra_clk_register_periph(name, parent_names, num_parents,
 217                        periph, clk_base, offset, false);
 218}
 219