root/drivers/clk/tegra/clk-super.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clk_super_get_parent
  2. clk_super_set_parent
  3. clk_super_round_rate
  4. clk_super_recalc_rate
  5. clk_super_set_rate
  6. tegra_clk_register_super_mux
  7. tegra_clk_register_super_clk

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

/* [<][>][^][v][top][bottom][index][help] */