diff mbox series

[2/3] clk: vc5: Enable addition output configurations of the Versaclock

Message ID 20200414193616.1368209-2-aford173@gmail.com (mailing list archive)
State Changes Requested, archived
Headers show
Series [1/3] clk: vc5: Allow Versaclock driver to support multiple instances | expand

Commit Message

Adam Ford April 14, 2020, 7:36 p.m. UTC
The existing driver is expecting the Versaclock to be pre-programmed,
and only sets the output frequency.  Unfortunately, not all devices
are pre-programmed, and the Versaclock chip has more options beyond
just the frequency.

This patch enables the following additional features:

   - Programmable voltage: 1.8V, 2.5V, or 3.3V​
   - Slew Percentage of normal: 85%, 90%, or 100%
   - Output Type: LVPECL, CMOS, HCSL, or LVDS

Signed-off-by: Adam Ford <aford173@gmail.com>

Comments

Stephen Boyd April 22, 2020, 10 a.m. UTC | #1
Quoting Adam Ford (2020-04-14 12:36:15)
> @@ -865,6 +904,77 @@ static int vc5_probe(struct i2c_client *client,
>                                 init.name);
>                         goto err_clk;
>                 }
> +
> +               /* Fetch Clock Output configuration from DT (if specified) */
> +               child_name = kasprintf(GFP_KERNEL, "OUT%d", n);
> +               np_output = of_get_child_by_name(client->dev.of_node, child_name);
> +               kfree(child_name);
> +               if (!np_output)
> +                       continue;
> +               if (!(ret || of_property_read_u32(np_output,
> +                       "idt,mode", &value))) {
> +                       vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK;
> +                       switch (value) {
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL:
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_CMOS:
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33:
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_LVDS:
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2:
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD:
> +                       case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25:
> +                               vc5->clk_out[n].clk_output_cfg0 |= value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT;
> +                               break;
> +                       default:
> +                               ret = -EINVAL;
> +                               break;
> +                       }
> +               }

Can these three things be functions that are called and passed a
vc5->clk_out[n] pointer? Then the code would be something like 

 ret = prop1_parse_and_update(vc5->clk_out[n]);
 if (ret)
 	goto err_clk;
 ret = prop2_parse_and_update(...)
 if (ret)
 	goto err_clk;


> +               if (!(ret || of_property_read_u32(np_output,
> +                       "idt,voltage-microvolts", &value))) {
> +                       vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK;
> diff --git a/include/dt-bindings/clk/versaclock.h b/include/dt-bindings/clk/versaclock.h
> new file mode 100644
> index 000000000000..30add3488713
> --- /dev/null
> +++ b/include/dt-bindings/clk/versaclock.h
> @@ -0,0 +1,13 @@
> +/* HEADER */

Any SPDX license for this in place of HEADER?

> +
> +/* This file defines field values used by the versaclock 6 family
> + * for defining output type
> + */
> +
> +#define VC5_LVPECL     0
> +#define VC5_CMOS       1
> +#define VC5_HCSL33     2
> +#define VC5_LVDS       3
> +#define VC5_CMOS2      4
> +#define VC5_CMOSD      5
> +#define VC5_HCSL25     6
diff mbox series

Patch

diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c
index 6e4b09f1f074..a1da88baf28c 100644
--- a/drivers/clk/clk-versaclock5.c
+++ b/drivers/clk/clk-versaclock5.c
@@ -24,6 +24,8 @@ 
 #include <linux/regmap.h>
 #include <linux/slab.h>
 
+#include <dt-bindings/clk/versaclock.h>
+
 /* VersaClock5 registers */
 #define VC5_OTP_CONTROL				0x00
 
@@ -89,6 +91,28 @@ 
 
 /* Clock control register for clock 1,2 */
 #define VC5_CLK_OUTPUT_CFG(idx, n)	(0x60 + ((idx) * 0x2) + (n))
+#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT	5
+#define VC5_CLK_OUTPUT_CFG0_CFG_MASK	GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT)
+
+#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL	(VC5_LVPECL)
+#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS		(VC5_CMOS)
+#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33	(VC5_HCSL33)
+#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS		(VC5_LVDS)
+#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2		(VC5_CMOS2)
+#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD		(VC5_CMOSD)
+#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25	(VC5_HCSL25)
+
+#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT	3
+#define VC5_CLK_OUTPUT_CFG0_PWR_MASK	GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_PWR_18	(0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_PWR_25	(2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_PWR_33	(3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT	0
+#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK	GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_SLEW_80	(0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_SLEW_85	(1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_SLEW_90	(2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
+#define VC5_CLK_OUTPUT_CFG0_SLEW_100	(3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
 #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF	BIT(0)
 
 #define VC5_CLK_OE_SHDN				0x68
@@ -143,6 +167,8 @@  struct vc5_hw_data {
 	u32			div_int;
 	u32			div_frc;
 	unsigned int		num;
+	unsigned int		clk_output_cfg0;
+	unsigned int		clk_output_cfg0_mask;
 };
 
 struct vc5_driver_data {
@@ -567,6 +593,16 @@  static int vc5_clk_out_prepare(struct clk_hw *hw)
 	regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
 			   VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
 			   VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
+	if (hwdata->clk_output_cfg0_mask) {
+		dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n",
+			hwdata->num, hwdata->clk_output_cfg0_mask,
+			hwdata->clk_output_cfg0);
+
+		regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
+			hwdata->clk_output_cfg0_mask,
+			hwdata->clk_output_cfg0);
+	}
+
 	return 0;
 }
 
