0001
0002
0003
0004
0005
0006
0007 #include <linux/clk-provider.h>
0008 #include <linux/io.h>
0009
0010 #include "ccu_gate.h"
0011 #include "ccu_nkm.h"
0012
0013 struct _ccu_nkm {
0014 unsigned long n, min_n, max_n;
0015 unsigned long k, min_k, max_k;
0016 unsigned long m, min_m, max_m;
0017 };
0018
0019 static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
0020 struct _ccu_nkm *nkm)
0021 {
0022 unsigned long best_rate = 0;
0023 unsigned long best_n = 0, best_k = 0, best_m = 0;
0024 unsigned long _n, _k, _m;
0025
0026 for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
0027 for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
0028 for (_m = nkm->min_m; _m <= nkm->max_m; _m++) {
0029 unsigned long tmp_rate;
0030
0031 tmp_rate = parent * _n * _k / _m;
0032
0033 if (tmp_rate > rate)
0034 continue;
0035 if ((rate - tmp_rate) < (rate - best_rate)) {
0036 best_rate = tmp_rate;
0037 best_n = _n;
0038 best_k = _k;
0039 best_m = _m;
0040 }
0041 }
0042 }
0043 }
0044
0045 nkm->n = best_n;
0046 nkm->k = best_k;
0047 nkm->m = best_m;
0048 }
0049
0050 static void ccu_nkm_disable(struct clk_hw *hw)
0051 {
0052 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0053
0054 return ccu_gate_helper_disable(&nkm->common, nkm->enable);
0055 }
0056
0057 static int ccu_nkm_enable(struct clk_hw *hw)
0058 {
0059 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0060
0061 return ccu_gate_helper_enable(&nkm->common, nkm->enable);
0062 }
0063
0064 static int ccu_nkm_is_enabled(struct clk_hw *hw)
0065 {
0066 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0067
0068 return ccu_gate_helper_is_enabled(&nkm->common, nkm->enable);
0069 }
0070
0071 static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
0072 unsigned long parent_rate)
0073 {
0074 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0075 unsigned long n, m, k, rate;
0076 u32 reg;
0077
0078 reg = readl(nkm->common.base + nkm->common.reg);
0079
0080 n = reg >> nkm->n.shift;
0081 n &= (1 << nkm->n.width) - 1;
0082 n += nkm->n.offset;
0083 if (!n)
0084 n++;
0085
0086 k = reg >> nkm->k.shift;
0087 k &= (1 << nkm->k.width) - 1;
0088 k += nkm->k.offset;
0089 if (!k)
0090 k++;
0091
0092 m = reg >> nkm->m.shift;
0093 m &= (1 << nkm->m.width) - 1;
0094 m += nkm->m.offset;
0095 if (!m)
0096 m++;
0097
0098 rate = parent_rate * n * k / m;
0099
0100 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
0101 rate /= nkm->fixed_post_div;
0102
0103 return rate;
0104 }
0105
0106 static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
0107 struct clk_hw *hw,
0108 unsigned long *parent_rate,
0109 unsigned long rate,
0110 void *data)
0111 {
0112 struct ccu_nkm *nkm = data;
0113 struct _ccu_nkm _nkm;
0114
0115 _nkm.min_n = nkm->n.min ?: 1;
0116 _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
0117 _nkm.min_k = nkm->k.min ?: 1;
0118 _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
0119 _nkm.min_m = 1;
0120 _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
0121
0122 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
0123 rate *= nkm->fixed_post_div;
0124
0125 ccu_nkm_find_best(*parent_rate, rate, &_nkm);
0126
0127 rate = *parent_rate * _nkm.n * _nkm.k / _nkm.m;
0128
0129 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
0130 rate /= nkm->fixed_post_div;
0131
0132 return rate;
0133 }
0134
0135 static int ccu_nkm_determine_rate(struct clk_hw *hw,
0136 struct clk_rate_request *req)
0137 {
0138 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0139
0140 return ccu_mux_helper_determine_rate(&nkm->common, &nkm->mux,
0141 req, ccu_nkm_round_rate, nkm);
0142 }
0143
0144 static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
0145 unsigned long parent_rate)
0146 {
0147 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0148 struct _ccu_nkm _nkm;
0149 unsigned long flags;
0150 u32 reg;
0151
0152 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
0153 rate *= nkm->fixed_post_div;
0154
0155 _nkm.min_n = nkm->n.min ?: 1;
0156 _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
0157 _nkm.min_k = nkm->k.min ?: 1;
0158 _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
0159 _nkm.min_m = 1;
0160 _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
0161
0162 ccu_nkm_find_best(parent_rate, rate, &_nkm);
0163
0164 spin_lock_irqsave(nkm->common.lock, flags);
0165
0166 reg = readl(nkm->common.base + nkm->common.reg);
0167 reg &= ~GENMASK(nkm->n.width + nkm->n.shift - 1, nkm->n.shift);
0168 reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift);
0169 reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift);
0170
0171 reg |= (_nkm.n - nkm->n.offset) << nkm->n.shift;
0172 reg |= (_nkm.k - nkm->k.offset) << nkm->k.shift;
0173 reg |= (_nkm.m - nkm->m.offset) << nkm->m.shift;
0174 writel(reg, nkm->common.base + nkm->common.reg);
0175
0176 spin_unlock_irqrestore(nkm->common.lock, flags);
0177
0178 ccu_helper_wait_for_lock(&nkm->common, nkm->lock);
0179
0180 return 0;
0181 }
0182
0183 static u8 ccu_nkm_get_parent(struct clk_hw *hw)
0184 {
0185 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0186
0187 return ccu_mux_helper_get_parent(&nkm->common, &nkm->mux);
0188 }
0189
0190 static int ccu_nkm_set_parent(struct clk_hw *hw, u8 index)
0191 {
0192 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
0193
0194 return ccu_mux_helper_set_parent(&nkm->common, &nkm->mux, index);
0195 }
0196
0197 const struct clk_ops ccu_nkm_ops = {
0198 .disable = ccu_nkm_disable,
0199 .enable = ccu_nkm_enable,
0200 .is_enabled = ccu_nkm_is_enabled,
0201
0202 .get_parent = ccu_nkm_get_parent,
0203 .set_parent = ccu_nkm_set_parent,
0204
0205 .determine_rate = ccu_nkm_determine_rate,
0206 .recalc_rate = ccu_nkm_recalc_rate,
0207 .set_rate = ccu_nkm_set_rate,
0208 };
0209 EXPORT_SYMBOL_NS_GPL(ccu_nkm_ops, SUNXI_CCU);