18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2016 Maxime Ripard
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
108c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "ccu_common.h"
148c2ecf20Sopenharmony_ci#include "ccu_gate.h"
158c2ecf20Sopenharmony_ci#include "ccu_reset.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ccu_lock);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	void __iomem *addr;
228c2ecf20Sopenharmony_ci	u32 reg;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (!lock)
258c2ecf20Sopenharmony_ci		return;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (common->features & CCU_FEATURE_LOCK_REG)
288c2ecf20Sopenharmony_ci		addr = common->base + common->lock_reg;
298c2ecf20Sopenharmony_ci	else
308c2ecf20Sopenharmony_ci		addr = common->base + common->reg;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000));
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * This clock notifier is called when the frequency of a PLL clock is
378c2ecf20Sopenharmony_ci * changed. In common PLL designs, changes to the dividers take effect
388c2ecf20Sopenharmony_ci * almost immediately, while changes to the multipliers (implemented
398c2ecf20Sopenharmony_ci * as dividers in the feedback loop) take a few cycles to work into
408c2ecf20Sopenharmony_ci * the feedback loop for the PLL to stablize.
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * Sometimes when the PLL clock rate is changed, the decrease in the
438c2ecf20Sopenharmony_ci * divider is too much for the decrease in the multiplier to catch up.
448c2ecf20Sopenharmony_ci * The PLL clock rate will spike, and in some cases, might lock up
458c2ecf20Sopenharmony_ci * completely.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * This notifier callback will gate and then ungate the clock,
488c2ecf20Sopenharmony_ci * effectively resetting it, so it proceeds to work. Care must be
498c2ecf20Sopenharmony_ci * taken to reparent consumers to other temporary clocks during the
508c2ecf20Sopenharmony_ci * rate change, and that this notifier callback must be the first
518c2ecf20Sopenharmony_ci * to be registered.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic int ccu_pll_notifier_cb(struct notifier_block *nb,
548c2ecf20Sopenharmony_ci			       unsigned long event, void *data)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct ccu_pll_nb *pll = to_ccu_pll_nb(nb);
578c2ecf20Sopenharmony_ci	int ret = 0;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (event != POST_RATE_CHANGE)
608c2ecf20Sopenharmony_ci		goto out;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	ccu_gate_helper_disable(pll->common, pll->enable);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = ccu_gate_helper_enable(pll->common, pll->enable);
658c2ecf20Sopenharmony_ci	if (ret)
668c2ecf20Sopenharmony_ci		goto out;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	ccu_helper_wait_for_lock(pll->common, pll->lock);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciout:
718c2ecf20Sopenharmony_ci	return notifier_from_errno(ret);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciint ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return clk_notifier_register(pll_nb->common->hw.clk,
798c2ecf20Sopenharmony_ci				     &pll_nb->clk_nb);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ciint sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
838c2ecf20Sopenharmony_ci		    const struct sunxi_ccu_desc *desc)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct ccu_reset *reset;
868c2ecf20Sopenharmony_ci	int i, ret;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	for (i = 0; i < desc->num_ccu_clks; i++) {
898c2ecf20Sopenharmony_ci		struct ccu_common *cclk = desc->ccu_clks[i];
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		if (!cclk)
928c2ecf20Sopenharmony_ci			continue;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		cclk->base = reg;
958c2ecf20Sopenharmony_ci		cclk->lock = &ccu_lock;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	for (i = 0; i < desc->hw_clks->num ; i++) {
998c2ecf20Sopenharmony_ci		struct clk_hw *hw = desc->hw_clks->hws[i];
1008c2ecf20Sopenharmony_ci		const char *name;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		if (!hw)
1038c2ecf20Sopenharmony_ci			continue;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		name = hw->init->name;
1068c2ecf20Sopenharmony_ci		ret = of_clk_hw_register(node, hw);
1078c2ecf20Sopenharmony_ci		if (ret) {
1088c2ecf20Sopenharmony_ci			pr_err("Couldn't register clock %d - %s\n", i, name);
1098c2ecf20Sopenharmony_ci			goto err_clk_unreg;
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
1148c2ecf20Sopenharmony_ci				     desc->hw_clks);
1158c2ecf20Sopenharmony_ci	if (ret)
1168c2ecf20Sopenharmony_ci		goto err_clk_unreg;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	reset = kzalloc(sizeof(*reset), GFP_KERNEL);
1198c2ecf20Sopenharmony_ci	if (!reset) {
1208c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1218c2ecf20Sopenharmony_ci		goto err_alloc_reset;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	reset->rcdev.of_node = node;
1258c2ecf20Sopenharmony_ci	reset->rcdev.ops = &ccu_reset_ops;
1268c2ecf20Sopenharmony_ci	reset->rcdev.owner = THIS_MODULE;
1278c2ecf20Sopenharmony_ci	reset->rcdev.nr_resets = desc->num_resets;
1288c2ecf20Sopenharmony_ci	reset->base = reg;
1298c2ecf20Sopenharmony_ci	reset->lock = &ccu_lock;
1308c2ecf20Sopenharmony_ci	reset->reset_map = desc->resets;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ret = reset_controller_register(&reset->rcdev);
1338c2ecf20Sopenharmony_ci	if (ret)
1348c2ecf20Sopenharmony_ci		goto err_of_clk_unreg;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cierr_of_clk_unreg:
1398c2ecf20Sopenharmony_ci	kfree(reset);
1408c2ecf20Sopenharmony_cierr_alloc_reset:
1418c2ecf20Sopenharmony_ci	of_clk_del_provider(node);
1428c2ecf20Sopenharmony_cierr_clk_unreg:
1438c2ecf20Sopenharmony_ci	while (--i >= 0) {
1448c2ecf20Sopenharmony_ci		struct clk_hw *hw = desc->hw_clks->hws[i];
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		if (!hw)
1478c2ecf20Sopenharmony_ci			continue;
1488c2ecf20Sopenharmony_ci		clk_hw_unregister(hw);
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci	return ret;
1518c2ecf20Sopenharmony_ci}
152