Allwinner clock changes for 6.6 part 2

- Parameter name correction for ccu_nkm_round_rate()
 - Implement CLK_SET_RATE_PARENT for NKM clocks, i.e. consider alternative
   parent rates when determining clock rates
 - Set CLK_SET_RATE_PARENT for A64 pll-mipi
 - Support finding closest (as opposed to closest but not higher) clock
   rate for NM, NKM, mux and div type clocks, as use it for A64
   pll-video0
 - Prefer current parent rate if able to generate ideal clock rate for
   NKM clocks
 -----BEGIN PGP SIGNATURE-----
 
 iQJCBAABCgAsFiEE2nN1m/hhnkhOWjtHOJpUIZwPJDAFAmTY1aQOHHdlbnNAY3Np
 ZS5vcmcACgkQOJpUIZwPJDA7JA/+KUOF8mI4PClCsjDq0y+d0GPbW0b0cFVkAlY7
 gYRSfzgKWiQEuls87cpUiVskKm5pADfvpDkB4eky7ijethwhwzjjEl7IKfDyzhwr
 XmjacWE+OVqMqwGObHorgz2bJWaeiPuh2ybh/MNt7taa7TgLHbx/atP+I8V69ovR
 bljHErTG21sRLsfpDxu2hVJaWVyY3tiibEm2751JoSq/xf/Lei07hSKuh3aVsJdm
 /j5YnWkOSMaICeYSxdhDprhBz1yMnSfVvOviTazXhSkkpTkJOz0eRu3dBN6xxrLP
 Y+mOxbt4xo/O2qflo0hyaTwzf/VNmgGqji2rKsCTdt2F2KwWpd8ropr+ssVKbO3B
 IqsHXGhiXrq7/LIxJ7AZtTnuY17f2VVx/5KiryngM6Got6zZPOqApJIoetckMXeZ
 TPGvcVWkDOXcCCArmgPEwIr9S+h15SJ0bc5F79go1e93b4BWTVDmCLQwVzeNdPLr
 Mrlnj3xGRh+adbx8BQYIkDo0XlO40DK5eak/bAZj7HmuIbMQNSv9A1BK6diFqqeF
 alao/io+6c4ezjJSaLWqzx7+ix4Gfkv0z8Ux0cSQj9+f9XQIITJmlRmsZvGFdkbC
 UaAJhCN7+Eg/LVhp1ifLTmcsEP04aCXuPPCfZ9NNfYj9f+aqE/5gF2XlE1KWbT+J
 lp0Edw4=
 =u5sg
 -----END PGP SIGNATURE-----

Merge tag 'sunxi-clk-for-6.6-2' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into clk-allwinner

Pull Allwinner clk driver changes from Chen-Yu Tsai:

 - Parameter name correction for ccu_nkm_round_rate()
 - Implement CLK_SET_RATE_PARENT for NKM clocks, i.e. consider alternative
   parent rates when determining clock rates
 - Set CLK_SET_RATE_PARENT for A64 pll-mipi
 - Support finding closest (as opposed to closest but not higher) clock
   rate for NM, NKM, mux and div type clocks, as use it for A64
   pll-video0
 - Prefer current parent rate if able to generate ideal clock rate for
   NKM clocks

* tag 'sunxi-clk-for-6.6-2' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux:
  clk: sunxi-ng: nkm: Prefer current parent rate
  clk: sunxi-ng: a64: select closest rate for pll-video0
  clk: sunxi-ng: div: Support finding closest rate
  clk: sunxi-ng: mux: Support finding closest rate
  clk: sunxi-ng: nkm: Support finding closest rate
  clk: sunxi-ng: nm: Support finding closest rate
  clk: sunxi-ng: Add helper function to find closest rate
  clk: sunxi-ng: Add feature to find closest rate
  clk: sunxi-ng: a64: allow pll-mipi to set parent's rate
  clk: sunxi-ng: nkm: consider alternative parent rates when determining rate
  clk: sunxi-ng: nkm: Use correct parameter name for parent HW
This commit is contained in:
Stephen Boyd 2023-08-14 17:29:38 -07:00
commit 364a609dc3
9 changed files with 204 additions and 54 deletions

View File

