uboot/drivers/clk/imx/clk-pllv3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2019 DENX Software Engineering
   4 * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
   5 */
   6
   7#include <common.h>
   8#include <asm/io.h>
   9#include <div64.h>
  10#include <malloc.h>
  11#include <clk-uclass.h>
  12#include <dm/device.h>
  13#include <dm/devres.h>
  14#include <dm/uclass.h>
  15#include <clk.h>
  16#include "clk.h"
  17#include <linux/err.h>
  18
  19#define UBOOT_DM_CLK_IMX_PLLV3_GENERIC  "imx_clk_pllv3_generic"
  20#define UBOOT_DM_CLK_IMX_PLLV3_SYS      "imx_clk_pllv3_sys"
  21#define UBOOT_DM_CLK_IMX_PLLV3_USB      "imx_clk_pllv3_usb"
  22#define UBOOT_DM_CLK_IMX_PLLV3_AV       "imx_clk_pllv3_av"
  23#define UBOOT_DM_CLK_IMX_PLLV3_ENET     "imx_clk_pllv3_enet"
  24
  25#define PLL_NUM_OFFSET          0x10
  26#define PLL_DENOM_OFFSET        0x20
  27
  28#define BM_PLL_POWER            (0x1 << 12)
  29#define BM_PLL_ENABLE           (0x1 << 13)
  30#define BM_PLL_LOCK             (0x1 << 31)
  31
  32struct clk_pllv3 {
  33        struct clk      clk;
  34        void __iomem    *base;
  35        u32             power_bit;
  36        bool            powerup_set;
  37        u32             enable_bit;
  38        u32             div_mask;
  39        u32             div_shift;
  40        unsigned long   ref_clock;
  41};
  42
  43#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
  44
  45static ulong clk_pllv3_generic_get_rate(struct clk *clk)
  46{
  47        struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev));
  48        unsigned long parent_rate = clk_get_parent_rate(clk);
  49
  50        u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
  51
  52        return (div == 1) ? parent_rate * 22 : parent_rate * 20;
  53}
  54
  55static ulong clk_pllv3_generic_set_rate(struct clk *clk, ulong rate)
  56{
  57        struct clk_pllv3 *pll = to_clk_pllv3(clk);
  58        unsigned long parent_rate = clk_get_parent_rate(clk);
  59        u32 val, div;
  60
  61        if (rate == parent_rate * 22)
  62                div = 1;
  63        else if (rate == parent_rate * 20)
  64                div = 0;
  65        else
  66                return -EINVAL;
  67
  68        val = readl(pll->base);
  69        val &= ~(pll->div_mask << pll->div_shift);
  70        val |= (div << pll->div_shift);
  71        writel(val, pll->base);
  72
  73        /* Wait for PLL to lock */
  74        while (!(readl(pll->base) & BM_PLL_LOCK))
  75                ;
  76
  77        return 0;
  78}
  79
  80static int clk_pllv3_generic_enable(struct clk *clk)
  81{
  82        struct clk_pllv3 *pll = to_clk_pllv3(clk);
  83        u32 val;
  84
  85        val = readl(pll->base);
  86        if (pll->powerup_set)
  87                val |= pll->power_bit;
  88        else
  89                val &= ~pll->power_bit;
  90
  91        val |= pll->enable_bit;
  92
  93        writel(val, pll->base);
  94
  95        return 0;
  96}
  97
  98static int clk_pllv3_generic_disable(struct clk *clk)
  99{
 100        struct clk_pllv3 *pll = to_clk_pllv3(clk);
 101        u32 val;
 102
 103        val = readl(pll->base);
 104        if (pll->powerup_set)
 105                val &= ~pll->power_bit;
 106        else
 107                val |= pll->power_bit;
 108
 109        val &= ~pll->enable_bit;
 110
 111        writel(val, pll->base);
 112
 113        return 0;
 114}
 115
 116static const struct clk_ops clk_pllv3_generic_ops = {
 117        .get_rate       = clk_pllv3_generic_get_rate,
 118        .enable         = clk_pllv3_generic_enable,
 119        .disable        = clk_pllv3_generic_disable,
 120        .set_rate       = clk_pllv3_generic_set_rate,
 121};
 122
 123static ulong clk_pllv3_sys_get_rate(struct clk *clk)
 124{
 125        struct clk_pllv3 *pll = to_clk_pllv3(clk);
 126        unsigned long parent_rate = clk_get_parent_rate(clk);
 127        u32 div = readl(pll->base) & pll->div_mask;
 128
 129        return parent_rate * div / 2;
 130}
 131
 132static ulong clk_pllv3_sys_set_rate(struct clk *clk, ulong rate)
 133{
 134        struct clk_pllv3 *pll = to_clk_pllv3(clk);
 135        unsigned long parent_rate = clk_get_parent_rate(clk);
 136        unsigned long min_rate;
 137        unsigned long max_rate;
 138        u32 val, div;
 139
 140        if (parent_rate == 0)
 141                return -EINVAL;
 142
 143        min_rate = parent_rate * 54 / 2;
 144        max_rate = parent_rate * 108 / 2;
 145
 146        if (rate < min_rate || rate > max_rate)
 147                return -EINVAL;
 148
 149        div = rate * 2 / parent_rate;
 150        val = readl(pll->base);
 151        val &= ~pll->div_mask;
 152        val |= div;
 153        writel(val, pll->base);
 154
 155        /* Wait for PLL to lock */
 156        while (!(readl(pll->base) & BM_PLL_LOCK))
 157                ;
 158
 159        return 0;
 160}
 161
 162static const struct clk_ops clk_pllv3_sys_ops = {
 163        .enable         = clk_pllv3_generic_enable,
 164        .disable        = clk_pllv3_generic_disable,
 165        .get_rate       = clk_pllv3_sys_get_rate,
 166        .set_rate       = clk_pllv3_sys_set_rate,
 167};
 168
 169static ulong clk_pllv3_av_get_rate(struct clk *clk)
 170{
 171        struct clk_pllv3 *pll = to_clk_pllv3(clk);
 172        unsigned long parent_rate = clk_get_parent_rate(clk);
 173        u32 mfn = readl(pll->base + PLL_NUM_OFFSET);
 174        u32 mfd = readl(pll->base + PLL_DENOM_OFFSET);
 175        u32 div = readl(pll->base) & pll->div_mask;
 176        u64 temp64 = (u64)parent_rate;
 177
 178        if (mfd == 0)
 179                return -EIO;
 180
 181        temp64 *= mfn;
 182        do_div(temp64, mfd);
 183
 184        return parent_rate * div + (unsigned long)temp64;
 185}
 186
 187static ulong clk_pllv3_av_set_rate(struct clk *clk, ulong rate)
 188{
 189        struct clk_pllv3 *pll = to_clk_pllv3(clk);
 190        unsigned long parent_rate = clk_get_parent_rate(clk);
 191        unsigned long min_rate;
 192        unsigned long max_rate;
 193        u32 val, div;
 194        u32 mfn, mfd = 1000000;
 195        u32 max_mfd = 0x3FFFFFFF;
 196        u64 temp64;
 197
 198        if (parent_rate == 0)
 199                return -EINVAL;
 200
 201        min_rate = parent_rate * 27;
 202        max_rate = parent_rate * 54;
 203
 204        if (rate < min_rate || rate > max_rate)
 205                return -EINVAL;
 206
 207        if (parent_rate <= max_mfd)
 208                mfd = parent_rate;
 209
 210        div = rate / parent_rate;
 211        temp64 = (u64)(rate - div * parent_rate);
 212        temp64 *= mfd;
 213        do_div(temp64, parent_rate);
 214        mfn = temp64;
 215
 216        val = readl(pll->base);
 217        val &= ~pll->div_mask;
 218        val |= div;
 219        writel(val, pll->base);
 220        writel(mfn, pll->base + PLL_NUM_OFFSET);
 221        writel(mfd, pll->base + PLL_DENOM_OFFSET);
 222
 223        /* Wait for PLL to lock */
 224        while (!(readl(pll->base) & BM_PLL_LOCK))
 225                ;
 226
 227        return 0;
 228}
 229
 230static const struct clk_ops clk_pllv3_av_ops = {
 231        .enable         = clk_pllv3_generic_enable,
 232        .disable        = clk_pllv3_generic_disable,
 233        .get_rate       = clk_pllv3_av_get_rate,
 234        .set_rate       = clk_pllv3_av_set_rate,
 235};
 236
 237static ulong clk_pllv3_enet_get_rate(struct clk *clk)
 238{
 239        struct clk_pllv3 *pll = to_clk_pllv3(clk);
 240
 241        return pll->ref_clock;
 242}
 243
 244static const struct clk_ops clk_pllv3_enet_ops = {
 245        .enable = clk_pllv3_generic_enable,
 246        .disable        = clk_pllv3_generic_disable,
 247        .get_rate       = clk_pllv3_enet_get_rate,
 248};
 249
 250struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
 251                          const char *parent_name, void __iomem *base,
 252                          u32 div_mask)
 253{
 254        struct clk_pllv3 *pll;
 255        struct clk *clk;
 256        char *drv_name;
 257        int ret;
 258
 259        pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 260        if (!pll)
 261                return ERR_PTR(-ENOMEM);
 262
 263        pll->power_bit = BM_PLL_POWER;
 264        pll->enable_bit = BM_PLL_ENABLE;
 265
 266        switch (type) {
 267        case IMX_PLLV3_GENERIC:
 268                drv_name = UBOOT_DM_CLK_IMX_PLLV3_GENERIC;
 269                pll->div_shift = 0;
 270                pll->powerup_set = false;
 271                break;
 272        case IMX_PLLV3_SYS:
 273                drv_name = UBOOT_DM_CLK_IMX_PLLV3_SYS;
 274                pll->div_shift = 0;
 275                pll->powerup_set = false;
 276                break;
 277        case IMX_PLLV3_USB:
 278                drv_name = UBOOT_DM_CLK_IMX_PLLV3_USB;
 279                pll->div_shift = 1;
 280                pll->powerup_set = true;
 281                break;
 282        case IMX_PLLV3_AV:
 283                drv_name = UBOOT_DM_CLK_IMX_PLLV3_AV;
 284                pll->div_shift = 0;
 285                pll->powerup_set = false;
 286                break;
 287        case IMX_PLLV3_ENET:
 288                drv_name = UBOOT_DM_CLK_IMX_PLLV3_ENET;
 289                pll->ref_clock = 500000000;
 290                break;
 291        default:
 292                kfree(pll);
 293                return ERR_PTR(-ENOTSUPP);
 294        }
 295
 296        pll->base = base;
 297        pll->div_mask = div_mask;
 298        clk = &pll->clk;
 299
 300        ret = clk_register(clk, drv_name, name, parent_name);
 301        if (ret) {
 302                kfree(pll);
 303                return ERR_PTR(ret);
 304        }
 305
 306        return clk;
 307}
 308
 309U_BOOT_DRIVER(clk_pllv3_generic) = {
 310        .name   = UBOOT_DM_CLK_IMX_PLLV3_GENERIC,
 311        .id     = UCLASS_CLK,
 312        .ops    = &clk_pllv3_generic_ops,
 313        .flags = DM_FLAG_PRE_RELOC,
 314};
 315
 316U_BOOT_DRIVER(clk_pllv3_sys) = {
 317        .name   = UBOOT_DM_CLK_IMX_PLLV3_SYS,
 318        .id     = UCLASS_CLK,
 319        .ops    = &clk_pllv3_sys_ops,
 320        .flags = DM_FLAG_PRE_RELOC,
 321};
 322
 323U_BOOT_DRIVER(clk_pllv3_usb) = {
 324        .name   = UBOOT_DM_CLK_IMX_PLLV3_USB,
 325        .id     = UCLASS_CLK,
 326        .ops    = &clk_pllv3_generic_ops,
 327        .flags = DM_FLAG_PRE_RELOC,
 328};
 329
 330U_BOOT_DRIVER(clk_pllv3_av) = {
 331        .name   = UBOOT_DM_CLK_IMX_PLLV3_AV,
 332        .id     = UCLASS_CLK,
 333        .ops    = &clk_pllv3_av_ops,
 334        .flags = DM_FLAG_PRE_RELOC,
 335};
 336
 337U_BOOT_DRIVER(clk_pllv3_enet) = {
 338        .name   = UBOOT_DM_CLK_IMX_PLLV3_ENET,
 339        .id     = UCLASS_CLK,
 340        .ops    = &clk_pllv3_enet_ops,
 341};
 342