root/drivers/clk/imx/clk-pllv4.c

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

DEFINITIONS

This source file includes following definitions.
  1. clk_pllv4_wait_lock
  2. clk_pllv4_is_enabled
  3. clk_pllv4_recalc_rate
  4. clk_pllv4_round_rate
  5. clk_pllv4_is_valid_mult
  6. clk_pllv4_set_rate
  7. clk_pllv4_enable
  8. clk_pllv4_disable
  9. imx_clk_pllv4

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright (C) 2016 Freescale Semiconductor, Inc.
   4  * Copyright 2017~2018 NXP
   5  *
   6  * Author: Dong Aisheng <aisheng.dong@nxp.com>
   7  *
   8  */
   9 
  10 #include <linux/clk-provider.h>
  11 #include <linux/err.h>
  12 #include <linux/io.h>
  13 #include <linux/iopoll.h>
  14 #include <linux/slab.h>
  15 
  16 #include "clk.h"
  17 
  18 /* PLL Control Status Register (xPLLCSR) */
  19 #define PLL_CSR_OFFSET          0x0
  20 #define PLL_VLD                 BIT(24)
  21 #define PLL_EN                  BIT(0)
  22 
  23 /* PLL Configuration Register (xPLLCFG) */
  24 #define PLL_CFG_OFFSET          0x08
  25 #define BP_PLL_MULT             16
  26 #define BM_PLL_MULT             (0x7f << 16)
  27 
  28 /* PLL Numerator Register (xPLLNUM) */
  29 #define PLL_NUM_OFFSET          0x10
  30 
  31 /* PLL Denominator Register (xPLLDENOM) */
  32 #define PLL_DENOM_OFFSET        0x14
  33 
  34 #define MAX_MFD                 0x3fffffff
  35 #define DEFAULT_MFD             1000000
  36 
  37 struct clk_pllv4 {
  38         struct clk_hw   hw;
  39         void __iomem    *base;
  40 };
  41 
  42 /* Valid PLL MULT Table */
  43 static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
  44 
  45 #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
  46 
  47 #define LOCK_TIMEOUT_US         USEC_PER_MSEC
  48 
  49 static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
  50 {
  51         u32 csr;
  52 
  53         return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
  54                                   csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
  55 }
  56 
  57 static int clk_pllv4_is_enabled(struct clk_hw *hw)
  58 {
  59         struct clk_pllv4 *pll = to_clk_pllv4(hw);
  60 
  61         if (readl_relaxed(pll->base) & PLL_EN)
  62                 return 1;
  63 
  64         return 0;
  65 }
  66 
  67 static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
  68                                            unsigned long parent_rate)
  69 {
  70         struct clk_pllv4 *pll = to_clk_pllv4(hw);
  71         u32 mult, mfn, mfd;
  72         u64 temp64;
  73 
  74         mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
  75         mult &= BM_PLL_MULT;
  76         mult >>= BP_PLL_MULT;
  77 
  78         mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
  79         mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
  80         temp64 = parent_rate;
  81         temp64 *= mfn;
  82         do_div(temp64, mfd);
  83 
  84         return (parent_rate * mult) + (u32)temp64;
  85 }
  86 
  87 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
  88                                  unsigned long *prate)
  89 {
  90         unsigned long parent_rate = *prate;
  91         unsigned long round_rate, i;
  92         u32 mfn, mfd = DEFAULT_MFD;
  93         bool found = false;
  94         u64 temp64;
  95 
  96         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
  97                 round_rate = parent_rate * pllv4_mult_table[i];
  98                 if (rate >= round_rate) {
  99                         found = true;
 100                         break;
 101                 }
 102         }
 103 
 104         if (!found) {
 105                 pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
 106                         clk_hw_get_name(hw), rate, parent_rate);
 107                 return 0;
 108         }
 109 
 110         if (parent_rate <= MAX_MFD)
 111                 mfd = parent_rate;
 112 
 113         temp64 = (u64)(rate - round_rate);
 114         temp64 *= mfd;
 115         do_div(temp64, parent_rate);
 116         mfn = temp64;
 117 
 118         /*
 119          * NOTE: The value of numerator must always be configured to be
 120          * less than the value of the denominator. If we can't get a proper
 121          * pair of mfn/mfd, we simply return the round_rate without using
 122          * the frac part.
 123          */
 124         if (mfn >= mfd)
 125                 return round_rate;
 126 
 127         temp64 = (u64)parent_rate;
 128         temp64 *= mfn;
 129         do_div(temp64, mfd);
 130 
 131         return round_rate + (u32)temp64;
 132 }
 133 
 134 static bool clk_pllv4_is_valid_mult(unsigned int mult)
 135 {
 136         int i;
 137 
 138         /* check if mult is in valid MULT table */
 139         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
 140                 if (pllv4_mult_table[i] == mult)
 141                         return true;
 142         }
 143 
 144         return false;
 145 }
 146 
 147 static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
 148                               unsigned long parent_rate)
 149 {
 150         struct clk_pllv4 *pll = to_clk_pllv4(hw);
 151         u32 val, mult, mfn, mfd = DEFAULT_MFD;
 152         u64 temp64;
 153 
 154         mult = rate / parent_rate;
 155 
 156         if (!clk_pllv4_is_valid_mult(mult))
 157                 return -EINVAL;
 158 
 159         if (parent_rate <= MAX_MFD)
 160                 mfd = parent_rate;
 161 
 162         temp64 = (u64)(rate - mult * parent_rate);
 163         temp64 *= mfd;
 164         do_div(temp64, parent_rate);
 165         mfn = temp64;
 166 
 167         val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
 168         val &= ~BM_PLL_MULT;
 169         val |= mult << BP_PLL_MULT;
 170         writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
 171 
 172         writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
 173         writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
 174 
 175         return 0;
 176 }
 177 
 178 static int clk_pllv4_enable(struct clk_hw *hw)
 179 {
 180         u32 val;
 181         struct clk_pllv4 *pll = to_clk_pllv4(hw);
 182 
 183         val = readl_relaxed(pll->base);
 184         val |= PLL_EN;
 185         writel_relaxed(val, pll->base);
 186 
 187         return clk_pllv4_wait_lock(pll);
 188 }
 189 
 190 static void clk_pllv4_disable(struct clk_hw *hw)
 191 {
 192         u32 val;
 193         struct clk_pllv4 *pll = to_clk_pllv4(hw);
 194 
 195         val = readl_relaxed(pll->base);
 196         val &= ~PLL_EN;
 197         writel_relaxed(val, pll->base);
 198 }
 199 
 200 static const struct clk_ops clk_pllv4_ops = {
 201         .recalc_rate    = clk_pllv4_recalc_rate,
 202         .round_rate     = clk_pllv4_round_rate,
 203         .set_rate       = clk_pllv4_set_rate,
 204         .enable         = clk_pllv4_enable,
 205         .disable        = clk_pllv4_disable,
 206         .is_enabled     = clk_pllv4_is_enabled,
 207 };
 208 
 209 struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name,
 210                           void __iomem *base)
 211 {
 212         struct clk_pllv4 *pll;
 213         struct clk_hw *hw;
 214         struct clk_init_data init;
 215         int ret;
 216 
 217         pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 218         if (!pll)
 219                 return ERR_PTR(-ENOMEM);
 220 
 221         pll->base = base;
 222 
 223         init.name = name;
 224         init.ops = &clk_pllv4_ops;
 225         init.parent_names = &parent_name;
 226         init.num_parents = 1;
 227         init.flags = CLK_SET_RATE_GATE;
 228 
 229         pll->hw.init = &init;
 230 
 231         hw = &pll->hw;
 232         ret = clk_hw_register(NULL, hw);
 233         if (ret) {
 234                 kfree(pll);
 235                 hw = ERR_PTR(ret);
 236         }
 237 
 238         return hw;
 239 }

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