root/drivers/clk/qcom/clk-regmap-mux-div.c

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

DEFINITIONS

This source file includes following definitions.
  1. mux_div_set_src_div
  2. mux_div_get_src_div
  3. is_better_rate
  4. mux_div_determine_rate
  5. __mux_div_set_rate_and_parent
  6. mux_div_get_parent
  7. mux_div_set_parent
  8. mux_div_set_rate
  9. mux_div_set_rate_and_parent
  10. mux_div_recalc_rate

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (c) 2017, Linaro Limited
   4  * Author: Georgi Djakov <georgi.djakov@linaro.org>
   5  */
   6 
   7 #include <linux/bitops.h>
   8 #include <linux/delay.h>
   9 #include <linux/kernel.h>
  10 #include <linux/regmap.h>
  11 
  12 #include "clk-regmap-mux-div.h"
  13 
  14 #define CMD_RCGR                        0x0
  15 #define CMD_RCGR_UPDATE                 BIT(0)
  16 #define CMD_RCGR_DIRTY_CFG              BIT(4)
  17 #define CMD_RCGR_ROOT_OFF               BIT(31)
  18 #define CFG_RCGR                        0x4
  19 
  20 #define to_clk_regmap_mux_div(_hw) \
  21         container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
  22 
  23 int mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
  24 {
  25         int ret, count;
  26         u32 val, mask;
  27         const char *name = clk_hw_get_name(&md->clkr.hw);
  28 
  29         val = (div << md->hid_shift) | (src << md->src_shift);
  30         mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
  31                ((BIT(md->src_width) - 1) << md->src_shift);
  32 
  33         ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
  34                                  mask, val);
  35         if (ret)
  36                 return ret;
  37 
  38         ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
  39                                  CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
  40         if (ret)
  41                 return ret;
  42 
  43         /* Wait for update to take effect */
  44         for (count = 500; count > 0; count--) {
  45                 ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
  46                                   &val);
  47                 if (ret)
  48                         return ret;
  49                 if (!(val & CMD_RCGR_UPDATE))
  50                         return 0;
  51                 udelay(1);
  52         }
  53 
  54         pr_err("%s: RCG did not update its configuration", name);
  55         return -EBUSY;
  56 }
  57 EXPORT_SYMBOL_GPL(mux_div_set_src_div);
  58 
  59 static void mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
  60                                 u32 *div)
  61 {
  62         u32 val, d, s;
  63         const char *name = clk_hw_get_name(&md->clkr.hw);
  64 
  65         regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
  66 
  67         if (val & CMD_RCGR_DIRTY_CFG) {
  68                 pr_err("%s: RCG configuration is pending\n", name);
  69                 return;
  70         }
  71 
  72         regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
  73         s = (val >> md->src_shift);
  74         s &= BIT(md->src_width) - 1;
  75         *src = s;
  76 
  77         d = (val >> md->hid_shift);
  78         d &= BIT(md->hid_width) - 1;
  79         *div = d;
  80 }
  81 
  82 static inline bool is_better_rate(unsigned long req, unsigned long best,
  83                                   unsigned long new)
  84 {
  85         return (req <= new && new < best) || (best < req && best < new);
  86 }
  87 
  88 static int mux_div_determine_rate(struct clk_hw *hw,
  89                                   struct clk_rate_request *req)
  90 {
  91         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  92         unsigned int i, div, max_div;
  93         unsigned long actual_rate, best_rate = 0;
  94         unsigned long req_rate = req->rate;
  95 
  96         for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  97                 struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
  98                 unsigned long parent_rate = clk_hw_get_rate(parent);
  99 
 100                 max_div = BIT(md->hid_width) - 1;
 101                 for (div = 1; div < max_div; div++) {
 102                         parent_rate = mult_frac(req_rate, div, 2);
 103                         parent_rate = clk_hw_round_rate(parent, parent_rate);
 104                         actual_rate = mult_frac(parent_rate, 2, div);
 105 
 106                         if (is_better_rate(req_rate, best_rate, actual_rate)) {
 107                                 best_rate = actual_rate;
 108                                 req->rate = best_rate;
 109                                 req->best_parent_rate = parent_rate;
 110                                 req->best_parent_hw = parent;
 111                         }
 112 
 113                         if (actual_rate < req_rate || best_rate <= req_rate)
 114                                 break;
 115                 }
 116         }
 117 
 118         if (!best_rate)
 119                 return -EINVAL;
 120 
 121         return 0;
 122 }
 123 
 124 static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
 125                                          unsigned long prate, u32 src)
 126 {
 127         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
 128         int ret;
 129         u32 div, max_div, best_src = 0, best_div = 0;
 130         unsigned int i;
 131         unsigned long actual_rate, best_rate = 0;
 132 
 133         for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
 134                 struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
 135                 unsigned long parent_rate = clk_hw_get_rate(parent);
 136 
 137                 max_div = BIT(md->hid_width) - 1;
 138                 for (div = 1; div < max_div; div++) {
 139                         parent_rate = mult_frac(rate, div, 2);
 140                         parent_rate = clk_hw_round_rate(parent, parent_rate);
 141                         actual_rate = mult_frac(parent_rate, 2, div);
 142 
 143                         if (is_better_rate(rate, best_rate, actual_rate)) {
 144                                 best_rate = actual_rate;
 145                                 best_src = md->parent_map[i];
 146                                 best_div = div - 1;
 147                         }
 148 
 149                         if (actual_rate < rate || best_rate <= rate)
 150                                 break;
 151                 }
 152         }
 153 
 154         ret = mux_div_set_src_div(md, best_src, best_div);
 155         if (!ret) {
 156                 md->div = best_div;
 157                 md->src = best_src;
 158         }
 159 
 160         return ret;
 161 }
 162 
 163 static u8 mux_div_get_parent(struct clk_hw *hw)
 164 {
 165         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
 166         const char *name = clk_hw_get_name(hw);
 167         u32 i, div, src = 0;
 168 
 169         mux_div_get_src_div(md, &src, &div);
 170 
 171         for (i = 0; i < clk_hw_get_num_parents(hw); i++)
 172                 if (src == md->parent_map[i])
 173                         return i;
 174 
 175         pr_err("%s: Can't find parent with src %d\n", name, src);
 176         return 0;
 177 }
 178 
 179 static int mux_div_set_parent(struct clk_hw *hw, u8 index)
 180 {
 181         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
 182 
 183         return mux_div_set_src_div(md, md->parent_map[index], md->div);
 184 }
 185 
 186 static int mux_div_set_rate(struct clk_hw *hw,
 187                             unsigned long rate, unsigned long prate)
 188 {
 189         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
 190 
 191         return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
 192 }
 193 
 194 static int mux_div_set_rate_and_parent(struct clk_hw *hw,  unsigned long rate,
 195                                        unsigned long prate, u8 index)
 196 {
 197         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
 198 
 199         return __mux_div_set_rate_and_parent(hw, rate, prate,
 200                                              md->parent_map[index]);
 201 }
 202 
 203 static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
 204 {
 205         struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
 206         u32 div, src;
 207         int i, num_parents = clk_hw_get_num_parents(hw);
 208         const char *name = clk_hw_get_name(hw);
 209 
 210         mux_div_get_src_div(md, &src, &div);
 211         for (i = 0; i < num_parents; i++)
 212                 if (src == md->parent_map[i]) {
 213                         struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
 214                         unsigned long parent_rate = clk_hw_get_rate(p);
 215 
 216                         return mult_frac(parent_rate, 2, div + 1);
 217                 }
 218 
 219         pr_err("%s: Can't find parent %d\n", name, src);
 220         return 0;
 221 }
 222 
 223 const struct clk_ops clk_regmap_mux_div_ops = {
 224         .get_parent = mux_div_get_parent,
 225         .set_parent = mux_div_set_parent,
 226         .set_rate = mux_div_set_rate,
 227         .set_rate_and_parent = mux_div_set_rate_and_parent,
 228         .determine_rate = mux_div_determine_rate,
 229         .recalc_rate = mux_div_recalc_rate,
 230 };
 231 EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);

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