Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Xilinx VCU Init
0004  *
0005  * Copyright (C) 2016 - 2017 Xilinx, Inc.
0006  *
0007  * Contacts   Dhaval Shah <dshah@xilinx.com>
0008  */
0009 #include <linux/bitfield.h>
0010 #include <linux/clk.h>
0011 #include <linux/clk-provider.h>
0012 #include <linux/device.h>
0013 #include <linux/errno.h>
0014 #include <linux/io.h>
0015 #include <linux/mfd/syscon.h>
0016 #include <linux/mfd/syscon/xlnx-vcu.h>
0017 #include <linux/module.h>
0018 #include <linux/of_platform.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/regmap.h>
0021 
0022 #include <dt-bindings/clock/xlnx-vcu.h>
0023 
0024 #define VCU_PLL_CTRL            0x24
0025 #define VCU_PLL_CTRL_RESET      BIT(0)
0026 #define VCU_PLL_CTRL_POR_IN     BIT(1)
0027 #define VCU_PLL_CTRL_PWR_POR        BIT(2)
0028 #define VCU_PLL_CTRL_BYPASS     BIT(3)
0029 #define VCU_PLL_CTRL_FBDIV      GENMASK(14, 8)
0030 #define VCU_PLL_CTRL_CLKOUTDIV      GENMASK(18, 16)
0031 
0032 #define VCU_PLL_CFG         0x28
0033 #define VCU_PLL_CFG_RES         GENMASK(3, 0)
0034 #define VCU_PLL_CFG_CP          GENMASK(8, 5)
0035 #define VCU_PLL_CFG_LFHF        GENMASK(12, 10)
0036 #define VCU_PLL_CFG_LOCK_CNT        GENMASK(22, 13)
0037 #define VCU_PLL_CFG_LOCK_DLY        GENMASK(31, 25)
0038 #define VCU_ENC_CORE_CTRL       0x30
0039 #define VCU_ENC_MCU_CTRL        0x34
0040 #define VCU_DEC_CORE_CTRL       0x38
0041 #define VCU_DEC_MCU_CTRL        0x3c
0042 #define VCU_PLL_STATUS          0x60
0043 #define VCU_PLL_STATUS_LOCK_STATUS  BIT(0)
0044 
0045 #define MHZ             1000000
0046 #define FVCO_MIN            (1500U * MHZ)
0047 #define FVCO_MAX            (3000U * MHZ)
0048 
0049 /**
0050  * struct xvcu_device - Xilinx VCU init device structure
0051  * @dev: Platform device
0052  * @pll_ref: pll ref clock source
0053  * @aclk: axi clock source
0054  * @logicore_reg_ba: logicore reg base address
0055  * @vcu_slcr_ba: vcu_slcr Register base address
0056  * @pll: handle for the VCU PLL
0057  * @pll_post: handle for the VCU PLL post divider
0058  * @clk_data: clocks provided by the vcu clock provider
0059  */
0060 struct xvcu_device {
0061     struct device *dev;
0062     struct clk *pll_ref;
0063     struct clk *aclk;
0064     struct regmap *logicore_reg_ba;
0065     void __iomem *vcu_slcr_ba;
0066     struct clk_hw *pll;
0067     struct clk_hw *pll_post;
0068     struct clk_hw_onecell_data *clk_data;
0069 };
0070 
0071 static struct regmap_config vcu_settings_regmap_config = {
0072     .name = "regmap",
0073     .reg_bits = 32,
0074     .val_bits = 32,
0075     .reg_stride = 4,
0076     .max_register = 0xfff,
0077     .cache_type = REGCACHE_NONE,
0078 };
0079 
0080 /**
0081  * struct xvcu_pll_cfg - Helper data
0082  * @fbdiv: The integer portion of the feedback divider to the PLL
0083  * @cp: PLL charge pump control
0084  * @res: PLL loop filter resistor control
0085  * @lfhf: PLL loop filter high frequency capacitor control
0086  * @lock_dly: Lock circuit configuration settings for lock windowsize
0087  * @lock_cnt: Lock circuit counter setting
0088  */
0089 struct xvcu_pll_cfg {
0090     u32 fbdiv;
0091     u32 cp;
0092     u32 res;
0093     u32 lfhf;
0094     u32 lock_dly;
0095     u32 lock_cnt;
0096 };
0097 
0098 static const struct xvcu_pll_cfg xvcu_pll_cfg[] = {
0099     { 25, 3, 10, 3, 63, 1000 },
0100     { 26, 3, 10, 3, 63, 1000 },
0101     { 27, 4, 6, 3, 63, 1000 },
0102     { 28, 4, 6, 3, 63, 1000 },
0103     { 29, 4, 6, 3, 63, 1000 },
0104     { 30, 4, 6, 3, 63, 1000 },
0105     { 31, 6, 1, 3, 63, 1000 },
0106     { 32, 6, 1, 3, 63, 1000 },
0107     { 33, 4, 10, 3, 63, 1000 },
0108     { 34, 5, 6, 3, 63, 1000 },
0109     { 35, 5, 6, 3, 63, 1000 },
0110     { 36, 5, 6, 3, 63, 1000 },
0111     { 37, 5, 6, 3, 63, 1000 },
0112     { 38, 5, 6, 3, 63, 975 },
0113     { 39, 3, 12, 3, 63, 950 },
0114     { 40, 3, 12, 3, 63, 925 },
0115     { 41, 3, 12, 3, 63, 900 },
0116     { 42, 3, 12, 3, 63, 875 },
0117     { 43, 3, 12, 3, 63, 850 },
0118     { 44, 3, 12, 3, 63, 850 },
0119     { 45, 3, 12, 3, 63, 825 },
0120     { 46, 3, 12, 3, 63, 800 },
0121     { 47, 3, 12, 3, 63, 775 },
0122     { 48, 3, 12, 3, 63, 775 },
0123     { 49, 3, 12, 3, 63, 750 },
0124     { 50, 3, 12, 3, 63, 750 },
0125     { 51, 3, 2, 3, 63, 725 },
0126     { 52, 3, 2, 3, 63, 700 },
0127     { 53, 3, 2, 3, 63, 700 },
0128     { 54, 3, 2, 3, 63, 675 },
0129     { 55, 3, 2, 3, 63, 675 },
0130     { 56, 3, 2, 3, 63, 650 },
0131     { 57, 3, 2, 3, 63, 650 },
0132     { 58, 3, 2, 3, 63, 625 },
0133     { 59, 3, 2, 3, 63, 625 },
0134     { 60, 3, 2, 3, 63, 625 },
0135     { 61, 3, 2, 3, 63, 600 },
0136     { 62, 3, 2, 3, 63, 600 },
0137     { 63, 3, 2, 3, 63, 600 },
0138     { 64, 3, 2, 3, 63, 600 },
0139     { 65, 3, 2, 3, 63, 600 },
0140     { 66, 3, 2, 3, 63, 600 },
0141     { 67, 3, 2, 3, 63, 600 },
0142     { 68, 3, 2, 3, 63, 600 },
0143     { 69, 3, 2, 3, 63, 600 },
0144     { 70, 3, 2, 3, 63, 600 },
0145     { 71, 3, 2, 3, 63, 600 },
0146     { 72, 3, 2, 3, 63, 600 },
0147     { 73, 3, 2, 3, 63, 600 },
0148     { 74, 3, 2, 3, 63, 600 },
0149     { 75, 3, 2, 3, 63, 600 },
0150     { 76, 3, 2, 3, 63, 600 },
0151     { 77, 3, 2, 3, 63, 600 },
0152     { 78, 3, 2, 3, 63, 600 },
0153     { 79, 3, 2, 3, 63, 600 },
0154     { 80, 3, 2, 3, 63, 600 },
0155     { 81, 3, 2, 3, 63, 600 },
0156     { 82, 3, 2, 3, 63, 600 },
0157     { 83, 4, 2, 3, 63, 600 },
0158     { 84, 4, 2, 3, 63, 600 },
0159     { 85, 4, 2, 3, 63, 600 },
0160     { 86, 4, 2, 3, 63, 600 },
0161     { 87, 4, 2, 3, 63, 600 },
0162     { 88, 4, 2, 3, 63, 600 },
0163     { 89, 4, 2, 3, 63, 600 },
0164     { 90, 4, 2, 3, 63, 600 },
0165     { 91, 4, 2, 3, 63, 600 },
0166     { 92, 4, 2, 3, 63, 600 },
0167     { 93, 4, 2, 3, 63, 600 },
0168     { 94, 4, 2, 3, 63, 600 },
0169     { 95, 4, 2, 3, 63, 600 },
0170     { 96, 4, 2, 3, 63, 600 },
0171     { 97, 4, 2, 3, 63, 600 },
0172     { 98, 4, 2, 3, 63, 600 },
0173     { 99, 4, 2, 3, 63, 600 },
0174     { 100, 4, 2, 3, 63, 600 },
0175     { 101, 4, 2, 3, 63, 600 },
0176     { 102, 4, 2, 3, 63, 600 },
0177     { 103, 5, 2, 3, 63, 600 },
0178     { 104, 5, 2, 3, 63, 600 },
0179     { 105, 5, 2, 3, 63, 600 },
0180     { 106, 5, 2, 3, 63, 600 },
0181     { 107, 3, 4, 3, 63, 600 },
0182     { 108, 3, 4, 3, 63, 600 },
0183     { 109, 3, 4, 3, 63, 600 },
0184     { 110, 3, 4, 3, 63, 600 },
0185     { 111, 3, 4, 3, 63, 600 },
0186     { 112, 3, 4, 3, 63, 600 },
0187     { 113, 3, 4, 3, 63, 600 },
0188     { 114, 3, 4, 3, 63, 600 },
0189     { 115, 3, 4, 3, 63, 600 },
0190     { 116, 3, 4, 3, 63, 600 },
0191     { 117, 3, 4, 3, 63, 600 },
0192     { 118, 3, 4, 3, 63, 600 },
0193     { 119, 3, 4, 3, 63, 600 },
0194     { 120, 3, 4, 3, 63, 600 },
0195     { 121, 3, 4, 3, 63, 600 },
0196     { 122, 3, 4, 3, 63, 600 },
0197     { 123, 3, 4, 3, 63, 600 },
0198     { 124, 3, 4, 3, 63, 600 },
0199     { 125, 3, 4, 3, 63, 600 },
0200 };
0201 
0202 /**
0203  * xvcu_read - Read from the VCU register space
0204  * @iomem:  vcu reg space base address
0205  * @offset: vcu reg offset from base
0206  *
0207  * Return:  Returns 32bit value from VCU register specified
0208  *
0209  */
0210 static inline u32 xvcu_read(void __iomem *iomem, u32 offset)
0211 {
0212     return ioread32(iomem + offset);
0213 }
0214 
0215 /**
0216  * xvcu_write - Write to the VCU register space
0217  * @iomem:  vcu reg space base address
0218  * @offset: vcu reg offset from base
0219  * @value:  Value to write
0220  */
0221 static inline void xvcu_write(void __iomem *iomem, u32 offset, u32 value)
0222 {
0223     iowrite32(value, iomem + offset);
0224 }
0225 
0226 #define to_vcu_pll(_hw) container_of(_hw, struct vcu_pll, hw)
0227 
0228 struct vcu_pll {
0229     struct clk_hw hw;
0230     void __iomem *reg_base;
0231     unsigned long fvco_min;
0232     unsigned long fvco_max;
0233 };
0234 
0235 static int xvcu_pll_wait_for_lock(struct vcu_pll *pll)
0236 {
0237     void __iomem *base = pll->reg_base;
0238     unsigned long timeout;
0239     u32 lock_status;
0240 
0241     timeout = jiffies + msecs_to_jiffies(2000);
0242     do {
0243         lock_status = xvcu_read(base, VCU_PLL_STATUS);
0244         if (lock_status & VCU_PLL_STATUS_LOCK_STATUS)
0245             return 0;
0246     } while (!time_after(jiffies, timeout));
0247 
0248     return -ETIMEDOUT;
0249 }
0250 
0251 static struct clk_hw *xvcu_register_pll_post(struct device *dev,
0252                          const char *name,
0253                          const struct clk_hw *parent_hw,
0254                          void __iomem *reg_base)
0255 {
0256     u32 div;
0257     u32 vcu_pll_ctrl;
0258 
0259     /*
0260      * The output divider of the PLL must be set to 1/2 to meet the
0261      * timing in the design.
0262      */
0263     vcu_pll_ctrl = xvcu_read(reg_base, VCU_PLL_CTRL);
0264     div = FIELD_GET(VCU_PLL_CTRL_CLKOUTDIV, vcu_pll_ctrl);
0265     if (div != 1)
0266         return ERR_PTR(-EINVAL);
0267 
0268     return clk_hw_register_fixed_factor(dev, "vcu_pll_post",
0269                         clk_hw_get_name(parent_hw),
0270                         CLK_SET_RATE_PARENT, 1, 2);
0271 }
0272 
0273 static const struct xvcu_pll_cfg *xvcu_find_cfg(int div)
0274 {
0275     const struct xvcu_pll_cfg *cfg = NULL;
0276     unsigned int i;
0277 
0278     for (i = 0; i < ARRAY_SIZE(xvcu_pll_cfg) - 1; i++)
0279         if (xvcu_pll_cfg[i].fbdiv == div)
0280             cfg = &xvcu_pll_cfg[i];
0281 
0282     return cfg;
0283 }
0284 
0285 static int xvcu_pll_set_div(struct vcu_pll *pll, int div)
0286 {
0287     void __iomem *base = pll->reg_base;
0288     const struct xvcu_pll_cfg *cfg = NULL;
0289     u32 vcu_pll_ctrl;
0290     u32 cfg_val;
0291 
0292     cfg = xvcu_find_cfg(div);
0293     if (!cfg)
0294         return -EINVAL;
0295 
0296     vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
0297     vcu_pll_ctrl &= ~VCU_PLL_CTRL_FBDIV;
0298     vcu_pll_ctrl |= FIELD_PREP(VCU_PLL_CTRL_FBDIV, cfg->fbdiv);
0299     xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
0300 
0301     cfg_val = FIELD_PREP(VCU_PLL_CFG_RES, cfg->res) |
0302           FIELD_PREP(VCU_PLL_CFG_CP, cfg->cp) |
0303           FIELD_PREP(VCU_PLL_CFG_LFHF, cfg->lfhf) |
0304           FIELD_PREP(VCU_PLL_CFG_LOCK_CNT, cfg->lock_cnt) |
0305           FIELD_PREP(VCU_PLL_CFG_LOCK_DLY, cfg->lock_dly);
0306     xvcu_write(base, VCU_PLL_CFG, cfg_val);
0307 
0308     return 0;
0309 }
0310 
0311 static long xvcu_pll_round_rate(struct clk_hw *hw,
0312                 unsigned long rate, unsigned long *parent_rate)
0313 {
0314     struct vcu_pll *pll = to_vcu_pll(hw);
0315     unsigned int feedback_div;
0316 
0317     rate = clamp_t(unsigned long, rate, pll->fvco_min, pll->fvco_max);
0318 
0319     feedback_div = DIV_ROUND_CLOSEST_ULL(rate, *parent_rate);
0320     feedback_div = clamp_t(unsigned int, feedback_div, 25, 125);
0321 
0322     return *parent_rate * feedback_div;
0323 }
0324 
0325 static unsigned long xvcu_pll_recalc_rate(struct clk_hw *hw,
0326                       unsigned long parent_rate)
0327 {
0328     struct vcu_pll *pll = to_vcu_pll(hw);
0329     void __iomem *base = pll->reg_base;
0330     unsigned int div;
0331     u32 vcu_pll_ctrl;
0332 
0333     vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
0334     div = FIELD_GET(VCU_PLL_CTRL_FBDIV, vcu_pll_ctrl);
0335 
0336     return div * parent_rate;
0337 }
0338 
0339 static int xvcu_pll_set_rate(struct clk_hw *hw,
0340                  unsigned long rate, unsigned long parent_rate)
0341 {
0342     struct vcu_pll *pll = to_vcu_pll(hw);
0343 
0344     return xvcu_pll_set_div(pll, rate / parent_rate);
0345 }
0346 
0347 static int xvcu_pll_enable(struct clk_hw *hw)
0348 {
0349     struct vcu_pll *pll = to_vcu_pll(hw);
0350     void __iomem *base = pll->reg_base;
0351     u32 vcu_pll_ctrl;
0352     int ret;
0353 
0354     vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
0355     vcu_pll_ctrl |= VCU_PLL_CTRL_BYPASS;
0356     xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
0357 
0358     vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
0359     vcu_pll_ctrl &= ~VCU_PLL_CTRL_POR_IN;
0360     vcu_pll_ctrl &= ~VCU_PLL_CTRL_PWR_POR;
0361     vcu_pll_ctrl &= ~VCU_PLL_CTRL_RESET;
0362     xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
0363 
0364     ret = xvcu_pll_wait_for_lock(pll);
0365     if (ret) {
0366         pr_err("VCU PLL is not locked\n");
0367         goto err;
0368     }
0369 
0370     vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
0371     vcu_pll_ctrl &= ~VCU_PLL_CTRL_BYPASS;
0372     xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
0373 
0374 err:
0375     return ret;
0376 }
0377 
0378 static void xvcu_pll_disable(struct clk_hw *hw)
0379 {
0380     struct vcu_pll *pll = to_vcu_pll(hw);
0381     void __iomem *base = pll->reg_base;
0382     u32 vcu_pll_ctrl;
0383 
0384     vcu_pll_ctrl = xvcu_read(base, VCU_PLL_CTRL);
0385     vcu_pll_ctrl |= VCU_PLL_CTRL_POR_IN;
0386     vcu_pll_ctrl |= VCU_PLL_CTRL_PWR_POR;
0387     vcu_pll_ctrl |= VCU_PLL_CTRL_RESET;
0388     xvcu_write(base, VCU_PLL_CTRL, vcu_pll_ctrl);
0389 }
0390 
0391 static const struct clk_ops vcu_pll_ops = {
0392     .enable = xvcu_pll_enable,
0393     .disable = xvcu_pll_disable,
0394     .round_rate = xvcu_pll_round_rate,
0395     .recalc_rate = xvcu_pll_recalc_rate,
0396     .set_rate = xvcu_pll_set_rate,
0397 };
0398 
0399 static struct clk_hw *xvcu_register_pll(struct device *dev,
0400                     void __iomem *reg_base,
0401                     const char *name, const char *parent,
0402                     unsigned long flags)
0403 {
0404     struct vcu_pll *pll;
0405     struct clk_hw *hw;
0406     struct clk_init_data init;
0407     int ret;
0408 
0409     init.name = name;
0410     init.parent_names = &parent;
0411     init.ops = &vcu_pll_ops;
0412     init.num_parents = 1;
0413     init.flags = flags;
0414 
0415     pll = devm_kmalloc(dev, sizeof(*pll), GFP_KERNEL);
0416     if (!pll)
0417         return ERR_PTR(-ENOMEM);
0418 
0419     pll->hw.init = &init;
0420     pll->reg_base = reg_base;
0421     pll->fvco_min = FVCO_MIN;
0422     pll->fvco_max = FVCO_MAX;
0423 
0424     hw = &pll->hw;
0425     ret = devm_clk_hw_register(dev, hw);
0426     if (ret)
0427         return ERR_PTR(ret);
0428 
0429     clk_hw_set_rate_range(hw, pll->fvco_min, pll->fvco_max);
0430 
0431     return hw;
0432 }
0433 
0434 static struct clk_hw *xvcu_clk_hw_register_leaf(struct device *dev,
0435                         const char *name,
0436                         const struct clk_parent_data *parent_data,
0437                         u8 num_parents,
0438                         void __iomem *reg)
0439 {
0440     u8 mux_flags = CLK_MUX_ROUND_CLOSEST;
0441     u8 divider_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO |
0442                CLK_DIVIDER_ROUND_CLOSEST;
0443     struct clk_hw *mux = NULL;
0444     struct clk_hw *divider = NULL;
0445     struct clk_hw *gate = NULL;
0446     char *name_mux;
0447     char *name_div;
0448     int err;
0449     /* Protect register shared by clocks */
0450     spinlock_t *lock;
0451 
0452     lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
0453     if (!lock)
0454         return ERR_PTR(-ENOMEM);
0455     spin_lock_init(lock);
0456 
0457     name_mux = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_mux");
0458     if (!name_mux)
0459         return ERR_PTR(-ENOMEM);
0460     mux = clk_hw_register_mux_parent_data(dev, name_mux,
0461                           parent_data, num_parents,
0462                           CLK_SET_RATE_PARENT,
0463                           reg, 0, 1, mux_flags, lock);
0464     if (IS_ERR(mux))
0465         return mux;
0466 
0467     name_div = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_div");
0468     if (!name_div) {
0469         err = -ENOMEM;
0470         goto unregister_mux;
0471     }
0472     divider = clk_hw_register_divider_parent_hw(dev, name_div, mux,
0473                             CLK_SET_RATE_PARENT,
0474                             reg, 4, 6, divider_flags,
0475                             lock);
0476     if (IS_ERR(divider)) {
0477         err = PTR_ERR(divider);
0478         goto unregister_mux;
0479     }
0480 
0481     gate = clk_hw_register_gate_parent_hw(dev, name, divider,
0482                           CLK_SET_RATE_PARENT, reg, 12, 0,
0483                           lock);
0484     if (IS_ERR(gate)) {
0485         err = PTR_ERR(gate);
0486         goto unregister_divider;
0487     }
0488 
0489     return gate;
0490 
0491 unregister_divider:
0492     clk_hw_unregister_divider(divider);
0493 unregister_mux:
0494     clk_hw_unregister_mux(mux);
0495 
0496     return ERR_PTR(err);
0497 }
0498 
0499 static void xvcu_clk_hw_unregister_leaf(struct clk_hw *hw)
0500 {
0501     struct clk_hw *gate = hw;
0502     struct clk_hw *divider;
0503     struct clk_hw *mux;
0504 
0505     if (!gate)
0506         return;
0507 
0508     divider = clk_hw_get_parent(gate);
0509     clk_hw_unregister_gate(gate);
0510     if (!divider)
0511         return;
0512 
0513     mux = clk_hw_get_parent(divider);
0514     clk_hw_unregister_mux(mux);
0515     if (!divider)
0516         return;
0517 
0518     clk_hw_unregister_divider(divider);
0519 }
0520 
0521 static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
0522 {
0523     struct device *dev = xvcu->dev;
0524     struct clk_parent_data parent_data[2] = { 0 };
0525     struct clk_hw_onecell_data *data;
0526     struct clk_hw **hws;
0527     struct clk_hw *hw;
0528     void __iomem *reg_base = xvcu->vcu_slcr_ba;
0529 
0530     data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL);
0531     if (!data)
0532         return -ENOMEM;
0533     data->num = CLK_XVCU_NUM_CLOCKS;
0534     hws = data->hws;
0535 
0536     xvcu->clk_data = data;
0537 
0538     hw = xvcu_register_pll(dev, reg_base,
0539                    "vcu_pll", __clk_get_name(xvcu->pll_ref),
0540                    CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE);
0541     if (IS_ERR(hw))
0542         return PTR_ERR(hw);
0543     xvcu->pll = hw;
0544 
0545     hw = xvcu_register_pll_post(dev, "vcu_pll_post", xvcu->pll, reg_base);
0546     if (IS_ERR(hw))
0547         return PTR_ERR(hw);
0548     xvcu->pll_post = hw;
0549 
0550     parent_data[0].fw_name = "pll_ref";
0551     parent_data[1].hw = xvcu->pll_post;
0552 
0553     hws[CLK_XVCU_ENC_CORE] =
0554         xvcu_clk_hw_register_leaf(dev, "venc_core_clk",
0555                       parent_data,
0556                       ARRAY_SIZE(parent_data),
0557                       reg_base + VCU_ENC_CORE_CTRL);
0558     hws[CLK_XVCU_ENC_MCU] =
0559         xvcu_clk_hw_register_leaf(dev, "venc_mcu_clk",
0560                       parent_data,
0561                       ARRAY_SIZE(parent_data),
0562                       reg_base + VCU_ENC_MCU_CTRL);
0563     hws[CLK_XVCU_DEC_CORE] =
0564         xvcu_clk_hw_register_leaf(dev, "vdec_core_clk",
0565                       parent_data,
0566                       ARRAY_SIZE(parent_data),
0567                       reg_base + VCU_DEC_CORE_CTRL);
0568     hws[CLK_XVCU_DEC_MCU] =
0569         xvcu_clk_hw_register_leaf(dev, "vdec_mcu_clk",
0570                       parent_data,
0571                       ARRAY_SIZE(parent_data),
0572                       reg_base + VCU_DEC_MCU_CTRL);
0573 
0574     return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
0575 }
0576 
0577 static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu)
0578 {
0579     struct clk_hw_onecell_data *data = xvcu->clk_data;
0580     struct clk_hw **hws = data->hws;
0581 
0582     if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_MCU]))
0583         xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_MCU]);
0584     if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_CORE]))
0585         xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_CORE]);
0586     if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_MCU]))
0587         xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]);
0588     if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE]))
0589         xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]);
0590 
0591     clk_hw_unregister_fixed_factor(xvcu->pll_post);
0592 }
0593 
0594 /**
0595  * xvcu_probe - Probe existence of the logicoreIP
0596  *          and initialize PLL
0597  *
0598  * @pdev:   Pointer to the platform_device structure
0599  *
0600  * Return:  Returns 0 on success
0601  *      Negative error code otherwise
0602  */
0603 static int xvcu_probe(struct platform_device *pdev)
0604 {
0605     struct resource *res;
0606     struct xvcu_device *xvcu;
0607     void __iomem *regs;
0608     int ret;
0609 
0610     xvcu = devm_kzalloc(&pdev->dev, sizeof(*xvcu), GFP_KERNEL);
0611     if (!xvcu)
0612         return -ENOMEM;
0613 
0614     xvcu->dev = &pdev->dev;
0615     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vcu_slcr");
0616     if (!res) {
0617         dev_err(&pdev->dev, "get vcu_slcr memory resource failed.\n");
0618         return -ENODEV;
0619     }
0620 
0621     xvcu->vcu_slcr_ba = devm_ioremap(&pdev->dev, res->start,
0622                      resource_size(res));
0623     if (!xvcu->vcu_slcr_ba) {
0624         dev_err(&pdev->dev, "vcu_slcr register mapping failed.\n");
0625         return -ENOMEM;
0626     }
0627 
0628     xvcu->logicore_reg_ba =
0629         syscon_regmap_lookup_by_compatible("xlnx,vcu-settings");
0630     if (IS_ERR(xvcu->logicore_reg_ba)) {
0631         dev_info(&pdev->dev,
0632              "could not find xlnx,vcu-settings: trying direct register access\n");
0633 
0634         res = platform_get_resource_byname(pdev,
0635                            IORESOURCE_MEM, "logicore");
0636         if (!res) {
0637             dev_err(&pdev->dev, "get logicore memory resource failed.\n");
0638             return -ENODEV;
0639         }
0640 
0641         regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
0642         if (!regs) {
0643             dev_err(&pdev->dev, "logicore register mapping failed.\n");
0644             return -ENOMEM;
0645         }
0646 
0647         xvcu->logicore_reg_ba =
0648             devm_regmap_init_mmio(&pdev->dev, regs,
0649                           &vcu_settings_regmap_config);
0650         if (IS_ERR(xvcu->logicore_reg_ba)) {
0651             dev_err(&pdev->dev, "failed to init regmap\n");
0652             return PTR_ERR(xvcu->logicore_reg_ba);
0653         }
0654     }
0655 
0656     xvcu->aclk = devm_clk_get(&pdev->dev, "aclk");
0657     if (IS_ERR(xvcu->aclk)) {
0658         dev_err(&pdev->dev, "Could not get aclk clock\n");
0659         return PTR_ERR(xvcu->aclk);
0660     }
0661 
0662     xvcu->pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
0663     if (IS_ERR(xvcu->pll_ref)) {
0664         dev_err(&pdev->dev, "Could not get pll_ref clock\n");
0665         return PTR_ERR(xvcu->pll_ref);
0666     }
0667 
0668     ret = clk_prepare_enable(xvcu->aclk);
0669     if (ret) {
0670         dev_err(&pdev->dev, "aclk clock enable failed\n");
0671         return ret;
0672     }
0673 
0674     /*
0675      * Do the Gasket isolation and put the VCU out of reset
0676      * Bit 0 : Gasket isolation
0677      * Bit 1 : put VCU out of reset
0678      */
0679     regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, VCU_GASKET_VALUE);
0680 
0681     ret = xvcu_register_clock_provider(xvcu);
0682     if (ret) {
0683         dev_err(&pdev->dev, "failed to register clock provider\n");
0684         goto error_clk_provider;
0685     }
0686 
0687     dev_set_drvdata(&pdev->dev, xvcu);
0688 
0689     return 0;
0690 
0691 error_clk_provider:
0692     xvcu_unregister_clock_provider(xvcu);
0693     clk_disable_unprepare(xvcu->aclk);
0694     return ret;
0695 }
0696 
0697 /**
0698  * xvcu_remove - Insert gasket isolation
0699  *          and disable the clock
0700  * @pdev:   Pointer to the platform_device structure
0701  *
0702  * Return:  Returns 0 on success
0703  *      Negative error code otherwise
0704  */
0705 static int xvcu_remove(struct platform_device *pdev)
0706 {
0707     struct xvcu_device *xvcu;
0708 
0709     xvcu = platform_get_drvdata(pdev);
0710     if (!xvcu)
0711         return -ENODEV;
0712 
0713     xvcu_unregister_clock_provider(xvcu);
0714 
0715     /* Add the Gasket isolation and put the VCU in reset. */
0716     regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0);
0717 
0718     clk_disable_unprepare(xvcu->aclk);
0719 
0720     return 0;
0721 }
0722 
0723 static const struct of_device_id xvcu_of_id_table[] = {
0724     { .compatible = "xlnx,vcu" },
0725     { .compatible = "xlnx,vcu-logicoreip-1.0" },
0726     { }
0727 };
0728 MODULE_DEVICE_TABLE(of, xvcu_of_id_table);
0729 
0730 static struct platform_driver xvcu_driver = {
0731     .driver = {
0732         .name           = "xilinx-vcu",
0733         .of_match_table = xvcu_of_id_table,
0734     },
0735     .probe                  = xvcu_probe,
0736     .remove                 = xvcu_remove,
0737 };
0738 
0739 module_platform_driver(xvcu_driver);
0740 
0741 MODULE_AUTHOR("Dhaval Shah <dshah@xilinx.com>");
0742 MODULE_DESCRIPTION("Xilinx VCU init Driver");
0743 MODULE_LICENSE("GPL v2");