@ -68,7 +68,7 @@ static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
BIT(28), /* lock */
CLK_SET_RATE_UNGATE);
static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX(pll_video0_clk, "pll-video0",
static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_CLOSEST(pll_video0_clk, "pll-video0",
"osc24M", 0x010,
192000000, /* Minimum rate */
1008000000, /* Maximum rate */
@ -179,7 +179,9 @@ static struct ccu_nkm pll_mipi_clk = {
.common = {
.reg = 0x040,
.hw.init = CLK_HW_INIT("pll-mipi", "pll-video0",
&ccu_nkm_ops, CLK_SET_RATE_UNGATE),
&ccu_nkm_ops,
CLK_SET_RATE_UNGATE | CLK_SET_RATE_PARENT),
.features = CCU_FEATURE_CLOSEST_RATE,
},
};
@ -536,25 +538,18 @@ static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents,
static const char * const tcon0_parents[] = { "pll-mipi", "pll-video0-2x" };
static const u8 tcon0_table[] = { 0, 2, };
static SUNXI_CCU_MUX_TABLE_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents,
tcon0_table, 0x118, 24, 3, BIT(31),
CLK_SET_RATE_PARENT |
CLK_SET_RATE_NO_REPARENT);
static SUNXI_CCU_MUX_TABLE_WITH_GATE_CLOSEST(tcon0_clk, "tcon0", tcon0_parents,
tcon0_table, 0x118, 24, 3, BIT(31),
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
static const char * const tcon1_parents[] = { "pll-video0", "pll-video1" };
static const u8 tcon1_table[] = { 0, 2, };
static struct ccu_div tcon1_clk = {
.enable = BIT(31),
.div = _SUNXI_CCU_DIV(0, 4),
.mux = _SUNXI_CCU_MUX_TABLE(24, 2, tcon1_table),
.common = {
.reg = 0x11c,
.hw.init = CLK_HW_INIT_PARENTS("tcon1",
tcon1_parents,
&ccu_div_ops,
CLK_SET_RATE_PARENT),
},
};
static SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(tcon1_clk, "tcon1", tcon1_parents,
tcon1_table, 0x11c,
0, 4, /* M */
24, 2, /* mux */
BIT(31), /* gate */
CLK_SET_RATE_PARENT);
static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" };
static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents,
@ -584,8 +579,8 @@ static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
0x144, BIT(31), 0);
static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" };
static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents,
0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_M_WITH_MUX_GATE_CLOSEST(hdmi_clk, "hdmi", hdmi_parents,
0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M",
0x154, BIT(31), 0);
@ -597,9 +592,9 @@ static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-periph0" };
static const u8 dsi_dphy_table[] = { 0, 2, };
static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy",
dsi_dphy_parents, dsi_dphy_table,
0x168, 0, 4, 8, 2, BIT(15), CLK_SET_RATE_PARENT);
static SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(dsi_dphy_clk, "dsi-dphy",
dsi_dphy_parents, dsi_dphy_table,
0x168, 0, 4, 8, 2, BIT(15), CLK_SET_RATE_PARENT);
static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT);

View File

@ -39,6 +39,18 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
}
EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU);
bool ccu_is_better_rate(struct ccu_common *common,
unsigned long target_rate,
unsigned long current_rate,
unsigned long best_rate)
{
if (common->features & CCU_FEATURE_CLOSEST_RATE)
return abs(current_rate - target_rate) < abs(best_rate - target_rate);
return current_rate <= target_rate && current_rate > best_rate;
}
EXPORT_SYMBOL_NS_GPL(ccu_is_better_rate, SUNXI_CCU);
/*
* This clock notifier is called when the frequency of a PLL clock is
* changed. In common PLL designs, changes to the dividers take effect

View File

@ -18,6 +18,7 @@
#define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6)
#define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7)
#define CCU_FEATURE_KEY_FIELD BIT(8)
#define CCU_FEATURE_CLOSEST_RATE BIT(9)
/* MMC timing mode switch bit */
#define CCU_MMC_NEW_TIMING_MODE BIT(30)
@ -52,6 +53,11 @@ struct sunxi_ccu_desc {
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock);
bool ccu_is_better_rate(struct ccu_common *common,
unsigned long target_rate,
unsigned long current_rate,
unsigned long best_rate);
struct ccu_pll_nb {
struct notifier_block clk_nb;
struct ccu_common *common;

View File

@ -143,6 +143,26 @@ struct ccu_div {
}, \
}
#define SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(_struct, _name, \
_parents, _table, \
_reg, \
_mshift, _mwidth, \
_muxshift, _muxwidth, \
_gate, _flags) \
struct ccu_div _struct = { \
.enable = _gate, \
.div = _SUNXI_CCU_DIV_FLAGS(_mshift, _mwidth, CLK_DIVIDER_ROUND_CLOSEST), \
.mux = _SUNXI_CCU_MUX_TABLE(_muxshift, _muxwidth, _table), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_div_ops, \
_flags), \
.features = CCU_FEATURE_CLOSEST_RATE, \
}, \
}
#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
_mshift, _mwidth, _muxshift, _muxwidth, \
_gate, _flags) \
@ -152,6 +172,16 @@ struct ccu_div {
_muxshift, _muxwidth, \
_gate, _flags)
#define SUNXI_CCU_M_WITH_MUX_GATE_CLOSEST(_struct, _name, _parents, \
_reg, _mshift, _mwidth, \
_muxshift, _muxwidth, \
_gate, _flags) \
SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(_struct, _name, \
_parents, NULL, \
_reg, _mshift, _mwidth, \
_muxshift, _muxwidth, \
_gate, _flags)
#define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg, \
_mshift, _mwidth, _muxshift, _muxwidth, \
_flags) \

