root/drivers/clk/zynqmp/pll.c

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

DEFINITIONS

This source file includes following definitions.
  1. zynqmp_pll_get_mode
  2. zynqmp_pll_set_mode
  3. zynqmp_pll_round_rate
  4. zynqmp_pll_recalc_rate
  5. zynqmp_pll_set_rate
  6. zynqmp_pll_is_enabled
  7. zynqmp_pll_enable
  8. zynqmp_pll_disable
  9. zynqmp_clk_register_pll

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Zynq UltraScale+ MPSoC PLL driver
   4  *
   5  *  Copyright (C) 2016-2018 Xilinx
   6  */
   7 
   8 #include <linux/clk.h>
   9 #include <linux/clk-provider.h>
  10 #include <linux/slab.h>
  11 #include "clk-zynqmp.h"
  12 
  13 /**
  14  * struct zynqmp_pll - PLL clock
  15  * @hw:         Handle between common and hardware-specific interfaces
  16  * @clk_id:     PLL clock ID
  17  */
  18 struct zynqmp_pll {
  19         struct clk_hw hw;
  20         u32 clk_id;
  21 };
  22 
  23 #define to_zynqmp_pll(_hw)      container_of(_hw, struct zynqmp_pll, hw)
  24 
  25 #define PLL_FBDIV_MIN   25
  26 #define PLL_FBDIV_MAX   125
  27 
  28 #define PS_PLL_VCO_MIN 1500000000
  29 #define PS_PLL_VCO_MAX 3000000000UL
  30 
  31 enum pll_mode {
  32         PLL_MODE_INT,
  33         PLL_MODE_FRAC,
  34 };
  35 
  36 #define FRAC_OFFSET 0x8
  37 #define PLLFCFG_FRAC_EN BIT(31)
  38 #define FRAC_DIV  BIT(16)  /* 2^16 */
  39 
  40 /**
  41  * zynqmp_pll_get_mode() - Get mode of PLL
  42  * @hw:         Handle between common and hardware-specific interfaces
  43  *
  44  * Return: Mode of PLL
  45  */
  46 static inline enum pll_mode zynqmp_pll_get_mode(struct clk_hw *hw)
  47 {
  48         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
  49         u32 clk_id = clk->clk_id;
  50         const char *clk_name = clk_hw_get_name(hw);
  51         u32 ret_payload[PAYLOAD_ARG_CNT];
  52         int ret;
  53         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
  54 
  55         ret = eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_MODE, clk_id, 0,
  56                               ret_payload);
  57         if (ret)
  58                 pr_warn_once("%s() PLL get frac mode failed for %s, ret = %d\n",
  59                              __func__, clk_name, ret);
  60 
  61         return ret_payload[1];
  62 }
  63 
  64 /**
  65  * zynqmp_pll_set_mode() - Set the PLL mode
  66  * @hw:         Handle between common and hardware-specific interfaces
  67  * @on:         Flag to determine the mode
  68  */
  69 static inline void zynqmp_pll_set_mode(struct clk_hw *hw, bool on)
  70 {
  71         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
  72         u32 clk_id = clk->clk_id;
  73         const char *clk_name = clk_hw_get_name(hw);
  74         int ret;
  75         u32 mode;
  76         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
  77 
  78         if (on)
  79                 mode = PLL_MODE_FRAC;
  80         else
  81                 mode = PLL_MODE_INT;
  82 
  83         ret = eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode, NULL);
  84         if (ret)
  85                 pr_warn_once("%s() PLL set frac mode failed for %s, ret = %d\n",
  86                              __func__, clk_name, ret);
  87 }
  88 
  89 /**
  90  * zynqmp_pll_round_rate() - Round a clock frequency
  91  * @hw:         Handle between common and hardware-specific interfaces
  92  * @rate:       Desired clock frequency
  93  * @prate:      Clock frequency of parent clock
  94  *
  95  * Return: Frequency closest to @rate the hardware can generate
  96  */
  97 static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
  98                                   unsigned long *prate)
  99 {
 100         u32 fbdiv;
 101         long rate_div, f;
 102 
 103         /* Enable the fractional mode if needed */
 104         rate_div = (rate * FRAC_DIV) / *prate;
 105         f = rate_div % FRAC_DIV;
 106         zynqmp_pll_set_mode(hw, !!f);
 107 
 108         if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
 109                 if (rate > PS_PLL_VCO_MAX) {
 110                         fbdiv = rate / PS_PLL_VCO_MAX;
 111                         rate = rate / (fbdiv + 1);
 112                 }
 113                 if (rate < PS_PLL_VCO_MIN) {
 114                         fbdiv = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate);
 115                         rate = rate * fbdiv;
 116                 }
 117                 return rate;
 118         }
 119 
 120         fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
 121         fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
 122         return *prate * fbdiv;
 123 }
 124 
 125 /**
 126  * zynqmp_pll_recalc_rate() - Recalculate clock frequency
 127  * @hw:                 Handle between common and hardware-specific interfaces
 128  * @parent_rate:        Clock frequency of parent clock
 129  *
 130  * Return: Current clock frequency
 131  */
 132 static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw,
 133                                             unsigned long parent_rate)
 134 {
 135         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 136         u32 clk_id = clk->clk_id;
 137         const char *clk_name = clk_hw_get_name(hw);
 138         u32 fbdiv, data;
 139         unsigned long rate, frac;
 140         u32 ret_payload[PAYLOAD_ARG_CNT];
 141         int ret;
 142         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 143 
 144         ret = eemi_ops->clock_getdivider(clk_id, &fbdiv);
 145         if (ret)
 146                 pr_warn_once("%s() get divider failed for %s, ret = %d\n",
 147                              __func__, clk_name, ret);
 148 
 149         rate =  parent_rate * fbdiv;
 150         if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
 151                 eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_DATA, clk_id, 0,
 152                                 ret_payload);
 153                 data = ret_payload[1];
 154                 frac = (parent_rate * data) / FRAC_DIV;
 155                 rate = rate + frac;
 156         }
 157 
 158         return rate;
 159 }
 160 
 161 /**
 162  * zynqmp_pll_set_rate() - Set rate of PLL
 163  * @hw:                 Handle between common and hardware-specific interfaces
 164  * @rate:               Frequency of clock to be set
 165  * @parent_rate:        Clock frequency of parent clock
 166  *
 167  * Set PLL divider to set desired rate.
 168  *
 169  * Returns:            rate which is set on success else error code
 170  */
 171 static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 172                                unsigned long parent_rate)
 173 {
 174         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 175         u32 clk_id = clk->clk_id;
 176         const char *clk_name = clk_hw_get_name(hw);
 177         u32 fbdiv;
 178         long rate_div, frac, m, f;
 179         int ret;
 180         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 181 
 182         if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
 183                 rate_div = (rate * FRAC_DIV) / parent_rate;
 184                 m = rate_div / FRAC_DIV;
 185                 f = rate_div % FRAC_DIV;
 186                 m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
 187                 rate = parent_rate * m;
 188                 frac = (parent_rate * f) / FRAC_DIV;
 189 
 190                 ret = eemi_ops->clock_setdivider(clk_id, m);
 191                 if (ret)
 192                         pr_warn_once("%s() set divider failed for %s, ret = %d\n",
 193                                      __func__, clk_name, ret);
 194 
 195                 eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, f, NULL);
 196 
 197                 return rate + frac;
 198         }
 199 
 200         fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
 201         fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
 202         ret = eemi_ops->clock_setdivider(clk_id, fbdiv);
 203         if (ret)
 204                 pr_warn_once("%s() set divider failed for %s, ret = %d\n",
 205                              __func__, clk_name, ret);
 206 
 207         return parent_rate * fbdiv;
 208 }
 209 
 210 /**
 211  * zynqmp_pll_is_enabled() - Check if a clock is enabled
 212  * @hw:         Handle between common and hardware-specific interfaces
 213  *
 214  * Return: 1 if the clock is enabled, 0 otherwise
 215  */
 216 static int zynqmp_pll_is_enabled(struct clk_hw *hw)
 217 {
 218         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 219         const char *clk_name = clk_hw_get_name(hw);
 220         u32 clk_id = clk->clk_id;
 221         unsigned int state;
 222         int ret;
 223         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 224 
 225         ret = eemi_ops->clock_getstate(clk_id, &state);
 226         if (ret) {
 227                 pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
 228                              __func__, clk_name, ret);
 229                 return -EIO;
 230         }
 231 
 232         return state ? 1 : 0;
 233 }
 234 
 235 /**
 236  * zynqmp_pll_enable() - Enable clock
 237  * @hw:         Handle between common and hardware-specific interfaces
 238  *
 239  * Return: 0 on success else error code
 240  */
 241 static int zynqmp_pll_enable(struct clk_hw *hw)
 242 {
 243         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 244         const char *clk_name = clk_hw_get_name(hw);
 245         u32 clk_id = clk->clk_id;
 246         int ret;
 247         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 248 
 249         if (zynqmp_pll_is_enabled(hw))
 250                 return 0;
 251 
 252         ret = eemi_ops->clock_enable(clk_id);
 253         if (ret)
 254                 pr_warn_once("%s() clock enable failed for %s, ret = %d\n",
 255                              __func__, clk_name, ret);
 256 
 257         return ret;
 258 }
 259 
 260 /**
 261  * zynqmp_pll_disable() - Disable clock
 262  * @hw:         Handle between common and hardware-specific interfaces
 263  */
 264 static void zynqmp_pll_disable(struct clk_hw *hw)
 265 {
 266         struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 267         const char *clk_name = clk_hw_get_name(hw);
 268         u32 clk_id = clk->clk_id;
 269         int ret;
 270         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 271 
 272         if (!zynqmp_pll_is_enabled(hw))
 273                 return;
 274 
 275         ret = eemi_ops->clock_disable(clk_id);
 276         if (ret)
 277                 pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
 278                              __func__, clk_name, ret);
 279 }
 280 
 281 static const struct clk_ops zynqmp_pll_ops = {
 282         .enable = zynqmp_pll_enable,
 283         .disable = zynqmp_pll_disable,
 284         .is_enabled = zynqmp_pll_is_enabled,
 285         .round_rate = zynqmp_pll_round_rate,
 286         .recalc_rate = zynqmp_pll_recalc_rate,
 287         .set_rate = zynqmp_pll_set_rate,
 288 };
 289 
 290 /**
 291  * zynqmp_clk_register_pll() - Register PLL with the clock framework
 292  * @name:               PLL name
 293  * @clk_id:             Clock ID
 294  * @parents:            Name of this clock's parents
 295  * @num_parents:        Number of parents
 296  * @nodes:              Clock topology node
 297  *
 298  * Return: clock hardware to the registered clock
 299  */
 300 struct clk_hw *zynqmp_clk_register_pll(const char *name, u32 clk_id,
 301                                        const char * const *parents,
 302                                        u8 num_parents,
 303                                        const struct clock_topology *nodes)
 304 {
 305         struct zynqmp_pll *pll;
 306         struct clk_hw *hw;
 307         struct clk_init_data init;
 308         int ret;
 309 
 310         init.name = name;
 311         init.ops = &zynqmp_pll_ops;
 312         init.flags = nodes->flag;
 313         init.parent_names = parents;
 314         init.num_parents = 1;
 315 
 316         pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 317         if (!pll)
 318                 return ERR_PTR(-ENOMEM);
 319 
 320         pll->hw.init = &init;
 321         pll->clk_id = clk_id;
 322 
 323         hw = &pll->hw;
 324         ret = clk_hw_register(NULL, hw);
 325         if (ret) {
 326                 kfree(pll);
 327                 return ERR_PTR(ret);
 328         }
 329 
 330         clk_hw_set_rate_range(hw, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
 331         if (ret < 0)
 332                 pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, ret);
 333 
 334         return hw;
 335 }

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