[PATCH 08/12] soc: xilinx: vcu: implement clock provider for output clocks
Michal Simek
michal.simek at xilinx.com
Wed Dec 2 09:49:54 EST 2020
On 16. 11. 20 8:55, Michael Tretter wrote:
> The VCU System-Level Control uses an internal PLL to drive the core and
> MCU clock for the allegro encoder and decoder based on an external PL
> clock.
>
> In order be able to ensure that the clocks are enabled and to get their
> rate from other drivers, the module must implement a clock provider and
> register the clocks at the common clock framework. Other drivers are
> then able to access the clock via devicetree bindings.
>
> Signed-off-by: Michael Tretter <m.tretter at pengutronix.de>
> ---
> drivers/soc/xilinx/xlnx_vcu.c | 191 +++++++++++++++++++++++++++-------
> 1 file changed, 154 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/soc/xilinx/xlnx_vcu.c b/drivers/soc/xilinx/xlnx_vcu.c
> index 725e646aa726..cedc8b7859f7 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
> @@ -82,6 +79,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;
The same here with clk_data - please update kernel-doc format.
> };
>
> static struct regmap_config vcu_settings_regmap_config = {
> @@ -403,7 +401,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;
> @@ -478,37 +476,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;
> @@ -545,6 +512,146 @@ 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 char * const *parent_names,
> + u8 num_parents,
> + struct clk_hw *parent_default,
> + void __iomem *reg,
> + spinlock_t *lock)
> +{
> + unsigned long flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT;
> + 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;
> +
> + name_mux = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_mux");
> + if (!name_mux) {
> + err = -ENOMEM;
> + goto err;
> + }
> + mux = clk_hw_register_mux(dev, name_mux, parent_names, num_parents,
> + flags, reg, 0, 1, 0, lock);
> + if (IS_ERR(mux)) {
> + err = PTR_ERR(divider);
mux here?
And smatch is reporting also this issue. Please take a look.
drivers/soc/xilinx/xlnx_vcu.c:541 xvcu_clk_hw_register_leaf() warn:
passing zero to 'PTR_ERR'
drivers/soc/xilinx/xlnx_vcu.c:577 xvcu_clk_hw_register_leaf() warn:
passing zero to 'ERR_PTR'
> + goto err;
> + }
> + clk_hw_set_parent(mux, parent_default);
> +
> + name_div = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_div");
> + if (!name_div) {
> + err = -ENOMEM;
> + goto err;
> + }
> + divider = clk_hw_register_divider_parent_hw(dev, name_div, mux, flags,
> + reg, 4, 6, divider_flags,
> + lock);
> + if (IS_ERR(divider)) {
> + err = PTR_ERR(divider);
> + goto err;
> + }
> +
> + 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 err;
> + }
> +
> + return gate;
> +
> +err:
> + if (!IS_ERR_OR_NULL(gate))
> + clk_hw_unregister_gate(gate);
> + if (!IS_ERR_OR_NULL(divider))
> + clk_hw_unregister_divider(divider);
> + if (!IS_ERR_OR_NULL(mux))
> + clk_hw_unregister_divider(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 DEFINE_SPINLOCK(venc_core_lock);
> +static DEFINE_SPINLOCK(venc_mcu_lock);
> +
> +static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
> +{
> + struct device *dev = xvcu->dev;
> + const char *parent_names[2];
> + struct clk_hw *parent_default;
> + 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_default = xvcu->pll;
> + parent_names[0] = "dummy";
> + parent_names[1] = clk_hw_get_name(parent_default);
> +
> + hws[CLK_XVCU_ENC_CORE] =
> + xvcu_clk_hw_register_leaf(dev, "venc_core_clk",
> + parent_names,
> + ARRAY_SIZE(parent_names),
> + parent_default,
> + reg_base + VCU_ENC_CORE_CTRL,
> + &venc_core_lock);
> + hws[CLK_XVCU_ENC_MCU] =
> + xvcu_clk_hw_register_leaf(dev, "venc_mcu_clk",
> + parent_names,
> + ARRAY_SIZE(parent_names),
> + parent_default,
> + reg_base + VCU_ENC_MCU_CTRL,
> + &venc_mcu_lock);
> +
> + 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_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
> @@ -639,10 +746,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;
> @@ -664,6 +779,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);
>
>
M
More information about the linux-arm-kernel
mailing list