[PATCH 08/12] soc: xilinx: vcu: implement clock provider for output clocks

Stephen Boyd sboyd at kernel.org
Sun Dec 13 00:55:34 EST 2020


Quoting Michael Tretter (2020-11-15 23:55:28)
> 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
> @@ -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 |

Why u8?

> +                          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);
> +               goto err;
> +       }
> +       clk_hw_set_parent(mux, parent_default);

Can this be done from assigned-clock-parents binding?

> +
> +       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))

Would be nicer to have some more goto labels and skip the IS_ERR_OR_NULL
checks.

> +               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);

Any reason to not allocate these spinlocks too?

> +
> +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";

What is "dummy"?

> +       parent_names[1] = clk_hw_get_name(parent_default);

Can we use the new way of specifying clk parents from DT or by using
direct pointers instead of using string names? The idea is to get rid of
clk_hw_get_name() eventually.

> +
> +       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);
> +



More information about the linux-arm-kernel mailing list