@@ -675,6 +711,9 @@  static int vc5_probe(struct i2c_client *client,
 	struct clk_init_data init;
 	const char *parent_names[2];
 	unsigned int n, idx = 0;
+	u32 value;
+	struct device_node *np_output;
+	char *child_name;
 	int ret;
 
 	vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL);
@@ -865,6 +904,77 @@  static int vc5_probe(struct i2c_client *client,
 				init.name);
 			goto err_clk;
 		}
+
+		/* Fetch Clock Output configuration from DT (if specified) */
+		child_name = kasprintf(GFP_KERNEL, "OUT%d", n);
+		np_output = of_get_child_by_name(client->dev.of_node, child_name);
+		kfree(child_name);
+		if (!np_output)
+			continue;
+		if (!(ret || of_property_read_u32(np_output,
+			"idt,mode", &value))) {
+			vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK;
+			switch (value) {
+			case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL:
+			case VC5_CLK_OUTPUT_CFG0_CFG_CMOS:
+			case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33:
+			case VC5_CLK_OUTPUT_CFG0_CFG_LVDS:
+			case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2:
+			case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD:
+			case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25:
+				vc5->clk_out[n].clk_output_cfg0 |= value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT;
+				break;
+			default:
+				ret = -EINVAL;
+				break;
+			}
+		}
+		if (!(ret || of_property_read_u32(np_output,
+			"idt,voltage-microvolts", &value))) {
+			vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK;
+			switch (value) {
+			case 1800000:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18;
+				break;
+			case 2500000:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25;
+				break;
+			case 3300000:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33;
+				break;
+			default:
+				ret = -EINVAL;
+				break;
+			}
+		}
+		if (!(ret || of_property_read_u32(np_output,
+			"idt,slew-percent", &value))) {
+			vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK;
+			switch (value) {
+			case 80:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80;
+				break;
+			case 85:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85;
+				break;
+			case 90:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90;
+				break;
+			case 100:
+				vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_100;
+				break;
+			default:
+				ret = -EINVAL;
+				break;
+			}
+		}
+		of_node_put(np_output);
+		if (ret) {
+			dev_err(&client->dev,
+				 "Invalid clock output configuration OUT%d\n",
+				 n);
+			goto err_clk;
+		}
 	}
 
 	ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);
diff --git a/include/dt-bindings/clk/versaclock.h b/include/dt-bindings/clk/versaclock.h
new file mode 100644
index 000000000000..30add3488713
--- /dev/null
+++ b/include/dt-bindings/clk/versaclock.h
@@ -0,0 +1,13 @@ 
+/* HEADER */
+
+/* This file defines field values used by the versaclock 6 family
+ * for defining output type
+ */
+
+#define VC5_LVPECL	0
+#define VC5_CMOS	1
+#define VC5_HCSL33	2
+#define VC5_LVDS	3
+#define VC5_CMOS2	4
+#define VC5_CMOSD	5
+#define VC5_HCSL25	6