View File

@ -139,7 +139,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
goto out;
}
if ((req->rate - tmp_rate) < (req->rate - best_rate)) {
if (ccu_is_better_rate(common, req->rate, tmp_rate, best_rate)) {
best_rate = tmp_rate;
best_parent_rate = parent_rate;
best_parent = parent;
@ -242,6 +242,17 @@ static int ccu_mux_set_parent(struct clk_hw *hw, u8 index)
return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
}
static int ccu_mux_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct ccu_mux *cm = hw_to_ccu_mux(hw);
if (cm->common.features & CCU_FEATURE_CLOSEST_RATE)
return clk_mux_determine_rate_flags(hw, req, CLK_MUX_ROUND_CLOSEST);
return clk_mux_determine_rate_flags(hw, req, 0);
}
static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@ -259,7 +270,7 @@ const struct clk_ops ccu_mux_ops = {
.get_parent = ccu_mux_get_parent,
.set_parent = ccu_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
.determine_rate = ccu_mux_determine_rate,
.recalc_rate = ccu_mux_recalc_rate,
};
EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, SUNXI_CCU);

View File

@ -46,20 +46,36 @@ struct ccu_mux {
struct ccu_common common;
};
#define SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, _table, \
_reg, _shift, _width, _gate, \
_flags, _features) \
struct ccu_mux _struct = { \
.enable = _gate, \
.mux = _SUNXI_CCU_MUX_TABLE(_shift, _width, _table), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_mux_ops, \
_flags), \
.features = _features, \
} \
}
#define SUNXI_CCU_MUX_TABLE_WITH_GATE_CLOSEST(_struct, _name, _parents, \
_table, _reg, _shift, \
_width, _gate, _flags) \
SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, \
_table, _reg, _shift, \
_width, _gate, _flags, \
CCU_FEATURE_CLOSEST_RATE)
#define SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, _table, \
_reg, _shift, _width, _gate, \
_flags) \
struct ccu_mux _struct = { \
.enable = _gate, \
.mux = _SUNXI_CCU_MUX_TABLE(_shift, _width, _table), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_mux_ops, \
_flags), \
} \
}
SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, \
_table, _reg, _shift, \
_width, _gate, _flags, 0)
#define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg, \
_shift, _width, _gate, _flags) \

View File

