Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soc: xilinx: xlnx_vcu: Add Xilinx ZYNQMP VCU logicoreIP init driver

Xilinx ZYNQMP logicoreIP Init driver is based on the new
LogiCoreIP design created. This driver provides the processing system
and programmable logic isolation. Set the frequency based on the clock
information get from the logicoreIP register set.

Signed-off-by: Dhaval Shah <dshah@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>

authored by

Dhaval Shah and committed by
Michal Simek
cee8113a b7511552

+646
+15
drivers/soc/xilinx/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 menu "Xilinx SoC drivers" 3 3 4 + config XILINX_VCU 5 + tristate "Xilinx VCU logicoreIP Init" 6 + help 7 + Provides the driver to enable and disable the isolation between the 8 + processing system and programmable logic part by using the logicoreIP 9 + register set. This driver also configures the frequency based on the 10 + clock information from the logicoreIP register set. 11 + 12 + If you say yes here you get support for the logicoreIP. 13 + 14 + If unsure, say N. 15 + 16 + To compile this driver as a module, choose M here: the 17 + module will be called xlnx_vcu. 18 + 4 19 endmenu
+1
drivers/soc/xilinx/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o
+630
drivers/soc/xilinx/xlnx_vcu.c
··· 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 + */ 105 + struct 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 + */ 123 + struct 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 + 132 + static 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 + */ 244 + static 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 + */ 255 + static 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 + */ 268 + static 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 + */ 294 + static 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 + */ 447 + static 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 + */ 497 + static 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 + 578 + error_pll_ref: 579 + clk_disable_unprepare(xvcu->pll_ref); 580 + error_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 + */ 593 + static 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 + 610 + static const struct of_device_id xvcu_of_id_table[] = { 611 + { .compatible = "xlnx,vcu" }, 612 + { .compatible = "xlnx,vcu-logicoreip-1.0" }, 613 + { } 614 + }; 615 + MODULE_DEVICE_TABLE(of, xvcu_of_id_table); 616 + 617 + static 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 + 626 + module_platform_driver(xvcu_driver); 627 + 628 + MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>"); 629 + MODULE_DESCRIPTION("Xilinx VCU init Driver"); 630 + MODULE_LICENSE("GPL v2");