1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2016 Maxime Ripard 4 * 5 * Maxime Ripard <maxime.ripard@free-electrons.com> 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/clk-provider.h> 10 #include <linux/device.h> 11 #include <linux/iopoll.h> 12 #include <linux/module.h> 13 #include <linux/slab.h> 14 15 #include "ccu_common.h" 16 #include "ccu_gate.h" 17 #include "ccu_reset.h" 18 19 struct sunxi_ccu { 20 const struct sunxi_ccu_desc *desc; 21 spinlock_t lock; 22 struct ccu_reset reset; 23 }; 24 25 void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) 26 { 27 void __iomem *addr; 28 u32 reg; 29 30 if (!lock) 31 return; 32 33 if (common->features & CCU_FEATURE_LOCK_REG) 34 addr = common->base + common->lock_reg; 35 else 36 addr = common->base + common->reg; 37 38 WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); 39 } 40 EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU); 41 42 bool ccu_is_better_rate(struct ccu_common *common, 43 unsigned long target_rate, 44 unsigned long current_rate, 45 unsigned long best_rate) 46 { 47 if (common->features & CCU_FEATURE_CLOSEST_RATE) 48 return abs(current_rate - target_rate) < abs(best_rate - target_rate); 49 50 return current_rate <= target_rate && current_rate > best_rate; 51 } 52 EXPORT_SYMBOL_NS_GPL(ccu_is_better_rate, SUNXI_CCU); 53 54 /* 55 * This clock notifier is called when the frequency of a PLL clock is 56 * changed. In common PLL designs, changes to the dividers take effect 57 * almost immediately, while changes to the multipliers (implemented 58 * as dividers in the feedback loop) take a few cycles to work into 59 * the feedback loop for the PLL to stablize. 60 * 61 * Sometimes when the PLL clock rate is changed, the decrease in the 62 * divider is too much for the decrease in the multiplier to catch up. 63 * The PLL clock rate will spike, and in some cases, might lock up 64 * completely. 65 * 66 * This notifier callback will gate and then ungate the clock, 67 * effectively resetting it, so it proceeds to work. Care must be 68 * taken to reparent consumers to other temporary clocks during the 69 * rate change, and that this notifier callback must be the first 70 * to be registered. 71 */ 72 static int ccu_pll_notifier_cb(struct notifier_block *nb, 73 unsigned long event, void *data) 74 { 75 struct ccu_pll_nb *pll = to_ccu_pll_nb(nb); 76 int ret = 0; 77 78 if (event != POST_RATE_CHANGE) 79 goto out; 80 81 ccu_gate_helper_disable(pll->common, pll->enable); 82 83 ret = ccu_gate_helper_enable(pll->common, pll->enable); 84 if (ret) 85 goto out; 86 87 ccu_helper_wait_for_lock(pll->common, pll->lock); 88 89 out: 90 return notifier_from_errno(ret); 91 } 92 93 int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb) 94 { 95 pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb; 96 97 return clk_notifier_register(pll_nb->common->hw.clk, 98 &pll_nb->clk_nb); 99 } 100 EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, SUNXI_CCU); 101 102 static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev, 103 struct device_node *node, void __iomem *reg, 104 const struct sunxi_ccu_desc *desc) 105 { 106 struct ccu_reset *reset; 107 int i, ret; 108 109 ccu->desc = desc; 110 111 spin_lock_init(&ccu->lock); 112 113 for (i = 0; i < desc->num_ccu_clks; i++) { 114 struct ccu_common *cclk = desc->ccu_clks[i]; 115 116 if (!cclk) 117 continue; 118 119 cclk->base = reg; 120 cclk->lock = &ccu->lock; 121 } 122 123 for (i = 0; i < desc->hw_clks->num ; i++) { 124 struct clk_hw *hw = desc->hw_clks->hws[i]; 125 const char *name; 126 127 if (!hw) 128 continue; 129 130 name = hw->init->name; 131 if (dev) 132 ret = clk_hw_register(dev, hw); 133 else 134 ret = of_clk_hw_register(node, hw); 135 if (ret) { 136 pr_err("Couldn't register clock %d - %s\n", i, name); 137 goto err_clk_unreg; 138 } 139 } 140 141 ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, 142 desc->hw_clks); 143 if (ret) 144 goto err_clk_unreg; 145 146 reset = &ccu->reset; 147 reset->rcdev.of_node = node; 148 reset->rcdev.ops = &ccu_reset_ops; 149 reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE; 150 reset->rcdev.nr_resets = desc->num_resets; 151 reset->base = reg; 152 reset->lock = &ccu->lock; 153 reset->reset_map = desc->resets; 154 155 ret = reset_controller_register(&reset->rcdev); 156 if (ret) 157 goto err_del_provider; 158 159 return 0; 160 161 err_del_provider: 162 of_clk_del_provider(node); 163 err_clk_unreg: 164 while (--i >= 0) { 165 struct clk_hw *hw = desc->hw_clks->hws[i]; 166 167 if (!hw) 168 continue; 169 clk_hw_unregister(hw); 170 } 171 return ret; 172 } 173 174 static void devm_sunxi_ccu_release(struct device *dev, void *res) 175 { 176 struct sunxi_ccu *ccu = res; 177 const struct sunxi_ccu_desc *desc = ccu->desc; 178 int i; 179 180 reset_controller_unregister(&ccu->reset.rcdev); 181 of_clk_del_provider(dev->of_node); 182 183 for (i = 0; i < desc->hw_clks->num; i++) { 184 struct clk_hw *hw = desc->hw_clks->hws[i]; 185 186 if (!hw) 187 continue; 188 clk_hw_unregister(hw); 189 } 190 } 191 192 int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg, 193 const struct sunxi_ccu_desc *desc) 194 { 195 struct sunxi_ccu *ccu; 196 int ret; 197 198 ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL); 199 if (!ccu) 200 return -ENOMEM; 201 202 ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc); 203 if (ret) { 204 devres_free(ccu); 205 return ret; 206 } 207 208 devres_add(dev, ccu); 209 210 return 0; 211 } 212 EXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe, SUNXI_CCU); 213 214 void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg, 215 const struct sunxi_ccu_desc *desc) 216 { 217 struct sunxi_ccu *ccu; 218 int ret; 219 220 ccu = kzalloc(sizeof(*ccu), GFP_KERNEL); 221 if (!ccu) 222 return; 223 224 ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc); 225 if (ret) { 226 pr_err("%pOF: probing clocks failed: %d\n", node, ret); 227 kfree(ccu); 228 } 229 } 230 231 MODULE_LICENSE("GPL"); 232