root/drivers/gpu/drm/sun4i/sun4i_dotclock.c

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

DEFINITIONS

This source file includes following definitions.
  1. hw_to_dclk
  2. sun4i_dclk_disable
  3. sun4i_dclk_enable
  4. sun4i_dclk_is_enabled
  5. sun4i_dclk_recalc_rate
  6. sun4i_dclk_round_rate
  7. sun4i_dclk_set_rate
  8. sun4i_dclk_get_phase
  9. sun4i_dclk_set_phase
  10. sun4i_dclk_create
  11. sun4i_dclk_free

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (C) 2016 Free Electrons
   4  * Copyright (C) 2016 NextThing Co
   5  *
   6  * Maxime Ripard <maxime.ripard@free-electrons.com>
   7  */
   8 
   9 #include <linux/clk-provider.h>
  10 #include <linux/regmap.h>
  11 
  12 #include "sun4i_tcon.h"
  13 #include "sun4i_dotclock.h"
  14 
  15 struct sun4i_dclk {
  16         struct clk_hw           hw;
  17         struct regmap           *regmap;
  18         struct sun4i_tcon       *tcon;
  19 };
  20 
  21 static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw)
  22 {
  23         return container_of(hw, struct sun4i_dclk, hw);
  24 }
  25 
  26 static void sun4i_dclk_disable(struct clk_hw *hw)
  27 {
  28         struct sun4i_dclk *dclk = hw_to_dclk(hw);
  29 
  30         regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
  31                            BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0);
  32 }
  33 
  34 static int sun4i_dclk_enable(struct clk_hw *hw)
  35 {
  36         struct sun4i_dclk *dclk = hw_to_dclk(hw);
  37 
  38         return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
  39                                   BIT(SUN4I_TCON0_DCLK_GATE_BIT),
  40                                   BIT(SUN4I_TCON0_DCLK_GATE_BIT));
  41 }
  42 
  43 static int sun4i_dclk_is_enabled(struct clk_hw *hw)
  44 {
  45         struct sun4i_dclk *dclk = hw_to_dclk(hw);
  46         u32 val;
  47 
  48         regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
  49 
  50         return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT);
  51 }
  52 
  53 static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
  54                                             unsigned long parent_rate)
  55 {
  56         struct sun4i_dclk *dclk = hw_to_dclk(hw);
  57         u32 val;
  58 
  59         regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
  60 
  61         val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
  62         val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1;
  63 
  64         if (!val)
  65                 val = 1;
  66 
  67         return parent_rate / val;
  68 }
  69 
  70 static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
  71                                   unsigned long *parent_rate)
  72 {
  73         struct sun4i_dclk *dclk = hw_to_dclk(hw);
  74         struct sun4i_tcon *tcon = dclk->tcon;
  75         unsigned long best_parent = 0;
  76         u8 best_div = 1;
  77         int i;
  78 
  79         for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) {
  80                 u64 ideal = (u64)rate * i;
  81                 unsigned long rounded;
  82 
  83                 /*
  84                  * ideal has overflowed the max value that can be stored in an
  85                  * unsigned long, and every clk operation we might do on a
  86                  * truncated u64 value will give us incorrect results.
  87                  * Let's just stop there since bigger dividers will result in
  88                  * the same overflow issue.
  89                  */
  90                 if (ideal > ULONG_MAX)
  91                         goto out;
  92 
  93                 rounded = clk_hw_round_rate(clk_hw_get_parent(hw),
  94                                             ideal);
  95 
  96                 if (rounded == ideal) {
  97                         best_parent = rounded;
  98                         best_div = i;
  99                         goto out;
 100                 }
 101 
 102                 if (abs(rate - rounded / i) <
 103                     abs(rate - best_parent / best_div)) {
 104                         best_parent = rounded;
 105                         best_div = i;
 106                 }
 107         }
 108 
 109 out:
 110         *parent_rate = best_parent;
 111 
 112         return best_parent / best_div;
 113 }
 114 
 115 static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
 116                                unsigned long parent_rate)
 117 {
 118         struct sun4i_dclk *dclk = hw_to_dclk(hw);
 119         u8 div = parent_rate / rate;
 120 
 121         return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
 122                                   GENMASK(6, 0), div);
 123 }
 124 
 125 static int sun4i_dclk_get_phase(struct clk_hw *hw)
 126 {
 127         struct sun4i_dclk *dclk = hw_to_dclk(hw);
 128         u32 val;
 129 
 130         regmap_read(dclk->regmap, SUN4I_TCON0_IO_POL_REG, &val);
 131 
 132         val >>= 28;
 133         val &= 3;
 134 
 135         return val * 120;
 136 }
 137 
 138 static int sun4i_dclk_set_phase(struct clk_hw *hw, int degrees)
 139 {
 140         struct sun4i_dclk *dclk = hw_to_dclk(hw);
 141         u32 val = degrees / 120;
 142 
 143         val <<= 28;
 144 
 145         regmap_update_bits(dclk->regmap, SUN4I_TCON0_IO_POL_REG,
 146                            GENMASK(29, 28),
 147                            val);
 148 
 149         return 0;
 150 }
 151 
 152 static const struct clk_ops sun4i_dclk_ops = {
 153         .disable        = sun4i_dclk_disable,
 154         .enable         = sun4i_dclk_enable,
 155         .is_enabled     = sun4i_dclk_is_enabled,
 156 
 157         .recalc_rate    = sun4i_dclk_recalc_rate,
 158         .round_rate     = sun4i_dclk_round_rate,
 159         .set_rate       = sun4i_dclk_set_rate,
 160 
 161         .get_phase      = sun4i_dclk_get_phase,
 162         .set_phase      = sun4i_dclk_set_phase,
 163 };
 164 
 165 int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
 166 {
 167         const char *clk_name, *parent_name;
 168         struct clk_init_data init;
 169         struct sun4i_dclk *dclk;
 170         int ret;
 171 
 172         parent_name = __clk_get_name(tcon->sclk0);
 173         ret = of_property_read_string_index(dev->of_node,
 174                                             "clock-output-names", 0,
 175                                             &clk_name);
 176         if (ret)
 177                 return ret;
 178 
 179         dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
 180         if (!dclk)
 181                 return -ENOMEM;
 182         dclk->tcon = tcon;
 183 
 184         init.name = clk_name;
 185         init.ops = &sun4i_dclk_ops;
 186         init.parent_names = &parent_name;
 187         init.num_parents = 1;
 188         init.flags = CLK_SET_RATE_PARENT;
 189 
 190         dclk->regmap = tcon->regs;
 191         dclk->hw.init = &init;
 192 
 193         tcon->dclk = clk_register(dev, &dclk->hw);
 194         if (IS_ERR(tcon->dclk))
 195                 return PTR_ERR(tcon->dclk);
 196 
 197         return 0;
 198 }
 199 EXPORT_SYMBOL(sun4i_dclk_create);
 200 
 201 int sun4i_dclk_free(struct sun4i_tcon *tcon)
 202 {
 203         clk_unregister(tcon->dclk);
 204         return 0;
 205 }
 206 EXPORT_SYMBOL(sun4i_dclk_free);

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