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_mp.h" 12 13 static unsigned long ccu_mp_find_best(unsigned long parent, unsigned long rate, 14 unsigned int max_m, unsigned int max_p, 15 unsigned int *m, unsigned int *p) 16 { 17 unsigned long best_rate = 0; 18 unsigned int best_m = 0, best_p = 0; 19 unsigned int _m, _p; 20 21 for (_p = 1; _p <= max_p; _p <<= 1) { 22 for (_m = 1; _m <= max_m; _m++) { 23 unsigned long tmp_rate = parent / _p / _m; 24 25 if (tmp_rate > rate) 26 continue; 27 28 if ((rate - tmp_rate) < (rate - best_rate)) { 29 best_rate = tmp_rate; 30 best_m = _m; 31 best_p = _p; 32 } 33 } 34 } 35 36 *m = best_m; 37 *p = best_p; 38 39 return best_rate; 40 } 41 42 static unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw, 43 unsigned long *parent, 44 unsigned long rate, 45 unsigned int max_m, 46 unsigned int max_p) 47 { 48 unsigned long parent_rate_saved; 49 unsigned long parent_rate, now; 50 unsigned long best_rate = 0; 51 unsigned int _m, _p, div; 52 unsigned long maxdiv; 53 54 parent_rate_saved = *parent; 55 56 /* 57 * The maximum divider we can use without overflowing 58 * unsigned long in rate * m * p below 59 */ 60 maxdiv = max_m * max_p; 61 maxdiv = min(ULONG_MAX / rate, maxdiv); 62 63 for (_p = 1; _p <= max_p; _p <<= 1) { 64 for (_m = 1; _m <= max_m; _m++) { 65 div = _m * _p; 66 67 if (div > maxdiv) 68 break; 69 70 if (rate * div == parent_rate_saved) { 71 /* 72 * It's the most ideal case if the requested 73 * rate can be divided from parent clock without 74 * needing to change parent rate, so return the 75 * divider immediately. 76 */ 77 *parent = parent_rate_saved; 78 return rate; 79 } 80 81 parent_rate = clk_hw_round_rate(hw, rate * div); 82 now = parent_rate / div; 83 84 if (now <= rate && now > best_rate) { 85 best_rate = now; 86 *parent = parent_rate; 87 88 if (now == rate) 89 return rate; 90 } 91 } 92 } 93 94 return best_rate; 95 } 96 97 static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, 98 struct clk_hw *hw, 99 unsigned long *parent_rate, 100 unsigned long rate, 101 void *data) 102 { 103 struct ccu_mp *cmp = data; 104 unsigned int max_m, max_p; 105 unsigned int m, p; 106 107 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 108 rate *= cmp->fixed_post_div; 109 110 max_m = cmp->m.max ?: 1 << cmp->m.width; 111 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); 112 113 if (!clk_hw_can_set_rate_parent(&cmp->common.hw)) { 114 rate = ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); 115 } else { 116 rate = ccu_mp_find_best_with_parent_adj(hw, parent_rate, rate, 117 max_m, max_p); 118 } 119 120 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 121 rate /= cmp->fixed_post_div; 122 123 return rate; 124 } 125 126 static void ccu_mp_disable(struct clk_hw *hw) 127 { 128 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 129 130 return ccu_gate_helper_disable(&cmp->common, cmp->enable); 131 } 132 133 static int ccu_mp_enable(struct clk_hw *hw) 134 { 135 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 136 137 return ccu_gate_helper_enable(&cmp->common, cmp->enable); 138 } 139 140 static int ccu_mp_is_enabled(struct clk_hw *hw) 141 { 142 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 143 144 return ccu_gate_helper_is_enabled(&cmp->common, cmp->enable); 145 } 146 147 static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw, 148 unsigned long parent_rate) 149 { 150 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 151 unsigned long rate; 152 unsigned int m, p; 153 u32 reg; 154 155 /* Adjust parent_rate according to pre-dividers */ 156 parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1, 157 parent_rate); 158 159 reg = readl(cmp->common.base + cmp->common.reg); 160 161 m = reg >> cmp->m.shift; 162 m &= (1 << cmp->m.width) - 1; 163 m += cmp->m.offset; 164 if (!m) 165 m++; 166 167 p = reg >> cmp->p.shift; 168 p &= (1 << cmp->p.width) - 1; 169 170 rate = (parent_rate >> p) / m; 171 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 172 rate /= cmp->fixed_post_div; 173 174 return rate; 175 } 176 177 static int ccu_mp_determine_rate(struct clk_hw *hw, 178 struct clk_rate_request *req) 179 { 180 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 181 182 return ccu_mux_helper_determine_rate(&cmp->common, &cmp->mux, 183 req, ccu_mp_round_rate, cmp); 184 } 185 186 static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, 187 unsigned long parent_rate) 188 { 189 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 190 unsigned long flags; 191 unsigned int max_m, max_p; 192 unsigned int m, p; 193 u32 reg; 194 195 /* Adjust parent_rate according to pre-dividers */ 196 parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1, 197 parent_rate); 198 199 max_m = cmp->m.max ?: 1 << cmp->m.width; 200 max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); 201 202 /* Adjust target rate according to post-dividers */ 203 if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) 204 rate = rate * cmp->fixed_post_div; 205 206 ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p); 207 208 spin_lock_irqsave(cmp->common.lock, flags); 209 210 reg = readl(cmp->common.base + cmp->common.reg); 211 reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift); 212 reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift); 213 reg |= (m - cmp->m.offset) << cmp->m.shift; 214 reg |= ilog2(p) << cmp->p.shift; 215 216 writel(reg, cmp->common.base + cmp->common.reg); 217 218 spin_unlock_irqrestore(cmp->common.lock, flags); 219 220 return 0; 221 } 222 223 static u8 ccu_mp_get_parent(struct clk_hw *hw) 224 { 225 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 226 227 return ccu_mux_helper_get_parent(&cmp->common, &cmp->mux); 228 } 229 230 static int ccu_mp_set_parent(struct clk_hw *hw, u8 index) 231 { 232 struct ccu_mp *cmp = hw_to_ccu_mp(hw); 233 234 return ccu_mux_helper_set_parent(&cmp->common, &cmp->mux, index); 235 } 236 237 const struct clk_ops ccu_mp_ops = { 238 .disable = ccu_mp_disable, 239 .enable = ccu_mp_enable, 240 .is_enabled = ccu_mp_is_enabled, 241 242 .get_parent = ccu_mp_get_parent, 243 .set_parent = ccu_mp_set_parent, 244 245 .determine_rate = ccu_mp_determine_rate, 246 .recalc_rate = ccu_mp_recalc_rate, 247 .set_rate = ccu_mp_set_rate, 248 }; 249 EXPORT_SYMBOL_NS_GPL(ccu_mp_ops, SUNXI_CCU); 250 251 /* 252 * Support for MMC timing mode switching 253 * 254 * The MMC clocks on some SoCs support switching between old and 255 * new timing modes. A platform specific API is provided to query 256 * and set the timing mode on supported SoCs. 257 * 258 * In addition, a special class of ccu_mp_ops is provided, which 259 * takes in to account the timing mode switch. When the new timing 260 * mode is active, the clock output rate is halved. This new class 261 * is a wrapper around the generic ccu_mp_ops. When clock rates 262 * are passed through to ccu_mp_ops callbacks, they are doubled 263 * if the new timing mode bit is set, to account for the post 264 * divider. Conversely, when clock rates are passed back, they 265 * are halved if the mode bit is set. 266 */ 267 268 static unsigned long ccu_mp_mmc_recalc_rate(struct clk_hw *hw, 269 unsigned long parent_rate) 270 { 271 unsigned long rate = ccu_mp_recalc_rate(hw, parent_rate); 272 struct ccu_common *cm = hw_to_ccu_common(hw); 273 u32 val = readl(cm->base + cm->reg); 274 275 if (val & CCU_MMC_NEW_TIMING_MODE) 276 return rate / 2; 277 return rate; 278 } 279 280 static int ccu_mp_mmc_determine_rate(struct clk_hw *hw, 281 struct clk_rate_request *req) 282 { 283 struct ccu_common *cm = hw_to_ccu_common(hw); 284 u32 val = readl(cm->base + cm->reg); 285 int ret; 286 287 /* adjust the requested clock rate */ 288 if (val & CCU_MMC_NEW_TIMING_MODE) { 289 req->rate *= 2; 290 req->min_rate *= 2; 291 req->max_rate *= 2; 292 } 293 294 ret = ccu_mp_determine_rate(hw, req); 295 296 /* re-adjust the requested clock rate back */ 297 if (val & CCU_MMC_NEW_TIMING_MODE) { 298 req->rate /= 2; 299 req->min_rate /= 2; 300 req->max_rate /= 2; 301 } 302 303 return ret; 304 } 305 306 static int ccu_mp_mmc_set_rate(struct clk_hw *hw, unsigned long rate, 307 unsigned long parent_rate) 308 { 309 struct ccu_common *cm = hw_to_ccu_common(hw); 310 u32 val = readl(cm->base + cm->reg); 311 312 if (val & CCU_MMC_NEW_TIMING_MODE) 313 rate *= 2; 314 315 return ccu_mp_set_rate(hw, rate, parent_rate); 316 } 317 318 const struct clk_ops ccu_mp_mmc_ops = { 319 .disable = ccu_mp_disable, 320 .enable = ccu_mp_enable, 321 .is_enabled = ccu_mp_is_enabled, 322 323 .get_parent = ccu_mp_get_parent, 324 .set_parent = ccu_mp_set_parent, 325 326 .determine_rate = ccu_mp_mmc_determine_rate, 327 .recalc_rate = ccu_mp_mmc_recalc_rate, 328 .set_rate = ccu_mp_mmc_set_rate, 329 }; 330 EXPORT_SYMBOL_NS_GPL(ccu_mp_mmc_ops, SUNXI_CCU); 331