uboot/drivers/clk/ti/clk-k3-pll.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Texas Instruments K3 SoC PLL clock driver
   4 *
   5 * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/
   6 *      Tero Kristo <t-kristo@ti.com>
   7 */
   8
   9#include <common.h>
  10#include <asm/io.h>
  11#include <dm.h>
  12#include <div64.h>
  13#include <errno.h>
  14#include <clk-uclass.h>
  15#include <linux/clk-provider.h>
  16#include "k3-clk.h"
  17#include <linux/rational.h>
  18
  19/* 16FFT register offsets */
  20#define PLL_16FFT_CFG                   0x08
  21#define PLL_KICK0                       0x10
  22#define PLL_KICK1                       0x14
  23#define PLL_16FFT_CTRL                  0x20
  24#define PLL_16FFT_STAT                  0x24
  25#define PLL_16FFT_FREQ_CTRL0            0x30
  26#define PLL_16FFT_FREQ_CTRL1            0x34
  27#define PLL_16FFT_DIV_CTRL              0x38
  28
  29/* CTRL register bits */
  30#define PLL_16FFT_CTRL_BYPASS_EN        BIT(31)
  31#define PLL_16FFT_CTRL_PLL_EN           BIT(15)
  32#define PLL_16FFT_CTRL_DSM_EN           BIT(1)
  33
  34/* STAT register bits */
  35#define PLL_16FFT_STAT_LOCK             BIT(0)
  36
  37/* FREQ_CTRL0 bits */
  38#define PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK    0xfff
  39
  40/* DIV CTRL register bits */
  41#define PLL_16FFT_DIV_CTRL_REF_DIV_MASK         0x3f
  42
  43#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS   24
  44#define PLL_16FFT_HSDIV_CTRL_CLKOUT_EN          BIT(15)
  45
  46/* KICK register magic values */
  47#define PLL_KICK0_VALUE                         0x68ef3490
  48#define PLL_KICK1_VALUE                         0xd172bc5a
  49
  50/**
  51 * struct ti_pll_clk - TI PLL clock data info structure
  52 * @clk: core clock structure
  53 * @reg: memory address of the PLL controller
  54 */
  55struct ti_pll_clk {
  56        struct clk      clk;
  57        void __iomem    *reg;
  58};
  59
  60#define to_clk_pll(_clk) container_of(_clk, struct ti_pll_clk, clk)
  61
  62static int ti_pll_wait_for_lock(struct clk *clk)
  63{
  64        struct ti_pll_clk *pll = to_clk_pll(clk);
  65        u32 stat;
  66        int i;
  67
  68        for (i = 0; i < 100000; i++) {
  69                stat = readl(pll->reg + PLL_16FFT_STAT);
  70                if (stat & PLL_16FFT_STAT_LOCK)
  71                        return 0;
  72        }
  73
  74        printf("%s: pll (%s) failed to lock\n", __func__,
  75               clk->dev->name);
  76
  77        return -EBUSY;
  78}
  79
  80static ulong ti_pll_clk_get_rate(struct clk *clk)
  81{
  82        struct ti_pll_clk *pll = to_clk_pll(clk);
  83        u64 current_freq;
  84        u64 parent_freq = clk_get_parent_rate(clk);
  85        u32 pllm;
  86        u32 plld;
  87        u32 pllfm;
  88        u32 ctrl;
  89
  90        /* Check if we are in bypass */
  91        ctrl = readl(pll->reg + PLL_16FFT_CTRL);
  92        if (ctrl & PLL_16FFT_CTRL_BYPASS_EN)
  93                return parent_freq;
  94
  95        pllm = readl(pll->reg + PLL_16FFT_FREQ_CTRL0);
  96        pllfm = readl(pll->reg + PLL_16FFT_FREQ_CTRL1);
  97
  98        plld = readl(pll->reg + PLL_16FFT_DIV_CTRL) &
  99                PLL_16FFT_DIV_CTRL_REF_DIV_MASK;
 100
 101        current_freq = parent_freq * pllm / plld;
 102
 103        if (pllfm) {
 104                u64 tmp;
 105
 106                tmp = parent_freq * pllfm;
 107                do_div(tmp, plld);
 108                tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
 109                current_freq += tmp;
 110        }
 111
 112        return current_freq;
 113}
 114
 115static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate)
 116{
 117        struct ti_pll_clk *pll = to_clk_pll(clk);
 118        u64 current_freq;
 119        u64 parent_freq = clk_get_parent_rate(clk);
 120        int ret;
 121        u32 ctrl;
 122        unsigned long pllm;
 123        u32 pllfm = 0;
 124        unsigned long plld;
 125        u32 div_ctrl;
 126        u32 rem;
 127        int shift;
 128
 129        debug("%s(clk=%p, rate=%u)\n", __func__, clk, (u32)rate);
 130
 131        if (ti_pll_clk_get_rate(clk) == rate)
 132                return rate;
 133
 134        if (rate != parent_freq)
 135                /*
 136                 * Attempt with higher max multiplier value first to give
 137                 * some space for fractional divider to kick in.
 138                 */
 139                for (shift = 8; shift >= 0; shift -= 8) {
 140                        rational_best_approximation(rate, parent_freq,
 141                                ((PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK + 1) << shift) - 1,
 142                                PLL_16FFT_DIV_CTRL_REF_DIV_MASK, &pllm, &plld);
 143                        if (pllm / plld <= PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK)
 144                                break;
 145                }
 146
 147        /* Put PLL to bypass mode */
 148        ctrl = readl(pll->reg + PLL_16FFT_CTRL);
 149        ctrl |= PLL_16FFT_CTRL_BYPASS_EN;
 150        writel(ctrl, pll->reg + PLL_16FFT_CTRL);
 151
 152        if (rate == parent_freq) {
 153                debug("%s: put %s to bypass\n", __func__, clk->dev->name);
 154                return rate;
 155        }
 156
 157        debug("%s: pre-frac-calc: rate=%u, parent_freq=%u, plld=%u, pllm=%u\n",
 158              __func__, (u32)rate, (u32)parent_freq, (u32)plld, (u32)pllm);
 159
 160        /* Check if we need fractional config */
 161        if (plld > 1) {
 162                pllfm = pllm % plld;
 163                pllfm <<= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
 164                rem = pllfm % plld;
 165                pllfm /= plld;
 166                if (rem)
 167                        pllfm++;
 168                pllm /= plld;
 169                plld = 1;
 170        }
 171
 172        if (pllfm)
 173                ctrl |= PLL_16FFT_CTRL_DSM_EN;
 174        else
 175                ctrl &= ~PLL_16FFT_CTRL_DSM_EN;
 176
 177        writel(pllm, pll->reg + PLL_16FFT_FREQ_CTRL0);
 178        writel(pllfm, pll->reg + PLL_16FFT_FREQ_CTRL1);
 179
 180        /*
 181         * div_ctrl register contains other divider values, so rmw
 182         * only plld and leave existing values alone
 183         */
 184        div_ctrl = readl(pll->reg + PLL_16FFT_DIV_CTRL);
 185        div_ctrl &= ~PLL_16FFT_DIV_CTRL_REF_DIV_MASK;
 186        div_ctrl |= plld;
 187        writel(div_ctrl, pll->reg + PLL_16FFT_DIV_CTRL);
 188
 189        ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN;
 190        ctrl |= PLL_16FFT_CTRL_PLL_EN;
 191        writel(ctrl, pll->reg + PLL_16FFT_CTRL);
 192
 193        ret = ti_pll_wait_for_lock(clk);
 194        if (ret)
 195                return ret;
 196
 197        debug("%s: pllm=%u, plld=%u, pllfm=%u, parent_freq=%u\n",
 198              __func__, (u32)pllm, (u32)plld, (u32)pllfm, (u32)parent_freq);
 199
 200        current_freq = parent_freq * pllm / plld;
 201
 202        if (pllfm) {
 203                u64 tmp;
 204
 205                tmp = parent_freq * pllfm;
 206                do_div(tmp, plld);
 207                tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
 208                current_freq += tmp;
 209        }
 210
 211        return current_freq;
 212}
 213
 214static int ti_pll_clk_enable(struct clk *clk)
 215{
 216        struct ti_pll_clk *pll = to_clk_pll(clk);
 217        u32 ctrl;
 218
 219        ctrl = readl(pll->reg + PLL_16FFT_CTRL);
 220        ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN;
 221        ctrl |= PLL_16FFT_CTRL_PLL_EN;
 222        writel(ctrl, pll->reg + PLL_16FFT_CTRL);
 223
 224        return ti_pll_wait_for_lock(clk);
 225}
 226
 227static int ti_pll_clk_disable(struct clk *clk)
 228{
 229        struct ti_pll_clk *pll = to_clk_pll(clk);
 230        u32 ctrl;
 231
 232        ctrl = readl(pll->reg + PLL_16FFT_CTRL);
 233        ctrl |= PLL_16FFT_CTRL_BYPASS_EN;
 234        writel(ctrl, pll->reg + PLL_16FFT_CTRL);
 235
 236        return 0;
 237}
 238
 239static const struct clk_ops ti_pll_clk_ops = {
 240        .get_rate = ti_pll_clk_get_rate,
 241        .set_rate = ti_pll_clk_set_rate,
 242        .enable = ti_pll_clk_enable,
 243        .disable = ti_pll_clk_disable,
 244};
 245
 246struct clk *clk_register_ti_pll(const char *name, const char *parent_name,
 247                                void __iomem *reg)
 248{
 249        struct ti_pll_clk *pll;
 250        int ret;
 251        int i;
 252        u32 cfg, ctrl, hsdiv_presence_bit, hsdiv_ctrl_offs;
 253
 254        pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 255        if (!pll)
 256                return ERR_PTR(-ENOMEM);
 257
 258        pll->reg = reg;
 259
 260        ret = clk_register(&pll->clk, "ti-pll-clk", name, parent_name);
 261        if (ret) {
 262                printf("%s: failed to register: %d\n", __func__, ret);
 263                kfree(pll);
 264                return ERR_PTR(ret);
 265        }
 266
 267        /* Unlock the PLL registers */
 268        writel(PLL_KICK0_VALUE, pll->reg + PLL_KICK0);
 269        writel(PLL_KICK1_VALUE, pll->reg + PLL_KICK1);
 270
 271        /* Enable all HSDIV outputs */
 272        cfg = readl(pll->reg + PLL_16FFT_CFG);
 273        for (i = 0; i < 16; i++) {
 274                hsdiv_presence_bit = BIT(16 + i);
 275                hsdiv_ctrl_offs = 0x80 + (i * 4);
 276                /* Enable HSDIV output if present */
 277                if ((hsdiv_presence_bit & cfg) != 0UL) {
 278                        ctrl = readl(pll->reg + hsdiv_ctrl_offs);
 279                        ctrl |= PLL_16FFT_HSDIV_CTRL_CLKOUT_EN;
 280                        writel(ctrl, pll->reg + hsdiv_ctrl_offs);
 281                }
 282        }
 283
 284        return &pll->clk;
 285}
 286
 287U_BOOT_DRIVER(ti_pll_clk) = {
 288        .name = "ti-pll-clk",
 289        .id = UCLASS_CLK,
 290        .ops = &ti_pll_clk_ops,
 291        .flags = DM_FLAG_PRE_RELOC,
 292};
 293