linux/drivers/clk/imx/clk-frac-pll.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2018 NXP.
   4 *
   5 * This driver supports the fractional plls found in the imx8m SOCs
   6 *
   7 * Documentation for this fractional pll can be found at:
   8 *   https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834
   9 */
  10
  11#include <linux/clk-provider.h>
  12#include <linux/err.h>
  13#include <linux/export.h>
  14#include <linux/io.h>
  15#include <linux/iopoll.h>
  16#include <linux/slab.h>
  17#include <linux/bitfield.h>
  18
  19#include "clk.h"
  20
  21#define PLL_CFG0                0x0
  22#define PLL_CFG1                0x4
  23
  24#define PLL_LOCK_STATUS         BIT(31)
  25#define PLL_PD_MASK             BIT(19)
  26#define PLL_BYPASS_MASK         BIT(14)
  27#define PLL_NEWDIV_VAL          BIT(12)
  28#define PLL_NEWDIV_ACK          BIT(11)
  29#define PLL_FRAC_DIV_MASK       GENMASK(30, 7)
  30#define PLL_INT_DIV_MASK        GENMASK(6, 0)
  31#define PLL_OUTPUT_DIV_MASK     GENMASK(4, 0)
  32#define PLL_FRAC_DENOM          0x1000000
  33
  34#define PLL_FRAC_LOCK_TIMEOUT   10000
  35#define PLL_FRAC_ACK_TIMEOUT    500000
  36
  37struct clk_frac_pll {
  38        struct clk_hw   hw;
  39        void __iomem    *base;
  40};
  41
  42#define to_clk_frac_pll(_hw) container_of(_hw, struct clk_frac_pll, hw)
  43
  44static int clk_wait_lock(struct clk_frac_pll *pll)
  45{
  46        u32 val;
  47
  48        return readl_poll_timeout(pll->base, val, val & PLL_LOCK_STATUS, 0,
  49                                        PLL_FRAC_LOCK_TIMEOUT);
  50}
  51
  52static int clk_wait_ack(struct clk_frac_pll *pll)
  53{
  54        u32 val;
  55
  56        /* return directly if the pll is in powerdown or in bypass */
  57        if (readl_relaxed(pll->base) & (PLL_PD_MASK | PLL_BYPASS_MASK))
  58                return 0;
  59
  60        /* Wait for the pll's divfi and divff to be reloaded */
  61        return readl_poll_timeout(pll->base, val, val & PLL_NEWDIV_ACK, 0,
  62                                        PLL_FRAC_ACK_TIMEOUT);
  63}
  64
  65static int clk_pll_prepare(struct clk_hw *hw)
  66{
  67        struct clk_frac_pll *pll = to_clk_frac_pll(hw);
  68        u32 val;
  69
  70        val = readl_relaxed(pll->base + PLL_CFG0);
  71        val &= ~PLL_PD_MASK;
  72        writel_relaxed(val, pll->base + PLL_CFG0);
  73
  74        return clk_wait_lock(pll);
  75}
  76
  77static void clk_pll_unprepare(struct clk_hw *hw)
  78{
  79        struct clk_frac_pll *pll = to_clk_frac_pll(hw);
  80        u32 val;
  81
  82        val = readl_relaxed(pll->base + PLL_CFG0);
  83        val |= PLL_PD_MASK;
  84        writel_relaxed(val, pll->base + PLL_CFG0);
  85}
  86
  87static int clk_pll_is_prepared(struct clk_hw *hw)
  88{
  89        struct clk_frac_pll *pll = to_clk_frac_pll(hw);
  90        u32 val;
  91
  92        val = readl_relaxed(pll->base + PLL_CFG0);
  93        return (val & PLL_PD_MASK) ? 0 : 1;
  94}
  95
  96static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
  97                                         unsigned long parent_rate)
  98{
  99        struct clk_frac_pll *pll = to_clk_frac_pll(hw);
 100        u32 val, divff, divfi, divq;
 101        u64 temp64 = parent_rate;
 102        u64 rate;
 103
 104        val = readl_relaxed(pll->base + PLL_CFG0);
 105        divq = (FIELD_GET(PLL_OUTPUT_DIV_MASK, val) + 1) * 2;
 106        val = readl_relaxed(pll->base + PLL_CFG1);
 107        divff = FIELD_GET(PLL_FRAC_DIV_MASK, val);
 108        divfi = FIELD_GET(PLL_INT_DIV_MASK, val);
 109
 110        temp64 *= 8;
 111        temp64 *= divff;
 112        do_div(temp64, PLL_FRAC_DENOM);
 113        do_div(temp64, divq);
 114
 115        rate = parent_rate * 8 * (divfi + 1);
 116        do_div(rate, divq);
 117        rate += temp64;
 118
 119        return rate;
 120}
 121
 122static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 123                               unsigned long *prate)
 124{
 125        u64 parent_rate = *prate;
 126        u32 divff, divfi;
 127        u64 temp64;
 128
 129        parent_rate *= 8;
 130        rate *= 2;
 131        temp64 = rate;
 132        do_div(temp64, parent_rate);
 133        divfi = temp64;
 134        temp64 = rate - divfi * parent_rate;
 135        temp64 *= PLL_FRAC_DENOM;
 136        do_div(temp64, parent_rate);
 137        divff = temp64;
 138
 139        temp64 = parent_rate;
 140        temp64 *= divff;
 141        do_div(temp64, PLL_FRAC_DENOM);
 142
 143        rate = parent_rate * divfi + temp64;
 144
 145        return rate / 2;
 146}
 147
 148/*
 149 * To simplify the clock calculation, we can keep the 'PLL_OUTPUT_VAL' at zero
 150 * (means the PLL output will be divided by 2). So the PLL output can use
 151 * the below formula:
 152 * pllout = parent_rate * 8 / 2 * DIVF_VAL;
 153 * where DIVF_VAL = 1 + DIVFI + DIVFF / 2^24.
 154 */
 155static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 156                            unsigned long parent_rate)
 157{
 158        struct clk_frac_pll *pll = to_clk_frac_pll(hw);
 159        u32 val, divfi, divff;
 160        u64 temp64;
 161        int ret;
 162
 163        parent_rate *= 8;
 164        rate *= 2;
 165        divfi = rate / parent_rate;
 166        temp64 = parent_rate * divfi;
 167        temp64 = rate - temp64;
 168        temp64 *= PLL_FRAC_DENOM;
 169        do_div(temp64, parent_rate);
 170        divff = temp64;
 171
 172        val = readl_relaxed(pll->base + PLL_CFG1);
 173        val &= ~(PLL_FRAC_DIV_MASK | PLL_INT_DIV_MASK);
 174        val |= (divff << 7) | (divfi - 1);
 175        writel_relaxed(val, pll->base + PLL_CFG1);
 176
 177        val = readl_relaxed(pll->base + PLL_CFG0);
 178        val &= ~0x1f;
 179        writel_relaxed(val, pll->base + PLL_CFG0);
 180
 181        /* Set the NEV_DIV_VAL to reload the DIVFI and DIVFF */
 182        val = readl_relaxed(pll->base + PLL_CFG0);
 183        val |= PLL_NEWDIV_VAL;
 184        writel_relaxed(val, pll->base + PLL_CFG0);
 185
 186        ret = clk_wait_ack(pll);
 187
 188        /* clear the NEV_DIV_VAL */
 189        val = readl_relaxed(pll->base + PLL_CFG0);
 190        val &= ~PLL_NEWDIV_VAL;
 191        writel_relaxed(val, pll->base + PLL_CFG0);
 192
 193        return ret;
 194}
 195
 196static const struct clk_ops clk_frac_pll_ops = {
 197        .prepare        = clk_pll_prepare,
 198        .unprepare      = clk_pll_unprepare,
 199        .is_prepared    = clk_pll_is_prepared,
 200        .recalc_rate    = clk_pll_recalc_rate,
 201        .round_rate     = clk_pll_round_rate,
 202        .set_rate       = clk_pll_set_rate,
 203};
 204
 205struct clk_hw *imx_clk_hw_frac_pll(const char *name,
 206                                   const char *parent_name,
 207                                   void __iomem *base)
 208{
 209        struct clk_init_data init;
 210        struct clk_frac_pll *pll;
 211        struct clk_hw *hw;
 212        int ret;
 213
 214        pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 215        if (!pll)
 216                return ERR_PTR(-ENOMEM);
 217
 218        init.name = name;
 219        init.ops = &clk_frac_pll_ops;
 220        init.flags = 0;
 221        init.parent_names = &parent_name;
 222        init.num_parents = 1;
 223
 224        pll->base = base;
 225        pll->hw.init = &init;
 226
 227        hw = &pll->hw;
 228
 229        ret = clk_hw_register(NULL, hw);
 230        if (ret) {
 231                kfree(pll);
 232                return ERR_PTR(ret);
 233        }
 234
 235        return hw;
 236}
 237EXPORT_SYMBOL_GPL(imx_clk_hw_frac_pll);
 238