linux/drivers/clk/zynqmp/pll.c
<<
>>
Prefs
   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 */
  18struct 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
  31enum 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 */
  46static 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 */
  69static 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 */
  97static 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 */
 132static 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 */
 171static 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                        if (ret == -EUSERS)
 193                                WARN(1, "More than allowed devices are using the %s, which is forbidden\n", clk_name);
 194                        pr_err("%s() set divider failed for %s, ret = %d\n",
 195                               __func__, clk_name, ret);
 196                }
 197
 198                eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, f, NULL);
 199
 200                return rate + frac;
 201        }
 202
 203        fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
 204        fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
 205        ret = eemi_ops->clock_setdivider(clk_id, fbdiv);
 206        if (ret)
 207                pr_warn_once("%s() set divider failed for %s, ret = %d\n",
 208                             __func__, clk_name, ret);
 209
 210        return parent_rate * fbdiv;
 211}
 212
 213/**
 214 * zynqmp_pll_is_enabled() - Check if a clock is enabled
 215 * @hw:         Handle between common and hardware-specific interfaces
 216 *
 217 * Return: 1 if the clock is enabled, 0 otherwise
 218 */
 219static int zynqmp_pll_is_enabled(struct clk_hw *hw)
 220{
 221        struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 222        const char *clk_name = clk_hw_get_name(hw);
 223        u32 clk_id = clk->clk_id;
 224        unsigned int state;
 225        int ret;
 226        const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 227
 228        ret = eemi_ops->clock_getstate(clk_id, &state);
 229        if (ret) {
 230                pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
 231                             __func__, clk_name, ret);
 232                return -EIO;
 233        }
 234
 235        return state ? 1 : 0;
 236}
 237
 238/**
 239 * zynqmp_pll_enable() - Enable clock
 240 * @hw:         Handle between common and hardware-specific interfaces
 241 *
 242 * Return: 0 on success else error code
 243 */
 244static int zynqmp_pll_enable(struct clk_hw *hw)
 245{
 246        struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 247        const char *clk_name = clk_hw_get_name(hw);
 248        u32 clk_id = clk->clk_id;
 249        int ret;
 250        const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 251
 252        if (zynqmp_pll_is_enabled(hw))
 253                return 0;
 254
 255        ret = eemi_ops->clock_enable(clk_id);
 256        if (ret)
 257                pr_warn_once("%s() clock enable failed for %s, ret = %d\n",
 258                             __func__, clk_name, ret);
 259
 260        return ret;
 261}
 262
 263/**
 264 * zynqmp_pll_disable() - Disable clock
 265 * @hw:         Handle between common and hardware-specific interfaces
 266 */
 267static void zynqmp_pll_disable(struct clk_hw *hw)
 268{
 269        struct zynqmp_pll *clk = to_zynqmp_pll(hw);
 270        const char *clk_name = clk_hw_get_name(hw);
 271        u32 clk_id = clk->clk_id;
 272        int ret;
 273        const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
 274
 275        if (!zynqmp_pll_is_enabled(hw))
 276                return;
 277
 278        ret = eemi_ops->clock_disable(clk_id);
 279        if (ret)
 280                pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
 281                             __func__, clk_name, ret);
 282}
 283
 284static const struct clk_ops zynqmp_pll_ops = {
 285        .enable = zynqmp_pll_enable,
 286        .disable = zynqmp_pll_disable,
 287        .is_enabled = zynqmp_pll_is_enabled,
 288        .round_rate = zynqmp_pll_round_rate,
 289        .recalc_rate = zynqmp_pll_recalc_rate,
 290        .set_rate = zynqmp_pll_set_rate,
 291};
 292
 293/**
 294 * zynqmp_clk_register_pll() - Register PLL with the clock framework
 295 * @name:               PLL name
 296 * @clk_id:             Clock ID
 297 * @parents:            Name of this clock's parents
 298 * @num_parents:        Number of parents
 299 * @nodes:              Clock topology node
 300 *
 301 * Return: clock hardware to the registered clock
 302 */
 303struct clk_hw *zynqmp_clk_register_pll(const char *name, u32 clk_id,
 304                                       const char * const *parents,
 305                                       u8 num_parents,
 306                                       const struct clock_topology *nodes)
 307{
 308        struct zynqmp_pll *pll;
 309        struct clk_hw *hw;
 310        struct clk_init_data init;
 311        int ret;
 312
 313        init.name = name;
 314        init.ops = &zynqmp_pll_ops;
 315        init.flags = nodes->flag;
 316        init.parent_names = parents;
 317        init.num_parents = 1;
 318
 319        pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 320        if (!pll)
 321                return ERR_PTR(-ENOMEM);
 322
 323        pll->hw.init = &init;
 324        pll->clk_id = clk_id;
 325
 326        hw = &pll->hw;
 327        ret = clk_hw_register(NULL, hw);
 328        if (ret) {
 329                kfree(pll);
 330                return ERR_PTR(ret);
 331        }
 332
 333        clk_hw_set_rate_range(hw, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
 334        if (ret < 0)
 335                pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, ret);
 336
 337        return hw;
 338}
 339