root/drivers/clk/zynqmp/divider.c

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

DEFINITIONS

This source file includes following definitions.
  1. zynqmp_divider_get_val
  2. zynqmp_clk_divider_recalc_rate
  3. zynqmp_clk_divider_round_rate
  4. zynqmp_clk_divider_set_rate
  5. zynqmp_clk_register_divider

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Zynq UltraScale+ MPSoC Divider support
   4  *
   5  *  Copyright (C) 2016-2018 Xilinx
   6  *
   7  * Adjustable divider clock implementation
   8  */
   9 
  10 #include <linux/clk.h>
  11 #include <linux/clk-provider.h>
  12 #include <linux/slab.h>
  13 #include "clk-zynqmp.h"
  14 
  15 /*
  16  * DOC: basic adjustable divider clock that cannot gate
  17  *
  18  * Traits of this clock:
  19  * prepare - clk_prepare only ensures that parents are prepared
  20  * enable - clk_enable only ensures that parents are enabled
  21  * rate - rate is adjustable.  clk->rate = ceiling(parent->rate / divisor)
  22  * parent - fixed parent.  No clk_set_parent support
  23  */
  24 
  25 #define to_zynqmp_clk_divider(_hw)              \
  26         container_of(_hw, struct zynqmp_clk_divider, hw)
  27 
  28 #define CLK_FRAC        BIT(13) /* has a fractional parent */
  29 
  30 /**
  31  * struct zynqmp_clk_divider - adjustable divider clock
  32  * @hw:         handle between common and hardware-specific interfaces
  33  * @flags:      Hardware specific flags
  34  * @is_frac:    The divider is a fractional divider
  35  * @clk_id:     Id of clock
  36  * @div_type:   divisor type (TYPE_DIV1 or TYPE_DIV2)
  37  */
  38 struct zynqmp_clk_divider {
  39         struct clk_hw hw;
  40         u8 flags;
  41         bool is_frac;
  42         u32 clk_id;
  43         u32 div_type;
  44 };
  45 
  46 static inline int zynqmp_divider_get_val(unsigned long parent_rate,
  47                                          unsigned long rate)
  48 {
  49         return DIV_ROUND_CLOSEST(parent_rate, rate);
  50 }
  51 
  52 /**
  53  * zynqmp_clk_divider_recalc_rate() - Recalc rate of divider clock
  54  * @hw:                 handle between common and hardware-specific interfaces
  55  * @parent_rate:        rate of parent clock
  56  *
  57  * Return: 0 on success else error+reason
  58  */
  59 static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
  60                                                     unsigned long parent_rate)
  61 {
  62         struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
  63         const char *clk_name = clk_hw_get_name(hw);
  64         u32 clk_id = divider->clk_id;
  65         u32 div_type = divider->div_type;
  66         u32 div, value;
  67         int ret;
  68         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
  69 
  70         ret = eemi_ops->clock_getdivider(clk_id, &div);
  71 
  72         if (ret)
  73                 pr_warn_once("%s() get divider failed for %s, ret = %d\n",
  74                              __func__, clk_name, ret);
  75 
  76         if (div_type == TYPE_DIV1)
  77                 value = div & 0xFFFF;
  78         else
  79                 value = div >> 16;
  80 
  81         if (!value) {
  82                 WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
  83                      "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
  84                      clk_name);
  85                 return parent_rate;
  86         }
  87 
  88         return DIV_ROUND_UP_ULL(parent_rate, value);
  89 }
  90 
  91 /**
  92  * zynqmp_clk_divider_round_rate() - Round rate of divider clock
  93  * @hw:                 handle between common and hardware-specific interfaces
  94  * @rate:               rate of clock to be set
  95  * @prate:              rate of parent clock
  96  *
  97  * Return: 0 on success else error+reason
  98  */
  99 static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
 100                                           unsigned long rate,
 101                                           unsigned long *prate)
 102 {
 103         struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
 104         const char *clk_name = clk_hw_get_name(hw);
 105         u32 clk_id = divider->clk_id;
 106         u32 div_type = divider->div_type;
 107         u32 bestdiv;
 108         int ret;
 109         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 110 
 111         /* if read only, just return current value */
 112         if (divider->flags & CLK_DIVIDER_READ_ONLY) {
 113                 ret = eemi_ops->clock_getdivider(clk_id, &bestdiv);
 114 
 115                 if (ret)
 116                         pr_warn_once("%s() get divider failed for %s, ret = %d\n",
 117                                      __func__, clk_name, ret);
 118                 if (div_type == TYPE_DIV1)
 119                         bestdiv = bestdiv & 0xFFFF;
 120                 else
 121                         bestdiv  = bestdiv >> 16;
 122 
 123                 return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
 124         }
 125 
 126         bestdiv = zynqmp_divider_get_val(*prate, rate);
 127 
 128         if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && divider->is_frac)
 129                 bestdiv = rate % *prate ? 1 : bestdiv;
 130         *prate = rate * bestdiv;
 131 
 132         return rate;
 133 }
 134 
 135 /**
 136  * zynqmp_clk_divider_set_rate() - Set rate of divider clock
 137  * @hw:                 handle between common and hardware-specific interfaces
 138  * @rate:               rate of clock to be set
 139  * @parent_rate:        rate of parent clock
 140  *
 141  * Return: 0 on success else error+reason
 142  */
 143 static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 144                                        unsigned long parent_rate)
 145 {
 146         struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
 147         const char *clk_name = clk_hw_get_name(hw);
 148         u32 clk_id = divider->clk_id;
 149         u32 div_type = divider->div_type;
 150         u32 value, div;
 151         int ret;
 152         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 153 
 154         value = zynqmp_divider_get_val(parent_rate, rate);
 155         if (div_type == TYPE_DIV1) {
 156                 div = value & 0xFFFF;
 157                 div |= 0xffff << 16;
 158         } else {
 159                 div = 0xffff;
 160                 div |= value << 16;
 161         }
 162 
 163         ret = eemi_ops->clock_setdivider(clk_id, div);
 164 
 165         if (ret)
 166                 pr_warn_once("%s() set divider failed for %s, ret = %d\n",
 167                              __func__, clk_name, ret);
 168 
 169         return ret;
 170 }
 171 
 172 static const struct clk_ops zynqmp_clk_divider_ops = {
 173         .recalc_rate = zynqmp_clk_divider_recalc_rate,
 174         .round_rate = zynqmp_clk_divider_round_rate,
 175         .set_rate = zynqmp_clk_divider_set_rate,
 176 };
 177 
 178 /**
 179  * zynqmp_clk_register_divider() - Register a divider clock
 180  * @name:               Name of this clock
 181  * @clk_id:             Id of clock
 182  * @parents:            Name of this clock's parents
 183  * @num_parents:        Number of parents
 184  * @nodes:              Clock topology node
 185  *
 186  * Return: clock hardware to registered clock divider
 187  */
 188 struct clk_hw *zynqmp_clk_register_divider(const char *name,
 189                                            u32 clk_id,
 190                                            const char * const *parents,
 191                                            u8 num_parents,
 192                                            const struct clock_topology *nodes)
 193 {
 194         struct zynqmp_clk_divider *div;
 195         struct clk_hw *hw;
 196         struct clk_init_data init;
 197         int ret;
 198 
 199         /* allocate the divider */
 200         div = kzalloc(sizeof(*div), GFP_KERNEL);
 201         if (!div)
 202                 return ERR_PTR(-ENOMEM);
 203 
 204         init.name = name;
 205         init.ops = &zynqmp_clk_divider_ops;
 206         /* CLK_FRAC is not defined in the common clk framework */
 207         init.flags = nodes->flag & ~CLK_FRAC;
 208         init.parent_names = parents;
 209         init.num_parents = 1;
 210 
 211         /* struct clk_divider assignments */
 212         div->is_frac = !!(nodes->flag & CLK_FRAC);
 213         div->flags = nodes->type_flag;
 214         div->hw.init = &init;
 215         div->clk_id = clk_id;
 216         div->div_type = nodes->type;
 217 
 218         hw = &div->hw;
 219         ret = clk_hw_register(NULL, hw);
 220         if (ret) {
 221                 kfree(div);
 222                 hw = ERR_PTR(ret);
 223         }
 224 
 225         return hw;
 226 }

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