[PATCH 13/14] staging: clocking-wizard: Automatically set input clock rate

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Allow CLK_SET_RATE_PARENT to be optionally enabled on first divider clock.
This has the potential to set the rate of one output clock with more
precision.  On Zynq-7000 this is typically achieved using a PS FCLK as
input to the Clocking Wizard IP.

This feature is enabled by the optional device-tree property
"set-input-rate-range".  The presence of this property in the device tree
enables the feature.  This feature is only active if the
"set-parent-output" device-tree property is also present as it depends on
the feature enabled by that property.

The value of the "set-input-rate-range" allows the input rate to be
constrained to a narrower range than the hardware supports as this is
useful in some circumstances.

The input rate is then further constrained to ensure the PLL can always
lock when the input rate is changed.

Signed-off-by: James Kelly <jamespeterkelly@xxxxxxxxx>
---
 drivers/staging/clocking-wizard/TODO               |  1 -
 .../clocking-wizard/clk-xlnx-clock-wizard.c        | 89 +++++++++++++++++++++-
 drivers/staging/clocking-wizard/dt-binding.txt     |  4 +
 3 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO
index bf7435c5b67e..2a563ca67cd2 100644
--- a/drivers/staging/clocking-wizard/TODO
+++ b/drivers/staging/clocking-wizard/TODO
@@ -1,7 +1,6 @@
 TODO:
 	- review arithmetic
 	  - overflow after multiplication?
-	- implement CLK_SET_RATE_PARENT to set input clock
 	- test on 64-bit ARM and Microblaze architectures.
 	- support clk_set_phase
 
diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
index f706c3d6496e..bb64da849d9b 100644
--- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
+++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
@@ -356,6 +356,8 @@ struct clk_wzrd_clk_data {
  * @pll_locked:		Phase locked loop locked status regmap field
  * @reconfig:		Reconfiguration regmap field
  * @dev:		Handle to device
+ * @min_input:		Current minimum input rate to ensure PLL lock
+ * @max_input:		Current maximum input rate to ensure PLL lock
  * @clkout_data:	Array of output clock provider data
  */
 struct clk_wzrd {
@@ -373,6 +375,8 @@ struct clk_wzrd {
 	struct regmap_field		*pll_locked;
 	struct regmap_field		*reconfig;
 	struct device			*dev;
+	unsigned long			min_input;
+	unsigned long			max_input;
 	struct clk_wzrd_clk_data	clkout_data[0];
 };
 #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
@@ -529,6 +533,15 @@ static bool clk_wzrd_best_parent_rate(struct clk_wzrd_clk_data *cwc,
 	unsigned long inc = cwc->flags & WZRD_FLAG_FRAC ? 1 :
 			BIT(WZRD_FRAC_BITS);
 
+	/*
+	 * Apply extra constraint to ensure PLL lock when looking for the
+	 * best rate at the input of the Clocking Wizard IP.
+	 */
+	if (cwc == &cwc->cw->div_data) {
+		min_parent_rate = cwc->cw->min_input;
+		max_parent_rate = cwc->cw->max_input;
+	}
+
 	/*
 	 * Search by testing parent rates that corresponds to all possible
 	 * ratios that are within our parent rate constraints.
@@ -594,6 +607,30 @@ static int clk_wzrd_determine_rate(struct clk_hw *hw,
 	return 0;
 }
 
+static void clk_wzrd_update_input_constraint(struct clk_wzrd *cw)
+{
+	unsigned long min_pfd, max_pfd;
+	struct clk_wzrd_clk_data *out_cwc = &cw->clkout_data[0];
+	struct clk_wzrd_clk_data *pll_cwc = &cw->pll_data;
+	struct clk_wzrd_clk_data *div_cwc = &cw->div_data;
+	unsigned long current_pll_ratio = clk_wzrd_get_ratio(pll_cwc);
+	unsigned long current_div_ratio = clk_wzrd_get_ratio(div_cwc);
+
+	min_pfd = max(clk_wzrd_parent_rate(pll_cwc, out_cwc->min_parent,
+					   current_pll_ratio),
+		      pll_cwc->min_parent);
+	max_pfd = min(clk_wzrd_parent_rate(pll_cwc, out_cwc->max_parent,
+					   current_pll_ratio),
+		      pll_cwc->max_parent);
+
+	cw->min_input = max(clk_wzrd_parent_rate(div_cwc, min_pfd,
+						 current_div_ratio),
+			    div_cwc->min_parent);
+	cw->max_input = min(clk_wzrd_parent_rate(div_cwc, max_pfd,
+						 current_div_ratio),
+			    div_cwc->max_parent);
+}
+
 static bool clk_wzrd_set_ratio(struct clk_wzrd_clk_data *cwc,
 			       unsigned long parent_rate,
 			       unsigned long rate)
@@ -716,6 +753,9 @@ static int clk_wzrd_set_rate(struct clk_hw *hw, unsigned long req_rate,
 	if (!reconfigure)
 		return 0;
 
+	if (cwc == &cw->div_data || cwc == &cw->pll_data)
+		clk_wzrd_update_input_constraint(cw);
+
 	return clk_wzrd_reconfigure(cw);
 }
 
@@ -779,6 +819,18 @@ static int clk_wzrd_debug_init(struct clk_hw *hw, struct dentry *dentry)
 	if (IS_ERR(d))
 		return PTR_ERR(d);
 
+	if (cwc == &cwc->cw->div_data) {
+		d = debugfs_create_u32("clk_input_rate_min", 0444, dentry,
+				       (u32 *)&cwc->cw->min_input);
+		if (IS_ERR(d))
+			return PTR_ERR(d);
+
+		d = debugfs_create_u32("clk_input_rate_max", 0444, dentry,
+				       (u32 *)&cwc->cw->max_input);
+		if (IS_ERR(d))
+			return PTR_ERR(d);
+	}
+
 	d = debugfs_create_file_unsafe("clk_hw_flags", 0444, dentry, cwc,
 				       &clk_wzrd_flags_fops);
 	if (IS_ERR(d))
@@ -905,8 +957,8 @@ static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
 		return NOTIFY_OK;
 
 	if (ndata->clk == cw->clk_in1) {
-		max = cw->chip->max[cw->speed_grade][WZRD_RATE_FIN];
-		min = cw->chip->min[WZRD_RATE_FIN];
+		max = cw->max_input;
+		min = cw->min_input;
 	} else if (ndata->clk == cw->axi_clk) {
 		max = WZRD_ACLK_MAX_FREQ;
 		min = 0;
@@ -1025,6 +1077,28 @@ static int clk_wzrd_get_device_tree_data(struct device *dev)
 				cw->chip->max[cw->speed_grade][WZRD_RATE_VCO];
 	}
 
+	if (of_find_property(node, "set-input-rate-range", NULL)) {
+		unsigned int rate;
+
+		ret = of_property_read_u32_index(node, "set-input-rate-range",
+						 0, &rate);
+		if (ret || rate < cw->div_data.min_parent ||
+		    rate > cw->div_data.max_parent) {
+			dev_err(dev, "Invalid set input minimum rate\n");
+			return -EINVAL;
+		}
+		cw->div_data.min_parent = rate;
+
+		ret = of_property_read_u32_index(node, "set-input-rate-range",
+						 1, &rate);
+		if (ret || rate < cw->div_data.min_parent ||
+		    rate > cw->div_data.max_parent) {
+			dev_err(dev, "Invalid set input maximum rate\n");
+			return -EINVAL;
+		}
+		cw->div_data.max_parent = rate;
+	}
+
 	cw->clk_in1 = devm_clk_get(dev, WZRD_CLKNAME_IN1);
 	if (IS_ERR(cw->clk_in1)) {
 		if (cw->clk_in1 != ERR_PTR(-EPROBE_DEFER))
@@ -1055,6 +1129,8 @@ static int clk_wzrd_get_device_tree_data(struct device *dev)
 		 clk_wzrd_family_name[family],
 		 clk_wzrd_type_name[type],
 		 cw->speed_grade + 1);
+	dev_info(dev, "Input rate range min: %lu max: %lu\n",
+		 cw->div_data.min_parent, cw->div_data.max_parent);
 
 	return 0;
 }
@@ -1095,7 +1171,7 @@ static int clk_wzrd_register_ccf(struct device *dev)
 	const char *clk_div_name;
 	const char *clk_pll_name;
 
-	unsigned long pll_flags = 0, out_flags = 0;
+	unsigned long div_flags = 0, pll_flags = 0, out_flags = 0;
 	struct clk_wzrd *cw = dev_get_drvdata(dev);
 
 	ret = of_property_read_u32(dev->of_node, "set-parent-output",
@@ -1107,10 +1183,13 @@ static int clk_wzrd_register_ccf(struct device *dev)
 			pll_flags = CLK_SET_RATE_PARENT;
 	}
 
+	if (of_find_property(dev->of_node, "set-input-rate-range", NULL))
+		div_flags = pll_flags;
 	clk_div_name = kasprintf(GFP_KERNEL, "%s_div", dev_name(dev));
 	if (!clk_div_name)
 		return -ENOMEM;
-	ret = clk_wzrd_register_clk(dev, clk_div_name, WZRD_CLK_DIV, 0, 0);
+	ret = clk_wzrd_register_clk(dev, clk_div_name, WZRD_CLK_DIV, 0,
+				    div_flags);
 	if (ret)
 		goto err_free_div_name;
 
@@ -1144,6 +1223,8 @@ static int clk_wzrd_register_ccf(struct device *dev)
 		cw->clkout[i] = cw->clkout_data[i].hw.clk;
 	}
 
+	clk_wzrd_update_input_constraint(cw);
+
 	cw->clk_data.clks = cw->clkout;
 	of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
 			    &cw->clk_data);
diff --git a/drivers/staging/clocking-wizard/dt-binding.txt b/drivers/staging/clocking-wizard/dt-binding.txt
index 37133a3f2ee9..9dfc4e71b489 100644
--- a/drivers/staging/clocking-wizard/dt-binding.txt
+++ b/drivers/staging/clocking-wizard/dt-binding.txt
@@ -26,6 +26,9 @@ Optional properties:
 	1 - PLL
  - set-parent-output: Set rate on this output can set parent rates
 	Valid values are 0..n-1, where n is number of clock outputs.
+ - set-input-rate-range: Set rate on set-parent-output can set input rate.
+	Array of two 32-bit values, where 1st element is minimum input rate
+	constraint and 2nd element is maximum input rate constraint.
 
 Example:
 	clock-generator@40040000 {
@@ -40,4 +43,5 @@ Example:
 		xlnx,family = <0>;
 		xlnx,primitive = <0>;
 		set-parent-output <0>;
+		set-input-rate-range <10000000 80000000>;
 	};
-- 
2.15.1 (Apple Git-101)

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux