linux/drivers/clk/clk-plldig.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 NXP
   4 *
   5 * Clock driver for LS1028A Display output interfaces(LCD, DPHY).
   6 */
   7
   8#include <linux/clk-provider.h>
   9#include <linux/device.h>
  10#include <linux/module.h>
  11#include <linux/err.h>
  12#include <linux/io.h>
  13#include <linux/iopoll.h>
  14#include <linux/of.h>
  15#include <linux/of_address.h>
  16#include <linux/of_device.h>
  17#include <linux/platform_device.h>
  18#include <linux/slab.h>
  19#include <linux/bitfield.h>
  20
  21/* PLLDIG register offsets and bit masks */
  22#define PLLDIG_REG_PLLSR            0x24
  23#define PLLDIG_LOCK_MASK            BIT(2)
  24#define PLLDIG_REG_PLLDV            0x28
  25#define PLLDIG_MFD_MASK             GENMASK(7, 0)
  26#define PLLDIG_RFDPHI1_MASK         GENMASK(30, 25)
  27#define PLLDIG_REG_PLLFM            0x2c
  28#define PLLDIG_SSCGBYP_ENABLE       BIT(30)
  29#define PLLDIG_REG_PLLFD            0x30
  30#define PLLDIG_FDEN                 BIT(30)
  31#define PLLDIG_FRAC_MASK            GENMASK(15, 0)
  32#define PLLDIG_REG_PLLCAL1          0x38
  33#define PLLDIG_REG_PLLCAL2          0x3c
  34
  35/* Range of the VCO frequencies, in Hz */
  36#define PLLDIG_MIN_VCO_FREQ         650000000
  37#define PLLDIG_MAX_VCO_FREQ         1300000000
  38
  39/* Range of the output frequencies, in Hz */
  40#define PHI1_MIN_FREQ               27000000UL
  41#define PHI1_MAX_FREQ               600000000UL
  42
  43/* Maximum value of the reduced frequency divider */
  44#define MAX_RFDPHI1          63UL
  45
  46/* Best value of multiplication factor divider */
  47#define PLLDIG_DEFAULT_MFD   44
  48
  49/*
  50 * Denominator part of the fractional part of the
  51 * loop multiplication factor.
  52 */
  53#define MFDEN          20480
  54
  55static const struct clk_parent_data parent_data[] = {
  56        { .index = 0 },
  57};
  58
  59struct clk_plldig {
  60        struct clk_hw hw;
  61        void __iomem *regs;
  62        unsigned int vco_freq;
  63};
  64
  65#define to_clk_plldig(_hw)      container_of(_hw, struct clk_plldig, hw)
  66
  67static int plldig_enable(struct clk_hw *hw)
  68{
  69        struct clk_plldig *data = to_clk_plldig(hw);
  70        u32 val;
  71
  72        val = readl(data->regs + PLLDIG_REG_PLLFM);
  73        /*
  74         * Use Bypass mode with PLL off by default, the frequency overshoot
  75         * detector output was disable. SSCG Bypass mode should be enable.
  76         */
  77        val |= PLLDIG_SSCGBYP_ENABLE;
  78        writel(val, data->regs + PLLDIG_REG_PLLFM);
  79
  80        return 0;
  81}
  82
  83static void plldig_disable(struct clk_hw *hw)
  84{
  85        struct clk_plldig *data = to_clk_plldig(hw);
  86        u32 val;
  87
  88        val = readl(data->regs + PLLDIG_REG_PLLFM);
  89
  90        val &= ~PLLDIG_SSCGBYP_ENABLE;
  91        val |= FIELD_PREP(PLLDIG_SSCGBYP_ENABLE, 0x0);
  92
  93        writel(val, data->regs + PLLDIG_REG_PLLFM);
  94}
  95
  96static int plldig_is_enabled(struct clk_hw *hw)
  97{
  98        struct clk_plldig *data = to_clk_plldig(hw);
  99
 100        return readl(data->regs + PLLDIG_REG_PLLFM) &
 101                              PLLDIG_SSCGBYP_ENABLE;
 102}
 103
 104static unsigned long plldig_recalc_rate(struct clk_hw *hw,
 105                                        unsigned long parent_rate)
 106{
 107        struct clk_plldig *data = to_clk_plldig(hw);
 108        u32 val, rfdphi1;
 109
 110        val = readl(data->regs + PLLDIG_REG_PLLDV);
 111
 112        /* Check if PLL is bypassed */
 113        if (val & PLLDIG_SSCGBYP_ENABLE)
 114                return parent_rate;
 115
 116        rfdphi1 = FIELD_GET(PLLDIG_RFDPHI1_MASK, val);
 117
 118        /*
 119         * If RFDPHI1 has a value of 1 the VCO frequency is also divided by
 120         * one.
 121         */
 122        if (!rfdphi1)
 123                rfdphi1 = 1;
 124
 125        return DIV_ROUND_UP(data->vco_freq, rfdphi1);
 126}
 127
 128static unsigned long plldig_calc_target_div(unsigned long vco_freq,
 129                                            unsigned long target_rate)
 130{
 131        unsigned long div;
 132
 133        div = DIV_ROUND_CLOSEST(vco_freq, target_rate);
 134        div = clamp(div, 1UL, MAX_RFDPHI1);
 135
 136        return div;
 137}
 138
 139static int plldig_determine_rate(struct clk_hw *hw,
 140                                 struct clk_rate_request *req)
 141{
 142        struct clk_plldig *data = to_clk_plldig(hw);
 143        unsigned int div;
 144
 145        req->rate = clamp(req->rate, PHI1_MIN_FREQ, PHI1_MAX_FREQ);
 146        div = plldig_calc_target_div(data->vco_freq, req->rate);
 147        req->rate = DIV_ROUND_UP(data->vco_freq, div);
 148
 149        return 0;
 150}
 151
 152static int plldig_set_rate(struct clk_hw *hw, unsigned long rate,
 153                unsigned long parent_rate)
 154{
 155        struct clk_plldig *data = to_clk_plldig(hw);
 156        unsigned int val, cond;
 157        unsigned int rfdphi1;
 158
 159        rate = clamp(rate, PHI1_MIN_FREQ, PHI1_MAX_FREQ);
 160        rfdphi1 = plldig_calc_target_div(data->vco_freq, rate);
 161
 162        /* update the divider value */
 163        val = readl(data->regs + PLLDIG_REG_PLLDV);
 164        val &= ~PLLDIG_RFDPHI1_MASK;
 165        val |= FIELD_PREP(PLLDIG_RFDPHI1_MASK, rfdphi1);
 166        writel(val, data->regs + PLLDIG_REG_PLLDV);
 167
 168        /* waiting for old lock state to clear */
 169        udelay(200);
 170
 171        /* Wait until PLL is locked or timeout */
 172        return readl_poll_timeout_atomic(data->regs + PLLDIG_REG_PLLSR, cond,
 173                                         cond & PLLDIG_LOCK_MASK, 0,
 174                                         USEC_PER_MSEC);
 175}
 176
 177static const struct clk_ops plldig_clk_ops = {
 178        .enable = plldig_enable,
 179        .disable = plldig_disable,
 180        .is_enabled = plldig_is_enabled,
 181        .recalc_rate = plldig_recalc_rate,
 182        .determine_rate = plldig_determine_rate,
 183        .set_rate = plldig_set_rate,
 184};
 185
 186static int plldig_init(struct clk_hw *hw)
 187{
 188        struct clk_plldig *data = to_clk_plldig(hw);
 189        struct clk_hw *parent = clk_hw_get_parent(hw);
 190        unsigned long parent_rate;
 191        unsigned long val;
 192        unsigned long long lltmp;
 193        unsigned int mfd, fracdiv = 0;
 194
 195        if (!parent)
 196                return -EINVAL;
 197
 198        parent_rate = clk_hw_get_rate(parent);
 199
 200        if (data->vco_freq) {
 201                mfd = data->vco_freq / parent_rate;
 202                lltmp = data->vco_freq % parent_rate;
 203                lltmp *= MFDEN;
 204                do_div(lltmp, parent_rate);
 205                fracdiv = lltmp;
 206        } else {
 207                mfd = PLLDIG_DEFAULT_MFD;
 208                data->vco_freq = parent_rate * mfd;
 209        }
 210
 211        val = FIELD_PREP(PLLDIG_MFD_MASK, mfd);
 212        writel(val, data->regs + PLLDIG_REG_PLLDV);
 213
 214        /* Enable fractional divider */
 215        if (fracdiv) {
 216                val = FIELD_PREP(PLLDIG_FRAC_MASK, fracdiv);
 217                val |= PLLDIG_FDEN;
 218                writel(val, data->regs + PLLDIG_REG_PLLFD);
 219        }
 220
 221        return 0;
 222}
 223
 224static int plldig_clk_probe(struct platform_device *pdev)
 225{
 226        struct clk_plldig *data;
 227        struct device *dev = &pdev->dev;
 228        int ret;
 229
 230        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 231        if (!data)
 232                return -ENOMEM;
 233
 234        data->regs = devm_platform_ioremap_resource(pdev, 0);
 235        if (IS_ERR(data->regs))
 236                return PTR_ERR(data->regs);
 237
 238        data->hw.init = CLK_HW_INIT_PARENTS_DATA("dpclk",
 239                                                 parent_data,
 240                                                 &plldig_clk_ops,
 241                                                 0);
 242
 243        ret = devm_clk_hw_register(dev, &data->hw);
 244        if (ret) {
 245                dev_err(dev, "failed to register %s clock\n",
 246                                                dev->of_node->name);
 247                return ret;
 248        }
 249
 250        ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
 251                                          &data->hw);
 252        if (ret) {
 253                dev_err(dev, "unable to add clk provider\n");
 254                return ret;
 255        }
 256
 257        /*
 258         * The frequency of the VCO cannot be changed during runtime.
 259         * Therefore, let the user specify a desired frequency.
 260         */
 261        if (!of_property_read_u32(dev->of_node, "fsl,vco-hz",
 262                                  &data->vco_freq)) {
 263                if (data->vco_freq < PLLDIG_MIN_VCO_FREQ ||
 264                    data->vco_freq > PLLDIG_MAX_VCO_FREQ)
 265                        return -EINVAL;
 266        }
 267
 268        return plldig_init(&data->hw);
 269}
 270
 271static const struct of_device_id plldig_clk_id[] = {
 272        { .compatible = "fsl,ls1028a-plldig" },
 273        { }
 274};
 275MODULE_DEVICE_TABLE(of, plldig_clk_id);
 276
 277static struct platform_driver plldig_clk_driver = {
 278        .driver = {
 279                .name = "plldig-clock",
 280                .of_match_table = plldig_clk_id,
 281        },
 282        .probe = plldig_clk_probe,
 283};
 284module_platform_driver(plldig_clk_driver);
 285
 286MODULE_LICENSE("GPL v2");
 287MODULE_AUTHOR("Wen He <wen.he_1@nxp.com>");
 288MODULE_DESCRIPTION("LS1028A Display output interface pixel clock driver");
 289