In case the CLK_SET_RATE_PARENT flag is set, consider using a different
parent rate when determining a new rate.
To find the best match for the requested rate, perform the following
steps for each NKM combination:
- calculate the optimal parent rate,
- find the best parent rate that the parent clock actually supports
- use that parent rate to calculate the effective rate.
In case the clk does not support setting the parent rate, use the same
algorithm as before.
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Frank Oltmanns <frank@oltmanns.dev>
---
drivers/clk/sunxi-ng/ccu_nkm.c | 45 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index f267142e58b3..ea1b77e9b57f 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -6,6 +6,7 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
+#include <linux/math.h>
#include "ccu_gate.h"
#include "ccu_nkm.h"
@@ -16,6 +17,45 @@ struct _ccu_nkm {
unsigned long m, min_m, max_m;
};
+static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw *parent_hw,
+ unsigned long *parent, unsigned long rate,
+ struct _ccu_nkm *nkm)
+{
+ unsigned long best_rate = 0, best_parent_rate = *parent, tmp_parent = *parent;
+ unsigned long best_n = 0, best_k = 0, best_m = 0;
+ unsigned long _n, _k, _m;
+
+ for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
+ for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
+ for (_m = nkm->min_m; _m <= nkm->max_m; _m++) {
+ unsigned long tmp_rate;
+
+ tmp_parent = clk_hw_round_rate(parent_hw, rate * _m / (_n * _k));
+
+ tmp_rate = tmp_parent * _n * _k / _m;
+ if (tmp_rate > rate)
+ continue;
+
+ if ((rate - tmp_rate) < (rate - best_rate)) {
+ best_rate = tmp_rate;
+ best_parent_rate = tmp_parent;
+ best_n = _n;
+ best_k = _k;
+ best_m = _m;
+ }
+ }
+ }
+ }
+
+ nkm->n = best_n;
+ nkm->k = best_k;
+ nkm->m = best_m;
+
+ *parent = best_parent_rate;
+
+ return best_rate;
+}
+
static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long rate,
struct _ccu_nkm *nkm)
{
@@ -124,7 +164,10 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate *= nkm->fixed_post_div;
- rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm);
+ if (!clk_hw_can_set_rate_parent(&nkm->common.hw))
+ rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm);
+ else
+ rate = ccu_nkm_find_best_with_parent_adj(parent_hw, parent_rate, rate, &_nkm);
if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate /= nkm->fixed_post_div;
--
2.41.0
Dne nedelja, 06. avgust 2023 ob 15:06:47 CEST je Frank Oltmanns napisal(a): > In case the CLK_SET_RATE_PARENT flag is set, consider using a different > parent rate when determining a new rate. > > To find the best match for the requested rate, perform the following > steps for each NKM combination: > - calculate the optimal parent rate, > - find the best parent rate that the parent clock actually supports > - use that parent rate to calculate the effective rate. > > In case the clk does not support setting the parent rate, use the same > algorithm as before. > > Acked-by: Maxime Ripard <mripard@kernel.org> > Signed-off-by: Frank Oltmanns <frank@oltmanns.dev> > --- > drivers/clk/sunxi-ng/ccu_nkm.c | 45 > +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 > insertions(+), 1 deletion(-) > > diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c > index f267142e58b3..ea1b77e9b57f 100644 > --- a/drivers/clk/sunxi-ng/ccu_nkm.c > +++ b/drivers/clk/sunxi-ng/ccu_nkm.c > @@ -6,6 +6,7 @@ > > #include <linux/clk-provider.h> > #include <linux/io.h> > +#include <linux/math.h> Why do you need above include? Best regards, Jernej > > #include "ccu_gate.h" > #include "ccu_nkm.h" > @@ -16,6 +17,45 @@ struct _ccu_nkm { > unsigned long m, min_m, max_m; > }; > > +static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw > *parent_hw, + unsigned long *parent, unsigned long rate, > + struct _ccu_nkm *nkm) > +{ > + unsigned long best_rate = 0, best_parent_rate = *parent, tmp_parent = > *parent; + unsigned long best_n = 0, best_k = 0, best_m = 0; > + unsigned long _n, _k, _m; > + > + for (_k = nkm->min_k; _k <= nkm->max_k; _k++) { > + for (_n = nkm->min_n; _n <= nkm->max_n; _n++) { > + for (_m = nkm->min_m; _m <= nkm->max_m; _m++) { > + unsigned long tmp_rate; > + > + tmp_parent = clk_hw_round_rate(parent_hw, rate * _m / (_n * _k)); > + > + tmp_rate = tmp_parent * _n * _k / _m; > + if (tmp_rate > rate) > + continue; > + > + if ((rate - tmp_rate) < (rate - best_rate)) { > + best_rate = tmp_rate; > + best_parent_rate = tmp_parent; > + best_n = _n; > + best_k = _k; > + best_m = _m; > + } > + } > + } > + } > + > + nkm->n = best_n; > + nkm->k = best_k; > + nkm->m = best_m; > + > + *parent = best_parent_rate; > + > + return best_rate; > +} > + > static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long > rate, struct _ccu_nkm *nkm) > { > @@ -124,7 +164,10 @@ static unsigned long ccu_nkm_round_rate(struct > ccu_mux_internal *mux, if (nkm->common.features & > CCU_FEATURE_FIXED_POSTDIV) > rate *= nkm->fixed_post_div; > > - rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); > + if (!clk_hw_can_set_rate_parent(&nkm->common.hw)) > + rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); > + else > + rate = ccu_nkm_find_best_with_parent_adj(parent_hw, parent_rate, rate, > &_nkm); > > if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) > rate /= nkm->fixed_post_div;
On 2023-08-06 at 15:32:29 +0200, Jernej Škrabec <jernej.skrabec@gmail.com> wrote: > Dne nedelja, 06. avgust 2023 ob 15:06:47 CEST je Frank Oltmanns napisal(a): >> In case the CLK_SET_RATE_PARENT flag is set, consider using a different >> parent rate when determining a new rate. >> >> To find the best match for the requested rate, perform the following >> steps for each NKM combination: >> - calculate the optimal parent rate, >> - find the best parent rate that the parent clock actually supports >> - use that parent rate to calculate the effective rate. >> >> In case the clk does not support setting the parent rate, use the same >> algorithm as before. >> >> Acked-by: Maxime Ripard <mripard@kernel.org> >> Signed-off-by: Frank Oltmanns <frank@oltmanns.dev> >> --- >> drivers/clk/sunxi-ng/ccu_nkm.c | 45 >> +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 >> insertions(+), 1 deletion(-) >> >> diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c >> index f267142e58b3..ea1b77e9b57f 100644 >> --- a/drivers/clk/sunxi-ng/ccu_nkm.c >> +++ b/drivers/clk/sunxi-ng/ccu_nkm.c >> @@ -6,6 +6,7 @@ >> >> #include <linux/clk-provider.h> >> #include <linux/io.h> >> +#include <linux/math.h> > > Why do you need above include? It's not needed. It's a leftover from an earlier version. I'll remove it in v6. Thank you for your review, Frank > > Best regards, > Jernej > >> >> #include "ccu_gate.h" >> #include "ccu_nkm.h" >> @@ -16,6 +17,45 @@ struct _ccu_nkm { >> unsigned long m, min_m, max_m; >> }; >> >> +static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw >> *parent_hw, + > unsigned long *parent, unsigned long rate, >> + > struct _ccu_nkm *nkm) >> +{ >> + unsigned long best_rate = 0, best_parent_rate = *parent, tmp_parent > = >> *parent; + unsigned long best_n = 0, best_k = 0, best_m = 0; >> + unsigned long _n, _k, _m; >> + >> + for (_k = nkm->min_k; _k <= nkm->max_k; _k++) { >> + for (_n = nkm->min_n; _n <= nkm->max_n; _n++) { >> + for (_m = nkm->min_m; _m <= nkm->max_m; _m++) > { >> + unsigned long tmp_rate; >> + >> + tmp_parent = > clk_hw_round_rate(parent_hw, rate * _m / (_n * _k)); >> + >> + tmp_rate = tmp_parent * _n * _k / > _m; >> + if (tmp_rate > rate) >> + continue; >> + >> + if ((rate - tmp_rate) < (rate - > best_rate)) { >> + best_rate = tmp_rate; >> + best_parent_rate = > tmp_parent; >> + best_n = _n; >> + best_k = _k; >> + best_m = _m; >> + } >> + } >> + } >> + } >> + >> + nkm->n = best_n; >> + nkm->k = best_k; >> + nkm->m = best_m; >> + >> + *parent = best_parent_rate; >> + >> + return best_rate; >> +} >> + >> static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long >> rate, struct _ccu_nkm *nkm) >> { >> @@ -124,7 +164,10 @@ static unsigned long ccu_nkm_round_rate(struct >> ccu_mux_internal *mux, if (nkm->common.features & >> CCU_FEATURE_FIXED_POSTDIV) >> rate *= nkm->fixed_post_div; >> >> - rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); >> + if (!clk_hw_can_set_rate_parent(&nkm->common.hw)) >> + rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); >> + else >> + rate = ccu_nkm_find_best_with_parent_adj(parent_hw, > parent_rate, rate, >> &_nkm); >> >> if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) >> rate /= nkm->fixed_post_div;
© 2016 - 2025 Red Hat, Inc.