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