linux/drivers/clk/qcom/clk-pll.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013, 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/bitops.h>
  16#include <linux/err.h>
  17#include <linux/bug.h>
  18#include <linux/delay.h>
  19#include <linux/export.h>
  20#include <linux/clk-provider.h>
  21#include <linux/regmap.h>
  22
  23#include <asm/div64.h>
  24
  25#include "clk-pll.h"
  26
  27#define PLL_OUTCTRL             BIT(0)
  28#define PLL_BYPASSNL            BIT(1)
  29#define PLL_RESET_N             BIT(2)
  30#define PLL_LOCK_COUNT_SHIFT    8
  31#define PLL_LOCK_COUNT_MASK     0x3f
  32#define PLL_BIAS_COUNT_SHIFT    14
  33#define PLL_BIAS_COUNT_MASK     0x3f
  34#define PLL_VOTE_FSM_ENA        BIT(20)
  35#define PLL_VOTE_FSM_RESET      BIT(21)
  36
  37static int clk_pll_enable(struct clk_hw *hw)
  38{
  39        struct clk_pll *pll = to_clk_pll(hw);
  40        int ret;
  41        u32 mask, val;
  42
  43        mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
  44        ret = regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
  45        if (ret)
  46                return ret;
  47
  48        /* Skip if already enabled or in FSM mode */
  49        if ((val & mask) == mask || val & PLL_VOTE_FSM_ENA)
  50                return 0;
  51
  52        /* Disable PLL bypass mode. */
  53        ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_BYPASSNL,
  54                                 PLL_BYPASSNL);
  55        if (ret)
  56                return ret;
  57
  58        /*
  59         * H/W requires a 5us delay between disabling the bypass and
  60         * de-asserting the reset. Delay 10us just to be safe.
  61         */
  62        udelay(10);
  63
  64        /* De-assert active-low PLL reset. */
  65        ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_RESET_N,
  66                                 PLL_RESET_N);
  67        if (ret)
  68                return ret;
  69
  70        /* Wait until PLL is locked. */
  71        udelay(50);
  72
  73        /* Enable PLL output. */
  74        return regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_OUTCTRL,
  75                                 PLL_OUTCTRL);
  76}
  77
  78static void clk_pll_disable(struct clk_hw *hw)
  79{
  80        struct clk_pll *pll = to_clk_pll(hw);
  81        u32 mask;
  82        u32 val;
  83
  84        regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
  85        /* Skip if in FSM mode */
  86        if (val & PLL_VOTE_FSM_ENA)
  87                return;
  88        mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
  89        regmap_update_bits(pll->clkr.regmap, pll->mode_reg, mask, 0);
  90}
  91
  92static unsigned long
  93clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
  94{
  95        struct clk_pll *pll = to_clk_pll(hw);
  96        u32 l, m, n, config;
  97        unsigned long rate;
  98        u64 tmp;
  99
 100        regmap_read(pll->clkr.regmap, pll->l_reg, &l);
 101        regmap_read(pll->clkr.regmap, pll->m_reg, &m);
 102        regmap_read(pll->clkr.regmap, pll->n_reg, &n);
 103
 104        l &= 0x3ff;
 105        m &= 0x7ffff;
 106        n &= 0x7ffff;
 107
 108        rate = parent_rate * l;
 109        if (n) {
 110                tmp = parent_rate;
 111                tmp *= m;
 112                do_div(tmp, n);
 113                rate += tmp;
 114        }
 115        if (pll->post_div_width) {
 116                regmap_read(pll->clkr.regmap, pll->config_reg, &config);
 117                config >>= pll->post_div_shift;
 118                config &= BIT(pll->post_div_width) - 1;
 119                rate /= config + 1;
 120        }
 121
 122        return rate;
 123}
 124
 125static const
 126struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
 127{
 128        if (!f)
 129                return NULL;
 130
 131        for (; f->freq; f++)
 132                if (rate <= f->freq)
 133                        return f;
 134
 135        return NULL;
 136}
 137
 138static long
 139clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
 140                       unsigned long min_rate, unsigned long max_rate,
 141                       unsigned long *p_rate, struct clk_hw **p)
 142{
 143        struct clk_pll *pll = to_clk_pll(hw);
 144        const struct pll_freq_tbl *f;
 145
 146        f = find_freq(pll->freq_tbl, rate);
 147        if (!f)
 148                return clk_pll_recalc_rate(hw, *p_rate);
 149
 150        return f->freq;
 151}
 152
 153static int
 154clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
 155{
 156        struct clk_pll *pll = to_clk_pll(hw);
 157        const struct pll_freq_tbl *f;
 158        bool enabled;
 159        u32 mode;
 160        u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
 161
 162        f = find_freq(pll->freq_tbl, rate);
 163        if (!f)
 164                return -EINVAL;
 165
 166        regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
 167        enabled = (mode & enable_mask) == enable_mask;
 168
 169        if (enabled)
 170                clk_pll_disable(hw);
 171
 172        regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
 173        regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
 174        regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
 175        regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
 176
 177        if (enabled)
 178                clk_pll_enable(hw);
 179
 180        return 0;
 181}
 182
 183const struct clk_ops clk_pll_ops = {
 184        .enable = clk_pll_enable,
 185        .disable = clk_pll_disable,
 186        .recalc_rate = clk_pll_recalc_rate,
 187        .determine_rate = clk_pll_determine_rate,
 188        .set_rate = clk_pll_set_rate,
 189};
 190EXPORT_SYMBOL_GPL(clk_pll_ops);
 191
 192static int wait_for_pll(struct clk_pll *pll)
 193{
 194        u32 val;
 195        int count;
 196        int ret;
 197        const char *name = __clk_get_name(pll->clkr.hw.clk);
 198
 199        /* Wait for pll to enable. */
 200        for (count = 200; count > 0; count--) {
 201                ret = regmap_read(pll->clkr.regmap, pll->status_reg, &val);
 202                if (ret)
 203                        return ret;
 204                if (val & BIT(pll->status_bit))
 205                        return 0;
 206                udelay(1);
 207        }
 208
 209        WARN(1, "%s didn't enable after voting for it!\n", name);
 210        return -ETIMEDOUT;
 211}
 212
 213static int clk_pll_vote_enable(struct clk_hw *hw)
 214{
 215        int ret;
 216        struct clk_pll *p = to_clk_pll(__clk_get_hw(__clk_get_parent(hw->clk)));
 217
 218        ret = clk_enable_regmap(hw);
 219        if (ret)
 220                return ret;
 221
 222        return wait_for_pll(p);
 223}
 224
 225const struct clk_ops clk_pll_vote_ops = {
 226        .enable = clk_pll_vote_enable,
 227        .disable = clk_disable_regmap,
 228};
 229EXPORT_SYMBOL_GPL(clk_pll_vote_ops);
 230
 231static void
 232clk_pll_set_fsm_mode(struct clk_pll *pll, struct regmap *regmap, u8 lock_count)
 233{
 234        u32 val;
 235        u32 mask;
 236
 237        /* De-assert reset to FSM */
 238        regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_RESET, 0);
 239
 240        /* Program bias count and lock count */
 241        val = 1 << PLL_BIAS_COUNT_SHIFT | lock_count << PLL_LOCK_COUNT_SHIFT;
 242        mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
 243        mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
 244        regmap_update_bits(regmap, pll->mode_reg, mask, val);
 245
 246        /* Enable PLL FSM voting */
 247        regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_ENA,
 248                PLL_VOTE_FSM_ENA);
 249}
 250
 251static void clk_pll_configure(struct clk_pll *pll, struct regmap *regmap,
 252        const struct pll_config *config)
 253{
 254        u32 val;
 255        u32 mask;
 256
 257        regmap_write(regmap, pll->l_reg, config->l);
 258        regmap_write(regmap, pll->m_reg, config->m);
 259        regmap_write(regmap, pll->n_reg, config->n);
 260
 261        val = config->vco_val;
 262        val |= config->pre_div_val;
 263        val |= config->post_div_val;
 264        val |= config->mn_ena_mask;
 265        val |= config->main_output_mask;
 266        val |= config->aux_output_mask;
 267
 268        mask = config->vco_mask;
 269        mask |= config->pre_div_mask;
 270        mask |= config->post_div_mask;
 271        mask |= config->mn_ena_mask;
 272        mask |= config->main_output_mask;
 273        mask |= config->aux_output_mask;
 274
 275        regmap_update_bits(regmap, pll->config_reg, mask, val);
 276}
 277
 278void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap,
 279                const struct pll_config *config, bool fsm_mode)
 280{
 281        clk_pll_configure(pll, regmap, config);
 282        if (fsm_mode)
 283                clk_pll_set_fsm_mode(pll, regmap, 8);
 284}
 285EXPORT_SYMBOL_GPL(clk_pll_configure_sr);
 286
 287void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
 288                const struct pll_config *config, bool fsm_mode)
 289{
 290        clk_pll_configure(pll, regmap, config);
 291        if (fsm_mode)
 292                clk_pll_set_fsm_mode(pll, regmap, 0);
 293}
 294EXPORT_SYMBOL_GPL(clk_pll_configure_sr_hpm_lp);
 295