1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016 Maxime Ripard 4 * Maxime Ripard <maxime.ripard@free-electrons.com> 5 */ 6 7 #include <linux/clk-provider.h> 8 #include <linux/io.h> 9 10 #include "ccu_gate.h" 11 #include "ccu_nkm.h" 12 13 struct _ccu_nkm { 14 unsigned long n, min_n, max_n; 15 unsigned long k, min_k, max_k; 16 unsigned long m, min_m, max_m; 17 }; 18 19 static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long rate, 20 struct _ccu_nkm *nkm) 21 { 22 unsigned long best_rate = 0; 23 unsigned long best_n = 0, best_k = 0, best_m = 0; 24 unsigned long _n, _k, _m; 25 26 for (_k = nkm->min_k; _k <= nkm->max_k; _k++) { 27 for (_n = nkm->min_n; _n <= nkm->max_n; _n++) { 28 for (_m = nkm->min_m; _m <= nkm->max_m; _m++) { 29 unsigned long tmp_rate; 30 31 tmp_rate = parent * _n * _k / _m; 32 33 if (tmp_rate > rate) 34 continue; 35 if ((rate - tmp_rate) < (rate - best_rate)) { 36 best_rate = tmp_rate; 37 best_n = _n; 38 best_k = _k; 39 best_m = _m; 40 } 41 } 42 } 43 } 44 45 nkm->n = best_n; 46 nkm->k = best_k; 47 nkm->m = best_m; 48 49 return best_rate; 50 } 51 52 static void ccu_nkm_disable(struct clk_hw *hw) 53 { 54 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 55 56 return ccu_gate_helper_disable(&nkm->common, nkm->enable); 57 } 58 59 static int ccu_nkm_enable(struct clk_hw *hw) 60 { 61 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 62 63 return ccu_gate_helper_enable(&nkm->common, nkm->enable); 64 } 65 66 static int ccu_nkm_is_enabled(struct clk_hw *hw) 67 { 68 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 69 70 return ccu_gate_helper_is_enabled(&nkm->common, nkm->enable); 71 } 72 73 static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, 74 unsigned long parent_rate) 75 { 76 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 77 unsigned long n, m, k, rate; 78 u32 reg; 79 80 reg = readl(nkm->common.base + nkm->common.reg); 81 82 n = reg >> nkm->n.shift; 83 n &= (1 << nkm->n.width) - 1; 84 n += nkm->n.offset; 85 if (!n) 86 n++; 87 88 k = reg >> nkm->k.shift; 89 k &= (1 << nkm->k.width) - 1; 90 k += nkm->k.offset; 91 if (!k) 92 k++; 93 94 m = reg >> nkm->m.shift; 95 m &= (1 << nkm->m.width) - 1; 96 m += nkm->m.offset; 97 if (!m) 98 m++; 99 100 rate = parent_rate * n * k / m; 101 102 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) 103 rate /= nkm->fixed_post_div; 104 105 return rate; 106 } 107 108 static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, 109 struct clk_hw *hw, 110 unsigned long *parent_rate, 111 unsigned long rate, 112 void *data) 113 { 114 struct ccu_nkm *nkm = data; 115 struct _ccu_nkm _nkm; 116 117 _nkm.min_n = nkm->n.min ?: 1; 118 _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width; 119 _nkm.min_k = nkm->k.min ?: 1; 120 _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width; 121 _nkm.min_m = 1; 122 _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; 123 124 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) 125 rate *= nkm->fixed_post_div; 126 127 rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); 128 129 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) 130 rate /= nkm->fixed_post_div; 131 132 return rate; 133 } 134 135 static int ccu_nkm_determine_rate(struct clk_hw *hw, 136 struct clk_rate_request *req) 137 { 138 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 139 140 return ccu_mux_helper_determine_rate(&nkm->common, &nkm->mux, 141 req, ccu_nkm_round_rate, nkm); 142 } 143 144 static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, 145 unsigned long parent_rate) 146 { 147 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 148 struct _ccu_nkm _nkm; 149 unsigned long flags; 150 u32 reg; 151 152 if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) 153 rate *= nkm->fixed_post_div; 154 155 _nkm.min_n = nkm->n.min ?: 1; 156 _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width; 157 _nkm.min_k = nkm->k.min ?: 1; 158 _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width; 159 _nkm.min_m = 1; 160 _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; 161 162 ccu_nkm_find_best(parent_rate, rate, &_nkm); 163 164 spin_lock_irqsave(nkm->common.lock, flags); 165 166 reg = readl(nkm->common.base + nkm->common.reg); 167 reg &= ~GENMASK(nkm->n.width + nkm->n.shift - 1, nkm->n.shift); 168 reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift); 169 reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift); 170 171 reg |= (_nkm.n - nkm->n.offset) << nkm->n.shift; 172 reg |= (_nkm.k - nkm->k.offset) << nkm->k.shift; 173 reg |= (_nkm.m - nkm->m.offset) << nkm->m.shift; 174 writel(reg, nkm->common.base + nkm->common.reg); 175 176 spin_unlock_irqrestore(nkm->common.lock, flags); 177 178 ccu_helper_wait_for_lock(&nkm->common, nkm->lock); 179 180 return 0; 181 } 182 183 static u8 ccu_nkm_get_parent(struct clk_hw *hw) 184 { 185 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 186 187 return ccu_mux_helper_get_parent(&nkm->common, &nkm->mux); 188 } 189 190 static int ccu_nkm_set_parent(struct clk_hw *hw, u8 index) 191 { 192 struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); 193 194 return ccu_mux_helper_set_parent(&nkm->common, &nkm->mux, index); 195 } 196 197 const struct clk_ops ccu_nkm_ops = { 198 .disable = ccu_nkm_disable, 199 .enable = ccu_nkm_enable, 200 .is_enabled = ccu_nkm_is_enabled, 201 202 .get_parent = ccu_nkm_get_parent, 203 .set_parent = ccu_nkm_set_parent, 204 205 .determine_rate = ccu_nkm_determine_rate, 206 .recalc_rate = ccu_nkm_recalc_rate, 207 .set_rate = ccu_nkm_set_rate, 208 }; 209 EXPORT_SYMBOL_NS_GPL(ccu_nkm_ops, SUNXI_CCU); 210