xref: /openbmc/linux/drivers/clk/xilinx/xlnx_vcu.c (revision fc7a6209)
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  */
60 struct 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 
71 static 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  */
89 struct 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 
98 static 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  */
210 static 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  */
221 static 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 
228 struct vcu_pll {
229 	struct clk_hw hw;
230 	void __iomem *reg_base;
231 	unsigned long fvco_min;
232 	unsigned long fvco_max;
233 };
234 
235 static 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 
251 static 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 
273 static 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 
285 static 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 
311 static 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 
325 static 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 
339 static 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 
347 static 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 
374 err:
375 	return ret;
376 }
377 
378 static 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 
391 static 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 
399 static 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 
434 static 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 
491 unregister_divider:
492 	clk_hw_unregister_divider(divider);
493 unregister_mux:
494 	clk_hw_unregister_mux(mux);
495 
496 	return ERR_PTR(err);
497 }
498 
499 static 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 
521 static 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 
577 static 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  */
603 static 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 
691 error_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  */
705 static 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 
723 static const struct of_device_id xvcu_of_id_table[] = {
724 	{ .compatible = "xlnx,vcu" },
725 	{ .compatible = "xlnx,vcu-logicoreip-1.0" },
726 	{ }
727 };
728 MODULE_DEVICE_TABLE(of, xvcu_of_id_table);
729 
730 static 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 
739 module_platform_driver(xvcu_driver);
740 
741 MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>");
742 MODULE_DESCRIPTION("Xilinx VCU init Driver");
743 MODULE_LICENSE("GPL v2");
744