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.h> 8 #include <linux/clk-provider.h> 9 #include <linux/delay.h> 10 #include <linux/io.h> 11 12 #include "ccu_gate.h" 13 #include "ccu_mux.h" 14 15 #define CCU_MUX_KEY_VALUE 0x16aa0000 16 17 static u16 ccu_mux_get_prediv(struct ccu_common *common, 18 struct ccu_mux_internal *cm, 19 int parent_index) 20 { 21 u16 prediv = 1; 22 u32 reg; 23 24 if (!((common->features & CCU_FEATURE_FIXED_PREDIV) || 25 (common->features & CCU_FEATURE_VARIABLE_PREDIV) || 26 (common->features & CCU_FEATURE_ALL_PREDIV))) 27 return 1; 28 29 if (common->features & CCU_FEATURE_ALL_PREDIV) 30 return common->prediv; 31 32 reg = readl(common->base + common->reg); 33 if (parent_index < 0) { 34 parent_index = reg >> cm->shift; 35 parent_index &= (1 << cm->width) - 1; 36 } 37 38 if (common->features & CCU_FEATURE_FIXED_PREDIV) { 39 int i; 40 41 for (i = 0; i < cm->n_predivs; i++) 42 if (parent_index == cm->fixed_predivs[i].index) 43 prediv = cm->fixed_predivs[i].div; 44 } 45 46 if (common->features & CCU_FEATURE_VARIABLE_PREDIV) { 47 int i; 48 49 for (i = 0; i < cm->n_var_predivs; i++) 50 if (parent_index == cm->var_predivs[i].index) { 51 u8 div; 52 53 div = reg >> cm->var_predivs[i].shift; 54 div &= (1 << cm->var_predivs[i].width) - 1; 55 prediv = div + 1; 56 } 57 } 58 59 return prediv; 60 } 61 62 unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common, 63 struct ccu_mux_internal *cm, 64 int parent_index, 65 unsigned long parent_rate) 66 { 67 return parent_rate / ccu_mux_get_prediv(common, cm, parent_index); 68 } 69 EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_apply_prediv, SUNXI_CCU); 70 71 static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common, 72 struct ccu_mux_internal *cm, 73 int parent_index, 74 unsigned long parent_rate) 75 { 76 return parent_rate * ccu_mux_get_prediv(common, cm, parent_index); 77 } 78 79 int ccu_mux_helper_determine_rate(struct ccu_common *common, 80 struct ccu_mux_internal *cm, 81 struct clk_rate_request *req, 82 unsigned long (*round)(struct ccu_mux_internal *, 83 struct clk_hw *, 84 unsigned long *, 85 unsigned long, 86 void *), 87 void *data) 88 { 89 unsigned long best_parent_rate = 0, best_rate = 0; 90 struct clk_hw *best_parent, *hw = &common->hw; 91 unsigned int i; 92 93 if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) { 94 unsigned long adj_parent_rate; 95 96 best_parent = clk_hw_get_parent(hw); 97 best_parent_rate = clk_hw_get_rate(best_parent); 98 adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1, 99 best_parent_rate); 100 101 best_rate = round(cm, best_parent, &adj_parent_rate, 102 req->rate, data); 103 104 /* 105 * adj_parent_rate might have been modified by our clock. 106 * Unapply the pre-divider if there's one, and give 107 * the actual frequency the parent needs to run at. 108 */ 109 best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1, 110 adj_parent_rate); 111 112 goto out; 113 } 114 115 for (i = 0; i < clk_hw_get_num_parents(hw); i++) { 116 unsigned long tmp_rate, parent_rate; 117 struct clk_hw *parent; 118 119 parent = clk_hw_get_parent_by_index(hw, i); 120 if (!parent) 121 continue; 122 123 parent_rate = ccu_mux_helper_apply_prediv(common, cm, i, 124 clk_hw_get_rate(parent)); 125 126 tmp_rate = round(cm, parent, &parent_rate, req->rate, data); 127 128 /* 129 * parent_rate might have been modified by our clock. 130 * Unapply the pre-divider if there's one, and give 131 * the actual frequency the parent needs to run at. 132 */ 133 parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i, 134 parent_rate); 135 if (tmp_rate == req->rate) { 136 best_parent = parent; 137 best_parent_rate = parent_rate; 138 best_rate = tmp_rate; 139 goto out; 140 } 141 142 if (ccu_is_better_rate(common, req->rate, tmp_rate, best_rate)) { 143 best_rate = tmp_rate; 144 best_parent_rate = parent_rate; 145 best_parent = parent; 146 } 147 } 148 149 if (best_rate == 0) 150 return -EINVAL; 151 152 out: 153 req->best_parent_hw = best_parent; 154 req->best_parent_rate = best_parent_rate; 155 req->rate = best_rate; 156 return 0; 157 } 158 EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_determine_rate, SUNXI_CCU); 159 160 u8 ccu_mux_helper_get_parent(struct ccu_common *common, 161 struct ccu_mux_internal *cm) 162 { 163 u32 reg; 164 u8 parent; 165 166 reg = readl(common->base + common->reg); 167 parent = reg >> cm->shift; 168 parent &= (1 << cm->width) - 1; 169 170 if (cm->table) { 171 int num_parents = clk_hw_get_num_parents(&common->hw); 172 int i; 173 174 for (i = 0; i < num_parents; i++) 175 if (cm->table[i] == parent) 176 return i; 177 } 178 179 return parent; 180 } 181 EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_get_parent, SUNXI_CCU); 182 183 int ccu_mux_helper_set_parent(struct ccu_common *common, 184 struct ccu_mux_internal *cm, 185 u8 index) 186 { 187 unsigned long flags; 188 u32 reg; 189 190 if (cm->table) 191 index = cm->table[index]; 192 193 spin_lock_irqsave(common->lock, flags); 194 195 reg = readl(common->base + common->reg); 196 197 /* The key field always reads as zero. */ 198 if (common->features & CCU_FEATURE_KEY_FIELD) 199 reg |= CCU_MUX_KEY_VALUE; 200 201 reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift); 202 writel(reg | (index << cm->shift), common->base + common->reg); 203 204 spin_unlock_irqrestore(common->lock, flags); 205 206 return 0; 207 } 208 EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_set_parent, SUNXI_CCU); 209 210 static void ccu_mux_disable(struct clk_hw *hw) 211 { 212 struct ccu_mux *cm = hw_to_ccu_mux(hw); 213 214 return ccu_gate_helper_disable(&cm->common, cm->enable); 215 } 216 217 static int ccu_mux_enable(struct clk_hw *hw) 218 { 219 struct ccu_mux *cm = hw_to_ccu_mux(hw); 220 221 return ccu_gate_helper_enable(&cm->common, cm->enable); 222 } 223 224 static int ccu_mux_is_enabled(struct clk_hw *hw) 225 { 226 struct ccu_mux *cm = hw_to_ccu_mux(hw); 227 228 return ccu_gate_helper_is_enabled(&cm->common, cm->enable); 229 } 230 231 static u8 ccu_mux_get_parent(struct clk_hw *hw) 232 { 233 struct ccu_mux *cm = hw_to_ccu_mux(hw); 234 235 return ccu_mux_helper_get_parent(&cm->common, &cm->mux); 236 } 237 238 static int ccu_mux_set_parent(struct clk_hw *hw, u8 index) 239 { 240 struct ccu_mux *cm = hw_to_ccu_mux(hw); 241 242 return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index); 243 } 244 245 static int ccu_mux_determine_rate(struct clk_hw *hw, 246 struct clk_rate_request *req) 247 { 248 struct ccu_mux *cm = hw_to_ccu_mux(hw); 249 250 if (cm->common.features & CCU_FEATURE_CLOSEST_RATE) 251 return clk_mux_determine_rate_flags(hw, req, CLK_MUX_ROUND_CLOSEST); 252 253 return clk_mux_determine_rate_flags(hw, req, 0); 254 } 255 256 static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw, 257 unsigned long parent_rate) 258 { 259 struct ccu_mux *cm = hw_to_ccu_mux(hw); 260 261 return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, 262 parent_rate); 263 } 264 265 const struct clk_ops ccu_mux_ops = { 266 .disable = ccu_mux_disable, 267 .enable = ccu_mux_enable, 268 .is_enabled = ccu_mux_is_enabled, 269 270 .get_parent = ccu_mux_get_parent, 271 .set_parent = ccu_mux_set_parent, 272 273 .determine_rate = ccu_mux_determine_rate, 274 .recalc_rate = ccu_mux_recalc_rate, 275 }; 276 EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, SUNXI_CCU); 277 278 /* 279 * This clock notifier is called when the frequency of the of the parent 280 * PLL clock is to be changed. The idea is to switch the parent to a 281 * stable clock, such as the main oscillator, while the PLL frequency 282 * stabilizes. 283 */ 284 static int ccu_mux_notifier_cb(struct notifier_block *nb, 285 unsigned long event, void *data) 286 { 287 struct ccu_mux_nb *mux = to_ccu_mux_nb(nb); 288 int ret = 0; 289 290 if (event == PRE_RATE_CHANGE) { 291 mux->original_index = ccu_mux_helper_get_parent(mux->common, 292 mux->cm); 293 ret = ccu_mux_helper_set_parent(mux->common, mux->cm, 294 mux->bypass_index); 295 } else if (event == POST_RATE_CHANGE) { 296 ret = ccu_mux_helper_set_parent(mux->common, mux->cm, 297 mux->original_index); 298 } 299 300 udelay(mux->delay_us); 301 302 return notifier_from_errno(ret); 303 } 304 305 int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb) 306 { 307 mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb; 308 309 return clk_notifier_register(clk, &mux_nb->clk_nb); 310 } 311 EXPORT_SYMBOL_NS_GPL(ccu_mux_notifier_register, SUNXI_CCU); 312