linux/drivers/clk/qcom/clk-alpha-pll.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
   3 *
   4 * This software is licensed under the terms of the GNU General Public
   5 * License version 2, as published by the Free Software Foundation, and
   6 * may be copied, distributed, and modified under those terms.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/export.h>
  16#include <linux/clk-provider.h>
  17#include <linux/regmap.h>
  18#include <linux/delay.h>
  19
  20#include "clk-alpha-pll.h"
  21
  22#define PLL_MODE                0x00
  23# define PLL_OUTCTRL            BIT(0)
  24# define PLL_BYPASSNL           BIT(1)
  25# define PLL_RESET_N            BIT(2)
  26# define PLL_LOCK_COUNT_SHIFT   8
  27# define PLL_LOCK_COUNT_MASK    0x3f
  28# define PLL_BIAS_COUNT_SHIFT   14
  29# define PLL_BIAS_COUNT_MASK    0x3f
  30# define PLL_VOTE_FSM_ENA       BIT(20)
  31# define PLL_VOTE_FSM_RESET     BIT(21)
  32# define PLL_ACTIVE_FLAG        BIT(30)
  33# define PLL_LOCK_DET           BIT(31)
  34
  35#define PLL_L_VAL               0x04
  36#define PLL_ALPHA_VAL           0x08
  37#define PLL_ALPHA_VAL_U         0x0c
  38
  39#define PLL_USER_CTL            0x10
  40# define PLL_POST_DIV_SHIFT     8
  41# define PLL_POST_DIV_MASK      0xf
  42# define PLL_ALPHA_EN           BIT(24)
  43# define PLL_VCO_SHIFT          20
  44# define PLL_VCO_MASK           0x3
  45
  46#define PLL_USER_CTL_U          0x14
  47
  48#define PLL_CONFIG_CTL          0x18
  49#define PLL_TEST_CTL            0x1c
  50#define PLL_TEST_CTL_U          0x20
  51#define PLL_STATUS              0x24
  52
  53/*
  54 * Even though 40 bits are present, use only 32 for ease of calculation.
  55 */
  56#define ALPHA_REG_BITWIDTH      40
  57#define ALPHA_BITWIDTH          32
  58
  59#define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \
  60                                           struct clk_alpha_pll, clkr)
  61
  62#define to_clk_alpha_pll_postdiv(_hw) container_of(to_clk_regmap(_hw), \
  63                                           struct clk_alpha_pll_postdiv, clkr)
  64
  65static int wait_for_pll(struct clk_alpha_pll *pll)
  66{
  67        u32 val, mask, off;
  68        int count;
  69        int ret;
  70        const char *name = clk_hw_get_name(&pll->clkr.hw);
  71
  72        off = pll->offset;
  73        ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
  74        if (ret)
  75                return ret;
  76
  77        if (val & PLL_VOTE_FSM_ENA)
  78                mask = PLL_ACTIVE_FLAG;
  79        else
  80                mask = PLL_LOCK_DET;
  81
  82        /* Wait for pll to enable. */
  83        for (count = 100; count > 0; count--) {
  84                ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
  85                if (ret)
  86                        return ret;
  87                if ((val & mask) == mask)
  88                        return 0;
  89
  90                udelay(1);
  91        }
  92
  93        WARN(1, "%s didn't enable after voting for it!\n", name);
  94        return -ETIMEDOUT;
  95}
  96
  97static int clk_alpha_pll_enable(struct clk_hw *hw)
  98{
  99        int ret;
 100        struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 101        u32 val, mask, off;
 102
 103        off = pll->offset;
 104
 105        mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
 106        ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
 107        if (ret)
 108                return ret;
 109
 110        /* If in FSM mode, just vote for it */
 111        if (val & PLL_VOTE_FSM_ENA) {
 112                ret = clk_enable_regmap(hw);
 113                if (ret)
 114                        return ret;
 115                return wait_for_pll(pll);
 116        }
 117
 118        /* Skip if already enabled */
 119        if ((val & mask) == mask)
 120                return 0;
 121
 122        ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
 123                                 PLL_BYPASSNL, PLL_BYPASSNL);
 124        if (ret)
 125                return ret;
 126
 127        /*
 128         * H/W requires a 5us delay between disabling the bypass and
 129         * de-asserting the reset.
 130         */
 131        mb();
 132        udelay(5);
 133
 134        ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
 135                                 PLL_RESET_N, PLL_RESET_N);
 136        if (ret)
 137                return ret;
 138
 139        ret = wait_for_pll(pll);
 140        if (ret)
 141                return ret;
 142
 143        ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
 144                                 PLL_OUTCTRL, PLL_OUTCTRL);
 145
 146        /* Ensure that the write above goes through before returning. */
 147        mb();
 148        return ret;
 149}
 150
 151static void clk_alpha_pll_disable(struct clk_hw *hw)
 152{
 153        int ret;
 154        struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 155        u32 val, mask, off;
 156
 157        off = pll->offset;
 158
 159        ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
 160        if (ret)
 161                return;
 162
 163        /* If in FSM mode, just unvote it */
 164        if (val & PLL_VOTE_FSM_ENA) {
 165                clk_disable_regmap(hw);
 166                return;
 167        }
 168
 169        mask = PLL_OUTCTRL;
 170        regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, mask, 0);
 171
 172        /* Delay of 2 output clock ticks required until output is disabled */
 173        mb();
 174        udelay(1);
 175
 176        mask = PLL_RESET_N | PLL_BYPASSNL;
 177        regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, mask, 0);
 178}
 179
 180static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
 181{
 182        return (prate * l) + ((prate * a) >> ALPHA_BITWIDTH);
 183}
 184
 185static unsigned long
 186alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a)
 187{
 188        u64 remainder;
 189        u64 quotient;
 190
 191        quotient = rate;
 192        remainder = do_div(quotient, prate);
 193        *l = quotient;
 194
 195        if (!remainder) {
 196                *a = 0;
 197                return rate;
 198        }
 199
 200        /* Upper ALPHA_BITWIDTH bits of Alpha */
 201        quotient = remainder << ALPHA_BITWIDTH;
 202        remainder = do_div(quotient, prate);
 203
 204        if (remainder)
 205                quotient++;
 206
 207        *a = quotient;
 208        return alpha_pll_calc_rate(prate, *l, *a);
 209}
 210
 211static const struct pll_vco *
 212alpha_pll_find_vco(const struct clk_alpha_pll *pll, unsigned long rate)
 213{
 214        const struct pll_vco *v = pll->vco_table;
 215        const struct pll_vco *end = v + pll->num_vco;
 216
 217        for (; v < end; v++)
 218                if (rate >= v->min_freq && rate <= v->max_freq)
 219                        return v;
 220
 221        return NULL;
 222}
 223
 224static unsigned long
 225clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 226{
 227        u32 l, low, high, ctl;
 228        u64 a = 0, prate = parent_rate;
 229        struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 230        u32 off = pll->offset;
 231
 232        regmap_read(pll->clkr.regmap, off + PLL_L_VAL, &l);
 233
 234        regmap_read(pll->clkr.regmap, off + PLL_USER_CTL, &ctl);
 235        if (ctl & PLL_ALPHA_EN) {
 236                regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL, &low);
 237                regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, &high);
 238                a = (u64)high << 32 | low;
 239                a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
 240        }
 241
 242        return alpha_pll_calc_rate(prate, l, a);
 243}
 244
 245static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 246                                  unsigned long prate)
 247{
 248        struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 249        const struct pll_vco *vco;
 250        u32 l, off = pll->offset;
 251        u64 a;
 252
 253        rate = alpha_pll_round_rate(rate, prate, &l, &a);
 254        vco = alpha_pll_find_vco(pll, rate);
 255        if (!vco) {
 256                pr_err("alpha pll not in a valid vco range\n");
 257                return -EINVAL;
 258        }
 259
 260        a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
 261
 262        regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
 263        regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL, a);
 264        regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32);
 265
 266        regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
 267                           PLL_VCO_MASK << PLL_VCO_SHIFT,
 268                           vco->val << PLL_VCO_SHIFT);
 269
 270        regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN,
 271                           PLL_ALPHA_EN);
 272
 273        return 0;
 274}
 275
 276static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 277                                     unsigned long *prate)
 278{
 279        struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 280        u32 l;
 281        u64 a;
 282        unsigned long min_freq, max_freq;
 283
 284        rate = alpha_pll_round_rate(rate, *prate, &l, &a);
 285        if (alpha_pll_find_vco(pll, rate))
 286                return rate;
 287
 288        min_freq = pll->vco_table[0].min_freq;
 289        max_freq = pll->vco_table[pll->num_vco - 1].max_freq;
 290
 291        return clamp(rate, min_freq, max_freq);
 292}
 293
 294const struct clk_ops clk_alpha_pll_ops = {
 295        .enable = clk_alpha_pll_enable,
 296        .disable = clk_alpha_pll_disable,
 297        .recalc_rate = clk_alpha_pll_recalc_rate,
 298        .round_rate = clk_alpha_pll_round_rate,
 299        .set_rate = clk_alpha_pll_set_rate,
 300};
 301EXPORT_SYMBOL_GPL(clk_alpha_pll_ops);
 302
 303static unsigned long
 304clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 305{
 306        struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
 307        u32 ctl;
 308
 309        regmap_read(pll->clkr.regmap, pll->offset + PLL_USER_CTL, &ctl);
 310
 311        ctl >>= PLL_POST_DIV_SHIFT;
 312        ctl &= PLL_POST_DIV_MASK;
 313
 314        return parent_rate >> fls(ctl);
 315}
 316
 317static const struct clk_div_table clk_alpha_div_table[] = {
 318        { 0x0, 1 },
 319        { 0x1, 2 },
 320        { 0x3, 4 },
 321        { 0x7, 8 },
 322        { 0xf, 16 },
 323        { }
 324};
 325
 326static long
 327clk_alpha_pll_postdiv_round_rate(struct clk_hw *hw, unsigned long rate,
 328                                 unsigned long *prate)
 329{
 330        struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
 331
 332        return divider_round_rate(hw, rate, prate, clk_alpha_div_table,
 333                                  pll->width, CLK_DIVIDER_POWER_OF_TWO);
 334}
 335
 336static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
 337                                          unsigned long parent_rate)
 338{
 339        struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
 340        int div;
 341
 342        /* 16 -> 0xf, 8 -> 0x7, 4 -> 0x3, 2 -> 0x1, 1 -> 0x0 */
 343        div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
 344
 345        return regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_USER_CTL,
 346                                  PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
 347                                  div << PLL_POST_DIV_SHIFT);
 348}
 349
 350const struct clk_ops clk_alpha_pll_postdiv_ops = {
 351        .recalc_rate = clk_alpha_pll_postdiv_recalc_rate,
 352        .round_rate = clk_alpha_pll_postdiv_round_rate,
 353        .set_rate = clk_alpha_pll_postdiv_set_rate,
 354};
 355EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ops);
 356