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

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

DEFINITIONS

This source file includes following definitions.
  1. get_div
  2. clk_frac_div_recalc_rate
  3. clk_frac_div_round_rate
  4. clk_frac_div_set_rate
  5. tegra_clk_register_divider
  6. tegra_clk_register_mc

   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/err.h>
   9 #include <linux/slab.h>
  10 #include <linux/clk-provider.h>
  11 
  12 #include "clk.h"
  13 
  14 #define pll_out_override(p) (BIT((p->shift - 6)))
  15 #define div_mask(d) ((1 << (d->width)) - 1)
  16 #define get_mul(d) (1 << d->frac_width)
  17 #define get_max_div(d) div_mask(d)
  18 
  19 #define PERIPH_CLK_UART_DIV_ENB BIT(24)
  20 
  21 static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
  22                    unsigned long parent_rate)
  23 {
  24         int div;
  25 
  26         div = div_frac_get(rate, parent_rate, divider->width,
  27                            divider->frac_width, divider->flags);
  28 
  29         if (div < 0)
  30                 return 0;
  31 
  32         return div;
  33 }
  34 
  35 static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
  36                                              unsigned long parent_rate)
  37 {
  38         struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
  39         u32 reg;
  40         int div, mul;
  41         u64 rate = parent_rate;
  42 
  43         reg = readl_relaxed(divider->reg) >> divider->shift;
  44         div = reg & div_mask(divider);
  45 
  46         mul = get_mul(divider);
  47         div += mul;
  48 
  49         rate *= mul;
  50         rate += div - 1;
  51         do_div(rate, div);
  52 
  53         return rate;
  54 }
  55 
  56 static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate,
  57                                    unsigned long *prate)
  58 {
  59         struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
  60         int div, mul;
  61         unsigned long output_rate = *prate;
  62 
  63         if (!rate)
  64                 return output_rate;
  65 
  66         div = get_div(divider, rate, output_rate);
  67         if (div < 0)
  68                 return *prate;
  69 
  70         mul = get_mul(divider);
  71 
  72         return DIV_ROUND_UP(output_rate * mul, div + mul);
  73 }
  74 
  75 static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
  76                                 unsigned long parent_rate)
  77 {
  78         struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
  79         int div;
  80         unsigned long flags = 0;
  81         u32 val;
  82 
  83         div = get_div(divider, rate, parent_rate);
  84         if (div < 0)
  85                 return div;
  86 
  87         if (divider->lock)
  88                 spin_lock_irqsave(divider->lock, flags);
  89 
  90         val = readl_relaxed(divider->reg);
  91         val &= ~(div_mask(divider) << divider->shift);
  92         val |= div << divider->shift;
  93 
  94         if (divider->flags & TEGRA_DIVIDER_UART) {
  95                 if (div)
  96                         val |= PERIPH_CLK_UART_DIV_ENB;
  97                 else
  98                         val &= ~PERIPH_CLK_UART_DIV_ENB;
  99         }
 100 
 101         if (divider->flags & TEGRA_DIVIDER_FIXED)
 102                 val |= pll_out_override(divider);
 103 
 104         writel_relaxed(val, divider->reg);
 105 
 106         if (divider->lock)
 107                 spin_unlock_irqrestore(divider->lock, flags);
 108 
 109         return 0;
 110 }
 111 
 112 const struct clk_ops tegra_clk_frac_div_ops = {
 113         .recalc_rate = clk_frac_div_recalc_rate,
 114         .set_rate = clk_frac_div_set_rate,
 115         .round_rate = clk_frac_div_round_rate,
 116 };
 117 
 118 struct clk *tegra_clk_register_divider(const char *name,
 119                 const char *parent_name, void __iomem *reg,
 120                 unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
 121                 u8 frac_width, spinlock_t *lock)
 122 {
 123         struct tegra_clk_frac_div *divider;
 124         struct clk *clk;
 125         struct clk_init_data init;
 126 
 127         divider = kzalloc(sizeof(*divider), GFP_KERNEL);
 128         if (!divider) {
 129                 pr_err("%s: could not allocate fractional divider clk\n",
 130                        __func__);
 131                 return ERR_PTR(-ENOMEM);
 132         }
 133 
 134         init.name = name;
 135         init.ops = &tegra_clk_frac_div_ops;
 136         init.flags = flags;
 137         init.parent_names = parent_name ? &parent_name : NULL;
 138         init.num_parents = parent_name ? 1 : 0;
 139 
 140         divider->reg = reg;
 141         divider->shift = shift;
 142         divider->width = width;
 143         divider->frac_width = frac_width;
 144         divider->lock = lock;
 145         divider->flags = clk_divider_flags;
 146 
 147         /* Data in .init is copied by clk_register(), so stack variable OK */
 148         divider->hw.init = &init;
 149 
 150         clk = clk_register(NULL, &divider->hw);
 151         if (IS_ERR(clk))
 152                 kfree(divider);
 153 
 154         return clk;
 155 }
 156 
 157 static const struct clk_div_table mc_div_table[] = {
 158         { .val = 0, .div = 2 },
 159         { .val = 1, .div = 1 },
 160         { .val = 0, .div = 0 },
 161 };
 162 
 163 struct clk *tegra_clk_register_mc(const char *name, const char *parent_name,
 164                                   void __iomem *reg, spinlock_t *lock)
 165 {
 166         return clk_register_divider_table(NULL, name, parent_name,
 167                                           CLK_IS_CRITICAL,
 168                                           reg, 16, 1, CLK_DIVIDER_READ_ONLY,
 169                                           mc_div_table, lock);
 170 }

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