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                        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 */
 216static 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 */
 241static 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 */
 264static 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
 281static 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 */
 300struct 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}
 336