18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014 MundoReader S.L.
48c2ecf20Sopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
78c2ecf20Sopenharmony_ci * Author: Xing Zheng <zhengxing@rock-chips.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * based on
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * samsung/clk.c
128c2ecf20Sopenharmony_ci * Copyright (c) 2013 Samsung Electronics Co., Ltd.
138c2ecf20Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd.
148c2ecf20Sopenharmony_ci * Author: Thomas Abraham <thomas.ab@samsung.com>
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/clk.h>
198c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
208c2ecf20Sopenharmony_ci#include <linux/io.h>
218c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
228c2ecf20Sopenharmony_ci#include <linux/regmap.h>
238c2ecf20Sopenharmony_ci#include <linux/reboot.h>
248c2ecf20Sopenharmony_ci#include <linux/rational.h>
258c2ecf20Sopenharmony_ci#include "clk.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/**
288c2ecf20Sopenharmony_ci * Register a clock branch.
298c2ecf20Sopenharmony_ci * Most clock branches have a form like
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * src1 --|--\
328c2ecf20Sopenharmony_ci *        |M |--[GATE]-[DIV]-
338c2ecf20Sopenharmony_ci * src2 --|--/
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * sometimes without one of those components.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_cistatic struct clk *rockchip_clk_register_branch(const char *name,
388c2ecf20Sopenharmony_ci		const char *const *parent_names, u8 num_parents,
398c2ecf20Sopenharmony_ci		void __iomem *base,
408c2ecf20Sopenharmony_ci		int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
418c2ecf20Sopenharmony_ci		int div_offset, u8 div_shift, u8 div_width, u8 div_flags,
428c2ecf20Sopenharmony_ci		struct clk_div_table *div_table, int gate_offset,
438c2ecf20Sopenharmony_ci		u8 gate_shift, u8 gate_flags, unsigned long flags,
448c2ecf20Sopenharmony_ci		spinlock_t *lock)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct clk_hw *hw;
478c2ecf20Sopenharmony_ci	struct clk_mux *mux = NULL;
488c2ecf20Sopenharmony_ci	struct clk_gate *gate = NULL;
498c2ecf20Sopenharmony_ci	struct clk_divider *div = NULL;
508c2ecf20Sopenharmony_ci	const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
518c2ecf20Sopenharmony_ci			     *gate_ops = NULL;
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (num_parents > 1) {
558c2ecf20Sopenharmony_ci		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
568c2ecf20Sopenharmony_ci		if (!mux)
578c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		mux->reg = base + muxdiv_offset;
608c2ecf20Sopenharmony_ci		mux->shift = mux_shift;
618c2ecf20Sopenharmony_ci		mux->mask = BIT(mux_width) - 1;
628c2ecf20Sopenharmony_ci		mux->flags = mux_flags;
638c2ecf20Sopenharmony_ci		mux->lock = lock;
648c2ecf20Sopenharmony_ci		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
658c2ecf20Sopenharmony_ci							: &clk_mux_ops;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (gate_offset >= 0) {
698c2ecf20Sopenharmony_ci		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
708c2ecf20Sopenharmony_ci		if (!gate) {
718c2ecf20Sopenharmony_ci			ret = -ENOMEM;
728c2ecf20Sopenharmony_ci			goto err_gate;
738c2ecf20Sopenharmony_ci		}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		gate->flags = gate_flags;
768c2ecf20Sopenharmony_ci		gate->reg = base + gate_offset;
778c2ecf20Sopenharmony_ci		gate->bit_idx = gate_shift;
788c2ecf20Sopenharmony_ci		gate->lock = lock;
798c2ecf20Sopenharmony_ci		gate_ops = &clk_gate_ops;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (div_width > 0) {
838c2ecf20Sopenharmony_ci		div = kzalloc(sizeof(*div), GFP_KERNEL);
848c2ecf20Sopenharmony_ci		if (!div) {
858c2ecf20Sopenharmony_ci			ret = -ENOMEM;
868c2ecf20Sopenharmony_ci			goto err_div;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		div->flags = div_flags;
908c2ecf20Sopenharmony_ci		if (div_offset)
918c2ecf20Sopenharmony_ci			div->reg = base + div_offset;
928c2ecf20Sopenharmony_ci		else
938c2ecf20Sopenharmony_ci			div->reg = base + muxdiv_offset;
948c2ecf20Sopenharmony_ci		div->shift = div_shift;
958c2ecf20Sopenharmony_ci		div->width = div_width;
968c2ecf20Sopenharmony_ci		div->lock = lock;
978c2ecf20Sopenharmony_ci		div->table = div_table;
988c2ecf20Sopenharmony_ci		div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
998c2ecf20Sopenharmony_ci						? &clk_divider_ro_ops
1008c2ecf20Sopenharmony_ci						: &clk_divider_ops;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
1048c2ecf20Sopenharmony_ci				       mux ? &mux->hw : NULL, mux_ops,
1058c2ecf20Sopenharmony_ci				       div ? &div->hw : NULL, div_ops,
1068c2ecf20Sopenharmony_ci				       gate ? &gate->hw : NULL, gate_ops,
1078c2ecf20Sopenharmony_ci				       flags);
1088c2ecf20Sopenharmony_ci	if (IS_ERR(hw)) {
1098c2ecf20Sopenharmony_ci		kfree(div);
1108c2ecf20Sopenharmony_ci		kfree(gate);
1118c2ecf20Sopenharmony_ci		return ERR_CAST(hw);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return hw->clk;
1158c2ecf20Sopenharmony_cierr_div:
1168c2ecf20Sopenharmony_ci	kfree(gate);
1178c2ecf20Sopenharmony_cierr_gate:
1188c2ecf20Sopenharmony_ci	kfree(mux);
1198c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistruct rockchip_clk_frac {
1238c2ecf20Sopenharmony_ci	struct notifier_block			clk_nb;
1248c2ecf20Sopenharmony_ci	struct clk_fractional_divider		div;
1258c2ecf20Sopenharmony_ci	struct clk_gate				gate;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	struct clk_mux				mux;
1288c2ecf20Sopenharmony_ci	const struct clk_ops			*mux_ops;
1298c2ecf20Sopenharmony_ci	int					mux_frac_idx;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	bool					rate_change_remuxed;
1328c2ecf20Sopenharmony_ci	int					rate_change_idx;
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define to_rockchip_clk_frac_nb(nb) \
1368c2ecf20Sopenharmony_ci			container_of(nb, struct rockchip_clk_frac, clk_nb)
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
1398c2ecf20Sopenharmony_ci					 unsigned long event, void *data)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct clk_notifier_data *ndata = data;
1428c2ecf20Sopenharmony_ci	struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb);
1438c2ecf20Sopenharmony_ci	struct clk_mux *frac_mux = &frac->mux;
1448c2ecf20Sopenharmony_ci	int ret = 0;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
1478c2ecf20Sopenharmony_ci		 __func__, event, ndata->old_rate, ndata->new_rate);
1488c2ecf20Sopenharmony_ci	if (event == PRE_RATE_CHANGE) {
1498c2ecf20Sopenharmony_ci		frac->rate_change_idx =
1508c2ecf20Sopenharmony_ci				frac->mux_ops->get_parent(&frac_mux->hw);
1518c2ecf20Sopenharmony_ci		if (frac->rate_change_idx != frac->mux_frac_idx) {
1528c2ecf20Sopenharmony_ci			frac->mux_ops->set_parent(&frac_mux->hw,
1538c2ecf20Sopenharmony_ci						  frac->mux_frac_idx);
1548c2ecf20Sopenharmony_ci			frac->rate_change_remuxed = 1;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci	} else if (event == POST_RATE_CHANGE) {
1578c2ecf20Sopenharmony_ci		/*
1588c2ecf20Sopenharmony_ci		 * The POST_RATE_CHANGE notifier runs directly after the
1598c2ecf20Sopenharmony_ci		 * divider clock is set in clk_change_rate, so we'll have
1608c2ecf20Sopenharmony_ci		 * remuxed back to the original parent before clk_change_rate
1618c2ecf20Sopenharmony_ci		 * reaches the mux itself.
1628c2ecf20Sopenharmony_ci		 */
1638c2ecf20Sopenharmony_ci		if (frac->rate_change_remuxed) {
1648c2ecf20Sopenharmony_ci			frac->mux_ops->set_parent(&frac_mux->hw,
1658c2ecf20Sopenharmony_ci						  frac->rate_change_idx);
1668c2ecf20Sopenharmony_ci			frac->rate_change_remuxed = 0;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return notifier_from_errno(ret);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/**
1748c2ecf20Sopenharmony_ci * fractional divider must set that denominator is 20 times larger than
1758c2ecf20Sopenharmony_ci * numerator to generate precise clock frequency.
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_cistatic void rockchip_fractional_approximation(struct clk_hw *hw,
1788c2ecf20Sopenharmony_ci		unsigned long rate, unsigned long *parent_rate,
1798c2ecf20Sopenharmony_ci		unsigned long *m, unsigned long *n)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct clk_fractional_divider *fd = to_clk_fd(hw);
1828c2ecf20Sopenharmony_ci	unsigned long p_rate, p_parent_rate;
1838c2ecf20Sopenharmony_ci	struct clk_hw *p_parent;
1848c2ecf20Sopenharmony_ci	unsigned long scale;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
1878c2ecf20Sopenharmony_ci	if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
1888c2ecf20Sopenharmony_ci		p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
1898c2ecf20Sopenharmony_ci		p_parent_rate = clk_hw_get_rate(p_parent);
1908c2ecf20Sopenharmony_ci		*parent_rate = p_parent_rate;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/*
1948c2ecf20Sopenharmony_ci	 * Get rate closer to *parent_rate to guarantee there is no overflow
1958c2ecf20Sopenharmony_ci	 * for m and n. In the result it will be the nearest rate left shifted
1968c2ecf20Sopenharmony_ci	 * by (scale - fd->nwidth) bits.
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	scale = fls_long(*parent_rate / rate - 1);
1998c2ecf20Sopenharmony_ci	if (scale > fd->nwidth)
2008c2ecf20Sopenharmony_ci		rate <<= scale - fd->nwidth;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	rational_best_approximation(rate, *parent_rate,
2038c2ecf20Sopenharmony_ci			GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
2048c2ecf20Sopenharmony_ci			m, n);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct clk *rockchip_clk_register_frac_branch(
2088c2ecf20Sopenharmony_ci		struct rockchip_clk_provider *ctx, const char *name,
2098c2ecf20Sopenharmony_ci		const char *const *parent_names, u8 num_parents,
2108c2ecf20Sopenharmony_ci		void __iomem *base, int muxdiv_offset, u8 div_flags,
2118c2ecf20Sopenharmony_ci		int gate_offset, u8 gate_shift, u8 gate_flags,
2128c2ecf20Sopenharmony_ci		unsigned long flags, struct rockchip_clk_branch *child,
2138c2ecf20Sopenharmony_ci		spinlock_t *lock)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct clk_hw *hw;
2168c2ecf20Sopenharmony_ci	struct rockchip_clk_frac *frac;
2178c2ecf20Sopenharmony_ci	struct clk_gate *gate = NULL;
2188c2ecf20Sopenharmony_ci	struct clk_fractional_divider *div = NULL;
2198c2ecf20Sopenharmony_ci	const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (muxdiv_offset < 0)
2228c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (child && child->branch_type != branch_mux) {
2258c2ecf20Sopenharmony_ci		pr_err("%s: fractional child clock for %s can only be a mux\n",
2268c2ecf20Sopenharmony_ci		       __func__, name);
2278c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
2318c2ecf20Sopenharmony_ci	if (!frac)
2328c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (gate_offset >= 0) {
2358c2ecf20Sopenharmony_ci		gate = &frac->gate;
2368c2ecf20Sopenharmony_ci		gate->flags = gate_flags;
2378c2ecf20Sopenharmony_ci		gate->reg = base + gate_offset;
2388c2ecf20Sopenharmony_ci		gate->bit_idx = gate_shift;
2398c2ecf20Sopenharmony_ci		gate->lock = lock;
2408c2ecf20Sopenharmony_ci		gate_ops = &clk_gate_ops;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	div = &frac->div;
2448c2ecf20Sopenharmony_ci	div->flags = div_flags;
2458c2ecf20Sopenharmony_ci	div->reg = base + muxdiv_offset;
2468c2ecf20Sopenharmony_ci	div->mshift = 16;
2478c2ecf20Sopenharmony_ci	div->mwidth = 16;
2488c2ecf20Sopenharmony_ci	div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift;
2498c2ecf20Sopenharmony_ci	div->nshift = 0;
2508c2ecf20Sopenharmony_ci	div->nwidth = 16;
2518c2ecf20Sopenharmony_ci	div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
2528c2ecf20Sopenharmony_ci	div->lock = lock;
2538c2ecf20Sopenharmony_ci	div->approximation = rockchip_fractional_approximation;
2548c2ecf20Sopenharmony_ci	div_ops = &clk_fractional_divider_ops;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
2578c2ecf20Sopenharmony_ci				       NULL, NULL,
2588c2ecf20Sopenharmony_ci				       &div->hw, div_ops,
2598c2ecf20Sopenharmony_ci				       gate ? &gate->hw : NULL, gate_ops,
2608c2ecf20Sopenharmony_ci				       flags | CLK_SET_RATE_UNGATE);
2618c2ecf20Sopenharmony_ci	if (IS_ERR(hw)) {
2628c2ecf20Sopenharmony_ci		kfree(frac);
2638c2ecf20Sopenharmony_ci		return ERR_CAST(hw);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (child) {
2678c2ecf20Sopenharmony_ci		struct clk_mux *frac_mux = &frac->mux;
2688c2ecf20Sopenharmony_ci		struct clk_init_data init;
2698c2ecf20Sopenharmony_ci		struct clk *mux_clk;
2708c2ecf20Sopenharmony_ci		int ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		frac->mux_frac_idx = match_string(child->parent_names,
2738c2ecf20Sopenharmony_ci						  child->num_parents, name);
2748c2ecf20Sopenharmony_ci		frac->mux_ops = &clk_mux_ops;
2758c2ecf20Sopenharmony_ci		frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		frac_mux->reg = base + child->muxdiv_offset;
2788c2ecf20Sopenharmony_ci		frac_mux->shift = child->mux_shift;
2798c2ecf20Sopenharmony_ci		frac_mux->mask = BIT(child->mux_width) - 1;
2808c2ecf20Sopenharmony_ci		frac_mux->flags = child->mux_flags;
2818c2ecf20Sopenharmony_ci		frac_mux->lock = lock;
2828c2ecf20Sopenharmony_ci		frac_mux->hw.init = &init;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		init.name = child->name;
2858c2ecf20Sopenharmony_ci		init.flags = child->flags | CLK_SET_RATE_PARENT;
2868c2ecf20Sopenharmony_ci		init.ops = frac->mux_ops;
2878c2ecf20Sopenharmony_ci		init.parent_names = child->parent_names;
2888c2ecf20Sopenharmony_ci		init.num_parents = child->num_parents;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		mux_clk = clk_register(NULL, &frac_mux->hw);
2918c2ecf20Sopenharmony_ci		if (IS_ERR(mux_clk)) {
2928c2ecf20Sopenharmony_ci			kfree(frac);
2938c2ecf20Sopenharmony_ci			return mux_clk;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		rockchip_clk_add_lookup(ctx, mux_clk, child->id);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		/* notifier on the fraction divider to catch rate changes */
2998c2ecf20Sopenharmony_ci		if (frac->mux_frac_idx >= 0) {
3008c2ecf20Sopenharmony_ci			pr_debug("%s: found fractional parent in mux at pos %d\n",
3018c2ecf20Sopenharmony_ci				 __func__, frac->mux_frac_idx);
3028c2ecf20Sopenharmony_ci			ret = clk_notifier_register(hw->clk, &frac->clk_nb);
3038c2ecf20Sopenharmony_ci			if (ret)
3048c2ecf20Sopenharmony_ci				pr_err("%s: failed to register clock notifier for %s\n",
3058c2ecf20Sopenharmony_ci						__func__, name);
3068c2ecf20Sopenharmony_ci		} else {
3078c2ecf20Sopenharmony_ci			pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n",
3088c2ecf20Sopenharmony_ci				__func__, name, child->name);
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return hw->clk;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic struct clk *rockchip_clk_register_factor_branch(const char *name,
3168c2ecf20Sopenharmony_ci		const char *const *parent_names, u8 num_parents,
3178c2ecf20Sopenharmony_ci		void __iomem *base, unsigned int mult, unsigned int div,
3188c2ecf20Sopenharmony_ci		int gate_offset, u8 gate_shift, u8 gate_flags,
3198c2ecf20Sopenharmony_ci		unsigned long flags, spinlock_t *lock)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct clk_hw *hw;
3228c2ecf20Sopenharmony_ci	struct clk_gate *gate = NULL;
3238c2ecf20Sopenharmony_ci	struct clk_fixed_factor *fix = NULL;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* without gate, register a simple factor clock */
3268c2ecf20Sopenharmony_ci	if (gate_offset == 0) {
3278c2ecf20Sopenharmony_ci		return clk_register_fixed_factor(NULL, name,
3288c2ecf20Sopenharmony_ci				parent_names[0], flags, mult,
3298c2ecf20Sopenharmony_ci				div);
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
3338c2ecf20Sopenharmony_ci	if (!gate)
3348c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	gate->flags = gate_flags;
3378c2ecf20Sopenharmony_ci	gate->reg = base + gate_offset;
3388c2ecf20Sopenharmony_ci	gate->bit_idx = gate_shift;
3398c2ecf20Sopenharmony_ci	gate->lock = lock;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	fix = kzalloc(sizeof(*fix), GFP_KERNEL);
3428c2ecf20Sopenharmony_ci	if (!fix) {
3438c2ecf20Sopenharmony_ci		kfree(gate);
3448c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	fix->mult = mult;
3488c2ecf20Sopenharmony_ci	fix->div = div;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
3518c2ecf20Sopenharmony_ci				       NULL, NULL,
3528c2ecf20Sopenharmony_ci				       &fix->hw, &clk_fixed_factor_ops,
3538c2ecf20Sopenharmony_ci				       &gate->hw, &clk_gate_ops, flags);
3548c2ecf20Sopenharmony_ci	if (IS_ERR(hw)) {
3558c2ecf20Sopenharmony_ci		kfree(fix);
3568c2ecf20Sopenharmony_ci		kfree(gate);
3578c2ecf20Sopenharmony_ci		return ERR_CAST(hw);
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return hw->clk;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistruct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
3648c2ecf20Sopenharmony_ci						void __iomem *base,
3658c2ecf20Sopenharmony_ci						unsigned long nr_clks)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct rockchip_clk_provider *ctx;
3688c2ecf20Sopenharmony_ci	struct clk **clk_table;
3698c2ecf20Sopenharmony_ci	int i;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
3728c2ecf20Sopenharmony_ci	if (!ctx)
3738c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
3768c2ecf20Sopenharmony_ci	if (!clk_table)
3778c2ecf20Sopenharmony_ci		goto err_free;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	for (i = 0; i < nr_clks; ++i)
3808c2ecf20Sopenharmony_ci		clk_table[i] = ERR_PTR(-ENOENT);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	ctx->reg_base = base;
3838c2ecf20Sopenharmony_ci	ctx->clk_data.clks = clk_table;
3848c2ecf20Sopenharmony_ci	ctx->clk_data.clk_num = nr_clks;
3858c2ecf20Sopenharmony_ci	ctx->cru_node = np;
3868c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->lock);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
3898c2ecf20Sopenharmony_ci						   "rockchip,grf");
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return ctx;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cierr_free:
3948c2ecf20Sopenharmony_ci	kfree(ctx);
3958c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_init);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_civoid rockchip_clk_of_add_provider(struct device_node *np,
4008c2ecf20Sopenharmony_ci				  struct rockchip_clk_provider *ctx)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	if (of_clk_add_provider(np, of_clk_src_onecell_get,
4038c2ecf20Sopenharmony_ci				&ctx->clk_data))
4048c2ecf20Sopenharmony_ci		pr_err("%s: could not register clk provider\n", __func__);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_civoid rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
4098c2ecf20Sopenharmony_ci			     struct clk *clk, unsigned int id)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	if (ctx->clk_data.clks && id)
4128c2ecf20Sopenharmony_ci		ctx->clk_data.clks[id] = clk;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_add_lookup);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_civoid rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
4178c2ecf20Sopenharmony_ci				struct rockchip_pll_clock *list,
4188c2ecf20Sopenharmony_ci				unsigned int nr_pll, int grf_lock_offset)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct clk *clk;
4218c2ecf20Sopenharmony_ci	int idx;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	for (idx = 0; idx < nr_pll; idx++, list++) {
4248c2ecf20Sopenharmony_ci		clk = rockchip_clk_register_pll(ctx, list->type, list->name,
4258c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
4268c2ecf20Sopenharmony_ci				list->con_offset, grf_lock_offset,
4278c2ecf20Sopenharmony_ci				list->lock_shift, list->mode_offset,
4288c2ecf20Sopenharmony_ci				list->mode_shift, list->rate_table,
4298c2ecf20Sopenharmony_ci				list->flags, list->pll_flags);
4308c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
4318c2ecf20Sopenharmony_ci			pr_err("%s: failed to register clock %s\n", __func__,
4328c2ecf20Sopenharmony_ci				list->name);
4338c2ecf20Sopenharmony_ci			continue;
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		rockchip_clk_add_lookup(ctx, clk, list->id);
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_civoid rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
4428c2ecf20Sopenharmony_ci				    struct rockchip_clk_branch *list,
4438c2ecf20Sopenharmony_ci				    unsigned int nr_clk)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct clk *clk = NULL;
4468c2ecf20Sopenharmony_ci	unsigned int idx;
4478c2ecf20Sopenharmony_ci	unsigned long flags;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	for (idx = 0; idx < nr_clk; idx++, list++) {
4508c2ecf20Sopenharmony_ci		flags = list->flags;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		/* catch simple muxes */
4538c2ecf20Sopenharmony_ci		switch (list->branch_type) {
4548c2ecf20Sopenharmony_ci		case branch_mux:
4558c2ecf20Sopenharmony_ci			clk = clk_register_mux(NULL, list->name,
4568c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
4578c2ecf20Sopenharmony_ci				flags, ctx->reg_base + list->muxdiv_offset,
4588c2ecf20Sopenharmony_ci				list->mux_shift, list->mux_width,
4598c2ecf20Sopenharmony_ci				list->mux_flags, &ctx->lock);
4608c2ecf20Sopenharmony_ci			break;
4618c2ecf20Sopenharmony_ci		case branch_muxgrf:
4628c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_muxgrf(list->name,
4638c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
4648c2ecf20Sopenharmony_ci				flags, ctx->grf, list->muxdiv_offset,
4658c2ecf20Sopenharmony_ci				list->mux_shift, list->mux_width,
4668c2ecf20Sopenharmony_ci				list->mux_flags);
4678c2ecf20Sopenharmony_ci			break;
4688c2ecf20Sopenharmony_ci		case branch_divider:
4698c2ecf20Sopenharmony_ci			if (list->div_table)
4708c2ecf20Sopenharmony_ci				clk = clk_register_divider_table(NULL,
4718c2ecf20Sopenharmony_ci					list->name, list->parent_names[0],
4728c2ecf20Sopenharmony_ci					flags,
4738c2ecf20Sopenharmony_ci					ctx->reg_base + list->muxdiv_offset,
4748c2ecf20Sopenharmony_ci					list->div_shift, list->div_width,
4758c2ecf20Sopenharmony_ci					list->div_flags, list->div_table,
4768c2ecf20Sopenharmony_ci					&ctx->lock);
4778c2ecf20Sopenharmony_ci			else
4788c2ecf20Sopenharmony_ci				clk = clk_register_divider(NULL, list->name,
4798c2ecf20Sopenharmony_ci					list->parent_names[0], flags,
4808c2ecf20Sopenharmony_ci					ctx->reg_base + list->muxdiv_offset,
4818c2ecf20Sopenharmony_ci					list->div_shift, list->div_width,
4828c2ecf20Sopenharmony_ci					list->div_flags, &ctx->lock);
4838c2ecf20Sopenharmony_ci			break;
4848c2ecf20Sopenharmony_ci		case branch_fraction_divider:
4858c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_frac_branch(ctx, list->name,
4868c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
4878c2ecf20Sopenharmony_ci				ctx->reg_base, list->muxdiv_offset,
4888c2ecf20Sopenharmony_ci				list->div_flags,
4898c2ecf20Sopenharmony_ci				list->gate_offset, list->gate_shift,
4908c2ecf20Sopenharmony_ci				list->gate_flags, flags, list->child,
4918c2ecf20Sopenharmony_ci				&ctx->lock);
4928c2ecf20Sopenharmony_ci			break;
4938c2ecf20Sopenharmony_ci		case branch_half_divider:
4948c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_halfdiv(list->name,
4958c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
4968c2ecf20Sopenharmony_ci				ctx->reg_base, list->muxdiv_offset,
4978c2ecf20Sopenharmony_ci				list->mux_shift, list->mux_width,
4988c2ecf20Sopenharmony_ci				list->mux_flags, list->div_shift,
4998c2ecf20Sopenharmony_ci				list->div_width, list->div_flags,
5008c2ecf20Sopenharmony_ci				list->gate_offset, list->gate_shift,
5018c2ecf20Sopenharmony_ci				list->gate_flags, flags, &ctx->lock);
5028c2ecf20Sopenharmony_ci			break;
5038c2ecf20Sopenharmony_ci		case branch_gate:
5048c2ecf20Sopenharmony_ci			flags |= CLK_SET_RATE_PARENT;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci			clk = clk_register_gate(NULL, list->name,
5078c2ecf20Sopenharmony_ci				list->parent_names[0], flags,
5088c2ecf20Sopenharmony_ci				ctx->reg_base + list->gate_offset,
5098c2ecf20Sopenharmony_ci				list->gate_shift, list->gate_flags, &ctx->lock);
5108c2ecf20Sopenharmony_ci			break;
5118c2ecf20Sopenharmony_ci		case branch_composite:
5128c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_branch(list->name,
5138c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
5148c2ecf20Sopenharmony_ci				ctx->reg_base, list->muxdiv_offset,
5158c2ecf20Sopenharmony_ci				list->mux_shift,
5168c2ecf20Sopenharmony_ci				list->mux_width, list->mux_flags,
5178c2ecf20Sopenharmony_ci				list->div_offset, list->div_shift, list->div_width,
5188c2ecf20Sopenharmony_ci				list->div_flags, list->div_table,
5198c2ecf20Sopenharmony_ci				list->gate_offset, list->gate_shift,
5208c2ecf20Sopenharmony_ci				list->gate_flags, flags, &ctx->lock);
5218c2ecf20Sopenharmony_ci			break;
5228c2ecf20Sopenharmony_ci		case branch_mmc:
5238c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_mmc(
5248c2ecf20Sopenharmony_ci				list->name,
5258c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
5268c2ecf20Sopenharmony_ci				ctx->reg_base + list->muxdiv_offset,
5278c2ecf20Sopenharmony_ci				list->div_shift
5288c2ecf20Sopenharmony_ci			);
5298c2ecf20Sopenharmony_ci			break;
5308c2ecf20Sopenharmony_ci		case branch_inverter:
5318c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_inverter(
5328c2ecf20Sopenharmony_ci				list->name, list->parent_names,
5338c2ecf20Sopenharmony_ci				list->num_parents,
5348c2ecf20Sopenharmony_ci				ctx->reg_base + list->muxdiv_offset,
5358c2ecf20Sopenharmony_ci				list->div_shift, list->div_flags, &ctx->lock);
5368c2ecf20Sopenharmony_ci			break;
5378c2ecf20Sopenharmony_ci		case branch_factor:
5388c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_factor_branch(
5398c2ecf20Sopenharmony_ci				list->name, list->parent_names,
5408c2ecf20Sopenharmony_ci				list->num_parents, ctx->reg_base,
5418c2ecf20Sopenharmony_ci				list->div_shift, list->div_width,
5428c2ecf20Sopenharmony_ci				list->gate_offset, list->gate_shift,
5438c2ecf20Sopenharmony_ci				list->gate_flags, flags, &ctx->lock);
5448c2ecf20Sopenharmony_ci			break;
5458c2ecf20Sopenharmony_ci		case branch_ddrclk:
5468c2ecf20Sopenharmony_ci			clk = rockchip_clk_register_ddrclk(
5478c2ecf20Sopenharmony_ci				list->name, list->flags,
5488c2ecf20Sopenharmony_ci				list->parent_names, list->num_parents,
5498c2ecf20Sopenharmony_ci				list->muxdiv_offset, list->mux_shift,
5508c2ecf20Sopenharmony_ci				list->mux_width, list->div_shift,
5518c2ecf20Sopenharmony_ci				list->div_width, list->div_flags,
5528c2ecf20Sopenharmony_ci				ctx->reg_base, &ctx->lock);
5538c2ecf20Sopenharmony_ci			break;
5548c2ecf20Sopenharmony_ci		}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		/* none of the cases above matched */
5578c2ecf20Sopenharmony_ci		if (!clk) {
5588c2ecf20Sopenharmony_ci			pr_err("%s: unknown clock type %d\n",
5598c2ecf20Sopenharmony_ci			       __func__, list->branch_type);
5608c2ecf20Sopenharmony_ci			continue;
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
5648c2ecf20Sopenharmony_ci			pr_err("%s: failed to register clock %s: %ld\n",
5658c2ecf20Sopenharmony_ci			       __func__, list->name, PTR_ERR(clk));
5668c2ecf20Sopenharmony_ci			continue;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		rockchip_clk_add_lookup(ctx, clk, list->id);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_civoid rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
5758c2ecf20Sopenharmony_ci				  unsigned int lookup_id,
5768c2ecf20Sopenharmony_ci				  const char *name, const char *const *parent_names,
5778c2ecf20Sopenharmony_ci				  u8 num_parents,
5788c2ecf20Sopenharmony_ci				  const struct rockchip_cpuclk_reg_data *reg_data,
5798c2ecf20Sopenharmony_ci				  const struct rockchip_cpuclk_rate_table *rates,
5808c2ecf20Sopenharmony_ci				  int nrates)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct clk *clk;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
5858c2ecf20Sopenharmony_ci					   reg_data, rates, nrates,
5868c2ecf20Sopenharmony_ci					   ctx->reg_base, &ctx->lock);
5878c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
5888c2ecf20Sopenharmony_ci		pr_err("%s: failed to register clock %s: %ld\n",
5898c2ecf20Sopenharmony_ci		       __func__, name, PTR_ERR(clk));
5908c2ecf20Sopenharmony_ci		return;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	rockchip_clk_add_lookup(ctx, clk, lookup_id);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_civoid rockchip_clk_protect_critical(const char *const clocks[],
5988c2ecf20Sopenharmony_ci				   int nclocks)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	int i;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/* Protect the clocks that needs to stay on */
6038c2ecf20Sopenharmony_ci	for (i = 0; i < nclocks; i++) {
6048c2ecf20Sopenharmony_ci		struct clk *clk = __clk_lookup(clocks[i]);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		if (clk)
6078c2ecf20Sopenharmony_ci			clk_prepare_enable(clk);
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_protect_critical);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic void __iomem *rst_base;
6138c2ecf20Sopenharmony_cistatic unsigned int reg_restart;
6148c2ecf20Sopenharmony_cistatic void (*cb_restart)(void);
6158c2ecf20Sopenharmony_cistatic int rockchip_restart_notify(struct notifier_block *this,
6168c2ecf20Sopenharmony_ci				   unsigned long mode, void *cmd)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	if (cb_restart)
6198c2ecf20Sopenharmony_ci		cb_restart();
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	writel(0xfdb9, rst_base + reg_restart);
6228c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic struct notifier_block rockchip_restart_handler = {
6268c2ecf20Sopenharmony_ci	.notifier_call = rockchip_restart_notify,
6278c2ecf20Sopenharmony_ci	.priority = 128,
6288c2ecf20Sopenharmony_ci};
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_civoid
6318c2ecf20Sopenharmony_cirockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
6328c2ecf20Sopenharmony_ci				   unsigned int reg,
6338c2ecf20Sopenharmony_ci				   void (*cb)(void))
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	int ret;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	rst_base = ctx->reg_base;
6388c2ecf20Sopenharmony_ci	reg_restart = reg;
6398c2ecf20Sopenharmony_ci	cb_restart = cb;
6408c2ecf20Sopenharmony_ci	ret = register_restart_handler(&rockchip_restart_handler);
6418c2ecf20Sopenharmony_ci	if (ret)
6428c2ecf20Sopenharmony_ci		pr_err("%s: cannot register restart handler, %d\n",
6438c2ecf20Sopenharmony_ci		       __func__, ret);
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_register_restart_notifier);
646