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 ((req->rate - tmp_rate) < (req->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 unsigned long ccu_mux_recalc_rate(struct clk_hw *hw, 246 unsigned long parent_rate) 247 { 248 struct ccu_mux *cm = hw_to_ccu_mux(hw); 249 250 return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, 251 parent_rate); 252 } 253 254 const struct clk_ops ccu_mux_ops = { 255 .disable = ccu_mux_disable, 256 .enable = ccu_mux_enable, 257 .is_enabled = ccu_mux_is_enabled, 258 259 .get_parent = ccu_mux_get_parent, 260 .set_parent = ccu_mux_set_parent, 261 262 .determine_rate = __clk_mux_determine_rate, 263 .recalc_rate = ccu_mux_recalc_rate, 264 }; 265 EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, SUNXI_CCU); 266 267 /* 268 * This clock notifier is called when the frequency of the of the parent 269 * PLL clock is to be changed. The idea is to switch the parent to a 270 * stable clock, such as the main oscillator, while the PLL frequency 271 * stabilizes. 272 */ 273 static int ccu_mux_notifier_cb(struct notifier_block *nb, 274 unsigned long event, void *data) 275 { 276 struct ccu_mux_nb *mux = to_ccu_mux_nb(nb); 277 int ret = 0; 278 279 if (event == PRE_RATE_CHANGE) { 280 mux->original_index = ccu_mux_helper_get_parent(mux->common, 281 mux->cm); 282 ret = ccu_mux_helper_set_parent(mux->common, mux->cm, 283 mux->bypass_index); 284 } else if (event == POST_RATE_CHANGE) { 285 ret = ccu_mux_helper_set_parent(mux->common, mux->cm, 286 mux->original_index); 287 } 288 289 udelay(mux->delay_us); 290 291 return notifier_from_errno(ret); 292 } 293 294 int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb) 295 { 296 mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb; 297 298 return clk_notifier_register(clk, &mux_nb->clk_nb); 299 } 300 EXPORT_SYMBOL_NS_GPL(ccu_mux_notifier_register, SUNXI_CCU); 301