diff options
Diffstat (limited to 'drivers/clk/sunxi-ng/ccu_nm.c')
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_nm.c | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index a32158e8f2e3..a16de092bf94 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -70,11 +70,18 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct ccu_nm *nm = hw_to_ccu_nm(hw); + unsigned long rate; unsigned long n, m; u32 reg; - if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) - return ccu_frac_helper_read_rate(&nm->common, &nm->frac); + if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) { + rate = ccu_frac_helper_read_rate(&nm->common, &nm->frac); + + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nm->fixed_post_div; + + return rate; + } reg = readl(nm->common.base + nm->common.reg); @@ -90,7 +97,15 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw, if (!m) m++; - return parent_rate * n / m; + if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm)) + rate = ccu_sdm_helper_read_rate(&nm->common, &nm->sdm, m, n); + else + rate = parent_rate * n / m; + + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nm->fixed_post_div; + + return rate; } static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, @@ -99,14 +114,33 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, struct ccu_nm *nm = hw_to_ccu_nm(hw); struct _ccu_nm _nm; + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nm->fixed_post_div; + + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) { + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nm->fixed_post_div; + return rate; + } + + if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) { + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nm->fixed_post_div; + return rate; + } + _nm.min_n = nm->n.min ?: 1; _nm.max_n = nm->n.max ?: 1 << nm->n.width; _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; ccu_nm_find_best(*parent_rate, rate, &_nm); + rate = *parent_rate * _nm.n / _nm.m; + + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nm->fixed_post_div; - return *parent_rate * _nm.n / _nm.m; + return rate; } static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, @@ -117,6 +151,10 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; + /* Adjust target rate according to post-dividers */ + if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate = rate * nm->fixed_post_div; + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) { spin_lock_irqsave(nm->common.lock, flags); @@ -140,7 +178,16 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; - ccu_nm_find_best(parent_rate, rate, &_nm); + if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate)) { + ccu_sdm_helper_enable(&nm->common, &nm->sdm, rate); + + /* Sigma delta modulation requires specific N and M factors */ + ccu_sdm_helper_get_factors(&nm->common, &nm->sdm, rate, + &_nm.m, &_nm.n); + } else { + ccu_sdm_helper_disable(&nm->common, &nm->sdm); + ccu_nm_find_best(parent_rate, rate, &_nm); + } spin_lock_irqsave(nm->common.lock, flags); |