xlnx_vcu.c (19071B)
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/bitfield.h> 10#include <linux/clk.h> 11#include <linux/clk-provider.h> 12#include <linux/device.h> 13#include <linux/errno.h> 14#include <linux/io.h> 15#include <linux/mfd/syscon.h> 16#include <linux/mfd/syscon/xlnx-vcu.h> 17#include <linux/module.h> 18#include <linux/of_platform.h> 19#include <linux/platform_device.h> 20#include <linux/regmap.h> 21 22#include <dt-bindings/clock/xlnx-vcu.h> 23 24#define VCU_PLL_CTRL 0x24 25#define VCU_PLL_CTRL_RESET BIT(0) 26#define VCU_PLL_CTRL_POR_IN BIT(1) 27#define VCU_PLL_CTRL_PWR_POR BIT(2) 28#define VCU_PLL_CTRL_BYPASS BIT(3) 29#define VCU_PLL_CTRL_FBDIV GENMASK(14, 8) 30#define VCU_PLL_CTRL_CLKOUTDIV GENMASK(18, 16) 31 32#define VCU_PLL_CFG 0x28 33#define VCU_PLL_CFG_RES GENMASK(3, 0) 34#define VCU_PLL_CFG_CP GENMASK(8, 5) 35#define VCU_PLL_CFG_LFHF GENMASK(12, 10) 36#define VCU_PLL_CFG_LOCK_CNT GENMASK(22, 13) 37#define VCU_PLL_CFG_LOCK_DLY GENMASK(31, 25) 38#define VCU_ENC_CORE_CTRL 0x30 39#define VCU_ENC_MCU_CTRL 0x34 40#define VCU_DEC_CORE_CTRL 0x38 41#define VCU_DEC_MCU_CTRL 0x3c 42#define VCU_PLL_STATUS 0x60 43#define VCU_PLL_STATUS_LOCK_STATUS BIT(0) 44 45#define MHZ 1000000 46#define FVCO_MIN (1500U * MHZ) 47#define FVCO_MAX (3000U * MHZ) 48 49/** 50 * struct xvcu_device - Xilinx VCU init device structure 51 * @dev: Platform device 52 * @pll_ref: pll ref clock source 53 * @aclk: axi clock source 54 * @logicore_reg_ba: logicore reg base address 55 * @vcu_slcr_ba: vcu_slcr Register base address 56 * @pll: handle for the VCU PLL 57 * @pll_post: handle for the VCU PLL post divider 58 * @clk_data: clocks provided by the vcu clock provider 59 */ 60struct xvcu_device { 61 struct device *dev; 62 struct clk *pll_ref; 63 struct clk *aclk; 64 struct regmap *logicore_reg_ba; 65 void __iomem *vcu_slcr_ba; 66 struct clk_hw *pll; 67 struct clk_hw *pll_post; 68 struct clk_hw_onecell_data *clk_data; 69}; 70 71static struct regmap_config vcu_settings_regmap_config = { 72 .name = "regmap", 73 .reg_bits = 32, 74 .val_bits = 32, 75 .reg_stride = 4, 76 .max_register = 0xfff, 77 .cache_type = REGCACHE_NONE, 78}; 79 80/** 81 * struct xvcu_pll_cfg - Helper data 82 * @fbdiv: The integer portion of the feedback divider to the PLL 83 * @cp: PLL charge pump control 84 * @res: PLL loop filter resistor control 85 * @lfhf: PLL loop filter high frequency capacitor control 86 * @lock_dly: Lock circuit configuration settings for lock windowsize 87 * @lock_cnt: Lock circuit counter setting 88 */ 89struct xvcu_pll_cfg { 90 u32 fbdiv; 91 u32 cp; 92 u32 res; 93 u32 lfhf; 94 u32 lock_dly; 95 u32 lock_cnt; 96}; 97 98static const struct xvcu_pll_cfg xvcu_pll_cfg[] = { 99 { 25, 3, 10, 3, 63, 1000 }, 100 { 26, 3, 10, 3, 63, 1000 }, 101 { 27, 4, 6, 3, 63, 1000 }, 102 { 28, 4, 6, 3, 63, 1000 }, 103 { 29, 4, 6, 3, 63, 1000 }, 104 { 30, 4, 6, 3, 63, 1000 }, 105 { 31, 6, 1, 3, 63, 1000 }, 106 { 32, 6, 1, 3, 63, 1000 }, 107 { 33, 4, 10, 3, 63, 1000 }, 108 { 34, 5, 6, 3, 63, 1000 }, 109 { 35, 5, 6, 3, 63, 1000 }, 110 { 36, 5, 6, 3, 63, 1000 }, 111 { 37, 5, 6, 3, 63, 1000 }, 112 { 38, 5, 6, 3, 63, 975 }, 113 { 39, 3, 12, 3, 63, 950 }, 114 { 40, 3, 12, 3, 63, 925 }, 115 { 41, 3, 12, 3, 63, 900 }, 116 { 42, 3, 12, 3, 63, 875 }, 117 { 43, 3, 12, 3, 63, 850 }, 118 { 44, 3, 12, 3, 63, 850 }, 119 { 45, 3, 12, 3, 63, 825 }, 120 { 46, 3, 12, 3, 63, 800 }, 121 { 47, 3, 12, 3, 63, 775 }, 122 { 48, 3, 12, 3, 63, 775 }, 123 { 49, 3, 12, 3, 63, 750 }, 124 { 50, 3, 12, 3, 63, 750 }, 125 { 51, 3, 2, 3, 63, 725 }, 126 { 52, 3, 2, 3, 63, 700 }, 127 { 53, 3, 2, 3, 63, 700 }, 128 { 54, 3, 2, 3, 63, 675 }, 129 { 55, 3, 2, 3, 63, 675 }, 130 { 56, 3, 2, 3, 63, 650 }, 131 { 57, 3, 2, 3, 63, 650 }, 132 { 58, 3, 2, 3, 63, 625 }, 133 { 59, 3, 2, 3, 63, 625 }, 134 { 60, 3, 2, 3, 63, 625 }, 135 { 61, 3, 2, 3, 63, 600 }, 136 { 62, 3, 2, 3, 63, 600 }, 137 { 63, 3, 2, 3, 63, 600 }, 138 { 64, 3, 2, 3, 63, 600 }, 139 { 65, 3, 2, 3, 63, 600 }, 140 { 66, 3, 2, 3, 63, 600 }, 141 { 67, 3, 2, 3, 63, 600 }, 142 { 68, 3, 2, 3, 63, 600 }, 143 { 69, 3, 2, 3, 63, 600 }, 144 { 70, 3, 2, 3, 63, 600 }, 145 { 71, 3, 2, 3, 63, 600 }, 146 { 72, 3, 2, 3, 63, 600 }, 147 { 73, 3, 2, 3, 63, 600 }, 148 { 74, 3, 2, 3, 63, 600 }, 149 { 75, 3, 2, 3, 63, 600 }, 150 { 76, 3, 2, 3, 63, 600 }, 151 { 77, 3, 2, 3, 63, 600 }, 152 { 78, 3, 2, 3, 63, 600 }, 153 { 79, 3, 2, 3, 63, 600 }, 154 { 80, 3, 2, 3, 63, 600 }, 155 { 81, 3, 2, 3, 63, 600 }, 156 { 82, 3, 2, 3, 63, 600 }, 157 { 83, 4, 2, 3, 63, 600 }, 158 { 84, 4, 2, 3, 63, 600 }, 159 { 85, 4, 2, 3, 63, 600 }, 160 { 86, 4, 2, 3, 63, 600 }, 161 { 87, 4, 2, 3, 63, 600 }, 162 { 88, 4, 2, 3, 63, 600 }, 163 { 89, 4, 2, 3, 63, 600 }, 164 { 90, 4, 2, 3, 63, 600 }, 165 { 91, 4, 2, 3, 63, 600 }, 166 { 92, 4, 2, 3, 63, 600 }, 167 { 93, 4, 2, 3, 63, 600 }, 168 { 94, 4, 2, 3, 63, 600 }, 169 { 95, 4, 2, 3, 63, 600 }, 170 { 96, 4, 2, 3, 63, 600 }, 171 { 97, 4, 2, 3, 63, 600 }, 172 { 98, 4, 2, 3, 63, 600 }, 173 { 99, 4, 2, 3, 63, 600 }, 174 { 100, 4, 2, 3, 63, 600 }, 175 { 101, 4, 2, 3, 63, 600 }, 176 { 102, 4, 2, 3, 63, 600 }, 177 { 103, 5, 2, 3, 63, 600 }, 178 { 104, 5, 2, 3, 63, 600 }, 179 { 105, 5, 2, 3, 63, 600 }, 180 { 106, 5, 2, 3, 63, 600 }, 181 { 107, 3, 4, 3, 63, 600 }, 182 { 108, 3, 4, 3, 63, 600 }, 183 { 109, 3, 4, 3, 63, 600 }, 184 { 110, 3, 4, 3, 63, 600 }, 185 { 111, 3, 4, 3, 63, 600 }, 186 { 112, 3, 4, 3, 63, 600 }, 187 { 113, 3, 4, 3, 63, 600 }, 188 { 114, 3, 4, 3, 63, 600 }, 189 { 115, 3, 4, 3, 63, 600 }, 190 { 116, 3, 4, 3, 63, 600 }, 191 { 117, 3, 4, 3, 63, 600 }, 192 { 118, 3, 4, 3, 63, 600 }, 193 { 119, 3, 4, 3, 63, 600 }, 194 { 120, 3, 4, 3, 63, 600 }, 195 { 121, 3, 4, 3, 63, 600 }, 196 { 122, 3, 4, 3, 63, 600 }, 197 { 123, 3, 4, 3, 63, 600 }, 198 { 124, 3, 4, 3, 63, 600 }, 199 { 125, 3, 4, 3, 63, 600 }, 200}; 201 202/** 203 * xvcu_read - Read from the VCU register space 204 * @iomem: vcu reg space base address 205 * @offset: vcu reg offset from base 206 * 207 * Return: Returns 32bit value from VCU register specified 208 * 209 */ 210static inline u32 xvcu_read(void __iomem *iomem, u32 offset) 211{ 212 return ioread32(iomem + offset); 213} 214 215/** 216 * xvcu_write - Write to the VCU register space 217 * @iomem: vcu reg space base address 218 * @offset: vcu reg offset from base 219 * @value: Value to write 220 */ 221static inline void xvcu_write(void __iomem *iomem, u32 offset, u32 value) 222{ 223 iowrite32(value, iomem + offset); 224} 225 226#define to_vcu_pll(_hw) container_of(_hw, struct vcu_pll, hw) 227 228struct vcu_pll { 229 struct clk_hw hw; 230 void __iomem *reg_base; 231 unsigned long fvco_min; 232 unsigned long fvco_max; 233}; 234 235static int xvcu_pll_wait_for_lock(struct vcu_pll *pll) 236{ 237 void __iomem *base = pll->reg_base; 238 unsigned long timeout; 239 u32 lock_status; 240 241 timeout = jiffies + msecs_to_jiffies(2000); 242 do { 243 lock_status = xvcu_read(base, VCU_PLL_STATUS); 244 if (lock_status & VCU_PLL_STATUS_LOCK_STATUS) 245 return 0; 246 } while (!time_after(jiffies, timeout)); 247 248 return -ETIMEDOUT; 249} 250 251static struct clk_hw *xvcu_register_pll_post(struct device *dev, 252 const char *name, 253 const struct clk_hw *parent_hw, 254 void __iomem *reg_base) 255{ 256 u32 div; 257 u32 vcu_pll_ctrl; 258 259 /* 260 * The output divider of the PLL must be set to 1/2 to meet the 261 * timing in the design. 262 */ 263 vcu_pll_ctrl = xvcu_read(reg_base, VCU_PLL_CTRL); 264 div = FIELD_GET(VCU_PLL_CTRL_CLKOUTDIV, vcu_pll_ctrl); 265 if (div != 1) 266 return ERR_PTR(-EINVAL); 267 268 return clk_hw_register_fixed_factor(dev, "vcu_pll_post", 269 clk_hw_get_name(parent_hw), 270 CLK_SET_RATE_PARENT, 1, 2); 271} 272 273static const struct xvcu_pll_cfg *xvcu_find_cfg(int div) 274{ 275 const struct xvcu_pll_cfg *cfg = NULL; 276 unsigned int i; 277 278 for (i = 0; i < ARRAY_SIZE(xvcu_pll_cfg) - 1; i++) 279 if (xvcu_pll_cfg[i].fbdiv == div) 280 cfg = &xvcu_pll_cfg[i]; 281 282 return cfg; 283} 284 285static int xvcu_pll_set_div(struct vcu_pll *pll, int div) 286{ 287 void __iomem *base = pll->reg_base; 288 const struct xvcu_pll_cfg *cfg = NULL; 289 u32 vcu_pll_ctrl; 290 u32 cfg_val; 291 292 cfg = xvcu_find_cfg(div); 293 if (!cfg) 294 return -EINVAL; 295 296 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL); 297 vcu_pll_ctrl &= ~VCU_PLL_CTRL_FBDIV; 298 vcu_pll_ctrl |= FIELD_PREP(VCU_PLL_CTRL_FBDIV, cfg->fbdiv); 299 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl); 300 301 cfg_val = FIELD_PREP(VCU_PLL_CFG_RES, cfg->res) | 302 FIELD_PREP(VCU_PLL_CFG_CP, cfg->cp) | 303 FIELD_PREP(VCU_PLL_CFG_LFHF, cfg->lfhf) | 304 FIELD_PREP(VCU_PLL_CFG_LOCK_CNT, cfg->lock_cnt) | 305 FIELD_PREP(VCU_PLL_CFG_LOCK_DLY, cfg->lock_dly); 306 xvcu_write(base, VCU_PLL_CFG, cfg_val); 307 308 return 0; 309} 310 311static long xvcu_pll_round_rate(struct clk_hw *hw, 312 unsigned long rate, unsigned long *parent_rate) 313{ 314 struct vcu_pll *pll = to_vcu_pll(hw); 315 unsigned int feedback_div; 316 317 rate = clamp_t(unsigned long, rate, pll->fvco_min, pll->fvco_max); 318 319 feedback_div = DIV_ROUND_CLOSEST_ULL(rate, *parent_rate); 320 feedback_div = clamp_t(unsigned int, feedback_div, 25, 125); 321 322 return *parent_rate * feedback_div; 323} 324 325static unsigned long xvcu_pll_recalc_rate(struct clk_hw *hw, 326 unsigned long parent_rate) 327{ 328 struct vcu_pll *pll = to_vcu_pll(hw); 329 void __iomem *base = pll->reg_base; 330 unsigned int div; 331 u32 vcu_pll_ctrl; 332 333 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL); 334 div = FIELD_GET(VCU_PLL_CTRL_FBDIV, vcu_pll_ctrl); 335 336 return div * parent_rate; 337} 338 339static int xvcu_pll_set_rate(struct clk_hw *hw, 340 unsigned long rate, unsigned long parent_rate) 341{ 342 struct vcu_pll *pll = to_vcu_pll(hw); 343 344 return xvcu_pll_set_div(pll, rate / parent_rate); 345} 346 347static int xvcu_pll_enable(struct clk_hw *hw) 348{ 349 struct vcu_pll *pll = to_vcu_pll(hw); 350 void __iomem *base = pll->reg_base; 351 u32 vcu_pll_ctrl; 352 int ret; 353 354 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL); 355 vcu_pll_ctrl |= VCU_PLL_CTRL_BYPASS; 356 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl); 357 358 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL); 359 vcu_pll_ctrl &= ~VCU_PLL_CTRL_POR_IN; 360 vcu_pll_ctrl &= ~VCU_PLL_CTRL_PWR_POR; 361 vcu_pll_ctrl &= ~VCU_PLL_CTRL_RESET; 362 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl); 363 364 ret = xvcu_pll_wait_for_lock(pll); 365 if (ret) { 366 pr_err("VCU PLL is not locked\n"); 367 goto err; 368 } 369 370 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL); 371 vcu_pll_ctrl &= ~VCU_PLL_CTRL_BYPASS; 372 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl); 373 374err: 375 return ret; 376} 377 378static void xvcu_pll_disable(struct clk_hw *hw) 379{ 380 struct vcu_pll *pll = to_vcu_pll(hw); 381 void __iomem *base = pll->reg_base; 382 u32 vcu_pll_ctrl; 383 384 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL); 385 vcu_pll_ctrl |= VCU_PLL_CTRL_POR_IN; 386 vcu_pll_ctrl |= VCU_PLL_CTRL_PWR_POR; 387 vcu_pll_ctrl |= VCU_PLL_CTRL_RESET; 388 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl); 389} 390 391static const struct clk_ops vcu_pll_ops = { 392 .enable = xvcu_pll_enable, 393 .disable = xvcu_pll_disable, 394 .round_rate = xvcu_pll_round_rate, 395 .recalc_rate = xvcu_pll_recalc_rate, 396 .set_rate = xvcu_pll_set_rate, 397}; 398 399static struct clk_hw *xvcu_register_pll(struct device *dev, 400 void __iomem *reg_base, 401 const char *name, const char *parent, 402 unsigned long flags) 403{ 404 struct vcu_pll *pll; 405 struct clk_hw *hw; 406 struct clk_init_data init; 407 int ret; 408 409 init.name = name; 410 init.parent_names = &parent; 411 init.ops = &vcu_pll_ops; 412 init.num_parents = 1; 413 init.flags = flags; 414 415 pll = devm_kmalloc(dev, sizeof(*pll), GFP_KERNEL); 416 if (!pll) 417 return ERR_PTR(-ENOMEM); 418 419 pll->hw.init = &init; 420 pll->reg_base = reg_base; 421 pll->fvco_min = FVCO_MIN; 422 pll->fvco_max = FVCO_MAX; 423 424 hw = &pll->hw; 425 ret = devm_clk_hw_register(dev, hw); 426 if (ret) 427 return ERR_PTR(ret); 428 429 clk_hw_set_rate_range(hw, pll->fvco_min, pll->fvco_max); 430 431 return hw; 432} 433 434static struct clk_hw *xvcu_clk_hw_register_leaf(struct device *dev, 435 const char *name, 436 const struct clk_parent_data *parent_data, 437 u8 num_parents, 438 void __iomem *reg) 439{ 440 u8 mux_flags = CLK_MUX_ROUND_CLOSEST; 441 u8 divider_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | 442 CLK_DIVIDER_ROUND_CLOSEST; 443 struct clk_hw *mux = NULL; 444 struct clk_hw *divider = NULL; 445 struct clk_hw *gate = NULL; 446 char *name_mux; 447 char *name_div; 448 int err; 449 /* Protect register shared by clocks */ 450 spinlock_t *lock; 451 452 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); 453 if (!lock) 454 return ERR_PTR(-ENOMEM); 455 spin_lock_init(lock); 456 457 name_mux = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_mux"); 458 if (!name_mux) 459 return ERR_PTR(-ENOMEM); 460 mux = clk_hw_register_mux_parent_data(dev, name_mux, 461 parent_data, num_parents, 462 CLK_SET_RATE_PARENT, 463 reg, 0, 1, mux_flags, lock); 464 if (IS_ERR(mux)) 465 return mux; 466 467 name_div = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_div"); 468 if (!name_div) { 469 err = -ENOMEM; 470 goto unregister_mux; 471 } 472 divider = clk_hw_register_divider_parent_hw(dev, name_div, mux, 473 CLK_SET_RATE_PARENT, 474 reg, 4, 6, divider_flags, 475 lock); 476 if (IS_ERR(divider)) { 477 err = PTR_ERR(divider); 478 goto unregister_mux; 479 } 480 481 gate = clk_hw_register_gate_parent_hw(dev, name, divider, 482 CLK_SET_RATE_PARENT, reg, 12, 0, 483 lock); 484 if (IS_ERR(gate)) { 485 err = PTR_ERR(gate); 486 goto unregister_divider; 487 } 488 489 return gate; 490 491unregister_divider: 492 clk_hw_unregister_divider(divider); 493unregister_mux: 494 clk_hw_unregister_mux(mux); 495 496 return ERR_PTR(err); 497} 498 499static void xvcu_clk_hw_unregister_leaf(struct clk_hw *hw) 500{ 501 struct clk_hw *gate = hw; 502 struct clk_hw *divider; 503 struct clk_hw *mux; 504 505 if (!gate) 506 return; 507 508 divider = clk_hw_get_parent(gate); 509 clk_hw_unregister_gate(gate); 510 if (!divider) 511 return; 512 513 mux = clk_hw_get_parent(divider); 514 clk_hw_unregister_mux(mux); 515 if (!divider) 516 return; 517 518 clk_hw_unregister_divider(divider); 519} 520 521static int xvcu_register_clock_provider(struct xvcu_device *xvcu) 522{ 523 struct device *dev = xvcu->dev; 524 struct clk_parent_data parent_data[2] = { 0 }; 525 struct clk_hw_onecell_data *data; 526 struct clk_hw **hws; 527 struct clk_hw *hw; 528 void __iomem *reg_base = xvcu->vcu_slcr_ba; 529 530 data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL); 531 if (!data) 532 return -ENOMEM; 533 data->num = CLK_XVCU_NUM_CLOCKS; 534 hws = data->hws; 535 536 xvcu->clk_data = data; 537 538 hw = xvcu_register_pll(dev, reg_base, 539 "vcu_pll", __clk_get_name(xvcu->pll_ref), 540 CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE); 541 if (IS_ERR(hw)) 542 return PTR_ERR(hw); 543 xvcu->pll = hw; 544 545 hw = xvcu_register_pll_post(dev, "vcu_pll_post", xvcu->pll, reg_base); 546 if (IS_ERR(hw)) 547 return PTR_ERR(hw); 548 xvcu->pll_post = hw; 549 550 parent_data[0].fw_name = "pll_ref"; 551 parent_data[1].hw = xvcu->pll_post; 552 553 hws[CLK_XVCU_ENC_CORE] = 554 xvcu_clk_hw_register_leaf(dev, "venc_core_clk", 555 parent_data, 556 ARRAY_SIZE(parent_data), 557 reg_base + VCU_ENC_CORE_CTRL); 558 hws[CLK_XVCU_ENC_MCU] = 559 xvcu_clk_hw_register_leaf(dev, "venc_mcu_clk", 560 parent_data, 561 ARRAY_SIZE(parent_data), 562 reg_base + VCU_ENC_MCU_CTRL); 563 hws[CLK_XVCU_DEC_CORE] = 564 xvcu_clk_hw_register_leaf(dev, "vdec_core_clk", 565 parent_data, 566 ARRAY_SIZE(parent_data), 567 reg_base + VCU_DEC_CORE_CTRL); 568 hws[CLK_XVCU_DEC_MCU] = 569 xvcu_clk_hw_register_leaf(dev, "vdec_mcu_clk", 570 parent_data, 571 ARRAY_SIZE(parent_data), 572 reg_base + VCU_DEC_MCU_CTRL); 573 574 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); 575} 576 577static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu) 578{ 579 struct clk_hw_onecell_data *data = xvcu->clk_data; 580 struct clk_hw **hws = data->hws; 581 582 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_MCU])) 583 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_MCU]); 584 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_CORE])) 585 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_CORE]); 586 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_MCU])) 587 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]); 588 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE])) 589 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]); 590 591 clk_hw_unregister_fixed_factor(xvcu->pll_post); 592} 593 594/** 595 * xvcu_probe - Probe existence of the logicoreIP 596 * and initialize PLL 597 * 598 * @pdev: Pointer to the platform_device structure 599 * 600 * Return: Returns 0 on success 601 * Negative error code otherwise 602 */ 603static int xvcu_probe(struct platform_device *pdev) 604{ 605 struct resource *res; 606 struct xvcu_device *xvcu; 607 void __iomem *regs; 608 int ret; 609 610 xvcu = devm_kzalloc(&pdev->dev, sizeof(*xvcu), GFP_KERNEL); 611 if (!xvcu) 612 return -ENOMEM; 613 614 xvcu->dev = &pdev->dev; 615 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vcu_slcr"); 616 if (!res) { 617 dev_err(&pdev->dev, "get vcu_slcr memory resource failed.\n"); 618 return -ENODEV; 619 } 620 621 xvcu->vcu_slcr_ba = devm_ioremap(&pdev->dev, res->start, 622 resource_size(res)); 623 if (!xvcu->vcu_slcr_ba) { 624 dev_err(&pdev->dev, "vcu_slcr register mapping failed.\n"); 625 return -ENOMEM; 626 } 627 628 xvcu->logicore_reg_ba = 629 syscon_regmap_lookup_by_compatible("xlnx,vcu-settings"); 630 if (IS_ERR(xvcu->logicore_reg_ba)) { 631 dev_info(&pdev->dev, 632 "could not find xlnx,vcu-settings: trying direct register access\n"); 633 634 res = platform_get_resource_byname(pdev, 635 IORESOURCE_MEM, "logicore"); 636 if (!res) { 637 dev_err(&pdev->dev, "get logicore memory resource failed.\n"); 638 return -ENODEV; 639 } 640 641 regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 642 if (!regs) { 643 dev_err(&pdev->dev, "logicore register mapping failed.\n"); 644 return -ENOMEM; 645 } 646 647 xvcu->logicore_reg_ba = 648 devm_regmap_init_mmio(&pdev->dev, regs, 649 &vcu_settings_regmap_config); 650 if (IS_ERR(xvcu->logicore_reg_ba)) { 651 dev_err(&pdev->dev, "failed to init regmap\n"); 652 return PTR_ERR(xvcu->logicore_reg_ba); 653 } 654 } 655 656 xvcu->aclk = devm_clk_get(&pdev->dev, "aclk"); 657 if (IS_ERR(xvcu->aclk)) { 658 dev_err(&pdev->dev, "Could not get aclk clock\n"); 659 return PTR_ERR(xvcu->aclk); 660 } 661 662 xvcu->pll_ref = devm_clk_get(&pdev->dev, "pll_ref"); 663 if (IS_ERR(xvcu->pll_ref)) { 664 dev_err(&pdev->dev, "Could not get pll_ref clock\n"); 665 return PTR_ERR(xvcu->pll_ref); 666 } 667 668 ret = clk_prepare_enable(xvcu->aclk); 669 if (ret) { 670 dev_err(&pdev->dev, "aclk clock enable failed\n"); 671 return ret; 672 } 673 674 /* 675 * Do the Gasket isolation and put the VCU out of reset 676 * Bit 0 : Gasket isolation 677 * Bit 1 : put VCU out of reset 678 */ 679 regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE); 680 681 ret = xvcu_register_clock_provider(xvcu); 682 if (ret) { 683 dev_err(&pdev->dev, "failed to register clock provider\n"); 684 goto error_clk_provider; 685 } 686 687 dev_set_drvdata(&pdev->dev, xvcu); 688 689 return 0; 690 691error_clk_provider: 692 xvcu_unregister_clock_provider(xvcu); 693 clk_disable_unprepare(xvcu->aclk); 694 return ret; 695} 696 697/** 698 * xvcu_remove - Insert gasket isolation 699 * and disable the clock 700 * @pdev: Pointer to the platform_device structure 701 * 702 * Return: Returns 0 on success 703 * Negative error code otherwise 704 */ 705static int xvcu_remove(struct platform_device *pdev) 706{ 707 struct xvcu_device *xvcu; 708 709 xvcu = platform_get_drvdata(pdev); 710 if (!xvcu) 711 return -ENODEV; 712 713 xvcu_unregister_clock_provider(xvcu); 714 715 /* Add the Gasket isolation and put the VCU in reset. */ 716 regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0); 717 718 clk_disable_unprepare(xvcu->aclk); 719 720 return 0; 721} 722 723static const struct of_device_id xvcu_of_id_table[] = { 724 { .compatible = "xlnx,vcu" }, 725 { .compatible = "xlnx,vcu-logicoreip-1.0" }, 726 { } 727}; 728MODULE_DEVICE_TABLE(of, xvcu_of_id_table); 729 730static struct platform_driver xvcu_driver = { 731 .driver = { 732 .name = "xilinx-vcu", 733 .of_match_table = xvcu_of_id_table, 734 }, 735 .probe = xvcu_probe, 736 .remove = xvcu_remove, 737}; 738 739module_platform_driver(xvcu_driver); 740 741MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>"); 742MODULE_DESCRIPTION("Xilinx VCU init Driver"); 743MODULE_LICENSE("GPL v2");