162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2016 Maxime Ripard 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "ccu_common.h" 1662306a36Sopenharmony_ci#include "ccu_gate.h" 1762306a36Sopenharmony_ci#include "ccu_reset.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct sunxi_ccu { 2062306a36Sopenharmony_ci const struct sunxi_ccu_desc *desc; 2162306a36Sopenharmony_ci spinlock_t lock; 2262306a36Sopenharmony_ci struct ccu_reset reset; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci void __iomem *addr; 2862306a36Sopenharmony_ci u32 reg; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (!lock) 3162306a36Sopenharmony_ci return; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (common->features & CCU_FEATURE_LOCK_REG) 3462306a36Sopenharmony_ci addr = common->base + common->lock_reg; 3562306a36Sopenharmony_ci else 3662306a36Sopenharmony_ci addr = common->base + common->reg; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cibool ccu_is_better_rate(struct ccu_common *common, 4362306a36Sopenharmony_ci unsigned long target_rate, 4462306a36Sopenharmony_ci unsigned long current_rate, 4562306a36Sopenharmony_ci unsigned long best_rate) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci if (common->features & CCU_FEATURE_CLOSEST_RATE) 4862306a36Sopenharmony_ci return abs(current_rate - target_rate) < abs(best_rate - target_rate); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return current_rate <= target_rate && current_rate > best_rate; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ccu_is_better_rate, SUNXI_CCU); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * This clock notifier is called when the frequency of a PLL clock is 5662306a36Sopenharmony_ci * changed. In common PLL designs, changes to the dividers take effect 5762306a36Sopenharmony_ci * almost immediately, while changes to the multipliers (implemented 5862306a36Sopenharmony_ci * as dividers in the feedback loop) take a few cycles to work into 5962306a36Sopenharmony_ci * the feedback loop for the PLL to stablize. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Sometimes when the PLL clock rate is changed, the decrease in the 6262306a36Sopenharmony_ci * divider is too much for the decrease in the multiplier to catch up. 6362306a36Sopenharmony_ci * The PLL clock rate will spike, and in some cases, might lock up 6462306a36Sopenharmony_ci * completely. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * This notifier callback will gate and then ungate the clock, 6762306a36Sopenharmony_ci * effectively resetting it, so it proceeds to work. Care must be 6862306a36Sopenharmony_ci * taken to reparent consumers to other temporary clocks during the 6962306a36Sopenharmony_ci * rate change, and that this notifier callback must be the first 7062306a36Sopenharmony_ci * to be registered. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic int ccu_pll_notifier_cb(struct notifier_block *nb, 7362306a36Sopenharmony_ci unsigned long event, void *data) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct ccu_pll_nb *pll = to_ccu_pll_nb(nb); 7662306a36Sopenharmony_ci int ret = 0; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (event != POST_RATE_CHANGE) 7962306a36Sopenharmony_ci goto out; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ccu_gate_helper_disable(pll->common, pll->enable); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ret = ccu_gate_helper_enable(pll->common, pll->enable); 8462306a36Sopenharmony_ci if (ret) 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ccu_helper_wait_for_lock(pll->common, pll->lock); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciout: 9062306a36Sopenharmony_ci return notifier_from_errno(ret); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ciint ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return clk_notifier_register(pll_nb->common->hw.clk, 9862306a36Sopenharmony_ci &pll_nb->clk_nb); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, SUNXI_CCU); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev, 10362306a36Sopenharmony_ci struct device_node *node, void __iomem *reg, 10462306a36Sopenharmony_ci const struct sunxi_ccu_desc *desc) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct ccu_reset *reset; 10762306a36Sopenharmony_ci int i, ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ccu->desc = desc; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci spin_lock_init(&ccu->lock); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (i = 0; i < desc->num_ccu_clks; i++) { 11462306a36Sopenharmony_ci struct ccu_common *cclk = desc->ccu_clks[i]; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!cclk) 11762306a36Sopenharmony_ci continue; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci cclk->base = reg; 12062306a36Sopenharmony_ci cclk->lock = &ccu->lock; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; i < desc->hw_clks->num ; i++) { 12462306a36Sopenharmony_ci struct clk_hw *hw = desc->hw_clks->hws[i]; 12562306a36Sopenharmony_ci const char *name; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!hw) 12862306a36Sopenharmony_ci continue; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci name = hw->init->name; 13162306a36Sopenharmony_ci if (dev) 13262306a36Sopenharmony_ci ret = clk_hw_register(dev, hw); 13362306a36Sopenharmony_ci else 13462306a36Sopenharmony_ci ret = of_clk_hw_register(node, hw); 13562306a36Sopenharmony_ci if (ret) { 13662306a36Sopenharmony_ci pr_err("Couldn't register clock %d - %s\n", i, name); 13762306a36Sopenharmony_ci goto err_clk_unreg; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, 14262306a36Sopenharmony_ci desc->hw_clks); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci goto err_clk_unreg; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci reset = &ccu->reset; 14762306a36Sopenharmony_ci reset->rcdev.of_node = node; 14862306a36Sopenharmony_ci reset->rcdev.ops = &ccu_reset_ops; 14962306a36Sopenharmony_ci reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE; 15062306a36Sopenharmony_ci reset->rcdev.nr_resets = desc->num_resets; 15162306a36Sopenharmony_ci reset->base = reg; 15262306a36Sopenharmony_ci reset->lock = &ccu->lock; 15362306a36Sopenharmony_ci reset->reset_map = desc->resets; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = reset_controller_register(&reset->rcdev); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci goto err_del_provider; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cierr_del_provider: 16262306a36Sopenharmony_ci of_clk_del_provider(node); 16362306a36Sopenharmony_cierr_clk_unreg: 16462306a36Sopenharmony_ci while (--i >= 0) { 16562306a36Sopenharmony_ci struct clk_hw *hw = desc->hw_clks->hws[i]; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!hw) 16862306a36Sopenharmony_ci continue; 16962306a36Sopenharmony_ci clk_hw_unregister(hw); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void devm_sunxi_ccu_release(struct device *dev, void *res) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct sunxi_ccu *ccu = res; 17762306a36Sopenharmony_ci const struct sunxi_ccu_desc *desc = ccu->desc; 17862306a36Sopenharmony_ci int i; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci reset_controller_unregister(&ccu->reset.rcdev); 18162306a36Sopenharmony_ci of_clk_del_provider(dev->of_node); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < desc->hw_clks->num; i++) { 18462306a36Sopenharmony_ci struct clk_hw *hw = desc->hw_clks->hws[i]; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!hw) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci clk_hw_unregister(hw); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciint devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg, 19362306a36Sopenharmony_ci const struct sunxi_ccu_desc *desc) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct sunxi_ccu *ccu; 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL); 19962306a36Sopenharmony_ci if (!ccu) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc); 20362306a36Sopenharmony_ci if (ret) { 20462306a36Sopenharmony_ci devres_free(ccu); 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci devres_add(dev, ccu); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe, SUNXI_CCU); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_civoid of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg, 21562306a36Sopenharmony_ci const struct sunxi_ccu_desc *desc) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct sunxi_ccu *ccu; 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ccu = kzalloc(sizeof(*ccu), GFP_KERNEL); 22162306a36Sopenharmony_ci if (!ccu) 22262306a36Sopenharmony_ci return; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc); 22562306a36Sopenharmony_ci if (ret) { 22662306a36Sopenharmony_ci pr_err("%pOF: probing clocks failed: %d\n", node, ret); 22762306a36Sopenharmony_ci kfree(ccu); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 232