linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq
@ 2023-01-11 19:42 Christian Marangi
  2023-01-11 19:42 ` [RESEND PATCH 2/2] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple conf Christian Marangi
  2023-01-12  3:46 ` [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Dmitry Baryshkov
  0 siblings, 2 replies; 4+ messages in thread
From: Christian Marangi @ 2023-01-11 19:42 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Konrad Dybcio, Michael Turquette,
	Stephen Boyd, linux-arm-msm, linux-clk, linux-kernel
  Cc: Christian Marangi, Robert Marko

Some RCG frequency can be reached by multiple configuration.

We currently declare multiple configuration for the same frequency but
that is not supported and always the first configuration will be taken.

These multiple configuration are needed as based on the current parent
configuration, it may be needed to use a different configuration to
reach the same frequency.

To handle this introduce 2 new macro, FM and C.

- FM is used to declare an empty freq_tbl with just the frequency and an
  array of confs to insert all the config for the provided frequency.

- C is used to declare a fre_conf where src, pre_div, m and n are
  provided.

The driver is changed to handle this special freq_tbl and select the
correct config by calculating the final rate and deciding based on the
one that is less different than the requested one.

Tested-by: Robert Marko <robimarko@gmail.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 drivers/clk/qcom/clk-rcg.h  | 14 ++++++-
 drivers/clk/qcom/clk-rcg2.c | 84 +++++++++++++++++++++++++++++++++----
 2 files changed, 88 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 01581f4d2c39..18f4f7b59f36 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -7,7 +7,17 @@
 #include <linux/clk-provider.h>
 #include "clk-regmap.h"
 
-#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n), 0, NULL }
+
+#define FM(_f, _confs) { .freq = (_f), .confs_num = ARRAY_SIZE(_confs), .confs = (_confs) }
+#define C(s, h, m, n) { (s), (2 * (h) - 1), (m), (n) }
+
+struct freq_conf {
+	u8 src;
+	u8 pre_div;
+	u16 m;
+	u16 n;
+};
 
 struct freq_tbl {
 	unsigned long freq;
@@ -15,6 +25,8 @@ struct freq_tbl {
 	u8 pre_div;
 	u16 m;
 	u16 n;
+	int confs_num;
+	const struct freq_conf *confs;
 };
 
 /**
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 76551534f10d..7d3b59ec2b50 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -209,11 +209,60 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
 	return __clk_rcg2_recalc_rate(hw, parent_rate, cfg);
 }
 
+static void
+clk_rcg2_select_conf(struct clk_hw *hw, struct freq_tbl *f_tbl,
+		     const struct freq_tbl *f, unsigned long req_rate)
+{
+	unsigned long best_rate = 0, parent_rate, rate;
+	const struct freq_conf *conf, *best_conf;
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+	struct clk_hw *p;
+	int index, i;
+
+	/* Search in each provided config the one that is near the wanted rate */
+	for (i = 0, conf = f->confs; i < f->confs_num; i++, conf++) {
+		index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
+		if (index < 0)
+			continue;
+
+		p = clk_hw_get_parent_by_index(hw, index);
+		if (!p)
+			continue;
+
+		parent_rate =  clk_hw_get_rate(p);
+		rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div);
+
+		if (rate == req_rate) {
+			best_conf = conf;
+			break;
+		}
+
+		if (abs(req_rate - rate) < abs(best_rate - rate)) {
+			best_rate = rate;
+			best_conf = conf;
+		}
+	}
+
+	/*
+	 * Very unlikely.
+	 * Force the first conf if we can't find a correct config.
+	 */
+	if (unlikely(i == f->confs_num))
+		best_conf = f->confs;
+
+	/* Apply the config */
+	f_tbl->src = best_conf->src;
+	f_tbl->pre_div = best_conf->pre_div;
+	f_tbl->m = best_conf->m;
+	f_tbl->n = best_conf->n;
+}
+
 static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
 				    struct clk_rate_request *req,
 				    enum freq_policy policy)
 {
 	unsigned long clk_flags, rate = req->rate;
+	struct freq_tbl f_tbl;
 	struct clk_hw *p;
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
 	int index;
@@ -232,7 +281,15 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
 	if (!f)
 		return -EINVAL;
 
-	index = qcom_find_src_index(hw, rcg->parent_map, f->src);
+	f_tbl = *f;
+	/*
+	 * A single freq may be reached by multiple configuration.
+	 * Try to find the bast one if we have this kind of freq_table.
+	 */
+	if (f->confs)
+		clk_rcg2_select_conf(hw, &f_tbl, f, rate);
+
+	index = qcom_find_src_index(hw, rcg->parent_map, f_tbl.src);
 	if (index < 0)
 		return index;
 
@@ -242,18 +299,18 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
 		return -EINVAL;
 
 	if (clk_flags & CLK_SET_RATE_PARENT) {
-		rate = f->freq;
-		if (f->pre_div) {
+		rate = f_tbl.freq;
+		if (f_tbl.pre_div) {
 			if (!rate)
 				rate = req->rate;
 			rate /= 2;
-			rate *= f->pre_div + 1;
+			rate *= f_tbl.pre_div + 1;
 		}
 
-		if (f->n) {
+		if (f_tbl.n) {
 			u64 tmp = rate;
-			tmp = tmp * f->n;
-			do_div(tmp, f->m);
+			tmp = tmp * f_tbl.n;
+			do_div(tmp, f_tbl.m);
 			rate = tmp;
 		}
 	} else {
@@ -261,7 +318,7 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
 	}
 	req->best_parent_hw = p;
 	req->best_parent_rate = rate;
-	req->rate = f->freq;
+	req->rate = f_tbl.freq;
 
 	return 0;
 }
@@ -357,6 +414,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
 	const struct freq_tbl *f;
+	struct freq_tbl f_tbl;
 
 	switch (policy) {
 	case FLOOR:
@@ -372,7 +430,15 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
 	if (!f)
 		return -EINVAL;
 
-	return clk_rcg2_configure(rcg, f);
+	f_tbl = *f;
+	/*
+	 * A single freq may be reached by multiple configuration.
+	 * Try to find the best one if we have this kind of freq_table.
+	 */
+	if (f->confs)
+		clk_rcg2_select_conf(hw, &f_tbl, f, rate);
+
+	return clk_rcg2_configure(rcg, &f_tbl);
 }
 
 static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RESEND PATCH 2/2] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple conf
  2023-01-11 19:42 [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Christian Marangi
@ 2023-01-11 19:42 ` Christian Marangi
  2023-01-12  3:46 ` [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Dmitry Baryshkov
  1 sibling, 0 replies; 4+ messages in thread
From: Christian Marangi @ 2023-01-11 19:42 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Konrad Dybcio, Michael Turquette,
	Stephen Boyd, linux-arm-msm, linux-clk, linux-kernel
  Cc: Christian Marangi, Robert Marko

Rework nss_port5/6 to use the new multiple configuration implementation
and correctly fix the clocks for these port under some corner case.

This is particularly relevant for device that have 2.5G or 10G port
connected to port5 or port 6 on ipq8074. As the parent are shared
across multiple port it may be required to select the correct
configuration to accomplish the desired clock. Without this patch such
port doesn't work in some specific ethernet speed as the clock will be
set to the wrong frequency as we just select the first configuration for
the related frequency instead of selecting the best one.

Tested-by: Robert Marko <robimarko@gmail.com> # ipq8074 Qnap QHora-301W
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 drivers/clk/qcom/gcc-ipq8074.c | 64 +++++++++++++++++++++++++---------
 1 file changed, 48 insertions(+), 16 deletions(-)

diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c
index 42d185fe19c8..02d04a552b78 100644
--- a/drivers/clk/qcom/gcc-ipq8074.c
+++ b/drivers/clk/qcom/gcc-ipq8074.c
@@ -1787,13 +1787,21 @@ static struct clk_regmap_div nss_port4_tx_div_clk_src = {
 	},
 };
 
+static const struct freq_conf ftbl_nss_port5_rx_clk_src_25[] = {
+	C(P_UNIPHY1_RX, 12.5, 0, 0),
+	C(P_UNIPHY0_RX, 5, 0, 0),
+};
+
+static const struct freq_conf ftbl_nss_port5_rx_clk_src_125[] = {
+	C(P_UNIPHY1_RX, 2.5, 0, 0),
+	C(P_UNIPHY0_RX, 1, 0, 0),
+};
+
 static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = {
 	F(19200000, P_XO, 1, 0, 0),
-	F(25000000, P_UNIPHY1_RX, 12.5, 0, 0),
-	F(25000000, P_UNIPHY0_RX, 5, 0, 0),
+	FM(25000000, ftbl_nss_port5_rx_clk_src_25),
 	F(78125000, P_UNIPHY1_RX, 4, 0, 0),
-	F(125000000, P_UNIPHY1_RX, 2.5, 0, 0),
-	F(125000000, P_UNIPHY0_RX, 1, 0, 0),
+	FM(125000000, ftbl_nss_port5_rx_clk_src_125),
 	F(156250000, P_UNIPHY1_RX, 2, 0, 0),
 	F(312500000, P_UNIPHY1_RX, 1, 0, 0),
 	{ }
@@ -1829,13 +1837,21 @@ static struct clk_regmap_div nss_port5_rx_div_clk_src = {
 	},
 };
 
+static struct freq_conf ftbl_nss_port5_tx_clk_src_25[] = {
+	C(P_UNIPHY1_TX, 12.5, 0, 0),
+	C(P_UNIPHY0_TX, 5, 0, 0),
+};
+
+static struct freq_conf ftbl_nss_port5_tx_clk_src_125[] = {
+	C(P_UNIPHY1_TX, 2.5, 0, 0),
+	C(P_UNIPHY0_TX, 1, 0, 0),
+};
+
 static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = {
 	F(19200000, P_XO, 1, 0, 0),
-	F(25000000, P_UNIPHY1_TX, 12.5, 0, 0),
-	F(25000000, P_UNIPHY0_TX, 5, 0, 0),
+	FM(25000000, ftbl_nss_port5_tx_clk_src_25),
 	F(78125000, P_UNIPHY1_TX, 4, 0, 0),
-	F(125000000, P_UNIPHY1_TX, 2.5, 0, 0),
-	F(125000000, P_UNIPHY0_TX, 1, 0, 0),
+	FM(125000000, ftbl_nss_port5_tx_clk_src_125),
 	F(156250000, P_UNIPHY1_TX, 2, 0, 0),
 	F(312500000, P_UNIPHY1_TX, 1, 0, 0),
 	{ }
@@ -1871,13 +1887,21 @@ static struct clk_regmap_div nss_port5_tx_div_clk_src = {
 	},
 };
 
+static struct freq_conf ftbl_nss_port6_rx_clk_src_25[] = {
+	C(P_UNIPHY2_RX, 5, 0, 0),
+	C(P_UNIPHY2_RX, 12.5, 0, 0),
+};
+
+static struct freq_conf ftbl_nss_port6_rx_clk_src_125[] = {
+	C(P_UNIPHY2_RX, 1, 0, 0),
+	C(P_UNIPHY2_RX, 2.5, 0, 0),
+};
+
 static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = {
 	F(19200000, P_XO, 1, 0, 0),
-	F(25000000, P_UNIPHY2_RX, 5, 0, 0),
-	F(25000000, P_UNIPHY2_RX, 12.5, 0, 0),
+	FM(25000000, ftbl_nss_port6_rx_clk_src_25),
 	F(78125000, P_UNIPHY2_RX, 4, 0, 0),
-	F(125000000, P_UNIPHY2_RX, 1, 0, 0),
-	F(125000000, P_UNIPHY2_RX, 2.5, 0, 0),
+	FM(125000000, ftbl_nss_port6_rx_clk_src_125),
 	F(156250000, P_UNIPHY2_RX, 2, 0, 0),
 	F(312500000, P_UNIPHY2_RX, 1, 0, 0),
 	{ }
@@ -1913,13 +1937,21 @@ static struct clk_regmap_div nss_port6_rx_div_clk_src = {
 	},
 };
 
+static struct freq_conf ftbl_nss_port6_tx_clk_src_25[] = {
+	C(P_UNIPHY2_TX, 5, 0, 0),
+	C(P_UNIPHY2_TX, 12.5, 0, 0),
+};
+
+static struct freq_conf ftbl_nss_port6_tx_clk_src_125[] = {
+	C(P_UNIPHY2_TX, 1, 0, 0),
+	C(P_UNIPHY2_TX, 2.5, 0, 0),
+};
+
 static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = {
 	F(19200000, P_XO, 1, 0, 0),
-	F(25000000, P_UNIPHY2_TX, 5, 0, 0),
-	F(25000000, P_UNIPHY2_TX, 12.5, 0, 0),
+	FM(25000000, ftbl_nss_port6_tx_clk_src_25),
 	F(78125000, P_UNIPHY2_TX, 4, 0, 0),
-	F(125000000, P_UNIPHY2_TX, 1, 0, 0),
-	F(125000000, P_UNIPHY2_TX, 2.5, 0, 0),
+	FM(125000000, ftbl_nss_port6_tx_clk_src_125),
 	F(156250000, P_UNIPHY2_TX, 2, 0, 0),
 	F(312500000, P_UNIPHY2_TX, 1, 0, 0),
 	{ }
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq
  2023-01-11 19:42 [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Christian Marangi
  2023-01-11 19:42 ` [RESEND PATCH 2/2] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple conf Christian Marangi
@ 2023-01-12  3:46 ` Dmitry Baryshkov
  2023-01-13 13:50   ` Christian Marangi
  1 sibling, 1 reply; 4+ messages in thread
From: Dmitry Baryshkov @ 2023-01-12  3:46 UTC (permalink / raw)
  To: Christian Marangi, Andy Gross, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd, linux-arm-msm, linux-clk,
	linux-kernel
  Cc: Robert Marko

Hi,

On 11/01/2023 21:42, Christian Marangi wrote:
> Some RCG frequency can be reached by multiple configuration.
> 
> We currently declare multiple configuration for the same frequency but
> that is not supported and always the first configuration will be taken.
> 
> These multiple configuration are needed as based on the current parent
> configuration, it may be needed to use a different configuration to
> reach the same frequency.
> 
> To handle this introduce 2 new macro, FM and C.
> 
> - FM is used to declare an empty freq_tbl with just the frequency and an
>    array of confs to insert all the config for the provided frequency.
> 
> - C is used to declare a fre_conf where src, pre_div, m and n are
>    provided.
> 
> The driver is changed to handle this special freq_tbl and select the
> correct config by calculating the final rate and deciding based on the
> one that is less different than the requested one.

I gave this a thought.

First some generic thought about RCG2 risen by your patch. I'll talk 
about the CEIL functions, but the same applies to FLOOR ones.

The clk_rcg2_determine_rate() / freq_tbl_determine_rate() determines the 
best supported rate and and selects a best parent. Good. Then CCF will 
pass the determined target rate, parent index and parent rate either to 
clk_rcg2_set_rate() or to the clk_rcg2_set_rate_and_parent() depending 
on whether it determines that the parent should be switched or not. So 
far so good.

However then the __clk_rcg2_set_rate() will perform the same search 
again, taking care of FLOOR or CEIL, completely ignoring the fact that 
rate passed is already known to be the supported rate, so we can just 
look for it and ignoring the provided parent (or a note from CCF that 
the parent should remain the same).

This worked correctly in the single-possible-configuration case, however 
it IMHO becames fragile in the multiple-possible-configuration case. 
Consider CCF switching the parent rate during the clk_set_rate() call. 
Then the second lookup might end up selecting _different_ parent/mnd 
configuration.

Thus said, I'd suggest first changing clk_rcg2_set_rate/and_parent(). 
Make it look up the exact configuration without the rounding (it is 
unnecessary anyway). This would require something like 
qcom_find_freq_exact(). Or just loop over the table in 
__clk_rcg2_set_rate().

Then for your case make it actually use the provided parent index (or 
the current parent in the .set_rate() case). This will make sure that we 
follow CCF decisions instead of making them on our own.

Finally for your usecase to work correcly you need to select correct 
rate+parent in the .determine_rate callback.

We know that the freq_table is sorted already. Add a loop to 
_freq_tbl_determine_rate() policy switchcase to check next table entries 
determing if their parent rate is better than the parent rate of the 
selected entry.

> 
> Tested-by: Robert Marko <robimarko@gmail.com>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> ---
>   drivers/clk/qcom/clk-rcg.h  | 14 ++++++-
>   drivers/clk/qcom/clk-rcg2.c | 84 +++++++++++++++++++++++++++++++++----
>   2 files changed, 88 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
> index 01581f4d2c39..18f4f7b59f36 100644
> --- a/drivers/clk/qcom/clk-rcg.h
> +++ b/drivers/clk/qcom/clk-rcg.h
> @@ -7,7 +7,17 @@
>   #include <linux/clk-provider.h>
>   #include "clk-regmap.h"
>   
> -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
> +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n), 0, NULL }
> +
> +#define FM(_f, _confs) { .freq = (_f), .confs_num = ARRAY_SIZE(_confs), .confs = (_confs) }
> +#define C(s, h, m, n) { (s), (2 * (h) - 1), (m), (n) }
> +
> +struct freq_conf {
> +	u8 src;
> +	u8 pre_div;
> +	u16 m;
> +	u16 n;
> +};
>   
>   struct freq_tbl {
>   	unsigned long freq;
> @@ -15,6 +25,8 @@ struct freq_tbl {
>   	u8 pre_div;
>   	u16 m;
>   	u16 n;
> +	int confs_num;
> +	const struct freq_conf *confs;
>   };
>   
>   /**
> diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
> index 76551534f10d..7d3b59ec2b50 100644
> --- a/drivers/clk/qcom/clk-rcg2.c
> +++ b/drivers/clk/qcom/clk-rcg2.c
> @@ -209,11 +209,60 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
>   	return __clk_rcg2_recalc_rate(hw, parent_rate, cfg);
>   }
>   
> +static void
> +clk_rcg2_select_conf(struct clk_hw *hw, struct freq_tbl *f_tbl,
> +		     const struct freq_tbl *f, unsigned long req_rate)
> +{
> +	unsigned long best_rate = 0, parent_rate, rate;
> +	const struct freq_conf *conf, *best_conf;
> +	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
> +	struct clk_hw *p;
> +	int index, i;
> +
> +	/* Search in each provided config the one that is near the wanted rate */
> +	for (i = 0, conf = f->confs; i < f->confs_num; i++, conf++) {
> +		index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
> +		if (index < 0)
> +			continue;
> +
> +		p = clk_hw_get_parent_by_index(hw, index);
> +		if (!p)
> +			continue;
> +
> +		parent_rate =  clk_hw_get_rate(p);
> +		rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div);
> +
> +		if (rate == req_rate) {
> +			best_conf = conf;
> +			break;
> +		}
> +
> +		if (abs(req_rate - rate) < abs(best_rate - rate)) {
> +			best_rate = rate;
> +			best_conf = conf;
> +		}
> +	}
> +
> +	/*
> +	 * Very unlikely.
> +	 * Force the first conf if we can't find a correct config.
> +	 */
> +	if (unlikely(i == f->confs_num))
> +		best_conf = f->confs;
> +
> +	/* Apply the config */
> +	f_tbl->src = best_conf->src;
> +	f_tbl->pre_div = best_conf->pre_div;
> +	f_tbl->m = best_conf->m;
> +	f_tbl->n = best_conf->n;
> +}
> +
>   static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
>   				    struct clk_rate_request *req,
>   				    enum freq_policy policy)
>   {
>   	unsigned long clk_flags, rate = req->rate;
> +	struct freq_tbl f_tbl;
>   	struct clk_hw *p;
>   	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
>   	int index;
> @@ -232,7 +281,15 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
>   	if (!f)
>   		return -EINVAL;
>   
> -	index = qcom_find_src_index(hw, rcg->parent_map, f->src);
> +	f_tbl = *f;
> +	/*
> +	 * A single freq may be reached by multiple configuration.
> +	 * Try to find the bast one if we have this kind of freq_table.
> +	 */
> +	if (f->confs)
> +		clk_rcg2_select_conf(hw, &f_tbl, f, rate);
> +
> +	index = qcom_find_src_index(hw, rcg->parent_map, f_tbl.src);
>   	if (index < 0)
>   		return index;
>   
> @@ -242,18 +299,18 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
>   		return -EINVAL;
>   
>   	if (clk_flags & CLK_SET_RATE_PARENT) {
> -		rate = f->freq;
> -		if (f->pre_div) {
> +		rate = f_tbl.freq;
> +		if (f_tbl.pre_div) {
>   			if (!rate)
>   				rate = req->rate;
>   			rate /= 2;
> -			rate *= f->pre_div + 1;
> +			rate *= f_tbl.pre_div + 1;
>   		}
>   
> -		if (f->n) {
> +		if (f_tbl.n) {
>   			u64 tmp = rate;
> -			tmp = tmp * f->n;
> -			do_div(tmp, f->m);
> +			tmp = tmp * f_tbl.n;
> +			do_div(tmp, f_tbl.m);
>   			rate = tmp;
>   		}
>   	} else {
> @@ -261,7 +318,7 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
>   	}
>   	req->best_parent_hw = p;
>   	req->best_parent_rate = rate;
> -	req->rate = f->freq;
> +	req->rate = f_tbl.freq;
>   
>   	return 0;
>   }
> @@ -357,6 +414,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
>   {
>   	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
>   	const struct freq_tbl *f;
> +	struct freq_tbl f_tbl;
>   
>   	switch (policy) {
>   	case FLOOR:
> @@ -372,7 +430,15 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
>   	if (!f)
>   		return -EINVAL;
>   
> -	return clk_rcg2_configure(rcg, f);
> +	f_tbl = *f;
> +	/*
> +	 * A single freq may be reached by multiple configuration.
> +	 * Try to find the best one if we have this kind of freq_table.
> +	 */
> +	if (f->confs)
> +		clk_rcg2_select_conf(hw, &f_tbl, f, rate);
> +
> +	return clk_rcg2_configure(rcg, &f_tbl);
>   }
>   
>   static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq
  2023-01-12  3:46 ` [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Dmitry Baryshkov
@ 2023-01-13 13:50   ` Christian Marangi
  0 siblings, 0 replies; 4+ messages in thread
From: Christian Marangi @ 2023-01-13 13:50 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Bjorn Andersson, Konrad Dybcio, Michael Turquette,
	Stephen Boyd, linux-arm-msm, linux-clk, linux-kernel,
	Robert Marko

On Thu, Jan 12, 2023 at 05:46:09AM +0200, Dmitry Baryshkov wrote:
> Hi,
> 
> On 11/01/2023 21:42, Christian Marangi wrote:
> > Some RCG frequency can be reached by multiple configuration.
> > 
> > We currently declare multiple configuration for the same frequency but
> > that is not supported and always the first configuration will be taken.
> > 
> > These multiple configuration are needed as based on the current parent
> > configuration, it may be needed to use a different configuration to
> > reach the same frequency.
> > 
> > To handle this introduce 2 new macro, FM and C.
> > 
> > - FM is used to declare an empty freq_tbl with just the frequency and an
> >    array of confs to insert all the config for the provided frequency.
> > 
> > - C is used to declare a fre_conf where src, pre_div, m and n are
> >    provided.
> > 
> > The driver is changed to handle this special freq_tbl and select the
> > correct config by calculating the final rate and deciding based on the
> > one that is less different than the requested one.
> 
> I gave this a thought.
> 
> First some generic thought about RCG2 risen by your patch. I'll talk about
> the CEIL functions, but the same applies to FLOOR ones.
> 
> The clk_rcg2_determine_rate() / freq_tbl_determine_rate() determines the
> best supported rate and and selects a best parent. Good. Then CCF will pass
> the determined target rate, parent index and parent rate either to
> clk_rcg2_set_rate() or to the clk_rcg2_set_rate_and_parent() depending on
> whether it determines that the parent should be switched or not. So far so
> good.
> 
> However then the __clk_rcg2_set_rate() will perform the same search again,
> taking care of FLOOR or CEIL, completely ignoring the fact that rate passed
> is already known to be the supported rate, so we can just look for it and
> ignoring the provided parent (or a note from CCF that the parent should
> remain the same).
>

Interesting analysis and looks to be something to fix and improve for
sure. Will take some time but happy to fix. Also in theory on a system
we should have all kind of clk variant due to the amount of clocks
modern so a simple clk_summary diff should catch regression quite
easily.

> This worked correctly in the single-possible-configuration case, however it
> IMHO becames fragile in the multiple-possible-configuration case. Consider
> CCF switching the parent rate during the clk_set_rate() call. Then the
> second lookup might end up selecting _different_ parent/mnd configuration.
> 
> Thus said, I'd suggest first changing clk_rcg2_set_rate/and_parent(). Make
> it look up the exact configuration without the rounding (it is unnecessary
> anyway). This would require something like qcom_find_freq_exact(). Or just
> loop over the table in __clk_rcg2_set_rate().

Just to make sure I understood the logic. Since it's all driven by
determine_rate, in theory we should never do searching on the set rate
and just execute what it's said... With the following idea why the
set_floor_rate needed a __clk_rcg2_set_rate variant with the FLOOR
policy? If everything is decided by determine_rate then the set_rate
function should be able to just use a TO-IMPLEMENT find_freq_exact
function. Am I missing something?

> 
> Then for your case make it actually use the provided parent index (or the
> current parent in the .set_rate() case). This will make sure that we follow
> CCF decisions instead of making them on our own.
> 
> Finally for your usecase to work correcly you need to select correct
> rate+parent in the .determine_rate callback.
> 
> We know that the freq_table is sorted already. Add a loop to
> _freq_tbl_determine_rate() policy switchcase to check next table entries
> determing if their parent rate is better than the parent rate of the
> selected entry.

With this you mean that we should continue putting duplicate entry in
the freq table and always check the next element for alternative conf?

This was the qcom way and honestly I find it pretty confusing than a
well organized table with only unique entry and a way to directly check
if we are in a multi-conf scenario. (also lets not trust devs with
having the freq table sorted)

> 
> > 
> > Tested-by: Robert Marko <robimarko@gmail.com>
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > ---
> >   drivers/clk/qcom/clk-rcg.h  | 14 ++++++-
> >   drivers/clk/qcom/clk-rcg2.c | 84 +++++++++++++++++++++++++++++++++----
> >   2 files changed, 88 insertions(+), 10 deletions(-)
> > 
> > diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
> > index 01581f4d2c39..18f4f7b59f36 100644
> > --- a/drivers/clk/qcom/clk-rcg.h
> > +++ b/drivers/clk/qcom/clk-rcg.h
> > @@ -7,7 +7,17 @@
> >   #include <linux/clk-provider.h>
> >   #include "clk-regmap.h"
> > -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
> > +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n), 0, NULL }
> > +
> > +#define FM(_f, _confs) { .freq = (_f), .confs_num = ARRAY_SIZE(_confs), .confs = (_confs) }
> > +#define C(s, h, m, n) { (s), (2 * (h) - 1), (m), (n) }
> > +
> > +struct freq_conf {
> > +	u8 src;
> > +	u8 pre_div;
> > +	u16 m;
> > +	u16 n;
> > +};
> >   struct freq_tbl {
> >   	unsigned long freq;
> > @@ -15,6 +25,8 @@ struct freq_tbl {
> >   	u8 pre_div;
> >   	u16 m;
> >   	u16 n;
> > +	int confs_num;
> > +	const struct freq_conf *confs;
> >   };
> >   /**
> > diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
> > index 76551534f10d..7d3b59ec2b50 100644
> > --- a/drivers/clk/qcom/clk-rcg2.c
> > +++ b/drivers/clk/qcom/clk-rcg2.c
> > @@ -209,11 +209,60 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> >   	return __clk_rcg2_recalc_rate(hw, parent_rate, cfg);
> >   }
> > +static void
> > +clk_rcg2_select_conf(struct clk_hw *hw, struct freq_tbl *f_tbl,
> > +		     const struct freq_tbl *f, unsigned long req_rate)
> > +{
> > +	unsigned long best_rate = 0, parent_rate, rate;
> > +	const struct freq_conf *conf, *best_conf;
> > +	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
> > +	struct clk_hw *p;
> > +	int index, i;
> > +
> > +	/* Search in each provided config the one that is near the wanted rate */
> > +	for (i = 0, conf = f->confs; i < f->confs_num; i++, conf++) {
> > +		index = qcom_find_src_index(hw, rcg->parent_map, conf->src);
> > +		if (index < 0)
> > +			continue;
> > +
> > +		p = clk_hw_get_parent_by_index(hw, index);
> > +		if (!p)
> > +			continue;
> > +
> > +		parent_rate =  clk_hw_get_rate(p);
> > +		rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div);
> > +
> > +		if (rate == req_rate) {
> > +			best_conf = conf;
> > +			break;
> > +		}
> > +
> > +		if (abs(req_rate - rate) < abs(best_rate - rate)) {
> > +			best_rate = rate;
> > +			best_conf = conf;
> > +		}
> > +	}
> > +
> > +	/*
> > +	 * Very unlikely.
> > +	 * Force the first conf if we can't find a correct config.
> > +	 */
> > +	if (unlikely(i == f->confs_num))
> > +		best_conf = f->confs;
> > +
> > +	/* Apply the config */
> > +	f_tbl->src = best_conf->src;
> > +	f_tbl->pre_div = best_conf->pre_div;
> > +	f_tbl->m = best_conf->m;
> > +	f_tbl->n = best_conf->n;
> > +}
> > +
> >   static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
> >   				    struct clk_rate_request *req,
> >   				    enum freq_policy policy)
> >   {
> >   	unsigned long clk_flags, rate = req->rate;
> > +	struct freq_tbl f_tbl;
> >   	struct clk_hw *p;
> >   	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
> >   	int index;
> > @@ -232,7 +281,15 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
> >   	if (!f)
> >   		return -EINVAL;
> > -	index = qcom_find_src_index(hw, rcg->parent_map, f->src);
> > +	f_tbl = *f;
> > +	/*
> > +	 * A single freq may be reached by multiple configuration.
> > +	 * Try to find the bast one if we have this kind of freq_table.
> > +	 */
> > +	if (f->confs)
> > +		clk_rcg2_select_conf(hw, &f_tbl, f, rate);
> > +
> > +	index = qcom_find_src_index(hw, rcg->parent_map, f_tbl.src);
> >   	if (index < 0)
> >   		return index;
> > @@ -242,18 +299,18 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
> >   		return -EINVAL;
> >   	if (clk_flags & CLK_SET_RATE_PARENT) {
> > -		rate = f->freq;
> > -		if (f->pre_div) {
> > +		rate = f_tbl.freq;
> > +		if (f_tbl.pre_div) {
> >   			if (!rate)
> >   				rate = req->rate;
> >   			rate /= 2;
> > -			rate *= f->pre_div + 1;
> > +			rate *= f_tbl.pre_div + 1;
> >   		}
> > -		if (f->n) {
> > +		if (f_tbl.n) {
> >   			u64 tmp = rate;
> > -			tmp = tmp * f->n;
> > -			do_div(tmp, f->m);
> > +			tmp = tmp * f_tbl.n;
> > +			do_div(tmp, f_tbl.m);
> >   			rate = tmp;
> >   		}
> >   	} else {
> > @@ -261,7 +318,7 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
> >   	}
> >   	req->best_parent_hw = p;
> >   	req->best_parent_rate = rate;
> > -	req->rate = f->freq;
> > +	req->rate = f_tbl.freq;
> >   	return 0;
> >   }
> > @@ -357,6 +414,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
> >   {
> >   	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
> >   	const struct freq_tbl *f;
> > +	struct freq_tbl f_tbl;
> >   	switch (policy) {
> >   	case FLOOR:
> > @@ -372,7 +430,15 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
> >   	if (!f)
> >   		return -EINVAL;
> > -	return clk_rcg2_configure(rcg, f);
> > +	f_tbl = *f;
> > +	/*
> > +	 * A single freq may be reached by multiple configuration.
> > +	 * Try to find the best one if we have this kind of freq_table.
> > +	 */
> > +	if (f->confs)
> > +		clk_rcg2_select_conf(hw, &f_tbl, f, rate);
> > +
> > +	return clk_rcg2_configure(rcg, &f_tbl);
> >   }
> >   static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
> 
> -- 
> With best wishes
> Dmitry
> 

-- 
	Ansuel

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-01-13 13:54 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-11 19:42 [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Christian Marangi
2023-01-11 19:42 ` [RESEND PATCH 2/2] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple conf Christian Marangi
2023-01-12  3:46 ` [RESEND PATCH 1/2] clk: qcom: clk-rcg2: introduce support for multiple conf for same freq Dmitry Baryshkov
2023-01-13 13:50   ` Christian Marangi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).