Message ID | 20230806-pll-mipi_set_rate_parent-v5-2-db4f5ca33fc3@oltmanns.dev (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | clk: sunxi-ng: Consider alternative parent rates when determining NKM clock rate | expand |
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;
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;