File Index
Files and directories
Sign in
FILE

xlnx_vcu.c

File preview with syntax highlighting.

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