linux/drivers/soc/xilinx/xlnx_vcu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Xilinx VCU Init
   4 *
   5 * Copyright (C) 2016 - 2017 Xilinx, Inc.
   6 *
   7 * Contacts   Dhaval Shah <dshah@xilinx.com>
   8 */
   9#include <linux/clk.h>
  10#include <linux/device.h>
  11#include <linux/errno.h>
  12#include <linux/io.h>
  13#include <linux/mfd/syscon.h>
  14#include <linux/mfd/syscon/xlnx-vcu.h>
  15#include <linux/module.h>
  16#include <linux/of_platform.h>
  17#include <linux/platform_device.h>
  18#include <linux/regmap.h>
  19
  20/* vcu slcr registers, bitmask and shift */
  21#define VCU_PLL_CTRL                    0x24
  22#define VCU_PLL_CTRL_RESET_MASK         0x01
  23#define VCU_PLL_CTRL_RESET_SHIFT        0
  24#define VCU_PLL_CTRL_BYPASS_MASK        0x01
  25#define VCU_PLL_CTRL_BYPASS_SHIFT       3
  26#define VCU_PLL_CTRL_FBDIV_MASK         0x7f
  27#define VCU_PLL_CTRL_FBDIV_SHIFT        8
  28#define VCU_PLL_CTRL_POR_IN_MASK        0x01
  29#define VCU_PLL_CTRL_POR_IN_SHIFT       1
  30#define VCU_PLL_CTRL_PWR_POR_MASK       0x01
  31#define VCU_PLL_CTRL_PWR_POR_SHIFT      2
  32#define VCU_PLL_CTRL_CLKOUTDIV_MASK     0x03
  33#define VCU_PLL_CTRL_CLKOUTDIV_SHIFT    16
  34#define VCU_PLL_CTRL_DEFAULT            0
  35#define VCU_PLL_DIV2                    2
  36
  37#define VCU_PLL_CFG                     0x28
  38#define VCU_PLL_CFG_RES_MASK            0x0f
  39#define VCU_PLL_CFG_RES_SHIFT           0
  40#define VCU_PLL_CFG_CP_MASK             0x0f
  41#define VCU_PLL_CFG_CP_SHIFT            5
  42#define VCU_PLL_CFG_LFHF_MASK           0x03
  43#define VCU_PLL_CFG_LFHF_SHIFT          10
  44#define VCU_PLL_CFG_LOCK_CNT_MASK       0x03ff
  45#define VCU_PLL_CFG_LOCK_CNT_SHIFT      13
  46#define VCU_PLL_CFG_LOCK_DLY_MASK       0x7f
  47#define VCU_PLL_CFG_LOCK_DLY_SHIFT      25
  48#define VCU_ENC_CORE_CTRL               0x30
  49#define VCU_ENC_MCU_CTRL                0x34
  50#define VCU_DEC_CORE_CTRL               0x38
  51#define VCU_DEC_MCU_CTRL                0x3c
  52#define VCU_PLL_DIVISOR_MASK            0x3f
  53#define VCU_PLL_DIVISOR_SHIFT           4
  54#define VCU_SRCSEL_MASK                 0x01
  55#define VCU_SRCSEL_SHIFT                0
  56#define VCU_SRCSEL_PLL                  1
  57
  58#define VCU_PLL_STATUS                  0x60
  59#define VCU_PLL_STATUS_LOCK_STATUS_MASK 0x01
  60
  61#define MHZ                             1000000
  62#define FVCO_MIN                        (1500U * MHZ)
  63#define FVCO_MAX                        (3000U * MHZ)
  64#define DIVISOR_MIN                     0
  65#define DIVISOR_MAX                     63
  66#define FRAC                            100
  67#define LIMIT                           (10 * MHZ)
  68
  69/**
  70 * struct xvcu_device - Xilinx VCU init device structure
  71 * @dev: Platform device
  72 * @pll_ref: pll ref clock source
  73 * @aclk: axi clock source
  74 * @logicore_reg_ba: logicore reg base address
  75 * @vcu_slcr_ba: vcu_slcr Register base address
  76 * @coreclk: core clock frequency
  77 */
  78struct xvcu_device {
  79        struct device *dev;
  80        struct clk *pll_ref;
  81        struct clk *aclk;
  82        struct regmap *logicore_reg_ba;
  83        void __iomem *vcu_slcr_ba;
  84        u32 coreclk;
  85};
  86
  87static struct regmap_config vcu_settings_regmap_config = {
  88        .name = "regmap",
  89        .reg_bits = 32,
  90        .val_bits = 32,
  91        .reg_stride = 4,
  92        .max_register = 0xfff,
  93        .cache_type = REGCACHE_NONE,
  94};
  95
  96/**
  97 * struct xvcu_pll_cfg - Helper data
  98 * @fbdiv: The integer portion of the feedback divider to the PLL
  99 * @cp: PLL charge pump control
 100 * @res: PLL loop filter resistor control
 101 * @lfhf: PLL loop filter high frequency capacitor control
 102 * @lock_dly: Lock circuit configuration settings for lock windowsize
 103 * @lock_cnt: Lock circuit counter setting
 104 */
 105struct xvcu_pll_cfg {
 106        u32 fbdiv;
 107        u32 cp;
 108        u32 res;
 109        u32 lfhf;
 110        u32 lock_dly;
 111        u32 lock_cnt;
 112};
 113
 114static const struct xvcu_pll_cfg xvcu_pll_cfg[] = {
 115        { 25, 3, 10, 3, 63, 1000 },
 116        { 26, 3, 10, 3, 63, 1000 },
 117        { 27, 4, 6, 3, 63, 1000 },
 118        { 28, 4, 6, 3, 63, 1000 },
 119        { 29, 4, 6, 3, 63, 1000 },
 120        { 30, 4, 6, 3, 63, 1000 },
 121        { 31, 6, 1, 3, 63, 1000 },
 122        { 32, 6, 1, 3, 63, 1000 },
 123        { 33, 4, 10, 3, 63, 1000 },
 124        { 34, 5, 6, 3, 63, 1000 },
 125        { 35, 5, 6, 3, 63, 1000 },
 126        { 36, 5, 6, 3, 63, 1000 },
 127        { 37, 5, 6, 3, 63, 1000 },
 128        { 38, 5, 6, 3, 63, 975 },
 129        { 39, 3, 12, 3, 63, 950 },
 130        { 40, 3, 12, 3, 63, 925 },
 131        { 41, 3, 12, 3, 63, 900 },
 132        { 42, 3, 12, 3, 63, 875 },
 133        { 43, 3, 12, 3, 63, 850 },
 134        { 44, 3, 12, 3, 63, 850 },
 135        { 45, 3, 12, 3, 63, 825 },
 136        { 46, 3, 12, 3, 63, 800 },
 137        { 47, 3, 12, 3, 63, 775 },
 138        { 48, 3, 12, 3, 63, 775 },
 139        { 49, 3, 12, 3, 63, 750 },
 140        { 50, 3, 12, 3, 63, 750 },
 141        { 51, 3, 2, 3, 63, 725 },
 142        { 52, 3, 2, 3, 63, 700 },
 143        { 53, 3, 2, 3, 63, 700 },
 144        { 54, 3, 2, 3, 63, 675 },
 145        { 55, 3, 2, 3, 63, 675 },
 146        { 56, 3, 2, 3, 63, 650 },
 147        { 57, 3, 2, 3, 63, 650 },
 148        { 58, 3, 2, 3, 63, 625 },
 149        { 59, 3, 2, 3, 63, 625 },
 150        { 60, 3, 2, 3, 63, 625 },
 151        { 61, 3, 2, 3, 63, 600 },
 152        { 62, 3, 2, 3, 63, 600 },
 153        { 63, 3, 2, 3, 63, 600 },
 154        { 64, 3, 2, 3, 63, 600 },
 155        { 65, 3, 2, 3, 63, 600 },
 156        { 66, 3, 2, 3, 63, 600 },
 157        { 67, 3, 2, 3, 63, 600 },
 158        { 68, 3, 2, 3, 63, 600 },
 159        { 69, 3, 2, 3, 63, 600 },
 160        { 70, 3, 2, 3, 63, 600 },
 161        { 71, 3, 2, 3, 63, 600 },
 162        { 72, 3, 2, 3, 63, 600 },
 163        { 73, 3, 2, 3, 63, 600 },
 164        { 74, 3, 2, 3, 63, 600 },
 165        { 75, 3, 2, 3, 63, 600 },
 166        { 76, 3, 2, 3, 63, 600 },
 167        { 77, 3, 2, 3, 63, 600 },
 168        { 78, 3, 2, 3, 63, 600 },
 169        { 79, 3, 2, 3, 63, 600 },
 170        { 80, 3, 2, 3, 63, 600 },
 171        { 81, 3, 2, 3, 63, 600 },
 172        { 82, 3, 2, 3, 63, 600 },
 173        { 83, 4, 2, 3, 63, 600 },
 174        { 84, 4, 2, 3, 63, 600 },
 175        { 85, 4, 2, 3, 63, 600 },
 176        { 86, 4, 2, 3, 63, 600 },
 177        { 87, 4, 2, 3, 63, 600 },
 178        { 88, 4, 2, 3, 63, 600 },
 179        { 89, 4, 2, 3, 63, 600 },
 180        { 90, 4, 2, 3, 63, 600 },
 181        { 91, 4, 2, 3, 63, 600 },
 182        { 92, 4, 2, 3, 63, 600 },
 183        { 93, 4, 2, 3, 63, 600 },
 184        { 94, 4, 2, 3, 63, 600 },
 185        { 95, 4, 2, 3, 63, 600 },
 186        { 96, 4, 2, 3, 63, 600 },
 187        { 97, 4, 2, 3, 63, 600 },
 188        { 98, 4, 2, 3, 63, 600 },
 189        { 99, 4, 2, 3, 63, 600 },
 190        { 100, 4, 2, 3, 63, 600 },
 191        { 101, 4, 2, 3, 63, 600 },
 192        { 102, 4, 2, 3, 63, 600 },
 193        { 103, 5, 2, 3, 63, 600 },
 194        { 104, 5, 2, 3, 63, 600 },
 195        { 105, 5, 2, 3, 63, 600 },
 196        { 106, 5, 2, 3, 63, 600 },
 197        { 107, 3, 4, 3, 63, 600 },
 198        { 108, 3, 4, 3, 63, 600 },
 199        { 109, 3, 4, 3, 63, 600 },
 200        { 110, 3, 4, 3, 63, 600 },
 201        { 111, 3, 4, 3, 63, 600 },
 202        { 112, 3, 4, 3, 63, 600 },
 203        { 113, 3, 4, 3, 63, 600 },
 204        { 114, 3, 4, 3, 63, 600 },
 205        { 115, 3, 4, 3, 63, 600 },
 206        { 116, 3, 4, 3, 63, 600 },
 207        { 117, 3, 4, 3, 63, 600 },
 208        { 118, 3, 4, 3, 63, 600 },
 209        { 119, 3, 4, 3, 63, 600 },
 210        { 120, 3, 4, 3, 63, 600 },
 211        { 121, 3, 4, 3, 63, 600 },
 212        { 122, 3, 4, 3, 63, 600 },
 213        { 123, 3, 4, 3, 63, 600 },
 214        { 124, 3, 4, 3, 63, 600 },
 215        { 125, 3, 4, 3, 63, 600 },
 216};
 217
 218/**
 219 * xvcu_read - Read from the VCU register space
 220 * @iomem:      vcu reg space base address
 221 * @offset:     vcu reg offset from base
 222 *
 223 * Return:      Returns 32bit value from VCU register specified
 224 *
 225 */
 226static inline u32 xvcu_read(void __iomem *iomem, u32 offset)
 227{
 228        return ioread32(iomem + offset);
 229}
 230
 231/**
 232 * xvcu_write - Write to the VCU register space
 233 * @iomem:      vcu reg space base address
 234 * @offset:     vcu reg offset from base
 235 * @value:      Value to write
 236 */
 237static inline void xvcu_write(void __iomem *iomem, u32 offset, u32 value)
 238{
 239        iowrite32(value, iomem + offset);
 240}
 241
 242/**
 243 * xvcu_write_field_reg - Write to the vcu reg field
 244 * @iomem:      vcu reg space base address
 245 * @offset:     vcu reg offset from base
 246 * @field:      vcu reg field to write to
 247 * @mask:       vcu reg mask
 248 * @shift:      vcu reg number of bits to shift the bitfield
 249 */
 250static void xvcu_write_field_reg(void __iomem *iomem, int offset,
 251                                 u32 field, u32 mask, int shift)
 252{
 253        u32 val = xvcu_read(iomem, offset);
 254
 255        val &= ~(mask << shift);
 256        val |= (field & mask) << shift;
 257
 258        xvcu_write(iomem, offset, val);
 259}
 260
 261/**
 262 * xvcu_set_vcu_pll_info - Set the VCU PLL info
 263 * @xvcu:       Pointer to the xvcu_device structure
 264 *
 265 * Programming the VCU PLL based on the user configuration
 266 * (ref clock freq, core clock freq, mcu clock freq).
 267 * Core clock frequency has higher priority than mcu clock frequency
 268 * Errors in following cases
 269 *    - When mcu or clock clock get from logicoreIP is 0
 270 *    - When VCU PLL DIV related bits value other than 1
 271 *    - When proper data not found for given data
 272 *    - When sis570_1 clocksource related operation failed
 273 *
 274 * Return:      Returns status, either success or error+reason
 275 */
 276static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
 277{
 278        u32 refclk, coreclk, mcuclk, inte, deci;
 279        u32 divisor_mcu, divisor_core, fvco;
 280        u32 clkoutdiv, vcu_pll_ctrl, pll_clk;
 281        u32 cfg_val, mod, ctrl;
 282        int ret, i;
 283        const struct xvcu_pll_cfg *found = NULL;
 284
 285        regmap_read(xvcu->logicore_reg_ba, VCU_PLL_CLK, &inte);
 286        regmap_read(xvcu->logicore_reg_ba, VCU_PLL_CLK_DEC, &deci);
 287        regmap_read(xvcu->logicore_reg_ba, VCU_CORE_CLK, &coreclk);
 288        coreclk *= MHZ;
 289        regmap_read(xvcu->logicore_reg_ba, VCU_MCU_CLK, &mcuclk);
 290        mcuclk *= MHZ;
 291        if (!mcuclk || !coreclk) {
 292                dev_err(xvcu->dev, "Invalid mcu and core clock data\n");
 293                return -EINVAL;
 294        }
 295
 296        refclk = (inte * MHZ) + (deci * (MHZ / FRAC));
 297        dev_dbg(xvcu->dev, "Ref clock from logicoreIP is %uHz\n", refclk);
 298        dev_dbg(xvcu->dev, "Core clock from logicoreIP is %uHz\n", coreclk);
 299        dev_dbg(xvcu->dev, "Mcu clock from logicoreIP is %uHz\n", mcuclk);
 300
 301        clk_disable_unprepare(xvcu->pll_ref);
 302        ret = clk_set_rate(xvcu->pll_ref, refclk);
 303        if (ret)
 304                dev_warn(xvcu->dev, "failed to set logicoreIP refclk rate\n");
 305
 306        ret = clk_prepare_enable(xvcu->pll_ref);
 307        if (ret) {
 308                dev_err(xvcu->dev, "failed to enable pll_ref clock source\n");
 309                return ret;
 310        }
 311
 312        refclk = clk_get_rate(xvcu->pll_ref);
 313
 314        /*
 315         * The divide-by-2 should be always enabled (==1)
 316         * to meet the timing in the design.
 317         * Otherwise, it's an error
 318         */
 319        vcu_pll_ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_PLL_CTRL);
 320        clkoutdiv = vcu_pll_ctrl >> VCU_PLL_CTRL_CLKOUTDIV_SHIFT;
 321        clkoutdiv = clkoutdiv & VCU_PLL_CTRL_CLKOUTDIV_MASK;
 322        if (clkoutdiv != 1) {
 323                dev_err(xvcu->dev, "clkoutdiv value is invalid\n");
 324                return -EINVAL;
 325        }
 326
 327        for (i = ARRAY_SIZE(xvcu_pll_cfg) - 1; i >= 0; i--) {
 328                const struct xvcu_pll_cfg *cfg = &xvcu_pll_cfg[i];
 329
 330                fvco = cfg->fbdiv * refclk;
 331                if (fvco >= FVCO_MIN && fvco <= FVCO_MAX) {
 332                        pll_clk = fvco / VCU_PLL_DIV2;
 333                        if (fvco % VCU_PLL_DIV2 != 0)
 334                                pll_clk++;
 335                        mod = pll_clk % coreclk;
 336                        if (mod < LIMIT) {
 337                                divisor_core = pll_clk / coreclk;
 338                        } else if (coreclk - mod < LIMIT) {
 339                                divisor_core = pll_clk / coreclk;
 340                                divisor_core++;
 341                        } else {
 342                                continue;
 343                        }
 344                        if (divisor_core >= DIVISOR_MIN &&
 345                            divisor_core <= DIVISOR_MAX) {
 346                                found = cfg;
 347                                divisor_mcu = pll_clk / mcuclk;
 348                                mod = pll_clk % mcuclk;
 349                                if (mcuclk - mod < LIMIT)
 350                                        divisor_mcu++;
 351                                break;
 352                        }
 353                }
 354        }
 355
 356        if (!found) {
 357                dev_err(xvcu->dev, "Invalid clock combination.\n");
 358                return -EINVAL;
 359        }
 360
 361        xvcu->coreclk = pll_clk / divisor_core;
 362        mcuclk = pll_clk / divisor_mcu;
 363        dev_dbg(xvcu->dev, "Actual Ref clock freq is %uHz\n", refclk);
 364        dev_dbg(xvcu->dev, "Actual Core clock freq is %uHz\n", xvcu->coreclk);
 365        dev_dbg(xvcu->dev, "Actual Mcu clock freq is %uHz\n", mcuclk);
 366
 367        vcu_pll_ctrl &= ~(VCU_PLL_CTRL_FBDIV_MASK << VCU_PLL_CTRL_FBDIV_SHIFT);
 368        vcu_pll_ctrl |= (found->fbdiv & VCU_PLL_CTRL_FBDIV_MASK) <<
 369                         VCU_PLL_CTRL_FBDIV_SHIFT;
 370        vcu_pll_ctrl &= ~(VCU_PLL_CTRL_POR_IN_MASK <<
 371                          VCU_PLL_CTRL_POR_IN_SHIFT);
 372        vcu_pll_ctrl |= (VCU_PLL_CTRL_DEFAULT & VCU_PLL_CTRL_POR_IN_MASK) <<
 373                         VCU_PLL_CTRL_POR_IN_SHIFT;
 374        vcu_pll_ctrl &= ~(VCU_PLL_CTRL_PWR_POR_MASK <<
 375                          VCU_PLL_CTRL_PWR_POR_SHIFT);
 376        vcu_pll_ctrl |= (VCU_PLL_CTRL_DEFAULT & VCU_PLL_CTRL_PWR_POR_MASK) <<
 377                         VCU_PLL_CTRL_PWR_POR_SHIFT;
 378        xvcu_write(xvcu->vcu_slcr_ba, VCU_PLL_CTRL, vcu_pll_ctrl);
 379
 380        /* Set divisor for the core and mcu clock */
 381        ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL);
 382        ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
 383        ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) <<
 384                 VCU_PLL_DIVISOR_SHIFT;
 385        ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
 386        ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
 387        xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL, ctrl);
 388
 389        ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL);
 390        ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
 391        ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) <<
 392                 VCU_PLL_DIVISOR_SHIFT;
 393        ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
 394        ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
 395        xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL, ctrl);
 396
 397        ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL);
 398        ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
 399        ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT;
 400        ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
 401        ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
 402        xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL, ctrl);
 403
 404        ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL);
 405        ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
 406        ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT;
 407        ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
 408        ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
 409        xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL, ctrl);
 410
 411        /* Set RES, CP, LFHF, LOCK_CNT and LOCK_DLY cfg values */
 412        cfg_val = (found->res << VCU_PLL_CFG_RES_SHIFT) |
 413                   (found->cp << VCU_PLL_CFG_CP_SHIFT) |
 414                   (found->lfhf << VCU_PLL_CFG_LFHF_SHIFT) |
 415                   (found->lock_cnt << VCU_PLL_CFG_LOCK_CNT_SHIFT) |
 416                   (found->lock_dly << VCU_PLL_CFG_LOCK_DLY_SHIFT);
 417        xvcu_write(xvcu->vcu_slcr_ba, VCU_PLL_CFG, cfg_val);
 418
 419        return 0;
 420}
 421
 422/**
 423 * xvcu_set_pll - PLL init sequence
 424 * @xvcu:       Pointer to the xvcu_device structure
 425 *
 426 * Call the api to set the PLL info and once that is done then
 427 * init the PLL sequence to make the PLL stable.
 428 *
 429 * Return:      Returns status, either success or error+reason
 430 */
 431static int xvcu_set_pll(struct xvcu_device *xvcu)
 432{
 433        u32 lock_status;
 434        unsigned long timeout;
 435        int ret;
 436
 437        ret = xvcu_set_vcu_pll_info(xvcu);
 438        if (ret) {
 439                dev_err(xvcu->dev, "failed to set pll info\n");
 440                return ret;
 441        }
 442
 443        xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
 444                             1, VCU_PLL_CTRL_BYPASS_MASK,
 445                             VCU_PLL_CTRL_BYPASS_SHIFT);
 446        xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
 447                             1, VCU_PLL_CTRL_RESET_MASK,
 448                             VCU_PLL_CTRL_RESET_SHIFT);
 449        xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
 450                             0, VCU_PLL_CTRL_RESET_MASK,
 451                             VCU_PLL_CTRL_RESET_SHIFT);
 452        /*
 453         * Defined the timeout for the max time to wait the
 454         * PLL_STATUS to be locked.
 455         */
 456        timeout = jiffies + msecs_to_jiffies(2000);
 457        do {
 458                lock_status = xvcu_read(xvcu->vcu_slcr_ba, VCU_PLL_STATUS);
 459                if (lock_status & VCU_PLL_STATUS_LOCK_STATUS_MASK) {
 460                        xvcu_write_field_reg(xvcu->vcu_slcr_ba, VCU_PLL_CTRL,
 461                                             0, VCU_PLL_CTRL_BYPASS_MASK,
 462                                             VCU_PLL_CTRL_BYPASS_SHIFT);
 463                        return 0;
 464                }
 465        } while (!time_after(jiffies, timeout));
 466
 467        /* PLL is not locked even after the timeout of the 2sec */
 468        dev_err(xvcu->dev, "PLL is not locked\n");
 469        return -ETIMEDOUT;
 470}
 471
 472/**
 473 * xvcu_probe - Probe existence of the logicoreIP
 474 *                      and initialize PLL
 475 *
 476 * @pdev:       Pointer to the platform_device structure
 477 *
 478 * Return:      Returns 0 on success
 479 *              Negative error code otherwise
 480 */
 481static int xvcu_probe(struct platform_device *pdev)
 482{
 483        struct resource *res;
 484        struct xvcu_device *xvcu;
 485        void __iomem *regs;
 486        int ret;
 487
 488        xvcu = devm_kzalloc(&pdev->dev, sizeof(*xvcu), GFP_KERNEL);
 489        if (!xvcu)
 490                return -ENOMEM;
 491
 492        xvcu->dev = &pdev->dev;
 493        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vcu_slcr");
 494        if (!res) {
 495                dev_err(&pdev->dev, "get vcu_slcr memory resource failed.\n");
 496                return -ENODEV;
 497        }
 498
 499        xvcu->vcu_slcr_ba = devm_ioremap(&pdev->dev, res->start,
 500                                                 resource_size(res));
 501        if (!xvcu->vcu_slcr_ba) {
 502                dev_err(&pdev->dev, "vcu_slcr register mapping failed.\n");
 503                return -ENOMEM;
 504        }
 505
 506        xvcu->logicore_reg_ba =
 507                syscon_regmap_lookup_by_compatible("xlnx,vcu-settings");
 508        if (IS_ERR(xvcu->logicore_reg_ba)) {
 509                dev_info(&pdev->dev,
 510                         "could not find xlnx,vcu-settings: trying direct register access\n");
 511
 512                res = platform_get_resource_byname(pdev,
 513                                                   IORESOURCE_MEM, "logicore");
 514                if (!res) {
 515                        dev_err(&pdev->dev, "get logicore memory resource failed.\n");
 516                        return -ENODEV;
 517                }
 518
 519                regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 520                if (!regs) {
 521                        dev_err(&pdev->dev, "logicore register mapping failed.\n");
 522                        return -ENOMEM;
 523                }
 524
 525                xvcu->logicore_reg_ba =
 526                        devm_regmap_init_mmio(&pdev->dev, regs,
 527                                              &vcu_settings_regmap_config);
 528                if (IS_ERR(xvcu->logicore_reg_ba)) {
 529                        dev_err(&pdev->dev, "failed to init regmap\n");
 530                        return PTR_ERR(xvcu->logicore_reg_ba);
 531                }
 532        }
 533
 534        xvcu->aclk = devm_clk_get(&pdev->dev, "aclk");
 535        if (IS_ERR(xvcu->aclk)) {
 536                dev_err(&pdev->dev, "Could not get aclk clock\n");
 537                return PTR_ERR(xvcu->aclk);
 538        }
 539
 540        xvcu->pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
 541        if (IS_ERR(xvcu->pll_ref)) {
 542                dev_err(&pdev->dev, "Could not get pll_ref clock\n");
 543                return PTR_ERR(xvcu->pll_ref);
 544        }
 545
 546        ret = clk_prepare_enable(xvcu->aclk);
 547        if (ret) {
 548                dev_err(&pdev->dev, "aclk clock enable failed\n");
 549                return ret;
 550        }
 551
 552        ret = clk_prepare_enable(xvcu->pll_ref);
 553        if (ret) {
 554                dev_err(&pdev->dev, "pll_ref clock enable failed\n");
 555                goto error_aclk;
 556        }
 557
 558        /*
 559         * Do the Gasket isolation and put the VCU out of reset
 560         * Bit 0 : Gasket isolation
 561         * Bit 1 : put VCU out of reset
 562         */
 563        regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE);
 564
 565        /* Do the PLL Settings based on the ref clk,core and mcu clk freq */
 566        ret = xvcu_set_pll(xvcu);
 567        if (ret) {
 568                dev_err(&pdev->dev, "Failed to set the pll\n");
 569                goto error_pll_ref;
 570        }
 571
 572        dev_set_drvdata(&pdev->dev, xvcu);
 573
 574        return 0;
 575
 576error_pll_ref:
 577        clk_disable_unprepare(xvcu->pll_ref);
 578error_aclk:
 579        clk_disable_unprepare(xvcu->aclk);
 580        return ret;
 581}
 582
 583/**
 584 * xvcu_remove - Insert gasket isolation
 585 *                      and disable the clock
 586 * @pdev:       Pointer to the platform_device structure
 587 *
 588 * Return:      Returns 0 on success
 589 *              Negative error code otherwise
 590 */
 591static int xvcu_remove(struct platform_device *pdev)
 592{
 593        struct xvcu_device *xvcu;
 594
 595        xvcu = platform_get_drvdata(pdev);
 596        if (!xvcu)
 597                return -ENODEV;
 598
 599        /* Add the the Gasket isolation and put the VCU in reset. */
 600        regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0);
 601
 602        clk_disable_unprepare(xvcu->pll_ref);
 603        clk_disable_unprepare(xvcu->aclk);
 604
 605        return 0;
 606}
 607
 608static const struct of_device_id xvcu_of_id_table[] = {
 609        { .compatible = "xlnx,vcu" },
 610        { .compatible = "xlnx,vcu-logicoreip-1.0" },
 611        { }
 612};
 613MODULE_DEVICE_TABLE(of, xvcu_of_id_table);
 614
 615static struct platform_driver xvcu_driver = {
 616        .driver = {
 617                .name           = "xilinx-vcu",
 618                .of_match_table = xvcu_of_id_table,
 619        },
 620        .probe                  = xvcu_probe,
 621        .remove                 = xvcu_remove,
 622};
 623
 624module_platform_driver(xvcu_driver);
 625
 626MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>");
 627MODULE_DESCRIPTION("Xilinx VCU init Driver");
 628MODULE_LICENSE("GPL v2");
 629