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/export.h>
  20#include <linux/slab.h>
  21#include <linux/err.h>
  22
  23#include "clk.h"
  24
  25static u8 clk_periph_get_parent(struct clk_hw *hw)
  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        mux_hw->clk = hw->clk;
  32
  33        return mux_ops->get_parent(mux_hw);
  34}
  35
  36static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
  37{
  38        struct tegra_clk_periph *periph = to_clk_periph(hw);
  39        const struct clk_ops *mux_ops = periph->mux_ops;
  40        struct clk_hw *mux_hw = &periph->mux.hw;
  41
  42        mux_hw->clk = hw->clk;
  43
  44        return mux_ops->set_parent(mux_hw, index);
  45}
  46
  47static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
  48                                            unsigned long parent_rate)
  49{
  50        struct tegra_clk_periph *periph = to_clk_periph(hw);
  51        const struct clk_ops *div_ops = periph->div_ops;
  52        struct clk_hw *div_hw = &periph->divider.hw;
  53
  54        div_hw->clk = hw->clk;
  55
  56        return div_ops->recalc_rate(div_hw, parent_rate);
  57}
  58
  59static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
  60                                  unsigned long *prate)
  61{
  62        struct tegra_clk_periph *periph = to_clk_periph(hw);
  63        const struct clk_ops *div_ops = periph->div_ops;
  64        struct clk_hw *div_hw = &periph->divider.hw;
  65
  66        div_hw->clk = hw->clk;
  67
  68        return div_ops->round_rate(div_hw, rate, prate);
  69}
  70
  71static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
  72                               unsigned long parent_rate)
  73{
  74        struct tegra_clk_periph *periph = to_clk_periph(hw);
  75        const struct clk_ops *div_ops = periph->div_ops;
  76        struct clk_hw *div_hw = &periph->divider.hw;
  77
  78        div_hw->clk = hw->clk;
  79
  80        return div_ops->set_rate(div_hw, rate, parent_rate);
  81}
  82
  83static int clk_periph_is_enabled(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        gate_hw->clk = hw->clk;
  90
  91        return gate_ops->is_enabled(gate_hw);
  92}
  93
  94static int clk_periph_enable(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_hw->clk = hw->clk;
 101
 102        return gate_ops->enable(gate_hw);
 103}
 104
 105static void clk_periph_disable(struct clk_hw *hw)
 106{
 107        struct tegra_clk_periph *periph = to_clk_periph(hw);
 108        const struct clk_ops *gate_ops = periph->gate_ops;
 109        struct clk_hw *gate_hw = &periph->gate.hw;
 110
 111        gate_ops->disable(gate_hw);
 112}
 113
 114void tegra_periph_reset_deassert(struct clk *c)
 115{
 116        struct clk_hw *hw = __clk_get_hw(c);
 117        struct tegra_clk_periph *periph = to_clk_periph(hw);
 118        struct tegra_clk_periph_gate *gate;
 119
 120        if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
 121                gate = to_clk_periph_gate(hw);
 122                if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
 123                        WARN_ON(1);
 124                        return;
 125                }
 126        } else {
 127                gate = &periph->gate;
 128        }
 129
 130        tegra_periph_reset(gate, 0);
 131}
 132EXPORT_SYMBOL(tegra_periph_reset_deassert);
 133
 134void tegra_periph_reset_assert(struct clk *c)
 135{
 136        struct clk_hw *hw = __clk_get_hw(c);
 137        struct tegra_clk_periph *periph = to_clk_periph(hw);
 138        struct tegra_clk_periph_gate *gate;
 139
 140        if (periph->magic != TEGRA_CLK_PERIPH_MAGIC) {
 141                gate = to_clk_periph_gate(hw);
 142                if (gate->magic != TEGRA_CLK_PERIPH_GATE_MAGIC) {
 143                        WARN_ON(1);
 144                        return;
 145                }
 146        } else {
 147                gate = &periph->gate;
 148        }
 149
 150        tegra_periph_reset(gate, 1);
 151}
 152EXPORT_SYMBOL(tegra_periph_reset_assert);
 153
 154const struct clk_ops tegra_clk_periph_ops = {
 155        .get_parent = clk_periph_get_parent,
 156        .set_parent = clk_periph_set_parent,
 157        .recalc_rate = clk_periph_recalc_rate,
 158        .round_rate = clk_periph_round_rate,
 159        .set_rate = clk_periph_set_rate,
 160        .is_enabled = clk_periph_is_enabled,
 161        .enable = clk_periph_enable,
 162        .disable = clk_periph_disable,
 163};
 164
 165const struct clk_ops tegra_clk_periph_nodiv_ops = {
 166        .get_parent = clk_periph_get_parent,
 167        .set_parent = clk_periph_set_parent,
 168        .is_enabled = clk_periph_is_enabled,
 169        .enable = clk_periph_enable,
 170        .disable = clk_periph_disable,
 171};
 172
 173static struct clk *_tegra_clk_register_periph(const char *name,
 174                        const char **parent_names, int num_parents,
 175                        struct tegra_clk_periph *periph,
 176                        void __iomem *clk_base, u32 offset, bool div,
 177                        unsigned long flags)
 178{
 179        struct clk *clk;
 180        struct clk_init_data init;
 181
 182        init.name = name;
 183        init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
 184        init.flags = flags;
 185        init.parent_names = parent_names;
 186        init.num_parents = num_parents;
 187
 188        /* Data in .init is copied by clk_register(), so stack variable OK */
 189        periph->hw.init = &init;
 190        periph->magic = TEGRA_CLK_PERIPH_MAGIC;
 191        periph->mux.reg = clk_base + offset;
 192        periph->divider.reg = div ? (clk_base + offset) : NULL;
 193        periph->gate.clk_base = clk_base;
 194
 195        clk = clk_register(NULL, &periph->hw);
 196        if (IS_ERR(clk))
 197                return clk;
 198
 199        periph->mux.hw.clk = clk;
 200        periph->divider.hw.clk = div ? clk : NULL;
 201        periph->gate.hw.clk = clk;
 202
 203        return clk;
 204}
 205
 206struct clk *tegra_clk_register_periph(const char *name,
 207                const char **parent_names, int num_parents,
 208                struct tegra_clk_periph *periph, void __iomem *clk_base,
 209                u32 offset, unsigned long flags)
 210{
 211        return _tegra_clk_register_periph(name, parent_names, num_parents,
 212                        periph, clk_base, offset, true, flags);
 213}
 214
 215struct clk *tegra_clk_register_periph_nodiv(const char *name,
 216                const char **parent_names, int num_parents,
 217                struct tegra_clk_periph *periph, void __iomem *clk_base,
 218                u32 offset)
 219{
 220        return _tegra_clk_register_periph(name, parent_names, num_parents,
 221                        periph, clk_base, offset, false, CLK_SET_RATE_PARENT);
 222}
 223