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-provider.h>
  18#include <linux/export.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        __clk_hw_set_clk(mux_hw, hw);
  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        __clk_hw_set_clk(mux_hw, hw);
  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        __clk_hw_set_clk(div_hw, hw);
  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        __clk_hw_set_clk(div_hw, hw);
  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        __clk_hw_set_clk(div_hw, hw);
  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        __clk_hw_set_clk(gate_hw, hw);
  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        __clk_hw_set_clk(gate_hw, hw);
 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
 113const struct clk_ops tegra_clk_periph_ops = {
 114        .get_parent = clk_periph_get_parent,
 115        .set_parent = clk_periph_set_parent,
 116        .recalc_rate = clk_periph_recalc_rate,
 117        .round_rate = clk_periph_round_rate,
 118        .set_rate = clk_periph_set_rate,
 119        .is_enabled = clk_periph_is_enabled,
 120        .enable = clk_periph_enable,
 121        .disable = clk_periph_disable,
 122};
 123
 124static const struct clk_ops tegra_clk_periph_nodiv_ops = {
 125        .get_parent = clk_periph_get_parent,
 126        .set_parent = clk_periph_set_parent,
 127        .is_enabled = clk_periph_is_enabled,
 128        .enable = clk_periph_enable,
 129        .disable = clk_periph_disable,
 130};
 131
 132static const struct clk_ops tegra_clk_periph_no_gate_ops = {
 133        .get_parent = clk_periph_get_parent,
 134        .set_parent = clk_periph_set_parent,
 135        .recalc_rate = clk_periph_recalc_rate,
 136        .round_rate = clk_periph_round_rate,
 137        .set_rate = clk_periph_set_rate,
 138};
 139
 140static struct clk *_tegra_clk_register_periph(const char *name,
 141                        const char **parent_names, int num_parents,
 142                        struct tegra_clk_periph *periph,
 143                        void __iomem *clk_base, u32 offset,
 144                        unsigned long flags)
 145{
 146        struct clk *clk;
 147        struct clk_init_data init;
 148        struct tegra_clk_periph_regs *bank;
 149        bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
 150
 151        if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
 152                flags |= CLK_SET_RATE_PARENT;
 153                init.ops = &tegra_clk_periph_nodiv_ops;
 154        } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
 155                init.ops = &tegra_clk_periph_no_gate_ops;
 156        else
 157                init.ops = &tegra_clk_periph_ops;
 158
 159        init.name = name;
 160        init.flags = flags;
 161        init.parent_names = parent_names;
 162        init.num_parents = num_parents;
 163
 164        bank = get_reg_bank(periph->gate.clk_num);
 165        if (!bank)
 166                return ERR_PTR(-EINVAL);
 167
 168        /* Data in .init is copied by clk_register(), so stack variable OK */
 169        periph->hw.init = &init;
 170        periph->magic = TEGRA_CLK_PERIPH_MAGIC;
 171        periph->mux.reg = clk_base + offset;
 172        periph->divider.reg = div ? (clk_base + offset) : NULL;
 173        periph->gate.clk_base = clk_base;
 174        periph->gate.regs = bank;
 175        periph->gate.enable_refcnt = periph_clk_enb_refcnt;
 176
 177        clk = clk_register(NULL, &periph->hw);
 178        if (IS_ERR(clk))
 179                return clk;
 180
 181        periph->mux.hw.clk = clk;
 182        periph->divider.hw.clk = div ? clk : NULL;
 183        periph->gate.hw.clk = clk;
 184
 185        return clk;
 186}
 187
 188struct clk *tegra_clk_register_periph(const char *name,
 189                const char **parent_names, int num_parents,
 190                struct tegra_clk_periph *periph, void __iomem *clk_base,
 191                u32 offset, unsigned long flags)
 192{
 193        return _tegra_clk_register_periph(name, parent_names, num_parents,
 194                        periph, clk_base, offset, flags);
 195}
 196
 197struct clk *tegra_clk_register_periph_nodiv(const char *name,
 198                const char **parent_names, int num_parents,
 199                struct tegra_clk_periph *periph, void __iomem *clk_base,
 200                u32 offset)
 201{
 202        periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
 203        return _tegra_clk_register_periph(name, parent_names, num_parents,
 204                        periph, clk_base, offset, CLK_SET_RATE_PARENT);
 205}
 206