clk: sunxi-ng: nm: Add support for sigma-delta modulation
authorChen-Yu Tsai <wens@csie.org>
Thu, 12 Oct 2017 08:37:00 +0000 (16:37 +0800)
committerMaxime Ripard <maxime.ripard@free-electrons.com>
Fri, 13 Oct 2017 07:27:13 +0000 (09:27 +0200)
Some of the N-M-style clocks, namely the PLLs, support sigma-delta
modulation to do fractional-N frequency synthesis. This is used in
the audio PLL to generate the exact frequency the audio blocks need.
These frequencies can not be generated with integer N-M factors.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
drivers/clk/sunxi-ng/ccu_nm.c
drivers/clk/sunxi-ng/ccu_nm.h

index 84a5e7f17f6f542ea462c3cd924368d3a97e27a1..7620aa973a6e49f5af0371508eacfadc864f5508 100644 (file)
@@ -90,6 +90,14 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw,
        if (!m)
                m++;
 
+       if (ccu_sdm_helper_is_enabled(&nm->common, &nm->sdm)) {
+               unsigned long rate =
+                       ccu_sdm_helper_read_rate(&nm->common, &nm->sdm,
+                                                m, n);
+               if (rate)
+                       return rate;
+       }
+
        return parent_rate * n / m;
 }
 
@@ -102,6 +110,9 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
        if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate))
                return rate;
 
+       if (ccu_sdm_helper_has_rate(&nm->common, &nm->sdm, rate))
+               return rate;
+
        _nm.min_n = nm->n.min ?: 1;
        _nm.max_n = nm->n.max ?: 1 << nm->n.width;
        _nm.min_m = 1;
@@ -143,7 +154,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);
 
index e87fd186da7830cf4d79bfb5c8b621154735487c..c623b0c7a23c3f7229445aa0b40051245f95b028 100644 (file)
@@ -20,6 +20,7 @@
 #include "ccu_div.h"
 #include "ccu_frac.h"
 #include "ccu_mult.h"
+#include "ccu_sdm.h"
 
 /*
  * struct ccu_nm - Definition of an N-M clock
@@ -33,10 +34,34 @@ struct ccu_nm {
        struct ccu_mult_internal        n;
        struct ccu_div_internal         m;
        struct ccu_frac_internal        frac;
+       struct ccu_sdm_internal         sdm;
 
        struct ccu_common       common;
 };
 
+#define SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(_struct, _name, _parent, _reg, \
+                                       _nshift, _nwidth,               \
+                                       _mshift, _mwidth,               \
+                                       _sdm_table, _sdm_en,            \
+                                       _sdm_reg, _sdm_reg_en,          \
+                                       _gate, _lock, _flags)           \
+       struct ccu_nm _struct = {                                       \
+               .enable         = _gate,                                \
+               .lock           = _lock,                                \
+               .n              = _SUNXI_CCU_MULT(_nshift, _nwidth),    \
+               .m              = _SUNXI_CCU_DIV(_mshift, _mwidth),     \
+               .sdm            = _SUNXI_CCU_SDM(_sdm_table, _sdm_en,   \
+                                                _sdm_reg, _sdm_reg_en),\
+               .common         = {                                     \
+                       .reg            = _reg,                         \
+                       .features       = CCU_FEATURE_SIGMA_DELTA_MOD,  \
+                       .hw.init        = CLK_HW_INIT(_name,            \
+                                                     _parent,          \
+                                                     &ccu_nm_ops,      \
+                                                     _flags),          \
+               },                                                      \
+       }
+
 #define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(_struct, _name, _parent, _reg,        \
                                         _nshift, _nwidth,              \
                                         _mshift, _mwidth,              \