Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 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/clk.h> |
Michael Tretter | 5a2b2e1 | 2021-01-21 08:16:51 +0100 | [diff] [blame] | 10 | #include <linux/clk-provider.h> |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 11 | #include <linux/device.h> |
| 12 | #include <linux/errno.h> |
| 13 | #include <linux/io.h> |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 14 | #include <linux/mfd/syscon.h> |
| 15 | #include <linux/mfd/syscon/xlnx-vcu.h> |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 16 | #include <linux/module.h> |
| 17 | #include <linux/of_platform.h> |
| 18 | #include <linux/platform_device.h> |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 19 | #include <linux/regmap.h> |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 20 | |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 21 | #include <dt-bindings/clock/xlnx-vcu.h> |
| 22 | |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 23 | /* 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 55 | |
| 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 62 | |
| 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 Tretter | 5a2b2e1 | 2021-01-21 08:16:51 +0100 | [diff] [blame] | 70 | * @pll: handle for the VCU PLL |
Michael Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 71 | * @pll_post: handle for the VCU PLL post divider |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 72 | * @clk_data: clocks provided by the vcu clock provider |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 73 | */ |
| 74 | struct xvcu_device { |
| 75 | struct device *dev; |
| 76 | struct clk *pll_ref; |
| 77 | struct clk *aclk; |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 78 | struct regmap *logicore_reg_ba; |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 79 | void __iomem *vcu_slcr_ba; |
Michael Tretter | 5a2b2e1 | 2021-01-21 08:16:51 +0100 | [diff] [blame] | 80 | struct clk_hw *pll; |
Michael Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 81 | struct clk_hw *pll_post; |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 82 | struct clk_hw_onecell_data *clk_data; |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 83 | }; |
| 84 | |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 85 | static 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 94 | /** |
| 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 | */ |
| 103 | struct 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 | |
| 112 | static 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 | */ |
| 224 | static 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 | */ |
| 235 | static 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 | */ |
| 248 | static 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 Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 259 | #define to_vcu_pll(_hw) container_of(_hw, struct vcu_pll, hw) |
| 260 | |
| 261 | struct vcu_pll { |
| 262 | struct clk_hw hw; |
| 263 | void __iomem *reg_base; |
| 264 | unsigned long fvco_min; |
| 265 | unsigned long fvco_max; |
| 266 | }; |
| 267 | |
| 268 | static int xvcu_pll_wait_for_lock(struct vcu_pll *pll) |
Michael Tretter | a3ab984 | 2021-01-21 08:16:48 +0100 | [diff] [blame] | 269 | { |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 270 | void __iomem *base = pll->reg_base; |
Michael Tretter | a3ab984 | 2021-01-21 08:16:48 +0100 | [diff] [blame] | 271 | 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 Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 284 | static 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 Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 307 | static 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 Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 319 | static int xvcu_pll_set_div(struct vcu_pll *pll, int div) |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 320 | { |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 321 | void __iomem *base = pll->reg_base; |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 322 | 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 Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 346 | static long xvcu_pll_round_rate(struct clk_hw *hw, |
| 347 | unsigned long rate, unsigned long *parent_rate) |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 348 | { |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 349 | 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 Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 358 | } |
| 359 | |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 360 | static unsigned long xvcu_pll_recalc_rate(struct clk_hw *hw, |
| 361 | unsigned long parent_rate) |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 362 | { |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 363 | 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 | |
| 374 | static 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 | |
| 382 | static 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 Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 386 | u32 vcu_pll_ctrl; |
| 387 | int ret; |
| 388 | |
Michael Tretter | f1bc982 | 2021-01-21 08:16:50 +0100 | [diff] [blame] | 389 | xvcu_write_field_reg(base, VCU_PLL_CTRL, |
| 390 | 1, VCU_PLL_CTRL_BYPASS_MASK, |
| 391 | VCU_PLL_CTRL_BYPASS_SHIFT); |
| 392 | |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 393 | 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 Tretter | f1bc982 | 2021-01-21 08:16:50 +0100 | [diff] [blame] | 404 | 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 Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 407 | |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 408 | ret = xvcu_pll_wait_for_lock(pll); |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 409 | if (ret) { |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 410 | pr_err("VCU PLL is not locked\n"); |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 411 | 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 Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 418 | err: |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 419 | return ret; |
| 420 | } |
| 421 | |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 422 | static void xvcu_pll_disable(struct clk_hw *hw) |
Michael Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 423 | { |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 424 | struct vcu_pll *pll = to_vcu_pll(hw); |
| 425 | void __iomem *base = pll->reg_base; |
Michael Tretter | f1bc982 | 2021-01-21 08:16:50 +0100 | [diff] [blame] | 426 | 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 Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 436 | } |
Michael Tretter | f1bc982 | 2021-01-21 08:16:50 +0100 | [diff] [blame] | 437 | |
Michael Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 438 | static 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 | |
| 446 | static 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 Tretter | 354dcf7 | 2021-01-21 08:16:49 +0100 | [diff] [blame] | 479 | } |
| 480 | |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 481 | static 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 | |
| 538 | unregister_divider: |
| 539 | clk_hw_unregister_divider(divider); |
| 540 | unregister_mux: |
| 541 | clk_hw_unregister_mux(mux); |
| 542 | |
| 543 | return ERR_PTR(err); |
| 544 | } |
| 545 | |
| 546 | static 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 | |
| 568 | static 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 Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 574 | struct clk_hw *hw; |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 575 | 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 Tretter | 58ee6ba | 2021-01-21 08:16:54 +0100 | [diff] [blame] | 585 | 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 Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 592 | 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 Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 597 | parent_data[0].fw_name = "pll_ref"; |
Michael Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 598 | parent_data[1].hw = xvcu->pll_post; |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 599 | |
| 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 | |
| 624 | static 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 Tretter | 4472e18 | 2021-01-21 08:16:53 +0100 | [diff] [blame] | 637 | |
| 638 | clk_hw_unregister_fixed_factor(xvcu->pll_post); |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 639 | } |
| 640 | |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 641 | /** |
| 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 | */ |
| 650 | static int xvcu_probe(struct platform_device *pdev) |
| 651 | { |
| 652 | struct resource *res; |
| 653 | struct xvcu_device *xvcu; |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 654 | void __iomem *regs; |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 655 | 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 Hellwig | 4bdc0d6 | 2020-01-06 09:43:50 +0100 | [diff] [blame] | 668 | xvcu->vcu_slcr_ba = devm_ioremap(&pdev->dev, res->start, |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 669 | 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 Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 675 | 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 680 | |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 681 | 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 701 | } |
| 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 721 | /* |
| 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 Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 726 | regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE); |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 727 | |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 728 | 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 Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 734 | dev_set_drvdata(&pdev->dev, xvcu); |
| 735 | |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 736 | return 0; |
| 737 | |
Michael Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 738 | error_clk_provider: |
| 739 | xvcu_unregister_clock_provider(xvcu); |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 740 | 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 | */ |
| 752 | static 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 Tretter | 9c789de | 2021-01-21 08:16:52 +0100 | [diff] [blame] | 760 | xvcu_unregister_clock_provider(xvcu); |
| 761 | |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 762 | /* Add the the Gasket isolation and put the VCU in reset. */ |
Michael Tretter | 30b79eb | 2020-11-09 14:48:17 +0100 | [diff] [blame] | 763 | regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0); |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 764 | |
Dhaval Shah | cee8113 | 2017-12-21 10:33:06 -0800 | [diff] [blame] | 765 | clk_disable_unprepare(xvcu->aclk); |
| 766 | |
| 767 | return 0; |
| 768 | } |
| 769 | |
| 770 | static const struct of_device_id xvcu_of_id_table[] = { |
| 771 | { .compatible = "xlnx,vcu" }, |
| 772 | { .compatible = "xlnx,vcu-logicoreip-1.0" }, |
| 773 | { } |
| 774 | }; |
| 775 | MODULE_DEVICE_TABLE(of, xvcu_of_id_table); |
| 776 | |
| 777 | static 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 | |
| 786 | module_platform_driver(xvcu_driver); |
| 787 | |
| 788 | MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>"); |
| 789 | MODULE_DESCRIPTION("Xilinx VCU init Driver"); |
| 790 | MODULE_LICENSE("GPL v2"); |