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