@ -16,8 +16,47 @@ struct _ccu_nkm {
unsigned long m, min_m, max_m;
};
static unsigned long ccu_nkm_find_best_with_parent_adj(struct ccu_common *common,
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 (ccu_is_better_rate(common, rate, tmp_rate, best_rate) ||
(tmp_parent == *parent && tmp_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)
struct _ccu_nkm *nkm, struct ccu_common *common)
{
unsigned long best_rate = 0;
unsigned long best_n = 0, best_k = 0, best_m = 0;
@ -30,9 +69,7 @@ static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long rate,
tmp_rate = parent * _n * _k / _m;
if (tmp_rate > rate)
continue;
if ((rate - tmp_rate) < (rate - best_rate)) {
if (ccu_is_better_rate(common, rate, tmp_rate, best_rate)) {
best_rate = tmp_rate;
best_n = _n;
best_k = _k;
@ -106,7 +143,7 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
}
static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
struct clk_hw *hw,
struct clk_hw *parent_hw,
unsigned long *parent_rate,
unsigned long rate,
void *data)
@ -124,7 +161,11 @@ 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, &nkm->common);
else
rate = ccu_nkm_find_best_with_parent_adj(&nkm->common, parent_hw, parent_rate, rate,
&_nkm);
if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate /= nkm->fixed_post_div;
@ -159,7 +200,7 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
_nkm.min_m = 1;
_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
ccu_nkm_find_best(parent_rate, rate, &_nkm);
ccu_nkm_find_best(parent_rate, rate, &_nkm, &nkm->common);
spin_lock_irqsave(nkm->common.lock, flags);

View File

@ -27,8 +27,8 @@ static unsigned long ccu_nm_calc_rate(unsigned long parent,
return rate;
}
static unsigned long ccu_nm_find_best(unsigned long parent, unsigned long rate,
struct _ccu_nm *nm)
static unsigned long ccu_nm_find_best(struct ccu_common *common, unsigned long parent,
unsigned long rate, struct _ccu_nm *nm)
{
unsigned long best_rate = 0;
unsigned long best_n = 0, best_m = 0;
@ -39,10 +39,7 @@ static unsigned long ccu_nm_find_best(unsigned long parent, unsigned long rate,
unsigned long tmp_rate = ccu_nm_calc_rate(parent,
_n, _m);
if (tmp_rate > rate)
continue;
if ((rate - tmp_rate) < (rate - best_rate)) {
if (ccu_is_better_rate(common, rate, tmp_rate, best_rate)) {
best_rate = tmp_rate;
best_n = _n;
best_m = _m;
@ -159,7 +156,7 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
_nm.min_m = 1;
_nm.max_m = nm->m.max ?: 1 << nm->m.width;
rate = ccu_nm_find_best(*parent_rate, rate, &_nm);
rate = ccu_nm_find_best(&nm->common, *parent_rate, rate, &_nm);
if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV)
rate /= nm->fixed_post_div;
@ -210,7 +207,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
&_nm.m, &_nm.n);
} else {
ccu_sdm_helper_disable(&nm->common, &nm->sdm);
ccu_nm_find_best(parent_rate, rate, &_nm);
ccu_nm_find_best(&nm->common, parent_rate, rate, &_nm);
}
spin_lock_irqsave(nm->common.lock, flags);

View File

@ -108,7 +108,7 @@ struct ccu_nm {
}, \
}
#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX(_struct, _name, \
#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT(_struct, _name, \
_parent, _reg, \
_min_rate, _max_rate, \
_nshift, _nwidth, \
@ -116,7 +116,8 @@ struct ccu_nm {
_frac_en, _frac_sel, \
_frac_rate_0, \
_frac_rate_1, \
_gate, _lock, _flags) \
_gate, _lock, _flags, \
_features) \
struct ccu_nm _struct = { \
.enable = _gate, \
.lock = _lock, \
@ -129,7 +130,7 @@ struct ccu_nm {
.max_rate = _max_rate, \
.common = { \
.reg = _reg, \
.features = CCU_FEATURE_FRACTIONAL, \
.features = _features, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_nm_ops, \
@ -137,6 +138,47 @@ struct ccu_nm {
}, \
}
#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX(_struct, _name, \
_parent, _reg, \
_min_rate, _max_rate, \
_nshift, _nwidth, \
_mshift, _mwidth, \
_frac_en, _frac_sel, \
_frac_rate_0, \
_frac_rate_1, \
_gate, _lock, _flags) \
SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT(_struct, _name, \
_parent, _reg, \
_min_rate, _max_rate, \
_nshift, _nwidth, \
_mshift, _mwidth, \
_frac_en, _frac_sel, \
_frac_rate_0, \
_frac_rate_1, \
_gate, _lock, _flags, \
CCU_FEATURE_FRACTIONAL)
#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_CLOSEST(_struct, _name, \
_parent, _reg, \
_min_rate, _max_rate, \
_nshift, _nwidth, \
_mshift, _mwidth, \
_frac_en, _frac_sel, \
_frac_rate_0, \
_frac_rate_1, \
_gate, _lock, _flags) \
SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT(_struct, _name, \
_parent, _reg, \
_min_rate, _max_rate, \
_nshift, _nwidth, \
_mshift, _mwidth, \
_frac_en, _frac_sel, \
_frac_rate_0, \
_frac_rate_1, \
_gate, _lock, _flags, \
CCU_FEATURE_FRACTIONAL |\
CCU_FEATURE_CLOSEST_RATE)
#define SUNXI_CCU_NM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_mshift, _mwidth, \