Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2016 Freescale Semiconductor, Inc.
0004  * Copyright 2017~2018 NXP
0005  *
0006  * Author: Dong Aisheng <aisheng.dong@nxp.com>
0007  *
0008  */
0009 
0010 #include <linux/bits.h>
0011 #include <linux/clk-provider.h>
0012 #include <linux/err.h>
0013 #include <linux/io.h>
0014 #include <linux/iopoll.h>
0015 #include <linux/slab.h>
0016 
0017 #include "clk.h"
0018 
0019 /* PLL Control Status Register (xPLLCSR) */
0020 #define PLL_CSR_OFFSET      0x0
0021 #define PLL_VLD         BIT(24)
0022 #define PLL_EN          BIT(0)
0023 
0024 /* PLL Configuration Register (xPLLCFG) */
0025 #define PLL_CFG_OFFSET      0x08
0026 #define IMX8ULP_PLL_CFG_OFFSET  0x10
0027 #define BP_PLL_MULT     16
0028 #define BM_PLL_MULT     (0x7f << 16)
0029 
0030 /* PLL Numerator Register (xPLLNUM) */
0031 #define PLL_NUM_OFFSET      0x10
0032 #define IMX8ULP_PLL_NUM_OFFSET  0x1c
0033 
0034 /* PLL Denominator Register (xPLLDENOM) */
0035 #define PLL_DENOM_OFFSET    0x14
0036 #define IMX8ULP_PLL_DENOM_OFFSET    0x18
0037 
0038 #define MAX_MFD         0x3fffffff
0039 #define DEFAULT_MFD     1000000
0040 
0041 struct clk_pllv4 {
0042     struct clk_hw   hw;
0043     void __iomem    *base;
0044     u32     cfg_offset;
0045     u32     num_offset;
0046     u32     denom_offset;
0047 };
0048 
0049 /* Valid PLL MULT Table */
0050 static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
0051 
0052 #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
0053 
0054 #define LOCK_TIMEOUT_US     USEC_PER_MSEC
0055 
0056 static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
0057 {
0058     u32 csr;
0059 
0060     return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
0061                   csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
0062 }
0063 
0064 static int clk_pllv4_is_prepared(struct clk_hw *hw)
0065 {
0066     struct clk_pllv4 *pll = to_clk_pllv4(hw);
0067 
0068     if (readl_relaxed(pll->base) & PLL_EN)
0069         return 1;
0070 
0071     return 0;
0072 }
0073 
0074 static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
0075                        unsigned long parent_rate)
0076 {
0077     struct clk_pllv4 *pll = to_clk_pllv4(hw);
0078     u32 mult, mfn, mfd;
0079     u64 temp64;
0080 
0081     mult = readl_relaxed(pll->base + pll->cfg_offset);
0082     mult &= BM_PLL_MULT;
0083     mult >>= BP_PLL_MULT;
0084 
0085     mfn = readl_relaxed(pll->base + pll->num_offset);
0086     mfd = readl_relaxed(pll->base + pll->denom_offset);
0087     temp64 = parent_rate;
0088     temp64 *= mfn;
0089     do_div(temp64, mfd);
0090 
0091     return (parent_rate * mult) + (u32)temp64;
0092 }
0093 
0094 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
0095                  unsigned long *prate)
0096 {
0097     unsigned long parent_rate = *prate;
0098     unsigned long round_rate, i;
0099     u32 mfn, mfd = DEFAULT_MFD;
0100     bool found = false;
0101     u64 temp64;
0102 
0103     for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
0104         round_rate = parent_rate * pllv4_mult_table[i];
0105         if (rate >= round_rate) {
0106             found = true;
0107             break;
0108         }
0109     }
0110 
0111     if (!found) {
0112         pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
0113             clk_hw_get_name(hw), rate, parent_rate);
0114         return 0;
0115     }
0116 
0117     if (parent_rate <= MAX_MFD)
0118         mfd = parent_rate;
0119 
0120     temp64 = (u64)(rate - round_rate);
0121     temp64 *= mfd;
0122     do_div(temp64, parent_rate);
0123     mfn = temp64;
0124 
0125     /*
0126      * NOTE: The value of numerator must always be configured to be
0127      * less than the value of the denominator. If we can't get a proper
0128      * pair of mfn/mfd, we simply return the round_rate without using
0129      * the frac part.
0130      */
0131     if (mfn >= mfd)
0132         return round_rate;
0133 
0134     temp64 = (u64)parent_rate;
0135     temp64 *= mfn;
0136     do_div(temp64, mfd);
0137 
0138     return round_rate + (u32)temp64;
0139 }
0140 
0141 static bool clk_pllv4_is_valid_mult(unsigned int mult)
0142 {
0143     int i;
0144 
0145     /* check if mult is in valid MULT table */
0146     for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
0147         if (pllv4_mult_table[i] == mult)
0148             return true;
0149     }
0150 
0151     return false;
0152 }
0153 
0154 static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
0155                   unsigned long parent_rate)
0156 {
0157     struct clk_pllv4 *pll = to_clk_pllv4(hw);
0158     u32 val, mult, mfn, mfd = DEFAULT_MFD;
0159     u64 temp64;
0160 
0161     mult = rate / parent_rate;
0162 
0163     if (!clk_pllv4_is_valid_mult(mult))
0164         return -EINVAL;
0165 
0166     if (parent_rate <= MAX_MFD)
0167         mfd = parent_rate;
0168 
0169     temp64 = (u64)(rate - mult * parent_rate);
0170     temp64 *= mfd;
0171     do_div(temp64, parent_rate);
0172     mfn = temp64;
0173 
0174     val = readl_relaxed(pll->base + pll->cfg_offset);
0175     val &= ~BM_PLL_MULT;
0176     val |= mult << BP_PLL_MULT;
0177     writel_relaxed(val, pll->base + pll->cfg_offset);
0178 
0179     writel_relaxed(mfn, pll->base + pll->num_offset);
0180     writel_relaxed(mfd, pll->base + pll->denom_offset);
0181 
0182     return 0;
0183 }
0184 
0185 static int clk_pllv4_prepare(struct clk_hw *hw)
0186 {
0187     u32 val;
0188     struct clk_pllv4 *pll = to_clk_pllv4(hw);
0189 
0190     val = readl_relaxed(pll->base);
0191     val |= PLL_EN;
0192     writel_relaxed(val, pll->base);
0193 
0194     return clk_pllv4_wait_lock(pll);
0195 }
0196 
0197 static void clk_pllv4_unprepare(struct clk_hw *hw)
0198 {
0199     u32 val;
0200     struct clk_pllv4 *pll = to_clk_pllv4(hw);
0201 
0202     val = readl_relaxed(pll->base);
0203     val &= ~PLL_EN;
0204     writel_relaxed(val, pll->base);
0205 }
0206 
0207 static const struct clk_ops clk_pllv4_ops = {
0208     .recalc_rate    = clk_pllv4_recalc_rate,
0209     .round_rate = clk_pllv4_round_rate,
0210     .set_rate   = clk_pllv4_set_rate,
0211     .prepare    = clk_pllv4_prepare,
0212     .unprepare  = clk_pllv4_unprepare,
0213     .is_prepared    = clk_pllv4_is_prepared,
0214 };
0215 
0216 struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name,
0217          const char *parent_name, void __iomem *base)
0218 {
0219     struct clk_pllv4 *pll;
0220     struct clk_hw *hw;
0221     struct clk_init_data init;
0222     int ret;
0223 
0224     pll = kzalloc(sizeof(*pll), GFP_KERNEL);
0225     if (!pll)
0226         return ERR_PTR(-ENOMEM);
0227 
0228     pll->base = base;
0229 
0230     if (type == IMX_PLLV4_IMX8ULP) {
0231         pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET;
0232         pll->num_offset = IMX8ULP_PLL_NUM_OFFSET;
0233         pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET;
0234     } else {
0235         pll->cfg_offset = PLL_CFG_OFFSET;
0236         pll->num_offset = PLL_NUM_OFFSET;
0237         pll->denom_offset = PLL_DENOM_OFFSET;
0238     }
0239 
0240     init.name = name;
0241     init.ops = &clk_pllv4_ops;
0242     init.parent_names = &parent_name;
0243     init.num_parents = 1;
0244     init.flags = CLK_SET_RATE_GATE;
0245 
0246     pll->hw.init = &init;
0247 
0248     hw = &pll->hw;
0249     ret = clk_hw_register(NULL, hw);
0250     if (ret) {
0251         kfree(pll);
0252         hw = ERR_PTR(ret);
0253     }
0254 
0255     return hw;
0256 }
0257 EXPORT_SYMBOL_GPL(imx_clk_hw_pllv4);