diff mbox series

[5/5] clk: vc5: optionally configure the output drive mode

Message ID 20200702212837.10657-5-luca@lucaceresoli.net (mailing list archive)
State Superseded, archived
Headers show
Series [1/5] MAINTAINERS: take over IDT VersaClock 5 clock driver | expand

Commit Message

Luca Ceresoli July 2, 2020, 9:28 p.m. UTC
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@lucaceresoli.net>
---
 drivers/clk/clk-versaclock5.c | 71 +++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

Comments

Adam Ford July 6, 2020, 8:03 p.m. UTC | #1
On Thu, Jul 2, 2020 at 5:40 PM Luca Ceresoli <luca@lucaceresoli.net> 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@lucaceresoli.net>

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
>
Luca Ceresoli July 6, 2020, 8:31 p.m. UTC | #2
Hi Adam,

On 06/07/20 22:03, Adam Ford wrote:
> On Thu, Jul 2, 2020 at 5:40 PM Luca Ceresoli <luca@lucaceresoli.net> 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@lucaceresoli.net>
> 
> This might be duplicating what's been applied to linux-next already.

Oh dear, it is! I hadn't found this patch when I needed it...

Please consider this series superseded. There are still some (minor-ish)
improvements, but they definitely need to be rebased and partially
rewritten.
diff mbox series

Patch

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