Re: [PATCH 5/5] clk: vc5: optionally configure the output drive mode
From: Adam Ford
Date: Mon Jul 06 2020 - 16:03:44 EST
On Thu, Jul 2, 2020 at 5:40 PM Luca Ceresoli <luca@xxxxxxxxxxxxxxxx> wrote:
>
> The Versaclock chips can drive the output pins in several modes: LVDS,
> CMOS, LVPECL etc. Allow configuring the output mode from device tree.
>
> The configuration is optional. If not specified, the mode will not be
> configured and the drive mode will be the chip default.
>
> Signed-off-by: Luca Ceresoli <luca@xxxxxxxxxxxxxxxx>
This might be duplicating what's been applied to linux-next already.
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/clk/clk-versaclock5.c?h=next-20200706&id=260249f929e81d3d5764117fdd6b9e43eb8fb1d5
> ---
> drivers/clk/clk-versaclock5.c | 71 +++++++++++++++++++++++++++++++++++
> 1 file changed, 71 insertions(+)
>
> diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c
> index 60c7cf9acde3..eec57286fae0 100644
> --- a/drivers/clk/clk-versaclock5.c
> +++ b/drivers/clk/clk-versaclock5.c
> @@ -89,6 +89,8 @@
>
> /* Clock control register for clock 1,2 */
> #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n))
> +#define VC5_CLK_OUTPUT_CFG0_MODE_SHIFT 5
> +#define VC5_CLK_OUTPUT_CFG0_MODE_MASK GENMASK(7, 5)
> #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0)
>
> #define VC5_CLK_OE_SHDN 0x68
> @@ -117,6 +119,23 @@
> /* chip has PFD requency doubler */
> #define VC5_HAS_PFD_FREQ_DBL BIT(1)
>
> +/*
> + * Output modes. Values for VC5_CLK_OUTPUT_CFG(idx,0) bits [7:5].
> + * IDT_VC5_OUT_UNKNOWN = keep the hardware default.
> + */
> +enum vc5_out_mode {
> + IDT_VC5_OUT_MODE_LVPECL = 0,
> + IDT_VC5_OUT_MODE_CMOS = 1,
> + IDT_VC5_OUT_MODE_HCSL33 = 2,
> + IDT_VC5_OUT_MODE_LVDS = 3,
> + IDT_VC5_OUT_MODE_CMOS2 = 4,
> + IDT_VC5_OUT_MODE_CMOSD = 5,
> + IDT_VC5_OUT_MODE_HCSL25 = 6,
> +
> + IDT_VC5_OUT_NUM_MODES,
> + IDT_VC5_OUT_MODE_UNKNOWN = 99,
> +};
> +
> /* Supported IDT VC5 models. */
> enum vc5_model {
> IDT_VC5_5P49V5923,
> @@ -149,6 +168,7 @@ struct vc5_out_data {
> struct clk_hw hw;
> struct vc5_driver_data *vc5;
> unsigned int num;
> + enum vc5_out_mode mode:8;
> };
>
> struct vc5_driver_data {
> @@ -593,6 +613,13 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
> return ret;
> }
>
> + /* Set output drive mode */
> + if (hwdata->mode != IDT_VC5_OUT_MODE_UNKNOWN)
> + regmap_update_bits(vc5->regmap,
> + VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
> + VC5_CLK_OUTPUT_CFG0_MODE_MASK,
> + (hwdata->mode << VC5_CLK_OUTPUT_CFG0_MODE_SHIFT));
> +
> /* Enable the clock buffer */
> regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
> VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
> @@ -696,6 +723,46 @@ static int vc5_map_index_to_output(const enum vc5_model model,
> }
> }
>
> +static int vc5_parse_dt(struct vc5_driver_data *vc5)
> +{
> + struct device *dev = &vc5->client->dev;
> + struct device_node *np = dev->of_node;
> + struct device_node *child;
> + u32 val;
> + int n;
> +
> + for (n = 1; n < vc5->chip_info->clk_out_cnt; n++)
> + vc5->clk_out[n].mode = IDT_VC5_OUT_MODE_UNKNOWN;
> +
> + for_each_child_of_node(np, child) {
> + if (of_property_read_u32(child, "reg", &n)) {
> + dev_err(dev, "%pOF: missing reg property\n", child);
> + break;
> + }
> +
> + if (n == 0 || n >= vc5->chip_info->clk_out_cnt) {
> + dev_err(dev, "%pOF: invalid reg %d\n", child, n);
> + break;
> + }
> +
> + if (!of_property_read_u32(child, "idt,drive-mode", &val)) {
> + if (val >= IDT_VC5_OUT_NUM_MODES) {
> + dev_err(dev, "%pOF: invalid idt,drive-mode %u\n",
> + child, val);
> + break;
> + }
> + vc5->clk_out[n].mode = val;
> + }
> + }
> +
> + if (child) {
> + of_node_put(child);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> static const struct of_device_id clk_vc5_of_match[];
>
> static int vc5_probe(struct i2c_client *client,
> @@ -723,6 +790,10 @@ static int vc5_probe(struct i2c_client *client,
> if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER)
> return -EPROBE_DEFER;
>
> + ret = vc5_parse_dt(vc5);
> + if (ret)
> + return ret;
> +
> vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config);
> if (IS_ERR(vc5->regmap)) {
> dev_err(&client->dev, "failed to allocate register map\n");
> --
> 2.27.0
>