diff options
Diffstat (limited to 'drivers/soc/xilinx')
-rw-r--r-- | drivers/soc/xilinx/xlnx_vcu.c | 197 |
1 files changed, 160 insertions, 37 deletions
diff --git a/drivers/soc/xilinx/xlnx_vcu.c b/drivers/soc/xilinx/xlnx_vcu.c index 6a733a181982..e38e9c8325a7 100644 --- a/drivers/soc/xilinx/xlnx_vcu.c +++ b/drivers/soc/xilinx/xlnx_vcu.c @@ -18,6 +18,8 @@ #include <linux/platform_device.h> #include <linux/regmap.h> +#include <dt-bindings/clock/xlnx-vcu.h> + /* vcu slcr registers, bitmask and shift */ #define VCU_PLL_CTRL 0x24 #define VCU_PLL_CTRL_RESET_MASK 0x01 @@ -50,11 +52,6 @@ #define VCU_ENC_MCU_CTRL 0x34 #define VCU_DEC_CORE_CTRL 0x38 #define VCU_DEC_MCU_CTRL 0x3c -#define VCU_PLL_DIVISOR_MASK 0x3f -#define VCU_PLL_DIVISOR_SHIFT 4 -#define VCU_SRCSEL_MASK 0x01 -#define VCU_SRCSEL_SHIFT 0 -#define VCU_SRCSEL_PLL 1 #define VCU_PLL_STATUS 0x60 #define VCU_PLL_STATUS_LOCK_STATUS_MASK 0x01 @@ -75,6 +72,7 @@ * @logicore_reg_ba: logicore reg base address * @vcu_slcr_ba: vcu_slcr Register base address * @pll: handle for the VCU PLL + * @clk_data: clocks provided by the vcu clock provider */ struct xvcu_device { struct device *dev; @@ -83,6 +81,7 @@ struct xvcu_device { struct regmap *logicore_reg_ba; void __iomem *vcu_slcr_ba; struct clk_hw *pll; + struct clk_hw_onecell_data *clk_data; }; static struct regmap_config vcu_settings_regmap_config = { @@ -404,7 +403,7 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu) u32 refclk, coreclk, mcuclk, inte, deci; u32 divisor_mcu, divisor_core, fvco; u32 clkoutdiv, vcu_pll_ctrl, pll_clk; - u32 mod, ctrl; + u32 mod; int i; int ret; const struct xvcu_pll_cfg *found = NULL; @@ -479,37 +478,6 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu) dev_dbg(xvcu->dev, "Actual Core clock freq is %uHz\n", coreclk); dev_dbg(xvcu->dev, "Actual Mcu clock freq is %uHz\n", mcuclk); - /* Set divisor for the core and mcu clock */ - ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL); - ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT); - ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) << - VCU_PLL_DIVISOR_SHIFT; - ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT); - ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT; - xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL, ctrl); - - ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL); - ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT); - ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) << - VCU_PLL_DIVISOR_SHIFT; - ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT); - ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT; - xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL, ctrl); - - ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL); - ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT); - ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT; - ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT); - ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT; - xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL, ctrl); - - ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL); - ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT); - ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT; - ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT); - ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT; - xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL, ctrl); - ret = xvcu_pll_set_rate(xvcu, fvco, refclk); if (ret) return ret; @@ -546,6 +514,151 @@ static int xvcu_set_pll(struct xvcu_device *xvcu) return xvcu_pll_enable(xvcu); } +static struct clk_hw *xvcu_clk_hw_register_leaf(struct device *dev, + const char *name, + const struct clk_parent_data *parent_data, + u8 num_parents, + void __iomem *reg) +{ + u8 mux_flags = CLK_MUX_ROUND_CLOSEST; + u8 divider_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_DIVIDER_ROUND_CLOSEST; + struct clk_hw *mux = NULL; + struct clk_hw *divider = NULL; + struct clk_hw *gate = NULL; + char *name_mux; + char *name_div; + int err; + /* Protect register shared by clocks */ + spinlock_t *lock; + + lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return ERR_PTR(-ENOMEM); + spin_lock_init(lock); + + name_mux = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_mux"); + if (!name_mux) + return ERR_PTR(-ENOMEM); + mux = clk_hw_register_mux_parent_data(dev, name_mux, + parent_data, num_parents, + CLK_SET_RATE_PARENT, + reg, 0, 1, mux_flags, lock); + if (IS_ERR(mux)) + return mux; + + name_div = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_div"); + if (!name_div) { + err = -ENOMEM; + goto unregister_mux; + } + divider = clk_hw_register_divider_parent_hw(dev, name_div, mux, + CLK_SET_RATE_PARENT, + reg, 4, 6, divider_flags, + lock); + if (IS_ERR(divider)) { + err = PTR_ERR(divider); + goto unregister_mux; + } + + gate = clk_hw_register_gate_parent_hw(dev, name, divider, + CLK_SET_RATE_PARENT, reg, 12, 0, + lock); + if (IS_ERR(gate)) { + err = PTR_ERR(gate); + goto unregister_divider; + } + + return gate; + +unregister_divider: + clk_hw_unregister_divider(divider); +unregister_mux: + clk_hw_unregister_mux(mux); + + return ERR_PTR(err); +} + +static void xvcu_clk_hw_unregister_leaf(struct clk_hw *hw) +{ + struct clk_hw *gate = hw; + struct clk_hw *divider; + struct clk_hw *mux; + + if (!gate) + return; + + divider = clk_hw_get_parent(gate); + clk_hw_unregister_gate(gate); + if (!divider) + return; + + mux = clk_hw_get_parent(divider); + clk_hw_unregister_mux(mux); + if (!divider) + return; + + clk_hw_unregister_divider(divider); +} + +static int xvcu_register_clock_provider(struct xvcu_device *xvcu) +{ + struct device *dev = xvcu->dev; + struct clk_parent_data parent_data[2] = { 0 }; + struct clk_hw_onecell_data *data; + struct clk_hw **hws; + void __iomem *reg_base = xvcu->vcu_slcr_ba; + + data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->num = CLK_XVCU_NUM_CLOCKS; + hws = data->hws; + + xvcu->clk_data = data; + + parent_data[0].fw_name = "pll_ref"; + parent_data[1].hw = xvcu->pll; + + hws[CLK_XVCU_ENC_CORE] = + xvcu_clk_hw_register_leaf(dev, "venc_core_clk", + parent_data, + ARRAY_SIZE(parent_data), + reg_base + VCU_ENC_CORE_CTRL); + hws[CLK_XVCU_ENC_MCU] = + xvcu_clk_hw_register_leaf(dev, "venc_mcu_clk", + parent_data, + ARRAY_SIZE(parent_data), + reg_base + VCU_ENC_MCU_CTRL); + hws[CLK_XVCU_DEC_CORE] = + xvcu_clk_hw_register_leaf(dev, "vdec_core_clk", + parent_data, + ARRAY_SIZE(parent_data), + reg_base + VCU_DEC_CORE_CTRL); + hws[CLK_XVCU_DEC_MCU] = + xvcu_clk_hw_register_leaf(dev, "vdec_mcu_clk", + parent_data, + ARRAY_SIZE(parent_data), + reg_base + VCU_DEC_MCU_CTRL); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); +} + +static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu) +{ + struct clk_hw_onecell_data *data = xvcu->clk_data; + struct clk_hw **hws = data->hws; + + if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_MCU])) + xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_MCU]); + if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_CORE])) + xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_CORE]); + if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_MCU])) + xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]); + if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE])) + xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]); +} + /** * xvcu_probe - Probe existence of the logicoreIP * and initialize PLL @@ -640,10 +753,18 @@ static int xvcu_probe(struct platform_device *pdev) goto error_pll_ref; } + ret = xvcu_register_clock_provider(xvcu); + if (ret) { + dev_err(&pdev->dev, "failed to register clock provider\n"); + goto error_clk_provider; + } + dev_set_drvdata(&pdev->dev, xvcu); return 0; +error_clk_provider: + xvcu_unregister_clock_provider(xvcu); error_pll_ref: clk_disable_unprepare(xvcu->aclk); return ret; @@ -665,6 +786,8 @@ static int xvcu_remove(struct platform_device *pdev) if (!xvcu) return -ENODEV; + xvcu_unregister_clock_provider(xvcu); + /* Add the the Gasket isolation and put the VCU in reset. */ regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0); |