blob: f858c269e779ed0413d63393e3581147534a7f8c [file] [log] [blame]
Dhaval Shahcee81132017-12-21 10:33:06 -08001// 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>
Michael Tretter5a2b2e12021-01-21 08:16:51 +010010#include <linux/clk-provider.h>
Dhaval Shahcee81132017-12-21 10:33:06 -080011#include <linux/device.h>
12#include <linux/errno.h>
13#include <linux/io.h>
Michael Tretter30b79eb2020-11-09 14:48:17 +010014#include <linux/mfd/syscon.h>
15#include <linux/mfd/syscon/xlnx-vcu.h>
Dhaval Shahcee81132017-12-21 10:33:06 -080016#include <linux/module.h>
17#include <linux/of_platform.h>
18#include <linux/platform_device.h>
Michael Tretter30b79eb2020-11-09 14:48:17 +010019#include <linux/regmap.h>
Dhaval Shahcee81132017-12-21 10:33:06 -080020
Michael Tretter9c789de2021-01-21 08:16:52 +010021#include <dt-bindings/clock/xlnx-vcu.h>
22
Dhaval Shahcee81132017-12-21 10:33:06 -080023/* vcu slcr registers, bitmask and shift */
24#define VCU_PLL_CTRL 0x24
25#define VCU_PLL_CTRL_RESET_MASK 0x01
26#define VCU_PLL_CTRL_RESET_SHIFT 0
27#define VCU_PLL_CTRL_BYPASS_MASK 0x01
28#define VCU_PLL_CTRL_BYPASS_SHIFT 3
29#define VCU_PLL_CTRL_FBDIV_MASK 0x7f
30#define VCU_PLL_CTRL_FBDIV_SHIFT 8
31#define VCU_PLL_CTRL_POR_IN_MASK 0x01
32#define VCU_PLL_CTRL_POR_IN_SHIFT 1
33#define VCU_PLL_CTRL_PWR_POR_MASK 0x01
34#define VCU_PLL_CTRL_PWR_POR_SHIFT 2
35#define VCU_PLL_CTRL_CLKOUTDIV_MASK 0x03
36#define VCU_PLL_CTRL_CLKOUTDIV_SHIFT 16
37#define VCU_PLL_CTRL_DEFAULT 0
38#define VCU_PLL_DIV2 2
39
40#define VCU_PLL_CFG 0x28
41#define VCU_PLL_CFG_RES_MASK 0x0f
42#define VCU_PLL_CFG_RES_SHIFT 0
43#define VCU_PLL_CFG_CP_MASK 0x0f
44#define VCU_PLL_CFG_CP_SHIFT 5
45#define VCU_PLL_CFG_LFHF_MASK 0x03
46#define VCU_PLL_CFG_LFHF_SHIFT 10
47#define VCU_PLL_CFG_LOCK_CNT_MASK 0x03ff
48#define VCU_PLL_CFG_LOCK_CNT_SHIFT 13
49#define VCU_PLL_CFG_LOCK_DLY_MASK 0x7f
50#define VCU_PLL_CFG_LOCK_DLY_SHIFT 25
51#define VCU_ENC_CORE_CTRL 0x30
52#define VCU_ENC_MCU_CTRL 0x34
53#define VCU_DEC_CORE_CTRL 0x38
54#define VCU_DEC_MCU_CTRL 0x3c
Dhaval Shahcee81132017-12-21 10:33:06 -080055
56#define VCU_PLL_STATUS 0x60
57#define VCU_PLL_STATUS_LOCK_STATUS_MASK 0x01
58
59#define MHZ 1000000
60#define FVCO_MIN (1500U * MHZ)
61#define FVCO_MAX (3000U * MHZ)
Dhaval Shahcee81132017-12-21 10:33:06 -080062
63/**
64 * struct xvcu_device - Xilinx VCU init device structure
65 * @dev: Platform device
66 * @pll_ref: pll ref clock source
67 * @aclk: axi clock source
68 * @logicore_reg_ba: logicore reg base address
69 * @vcu_slcr_ba: vcu_slcr Register base address
Michael Tretter5a2b2e12021-01-21 08:16:51 +010070 * @pll: handle for the VCU PLL
Michael Tretter4472e182021-01-21 08:16:53 +010071 * @pll_post: handle for the VCU PLL post divider
Michael Tretter9c789de2021-01-21 08:16:52 +010072 * @clk_data: clocks provided by the vcu clock provider
Dhaval Shahcee81132017-12-21 10:33:06 -080073 */
74struct xvcu_device {
75 struct device *dev;
76 struct clk *pll_ref;
77 struct clk *aclk;
Michael Tretter30b79eb2020-11-09 14:48:17 +010078 struct regmap *logicore_reg_ba;
Dhaval Shahcee81132017-12-21 10:33:06 -080079 void __iomem *vcu_slcr_ba;
Michael Tretter5a2b2e12021-01-21 08:16:51 +010080 struct clk_hw *pll;
Michael Tretter4472e182021-01-21 08:16:53 +010081 struct clk_hw *pll_post;
Michael Tretter9c789de2021-01-21 08:16:52 +010082 struct clk_hw_onecell_data *clk_data;
Dhaval Shahcee81132017-12-21 10:33:06 -080083};
84
Michael Tretter30b79eb2020-11-09 14:48:17 +010085static struct regmap_config vcu_settings_regmap_config = {
86 .name = "regmap",
87 .reg_bits = 32,
88 .val_bits = 32,
89 .reg_stride = 4,
90 .max_register = 0xfff,
91 .cache_type = REGCACHE_NONE,
92};
93
Dhaval Shahcee81132017-12-21 10:33:06 -080094/**
95 * struct xvcu_pll_cfg - Helper data
96 * @fbdiv: The integer portion of the feedback divider to the PLL
97 * @cp: PLL charge pump control
98 * @res: PLL loop filter resistor control
99 * @lfhf: PLL loop filter high frequency capacitor control
100 * @lock_dly: Lock circuit configuration settings for lock windowsize
101 * @lock_cnt: Lock circuit counter setting
102 */
103struct xvcu_pll_cfg {
104 u32 fbdiv;
105 u32 cp;
106 u32 res;
107 u32 lfhf;
108 u32 lock_dly;
109 u32 lock_cnt;
110};
111
112static const struct xvcu_pll_cfg xvcu_pll_cfg[] = {
113 { 25, 3, 10, 3, 63, 1000 },
114 { 26, 3, 10, 3, 63, 1000 },
115 { 27, 4, 6, 3, 63, 1000 },
116 { 28, 4, 6, 3, 63, 1000 },
117 { 29, 4, 6, 3, 63, 1000 },
118 { 30, 4, 6, 3, 63, 1000 },
119 { 31, 6, 1, 3, 63, 1000 },
120 { 32, 6, 1, 3, 63, 1000 },
121 { 33, 4, 10, 3, 63, 1000 },
122 { 34, 5, 6, 3, 63, 1000 },
123 { 35, 5, 6, 3, 63, 1000 },
124 { 36, 5, 6, 3, 63, 1000 },
125 { 37, 5, 6, 3, 63, 1000 },
126 { 38, 5, 6, 3, 63, 975 },
127 { 39, 3, 12, 3, 63, 950 },
128 { 40, 3, 12, 3, 63, 925 },
129 { 41, 3, 12, 3, 63, 900 },
130 { 42, 3, 12, 3, 63, 875 },
131 { 43, 3, 12, 3, 63, 850 },
132 { 44, 3, 12, 3, 63, 850 },
133 { 45, 3, 12, 3, 63, 825 },
134 { 46, 3, 12, 3, 63, 800 },
135 { 47, 3, 12, 3, 63, 775 },
136 { 48, 3, 12, 3, 63, 775 },
137 { 49, 3, 12, 3, 63, 750 },
138 { 50, 3, 12, 3, 63, 750 },
139 { 51, 3, 2, 3, 63, 725 },
140 { 52, 3, 2, 3, 63, 700 },
141 { 53, 3, 2, 3, 63, 700 },
142 { 54, 3, 2, 3, 63, 675 },
143 { 55, 3, 2, 3, 63, 675 },
144 { 56, 3, 2, 3, 63, 650 },
145 { 57, 3, 2, 3, 63, 650 },
146 { 58, 3, 2, 3, 63, 625 },
147 { 59, 3, 2, 3, 63, 625 },
148 { 60, 3, 2, 3, 63, 625 },
149 { 61, 3, 2, 3, 63, 600 },
150 { 62, 3, 2, 3, 63, 600 },
151 { 63, 3, 2, 3, 63, 600 },
152 { 64, 3, 2, 3, 63, 600 },
153 { 65, 3, 2, 3, 63, 600 },
154 { 66, 3, 2, 3, 63, 600 },
155 { 67, 3, 2, 3, 63, 600 },
156 { 68, 3, 2, 3, 63, 600 },
157 { 69, 3, 2, 3, 63, 600 },
158 { 70, 3, 2, 3, 63, 600 },
159 { 71, 3, 2, 3, 63, 600 },
160 { 72, 3, 2, 3, 63, 600 },
161 { 73, 3, 2, 3, 63, 600 },
162 { 74, 3, 2, 3, 63, 600 },
163 { 75, 3, 2, 3, 63, 600 },
164 { 76, 3, 2, 3, 63, 600 },
165 { 77, 3, 2, 3, 63, 600 },
166 { 78, 3, 2, 3, 63, 600 },
167 { 79, 3, 2, 3, 63, 600 },
168 { 80, 3, 2, 3, 63, 600 },
169 { 81, 3, 2, 3, 63, 600 },
170 { 82, 3, 2, 3, 63, 600 },
171 { 83, 4, 2, 3, 63, 600 },
172 { 84, 4, 2, 3, 63, 600 },
173 { 85, 4, 2, 3, 63, 600 },
174 { 86, 4, 2, 3, 63, 600 },
175 { 87, 4, 2, 3, 63, 600 },
176 { 88, 4, 2, 3, 63, 600 },
177 { 89, 4, 2, 3, 63, 600 },
178 { 90, 4, 2, 3, 63, 600 },
179 { 91, 4, 2, 3, 63, 600 },
180 { 92, 4, 2, 3, 63, 600 },
181 { 93, 4, 2, 3, 63, 600 },
182 { 94, 4, 2, 3, 63, 600 },
183 { 95, 4, 2, 3, 63, 600 },
184 { 96, 4, 2, 3, 63, 600 },
185 { 97, 4, 2, 3, 63, 600 },
186 { 98, 4, 2, 3, 63, 600 },
187 { 99, 4, 2, 3, 63, 600 },
188 { 100, 4, 2, 3, 63, 600 },
189 { 101, 4, 2, 3, 63, 600 },
190 { 102, 4, 2, 3, 63, 600 },
191 { 103, 5, 2, 3, 63, 600 },
192 { 104, 5, 2, 3, 63, 600 },
193 { 105, 5, 2, 3, 63, 600 },
194 { 106, 5, 2, 3, 63, 600 },
195 { 107, 3, 4, 3, 63, 600 },
196 { 108, 3, 4, 3, 63, 600 },
197 { 109, 3, 4, 3, 63, 600 },
198 { 110, 3, 4, 3, 63, 600 },
199 { 111, 3, 4, 3, 63, 600 },
200 { 112, 3, 4, 3, 63, 600 },
201 { 113, 3, 4, 3, 63, 600 },
202 { 114, 3, 4, 3, 63, 600 },
203 { 115, 3, 4, 3, 63, 600 },
204 { 116, 3, 4, 3, 63, 600 },
205 { 117, 3, 4, 3, 63, 600 },
206 { 118, 3, 4, 3, 63, 600 },
207 { 119, 3, 4, 3, 63, 600 },
208 { 120, 3, 4, 3, 63, 600 },
209 { 121, 3, 4, 3, 63, 600 },
210 { 122, 3, 4, 3, 63, 600 },
211 { 123, 3, 4, 3, 63, 600 },
212 { 124, 3, 4, 3, 63, 600 },
213 { 125, 3, 4, 3, 63, 600 },
214};
215
216/**
217 * xvcu_read - Read from the VCU register space
218 * @iomem: vcu reg space base address
219 * @offset: vcu reg offset from base
220 *
221 * Return: Returns 32bit value from VCU register specified
222 *
223 */
224static inline u32 xvcu_read(void __iomem *iomem, u32 offset)
225{
226 return ioread32(iomem + offset);
227}
228
229/**
230 * xvcu_write - Write to the VCU register space
231 * @iomem: vcu reg space base address
232 * @offset: vcu reg offset from base
233 * @value: Value to write
234 */
235static inline void xvcu_write(void __iomem *iomem, u32 offset, u32 value)
236{
237 iowrite32(value, iomem + offset);
238}
239
240/**
241 * xvcu_write_field_reg - Write to the vcu reg field
242 * @iomem: vcu reg space base address
243 * @offset: vcu reg offset from base
244 * @field: vcu reg field to write to
245 * @mask: vcu reg mask
246 * @shift: vcu reg number of bits to shift the bitfield
247 */
248static void xvcu_write_field_reg(void __iomem *iomem, int offset,
249 u32 field, u32 mask, int shift)
250{
251 u32 val = xvcu_read(iomem, offset);
252
253 val &= ~(mask << shift);
254 val |= (field & mask) << shift;
255
256 xvcu_write(iomem, offset, val);
257}
258
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100259#define to_vcu_pll(_hw) container_of(_hw, struct vcu_pll, hw)
260
261struct vcu_pll {
262 struct clk_hw hw;
263 void __iomem *reg_base;
264 unsigned long fvco_min;
265 unsigned long fvco_max;
266};
267
268static int xvcu_pll_wait_for_lock(struct vcu_pll *pll)
Michael Trettera3ab9842021-01-21 08:16:48 +0100269{
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100270 void __iomem *base = pll->reg_base;
Michael Trettera3ab9842021-01-21 08:16:48 +0100271 unsigned long timeout;
272 u32 lock_status;
273
274 timeout = jiffies + msecs_to_jiffies(2000);
275 do {
276 lock_status = xvcu_read(base, VCU_PLL_STATUS);
277 if (lock_status & VCU_PLL_STATUS_LOCK_STATUS_MASK)
278 return 0;
279 } while (!time_after(jiffies, timeout));
280
281 return -ETIMEDOUT;
282}
283
Michael Tretter4472e182021-01-21 08:16:53 +0100284static struct clk_hw *xvcu_register_pll_post(struct device *dev,
285 const char *name,
286 const struct clk_hw *parent_hw,
287 void __iomem *reg_base)
288{
289 u32 div;
290 u32 vcu_pll_ctrl;
291
292 /*
293 * The output divider of the PLL must be set to 1/2 to meet the
294 * timing in the design.
295 */
296 vcu_pll_ctrl = xvcu_read(reg_base, VCU_PLL_CTRL);
297 div = vcu_pll_ctrl >> VCU_PLL_CTRL_CLKOUTDIV_SHIFT;
298 div = div & VCU_PLL_CTRL_CLKOUTDIV_MASK;
299 if (div != 1)
300 return ERR_PTR(-EINVAL);
301
302 return clk_hw_register_fixed_factor(dev, "vcu_pll_post",
303 clk_hw_get_name(parent_hw),
304 CLK_SET_RATE_PARENT, 1, 2);
305}
306
Michael Tretter354dcf72021-01-21 08:16:49 +0100307static const struct xvcu_pll_cfg *xvcu_find_cfg(int div)
308{
309 const struct xvcu_pll_cfg *cfg = NULL;
310 unsigned int i;
311
312 for (i = 0; i < ARRAY_SIZE(xvcu_pll_cfg) - 1; i++)
313 if (xvcu_pll_cfg[i].fbdiv == div)
314 cfg = &xvcu_pll_cfg[i];
315
316 return cfg;
317}
318
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100319static int xvcu_pll_set_div(struct vcu_pll *pll, int div)
Michael Tretter354dcf72021-01-21 08:16:49 +0100320{
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100321 void __iomem *base = pll->reg_base;
Michael Tretter354dcf72021-01-21 08:16:49 +0100322 const struct xvcu_pll_cfg *cfg = NULL;
323 u32 vcu_pll_ctrl;
324 u32 cfg_val;
325
326 cfg = xvcu_find_cfg(div);
327 if (!cfg)
328 return -EINVAL;
329
330 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
331 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_FBDIV_MASK << VCU_PLL_CTRL_FBDIV_SHIFT);
332 vcu_pll_ctrl |= (cfg->fbdiv & VCU_PLL_CTRL_FBDIV_MASK) <<
333 VCU_PLL_CTRL_FBDIV_SHIFT;
334 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
335
336 cfg_val = (cfg->res << VCU_PLL_CFG_RES_SHIFT) |
337 (cfg->cp << VCU_PLL_CFG_CP_SHIFT) |
338 (cfg->lfhf << VCU_PLL_CFG_LFHF_SHIFT) |
339 (cfg->lock_cnt << VCU_PLL_CFG_LOCK_CNT_SHIFT) |
340 (cfg->lock_dly << VCU_PLL_CFG_LOCK_DLY_SHIFT);
341 xvcu_write(base, VCU_PLL_CFG, cfg_val);
342
343 return 0;
344}
345
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100346static long xvcu_pll_round_rate(struct clk_hw *hw,
347 unsigned long rate, unsigned long *parent_rate)
Michael Tretter354dcf72021-01-21 08:16:49 +0100348{
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100349 struct vcu_pll *pll = to_vcu_pll(hw);
350 unsigned int feedback_div;
351
352 rate = clamp_t(unsigned long, rate, pll->fvco_min, pll->fvco_max);
353
354 feedback_div = DIV_ROUND_CLOSEST_ULL(rate, *parent_rate);
355 feedback_div = clamp_t(unsigned int, feedback_div, 25, 125);
356
357 return *parent_rate * feedback_div;
Michael Tretter354dcf72021-01-21 08:16:49 +0100358}
359
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100360static unsigned long xvcu_pll_recalc_rate(struct clk_hw *hw,
361 unsigned long parent_rate)
Michael Tretter354dcf72021-01-21 08:16:49 +0100362{
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100363 struct vcu_pll *pll = to_vcu_pll(hw);
364 void __iomem *base = pll->reg_base;
365 unsigned int div;
366 u32 vcu_pll_ctrl;
367
368 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
369 div = (vcu_pll_ctrl >> VCU_PLL_CTRL_FBDIV_SHIFT) & VCU_PLL_CTRL_FBDIV_MASK;
370
371 return div * parent_rate;
372}
373
374static int xvcu_pll_set_rate(struct clk_hw *hw,
375 unsigned long rate, unsigned long parent_rate)
376{
377 struct vcu_pll *pll = to_vcu_pll(hw);
378
379 return xvcu_pll_set_div(pll, rate / parent_rate);
380}
381
382static int xvcu_pll_enable(struct clk_hw *hw)
383{
384 struct vcu_pll *pll = to_vcu_pll(hw);
385 void __iomem *base = pll->reg_base;
Michael Tretter354dcf72021-01-21 08:16:49 +0100386 u32 vcu_pll_ctrl;
387 int ret;
388
Michael Tretterf1bc9822021-01-21 08:16:50 +0100389 xvcu_write_field_reg(base, VCU_PLL_CTRL,
390 1, VCU_PLL_CTRL_BYPASS_MASK,
391 VCU_PLL_CTRL_BYPASS_SHIFT);
392
Michael Tretter354dcf72021-01-21 08:16:49 +0100393 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
394 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_POR_IN_MASK <<
395 VCU_PLL_CTRL_POR_IN_SHIFT);
396 vcu_pll_ctrl |= (VCU_PLL_CTRL_DEFAULT & VCU_PLL_CTRL_POR_IN_MASK) <<
397 VCU_PLL_CTRL_POR_IN_SHIFT;
398 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_PWR_POR_MASK <<
399 VCU_PLL_CTRL_PWR_POR_SHIFT);
400 vcu_pll_ctrl |= (VCU_PLL_CTRL_DEFAULT & VCU_PLL_CTRL_PWR_POR_MASK) <<
401 VCU_PLL_CTRL_PWR_POR_SHIFT;
402 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
403
Michael Tretterf1bc9822021-01-21 08:16:50 +0100404 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_RESET_MASK << VCU_PLL_CTRL_RESET_SHIFT);
405 vcu_pll_ctrl |= (0 & VCU_PLL_CTRL_RESET_MASK) << VCU_PLL_CTRL_RESET_SHIFT;
406 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
Michael Tretter354dcf72021-01-21 08:16:49 +0100407
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100408 ret = xvcu_pll_wait_for_lock(pll);
Michael Tretter354dcf72021-01-21 08:16:49 +0100409 if (ret) {
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100410 pr_err("VCU PLL is not locked\n");
Michael Tretter354dcf72021-01-21 08:16:49 +0100411 goto err;
412 }
413
414 xvcu_write_field_reg(base, VCU_PLL_CTRL,
415 0, VCU_PLL_CTRL_BYPASS_MASK,
416 VCU_PLL_CTRL_BYPASS_SHIFT);
417
Michael Tretter354dcf72021-01-21 08:16:49 +0100418err:
Michael Tretter354dcf72021-01-21 08:16:49 +0100419 return ret;
420}
421
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100422static void xvcu_pll_disable(struct clk_hw *hw)
Michael Tretter354dcf72021-01-21 08:16:49 +0100423{
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100424 struct vcu_pll *pll = to_vcu_pll(hw);
425 void __iomem *base = pll->reg_base;
Michael Tretterf1bc9822021-01-21 08:16:50 +0100426 u32 vcu_pll_ctrl;
427
428 vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
429 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_POR_IN_MASK << VCU_PLL_CTRL_POR_IN_SHIFT);
430 vcu_pll_ctrl |= (1 & VCU_PLL_CTRL_POR_IN_MASK) << VCU_PLL_CTRL_POR_IN_SHIFT;
431 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_PWR_POR_MASK << VCU_PLL_CTRL_PWR_POR_SHIFT);
432 vcu_pll_ctrl |= (1 & VCU_PLL_CTRL_PWR_POR_MASK) << VCU_PLL_CTRL_PWR_POR_SHIFT;
433 vcu_pll_ctrl &= ~(VCU_PLL_CTRL_RESET_MASK << VCU_PLL_CTRL_RESET_SHIFT);
434 vcu_pll_ctrl |= (1 & VCU_PLL_CTRL_RESET_MASK) << VCU_PLL_CTRL_RESET_SHIFT;
435 xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100436}
Michael Tretterf1bc9822021-01-21 08:16:50 +0100437
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100438static const struct clk_ops vcu_pll_ops = {
439 .enable = xvcu_pll_enable,
440 .disable = xvcu_pll_disable,
441 .round_rate = xvcu_pll_round_rate,
442 .recalc_rate = xvcu_pll_recalc_rate,
443 .set_rate = xvcu_pll_set_rate,
444};
445
446static struct clk_hw *xvcu_register_pll(struct device *dev,
447 void __iomem *reg_base,
448 const char *name, const char *parent,
449 unsigned long flags)
450{
451 struct vcu_pll *pll;
452 struct clk_hw *hw;
453 struct clk_init_data init;
454 int ret;
455
456 init.name = name;
457 init.parent_names = &parent;
458 init.ops = &vcu_pll_ops;
459 init.num_parents = 1;
460 init.flags = flags;
461
462 pll = devm_kmalloc(dev, sizeof(*pll), GFP_KERNEL);
463 if (!pll)
464 return ERR_PTR(-ENOMEM);
465
466 pll->hw.init = &init;
467 pll->reg_base = reg_base;
468 pll->fvco_min = FVCO_MIN;
469 pll->fvco_max = FVCO_MAX;
470
471 hw = &pll->hw;
472 ret = devm_clk_hw_register(dev, hw);
473 if (ret)
474 return ERR_PTR(ret);
475
476 clk_hw_set_rate_range(hw, pll->fvco_min, pll->fvco_max);
477
478 return hw;
Michael Tretter354dcf72021-01-21 08:16:49 +0100479}
480
Michael Tretter9c789de2021-01-21 08:16:52 +0100481static struct clk_hw *xvcu_clk_hw_register_leaf(struct device *dev,
482 const char *name,
483 const struct clk_parent_data *parent_data,
484 u8 num_parents,
485 void __iomem *reg)
486{
487 u8 mux_flags = CLK_MUX_ROUND_CLOSEST;
488 u8 divider_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO |
489 CLK_DIVIDER_ROUND_CLOSEST;
490 struct clk_hw *mux = NULL;
491 struct clk_hw *divider = NULL;
492 struct clk_hw *gate = NULL;
493 char *name_mux;
494 char *name_div;
495 int err;
496 /* Protect register shared by clocks */
497 spinlock_t *lock;
498
499 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
500 if (!lock)
501 return ERR_PTR(-ENOMEM);
502 spin_lock_init(lock);
503
504 name_mux = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_mux");
505 if (!name_mux)
506 return ERR_PTR(-ENOMEM);
507 mux = clk_hw_register_mux_parent_data(dev, name_mux,
508 parent_data, num_parents,
509 CLK_SET_RATE_PARENT,
510 reg, 0, 1, mux_flags, lock);
511 if (IS_ERR(mux))
512 return mux;
513
514 name_div = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_div");
515 if (!name_div) {
516 err = -ENOMEM;
517 goto unregister_mux;
518 }
519 divider = clk_hw_register_divider_parent_hw(dev, name_div, mux,
520 CLK_SET_RATE_PARENT,
521 reg, 4, 6, divider_flags,
522 lock);
523 if (IS_ERR(divider)) {
524 err = PTR_ERR(divider);
525 goto unregister_mux;
526 }
527
528 gate = clk_hw_register_gate_parent_hw(dev, name, divider,
529 CLK_SET_RATE_PARENT, reg, 12, 0,
530 lock);
531 if (IS_ERR(gate)) {
532 err = PTR_ERR(gate);
533 goto unregister_divider;
534 }
535
536 return gate;
537
538unregister_divider:
539 clk_hw_unregister_divider(divider);
540unregister_mux:
541 clk_hw_unregister_mux(mux);
542
543 return ERR_PTR(err);
544}
545
546static void xvcu_clk_hw_unregister_leaf(struct clk_hw *hw)
547{
548 struct clk_hw *gate = hw;
549 struct clk_hw *divider;
550 struct clk_hw *mux;
551
552 if (!gate)
553 return;
554
555 divider = clk_hw_get_parent(gate);
556 clk_hw_unregister_gate(gate);
557 if (!divider)
558 return;
559
560 mux = clk_hw_get_parent(divider);
561 clk_hw_unregister_mux(mux);
562 if (!divider)
563 return;
564
565 clk_hw_unregister_divider(divider);
566}
567
568static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
569{
570 struct device *dev = xvcu->dev;
571 struct clk_parent_data parent_data[2] = { 0 };
572 struct clk_hw_onecell_data *data;
573 struct clk_hw **hws;
Michael Tretter4472e182021-01-21 08:16:53 +0100574 struct clk_hw *hw;
Michael Tretter9c789de2021-01-21 08:16:52 +0100575 void __iomem *reg_base = xvcu->vcu_slcr_ba;
576
577 data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL);
578 if (!data)
579 return -ENOMEM;
580 data->num = CLK_XVCU_NUM_CLOCKS;
581 hws = data->hws;
582
583 xvcu->clk_data = data;
584
Michael Tretter58ee6ba2021-01-21 08:16:54 +0100585 hw = xvcu_register_pll(dev, reg_base,
586 "vcu_pll", __clk_get_name(xvcu->pll_ref),
587 CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE);
588 if (IS_ERR(hw))
589 return PTR_ERR(hw);
590 xvcu->pll = hw;
591
Michael Tretter4472e182021-01-21 08:16:53 +0100592 hw = xvcu_register_pll_post(dev, "vcu_pll_post", xvcu->pll, reg_base);
593 if (IS_ERR(hw))
594 return PTR_ERR(hw);
595 xvcu->pll_post = hw;
596
Michael Tretter9c789de2021-01-21 08:16:52 +0100597 parent_data[0].fw_name = "pll_ref";
Michael Tretter4472e182021-01-21 08:16:53 +0100598 parent_data[1].hw = xvcu->pll_post;
Michael Tretter9c789de2021-01-21 08:16:52 +0100599
600 hws[CLK_XVCU_ENC_CORE] =
601 xvcu_clk_hw_register_leaf(dev, "venc_core_clk",
602 parent_data,
603 ARRAY_SIZE(parent_data),
604 reg_base + VCU_ENC_CORE_CTRL);
605 hws[CLK_XVCU_ENC_MCU] =
606 xvcu_clk_hw_register_leaf(dev, "venc_mcu_clk",
607 parent_data,
608 ARRAY_SIZE(parent_data),
609 reg_base + VCU_ENC_MCU_CTRL);
610 hws[CLK_XVCU_DEC_CORE] =
611 xvcu_clk_hw_register_leaf(dev, "vdec_core_clk",
612 parent_data,
613 ARRAY_SIZE(parent_data),
614 reg_base + VCU_DEC_CORE_CTRL);
615 hws[CLK_XVCU_DEC_MCU] =
616 xvcu_clk_hw_register_leaf(dev, "vdec_mcu_clk",
617 parent_data,
618 ARRAY_SIZE(parent_data),
619 reg_base + VCU_DEC_MCU_CTRL);
620
621 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
622}
623
624static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu)
625{
626 struct clk_hw_onecell_data *data = xvcu->clk_data;
627 struct clk_hw **hws = data->hws;
628
629 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_MCU]))
630 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_MCU]);
631 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_CORE]))
632 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_CORE]);
633 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_MCU]))
634 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]);
635 if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE]))
636 xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]);
Michael Tretter4472e182021-01-21 08:16:53 +0100637
638 clk_hw_unregister_fixed_factor(xvcu->pll_post);
Michael Tretter9c789de2021-01-21 08:16:52 +0100639}
640
Dhaval Shahcee81132017-12-21 10:33:06 -0800641/**
642 * xvcu_probe - Probe existence of the logicoreIP
643 * and initialize PLL
644 *
645 * @pdev: Pointer to the platform_device structure
646 *
647 * Return: Returns 0 on success
648 * Negative error code otherwise
649 */
650static int xvcu_probe(struct platform_device *pdev)
651{
652 struct resource *res;
653 struct xvcu_device *xvcu;
Michael Tretter30b79eb2020-11-09 14:48:17 +0100654 void __iomem *regs;
Dhaval Shahcee81132017-12-21 10:33:06 -0800655 int ret;
656
657 xvcu = devm_kzalloc(&pdev->dev, sizeof(*xvcu), GFP_KERNEL);
658 if (!xvcu)
659 return -ENOMEM;
660
661 xvcu->dev = &pdev->dev;
662 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vcu_slcr");
663 if (!res) {
664 dev_err(&pdev->dev, "get vcu_slcr memory resource failed.\n");
665 return -ENODEV;
666 }
667
Christoph Hellwig4bdc0d62020-01-06 09:43:50 +0100668 xvcu->vcu_slcr_ba = devm_ioremap(&pdev->dev, res->start,
Dhaval Shahcee81132017-12-21 10:33:06 -0800669 resource_size(res));
670 if (!xvcu->vcu_slcr_ba) {
671 dev_err(&pdev->dev, "vcu_slcr register mapping failed.\n");
672 return -ENOMEM;
673 }
674
Michael Tretter30b79eb2020-11-09 14:48:17 +0100675 xvcu->logicore_reg_ba =
676 syscon_regmap_lookup_by_compatible("xlnx,vcu-settings");
677 if (IS_ERR(xvcu->logicore_reg_ba)) {
678 dev_info(&pdev->dev,
679 "could not find xlnx,vcu-settings: trying direct register access\n");
Dhaval Shahcee81132017-12-21 10:33:06 -0800680
Michael Tretter30b79eb2020-11-09 14:48:17 +0100681 res = platform_get_resource_byname(pdev,
682 IORESOURCE_MEM, "logicore");
683 if (!res) {
684 dev_err(&pdev->dev, "get logicore memory resource failed.\n");
685 return -ENODEV;
686 }
687
688 regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
689 if (!regs) {
690 dev_err(&pdev->dev, "logicore register mapping failed.\n");
691 return -ENOMEM;
692 }
693
694 xvcu->logicore_reg_ba =
695 devm_regmap_init_mmio(&pdev->dev, regs,
696 &vcu_settings_regmap_config);
697 if (IS_ERR(xvcu->logicore_reg_ba)) {
698 dev_err(&pdev->dev, "failed to init regmap\n");
699 return PTR_ERR(xvcu->logicore_reg_ba);
700 }
Dhaval Shahcee81132017-12-21 10:33:06 -0800701 }
702
703 xvcu->aclk = devm_clk_get(&pdev->dev, "aclk");
704 if (IS_ERR(xvcu->aclk)) {
705 dev_err(&pdev->dev, "Could not get aclk clock\n");
706 return PTR_ERR(xvcu->aclk);
707 }
708
709 xvcu->pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
710 if (IS_ERR(xvcu->pll_ref)) {
711 dev_err(&pdev->dev, "Could not get pll_ref clock\n");
712 return PTR_ERR(xvcu->pll_ref);
713 }
714
715 ret = clk_prepare_enable(xvcu->aclk);
716 if (ret) {
717 dev_err(&pdev->dev, "aclk clock enable failed\n");
718 return ret;
719 }
720
Dhaval Shahcee81132017-12-21 10:33:06 -0800721 /*
722 * Do the Gasket isolation and put the VCU out of reset
723 * Bit 0 : Gasket isolation
724 * Bit 1 : put VCU out of reset
725 */
Michael Tretter30b79eb2020-11-09 14:48:17 +0100726 regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE);
Dhaval Shahcee81132017-12-21 10:33:06 -0800727
Michael Tretter9c789de2021-01-21 08:16:52 +0100728 ret = xvcu_register_clock_provider(xvcu);
729 if (ret) {
730 dev_err(&pdev->dev, "failed to register clock provider\n");
731 goto error_clk_provider;
732 }
733
Dhaval Shahcee81132017-12-21 10:33:06 -0800734 dev_set_drvdata(&pdev->dev, xvcu);
735
Dhaval Shahcee81132017-12-21 10:33:06 -0800736 return 0;
737
Michael Tretter9c789de2021-01-21 08:16:52 +0100738error_clk_provider:
739 xvcu_unregister_clock_provider(xvcu);
Dhaval Shahcee81132017-12-21 10:33:06 -0800740 clk_disable_unprepare(xvcu->aclk);
741 return ret;
742}
743
744/**
745 * xvcu_remove - Insert gasket isolation
746 * and disable the clock
747 * @pdev: Pointer to the platform_device structure
748 *
749 * Return: Returns 0 on success
750 * Negative error code otherwise
751 */
752static int xvcu_remove(struct platform_device *pdev)
753{
754 struct xvcu_device *xvcu;
755
756 xvcu = platform_get_drvdata(pdev);
757 if (!xvcu)
758 return -ENODEV;
759
Michael Tretter9c789de2021-01-21 08:16:52 +0100760 xvcu_unregister_clock_provider(xvcu);
761
Dhaval Shahcee81132017-12-21 10:33:06 -0800762 /* Add the the Gasket isolation and put the VCU in reset. */
Michael Tretter30b79eb2020-11-09 14:48:17 +0100763 regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0);
Dhaval Shahcee81132017-12-21 10:33:06 -0800764
Dhaval Shahcee81132017-12-21 10:33:06 -0800765 clk_disable_unprepare(xvcu->aclk);
766
767 return 0;
768}
769
770static const struct of_device_id xvcu_of_id_table[] = {
771 { .compatible = "xlnx,vcu" },
772 { .compatible = "xlnx,vcu-logicoreip-1.0" },
773 { }
774};
775MODULE_DEVICE_TABLE(of, xvcu_of_id_table);
776
777static struct platform_driver xvcu_driver = {
778 .driver = {
779 .name = "xilinx-vcu",
780 .of_match_table = xvcu_of_id_table,
781 },
782 .probe = xvcu_probe,
783 .remove = xvcu_remove,
784};
785
786module_platform_driver(xvcu_driver);
787
788MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>");
789MODULE_DESCRIPTION("Xilinx VCU init Driver");
790MODULE_LICENSE("GPL v2");