linux/drivers/clk/tegra/clk-super.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/kernel.h>
  18#include <linux/io.h>
  19#include <linux/delay.h>
  20#include <linux/err.h>
  21#include <linux/slab.h>
  22#include <linux/clk-provider.h>
  23
  24#include "clk.h"
  25
  26#define SUPER_STATE_IDLE 0
  27#define SUPER_STATE_RUN 1
  28#define SUPER_STATE_IRQ 2
  29#define SUPER_STATE_FIQ 3
  30
  31#define SUPER_STATE_SHIFT 28
  32#define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \
  33                           BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \
  34                          << SUPER_STATE_SHIFT)
  35
  36#define SUPER_LP_DIV2_BYPASS (1 << 16)
  37
  38#define super_state(s) (BIT(s) << SUPER_STATE_SHIFT)
  39#define super_state_to_src_shift(m, s) ((m->width * s))
  40#define super_state_to_src_mask(m) (((1 << m->width) - 1))
  41
  42static u8 clk_super_get_parent(struct clk_hw *hw)
  43{
  44        struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
  45        u32 val, state;
  46        u8 source, shift;
  47
  48        val = readl_relaxed(mux->reg);
  49
  50        state = val & SUPER_STATE_MASK;
  51
  52        BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
  53               (state != super_state(SUPER_STATE_IDLE)));
  54        shift = (state == super_state(SUPER_STATE_IDLE)) ?
  55                super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
  56                super_state_to_src_shift(mux, SUPER_STATE_RUN);
  57
  58        source = (val >> shift) & super_state_to_src_mask(mux);
  59
  60        /*
  61         * If LP_DIV2_BYPASS is not set and PLLX is current parent then
  62         * PLLX/2 is the input source to CCLKLP.
  63         */
  64        if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) &&
  65            (source == mux->pllx_index))
  66                source = mux->div2_index;
  67
  68        return source;
  69}
  70
  71static int clk_super_set_parent(struct clk_hw *hw, u8 index)
  72{
  73        struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
  74        u32 val, state;
  75        int err = 0;
  76        u8 parent_index, shift;
  77        unsigned long flags = 0;
  78
  79        if (mux->lock)
  80                spin_lock_irqsave(mux->lock, flags);
  81
  82        val = readl_relaxed(mux->reg);
  83        state = val & SUPER_STATE_MASK;
  84        BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
  85               (state != super_state(SUPER_STATE_IDLE)));
  86        shift = (state == super_state(SUPER_STATE_IDLE)) ?
  87                super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
  88                super_state_to_src_shift(mux, SUPER_STATE_RUN);
  89
  90        /*
  91         * For LP mode super-clock switch between PLLX direct
  92         * and divided-by-2 outputs is allowed only when other
  93         * than PLLX clock source is current parent.
  94         */
  95        if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) ||
  96                                               (index == mux->pllx_index))) {
  97                parent_index = clk_super_get_parent(hw);
  98                if ((parent_index == mux->div2_index) ||
  99                    (parent_index == mux->pllx_index)) {
 100                        err = -EINVAL;
 101                        goto out;
 102                }
 103
 104                val ^= SUPER_LP_DIV2_BYPASS;
 105                writel_relaxed(val, mux->reg);
 106                udelay(2);
 107
 108                if (index == mux->div2_index)
 109                        index = mux->pllx_index;
 110        }
 111        val &= ~((super_state_to_src_mask(mux)) << shift);
 112        val |= (index & (super_state_to_src_mask(mux))) << shift;
 113
 114        writel_relaxed(val, mux->reg);
 115        udelay(2);
 116
 117out:
 118        if (mux->lock)
 119                spin_unlock_irqrestore(mux->lock, flags);
 120
 121        return err;
 122}
 123
 124const struct clk_ops tegra_clk_super_mux_ops = {
 125        .get_parent = clk_super_get_parent,
 126        .set_parent = clk_super_set_parent,
 127};
 128
 129static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
 130                                 unsigned long *parent_rate)
 131{
 132        struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
 133        struct clk_hw *div_hw = &super->frac_div.hw;
 134
 135        __clk_hw_set_clk(div_hw, hw);
 136
 137        return super->div_ops->round_rate(div_hw, rate, parent_rate);
 138}
 139
 140static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
 141                                           unsigned long parent_rate)
 142{
 143        struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
 144        struct clk_hw *div_hw = &super->frac_div.hw;
 145
 146        __clk_hw_set_clk(div_hw, hw);
 147
 148        return super->div_ops->recalc_rate(div_hw, parent_rate);
 149}
 150
 151static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
 152                              unsigned long parent_rate)
 153{
 154        struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
 155        struct clk_hw *div_hw = &super->frac_div.hw;
 156
 157        __clk_hw_set_clk(div_hw, hw);
 158
 159        return super->div_ops->set_rate(div_hw, rate, parent_rate);
 160}
 161
 162const struct clk_ops tegra_clk_super_ops = {
 163        .get_parent = clk_super_get_parent,
 164        .set_parent = clk_super_set_parent,
 165        .set_rate = clk_super_set_rate,
 166        .round_rate = clk_super_round_rate,
 167        .recalc_rate = clk_super_recalc_rate,
 168};
 169
 170struct clk *tegra_clk_register_super_mux(const char *name,
 171                const char **parent_names, u8 num_parents,
 172                unsigned long flags, void __iomem *reg, u8 clk_super_flags,
 173                u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock)
 174{
 175        struct tegra_clk_super_mux *super;
 176        struct clk *clk;
 177        struct clk_init_data init;
 178
 179        super = kzalloc(sizeof(*super), GFP_KERNEL);
 180        if (!super)
 181                return ERR_PTR(-ENOMEM);
 182
 183        init.name = name;
 184        init.ops = &tegra_clk_super_mux_ops;
 185        init.flags = flags;
 186        init.parent_names = parent_names;
 187        init.num_parents = num_parents;
 188
 189        super->reg = reg;
 190        super->pllx_index = pllx_index;
 191        super->div2_index = div2_index;
 192        super->lock = lock;
 193        super->width = width;
 194        super->flags = clk_super_flags;
 195
 196        /* Data in .init is copied by clk_register(), so stack variable OK */
 197        super->hw.init = &init;
 198
 199        clk = clk_register(NULL, &super->hw);
 200        if (IS_ERR(clk))
 201                kfree(super);
 202
 203        return clk;
 204}
 205
 206struct clk *tegra_clk_register_super_clk(const char *name,
 207                const char * const *parent_names, u8 num_parents,
 208                unsigned long flags, void __iomem *reg, u8 clk_super_flags,
 209                spinlock_t *lock)
 210{
 211        struct tegra_clk_super_mux *super;
 212        struct clk *clk;
 213        struct clk_init_data init;
 214
 215        super = kzalloc(sizeof(*super), GFP_KERNEL);
 216        if (!super)
 217                return ERR_PTR(-ENOMEM);
 218
 219        init.name = name;
 220        init.ops = &tegra_clk_super_ops;
 221        init.flags = flags;
 222        init.parent_names = parent_names;
 223        init.num_parents = num_parents;
 224
 225        super->reg = reg;
 226        super->lock = lock;
 227        super->width = 4;
 228        super->flags = clk_super_flags;
 229        super->frac_div.reg = reg + 4;
 230        super->frac_div.shift = 16;
 231        super->frac_div.width = 8;
 232        super->frac_div.frac_width = 1;
 233        super->frac_div.lock = lock;
 234        super->div_ops = &tegra_clk_frac_div_ops;
 235
 236        /* Data in .init is copied by clk_register(), so stack variable OK */
 237        super->hw.init = &init;
 238
 239        clk = clk_register(NULL, &super->hw);
 240        if (IS_ERR(clk))
 241                kfree(super);
 242
 243        return clk;
 244}
 245