xref: /openbmc/linux/drivers/clk/rockchip/clk.c (revision 8ca1ca8f6039f19673fb61552f276b848539dbd6)
1a245fecbSHeiko Stübner /*
2a245fecbSHeiko Stübner  * Copyright (c) 2014 MundoReader S.L.
3a245fecbSHeiko Stübner  * Author: Heiko Stuebner <heiko@sntech.de>
4a245fecbSHeiko Stübner  *
5a245fecbSHeiko Stübner  * based on
6a245fecbSHeiko Stübner  *
7a245fecbSHeiko Stübner  * samsung/clk.c
8a245fecbSHeiko Stübner  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
9a245fecbSHeiko Stübner  * Copyright (c) 2013 Linaro Ltd.
10a245fecbSHeiko Stübner  * Author: Thomas Abraham <thomas.ab@samsung.com>
11a245fecbSHeiko Stübner  *
12a245fecbSHeiko Stübner  * This program is free software; you can redistribute it and/or modify
13a245fecbSHeiko Stübner  * it under the terms of the GNU General Public License as published by
14a245fecbSHeiko Stübner  * the Free Software Foundation; either version 2 of the License, or
15a245fecbSHeiko Stübner  * (at your option) any later version.
16a245fecbSHeiko Stübner  *
17a245fecbSHeiko Stübner  * This program is distributed in the hope that it will be useful,
18a245fecbSHeiko Stübner  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19a245fecbSHeiko Stübner  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20a245fecbSHeiko Stübner  * GNU General Public License for more details.
21a245fecbSHeiko Stübner  */
22a245fecbSHeiko Stübner 
23a245fecbSHeiko Stübner #include <linux/slab.h>
24a245fecbSHeiko Stübner #include <linux/clk.h>
25a245fecbSHeiko Stübner #include <linux/clk-provider.h>
2690c59025SHeiko Stübner #include <linux/mfd/syscon.h>
2790c59025SHeiko Stübner #include <linux/regmap.h>
286f1294b5SHeiko Stübner #include <linux/reboot.h>
29a245fecbSHeiko Stübner #include "clk.h"
30a245fecbSHeiko Stübner 
31a245fecbSHeiko Stübner /**
32a245fecbSHeiko Stübner  * Register a clock branch.
33a245fecbSHeiko Stübner  * Most clock branches have a form like
34a245fecbSHeiko Stübner  *
35a245fecbSHeiko Stübner  * src1 --|--\
36a245fecbSHeiko Stübner  *        |M |--[GATE]-[DIV]-
37a245fecbSHeiko Stübner  * src2 --|--/
38a245fecbSHeiko Stübner  *
39a245fecbSHeiko Stübner  * sometimes without one of those components.
40a245fecbSHeiko Stübner  */
411a4b1819SHeiko Stübner static struct clk *rockchip_clk_register_branch(const char *name,
424a1caed3SUwe Kleine-König 		const char *const *parent_names, u8 num_parents, void __iomem *base,
43a245fecbSHeiko Stübner 		int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
44a245fecbSHeiko Stübner 		u8 div_shift, u8 div_width, u8 div_flags,
45a245fecbSHeiko Stübner 		struct clk_div_table *div_table, int gate_offset,
46a245fecbSHeiko Stübner 		u8 gate_shift, u8 gate_flags, unsigned long flags,
47a245fecbSHeiko Stübner 		spinlock_t *lock)
48a245fecbSHeiko Stübner {
49a245fecbSHeiko Stübner 	struct clk *clk;
50a245fecbSHeiko Stübner 	struct clk_mux *mux = NULL;
51a245fecbSHeiko Stübner 	struct clk_gate *gate = NULL;
52a245fecbSHeiko Stübner 	struct clk_divider *div = NULL;
53a245fecbSHeiko Stübner 	const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
54a245fecbSHeiko Stübner 			     *gate_ops = NULL;
55a245fecbSHeiko Stübner 
56a245fecbSHeiko Stübner 	if (num_parents > 1) {
57a245fecbSHeiko Stübner 		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
58a245fecbSHeiko Stübner 		if (!mux)
59a245fecbSHeiko Stübner 			return ERR_PTR(-ENOMEM);
60a245fecbSHeiko Stübner 
61a245fecbSHeiko Stübner 		mux->reg = base + muxdiv_offset;
62a245fecbSHeiko Stübner 		mux->shift = mux_shift;
63a245fecbSHeiko Stübner 		mux->mask = BIT(mux_width) - 1;
64a245fecbSHeiko Stübner 		mux->flags = mux_flags;
65a245fecbSHeiko Stübner 		mux->lock = lock;
66a245fecbSHeiko Stübner 		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
67a245fecbSHeiko Stübner 							: &clk_mux_ops;
68a245fecbSHeiko Stübner 	}
69a245fecbSHeiko Stübner 
70a245fecbSHeiko Stübner 	if (gate_offset >= 0) {
71a245fecbSHeiko Stübner 		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
72a245fecbSHeiko Stübner 		if (!gate)
73a245fecbSHeiko Stübner 			return ERR_PTR(-ENOMEM);
74a245fecbSHeiko Stübner 
75a245fecbSHeiko Stübner 		gate->flags = gate_flags;
76a245fecbSHeiko Stübner 		gate->reg = base + gate_offset;
77a245fecbSHeiko Stübner 		gate->bit_idx = gate_shift;
78a245fecbSHeiko Stübner 		gate->lock = lock;
79a245fecbSHeiko Stübner 		gate_ops = &clk_gate_ops;
80a245fecbSHeiko Stübner 	}
81a245fecbSHeiko Stübner 
82a245fecbSHeiko Stübner 	if (div_width > 0) {
83a245fecbSHeiko Stübner 		div = kzalloc(sizeof(*div), GFP_KERNEL);
84a245fecbSHeiko Stübner 		if (!div)
85a245fecbSHeiko Stübner 			return ERR_PTR(-ENOMEM);
86a245fecbSHeiko Stübner 
87a245fecbSHeiko Stübner 		div->flags = div_flags;
88a245fecbSHeiko Stübner 		div->reg = base + muxdiv_offset;
89a245fecbSHeiko Stübner 		div->shift = div_shift;
90a245fecbSHeiko Stübner 		div->width = div_width;
91a245fecbSHeiko Stübner 		div->lock = lock;
92a245fecbSHeiko Stübner 		div->table = div_table;
93e6d5e7d9SJames Hogan 		div_ops = &clk_divider_ops;
94a245fecbSHeiko Stübner 	}
95a245fecbSHeiko Stübner 
96a245fecbSHeiko Stübner 	clk = clk_register_composite(NULL, name, parent_names, num_parents,
97a245fecbSHeiko Stübner 				     mux ? &mux->hw : NULL, mux_ops,
98a245fecbSHeiko Stübner 				     div ? &div->hw : NULL, div_ops,
99a245fecbSHeiko Stübner 				     gate ? &gate->hw : NULL, gate_ops,
100a245fecbSHeiko Stübner 				     flags);
101a245fecbSHeiko Stübner 
102a245fecbSHeiko Stübner 	return clk;
103a245fecbSHeiko Stübner }
104a245fecbSHeiko Stübner 
105*8ca1ca8fSHeiko Stuebner struct rockchip_clk_frac {
106*8ca1ca8fSHeiko Stuebner 	struct notifier_block			clk_nb;
107*8ca1ca8fSHeiko Stuebner 	struct clk_fractional_divider		div;
108*8ca1ca8fSHeiko Stuebner 	struct clk_gate				gate;
109*8ca1ca8fSHeiko Stuebner 
110*8ca1ca8fSHeiko Stuebner 	struct clk_mux				mux;
111*8ca1ca8fSHeiko Stuebner 	const struct clk_ops			*mux_ops;
112*8ca1ca8fSHeiko Stuebner 	int					mux_frac_idx;
113*8ca1ca8fSHeiko Stuebner 
114*8ca1ca8fSHeiko Stuebner 	bool					rate_change_remuxed;
115*8ca1ca8fSHeiko Stuebner 	int					rate_change_idx;
116*8ca1ca8fSHeiko Stuebner };
117*8ca1ca8fSHeiko Stuebner 
118*8ca1ca8fSHeiko Stuebner #define to_rockchip_clk_frac_nb(nb) \
119*8ca1ca8fSHeiko Stuebner 			container_of(nb, struct rockchip_clk_frac, clk_nb)
120*8ca1ca8fSHeiko Stuebner 
121*8ca1ca8fSHeiko Stuebner static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
122*8ca1ca8fSHeiko Stuebner 					 unsigned long event, void *data)
123*8ca1ca8fSHeiko Stuebner {
124*8ca1ca8fSHeiko Stuebner 	struct clk_notifier_data *ndata = data;
125*8ca1ca8fSHeiko Stuebner 	struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb);
126*8ca1ca8fSHeiko Stuebner 	struct clk_mux *frac_mux = &frac->mux;
127*8ca1ca8fSHeiko Stuebner 	int ret = 0;
128*8ca1ca8fSHeiko Stuebner 
129*8ca1ca8fSHeiko Stuebner 	pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
130*8ca1ca8fSHeiko Stuebner 		 __func__, event, ndata->old_rate, ndata->new_rate);
131*8ca1ca8fSHeiko Stuebner 	if (event == PRE_RATE_CHANGE) {
132*8ca1ca8fSHeiko Stuebner 		frac->rate_change_idx = frac->mux_ops->get_parent(&frac_mux->hw);
133*8ca1ca8fSHeiko Stuebner 		if (frac->rate_change_idx != frac->mux_frac_idx) {
134*8ca1ca8fSHeiko Stuebner 			frac->mux_ops->set_parent(&frac_mux->hw, frac->mux_frac_idx);
135*8ca1ca8fSHeiko Stuebner 			frac->rate_change_remuxed = 1;
136*8ca1ca8fSHeiko Stuebner 		}
137*8ca1ca8fSHeiko Stuebner 	} else if (event == POST_RATE_CHANGE) {
138*8ca1ca8fSHeiko Stuebner 		/*
139*8ca1ca8fSHeiko Stuebner 		 * The POST_RATE_CHANGE notifier runs directly after the
140*8ca1ca8fSHeiko Stuebner 		 * divider clock is set in clk_change_rate, so we'll have
141*8ca1ca8fSHeiko Stuebner 		 * remuxed back to the original parent before clk_change_rate
142*8ca1ca8fSHeiko Stuebner 		 * reaches the mux itself.
143*8ca1ca8fSHeiko Stuebner 		 */
144*8ca1ca8fSHeiko Stuebner 		if (frac->rate_change_remuxed) {
145*8ca1ca8fSHeiko Stuebner 			frac->mux_ops->set_parent(&frac_mux->hw, frac->rate_change_idx);
146*8ca1ca8fSHeiko Stuebner 			frac->rate_change_remuxed = 0;
147*8ca1ca8fSHeiko Stuebner 		}
148*8ca1ca8fSHeiko Stuebner 	}
149*8ca1ca8fSHeiko Stuebner 
150*8ca1ca8fSHeiko Stuebner 	return notifier_from_errno(ret);
151*8ca1ca8fSHeiko Stuebner }
152*8ca1ca8fSHeiko Stuebner 
153b2155a71SHeiko Stübner static struct clk *rockchip_clk_register_frac_branch(const char *name,
1544a1caed3SUwe Kleine-König 		const char *const *parent_names, u8 num_parents,
1554a1caed3SUwe Kleine-König 		void __iomem *base, int muxdiv_offset, u8 div_flags,
156b2155a71SHeiko Stübner 		int gate_offset, u8 gate_shift, u8 gate_flags,
157*8ca1ca8fSHeiko Stuebner 		unsigned long flags, struct rockchip_clk_branch *child,
158*8ca1ca8fSHeiko Stuebner 		spinlock_t *lock)
159b2155a71SHeiko Stübner {
160*8ca1ca8fSHeiko Stuebner 	struct rockchip_clk_frac *frac;
161b2155a71SHeiko Stübner 	struct clk *clk;
162b2155a71SHeiko Stübner 	struct clk_gate *gate = NULL;
163b2155a71SHeiko Stübner 	struct clk_fractional_divider *div = NULL;
164b2155a71SHeiko Stübner 	const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
165b2155a71SHeiko Stübner 
166*8ca1ca8fSHeiko Stuebner 	if (muxdiv_offset < 0)
167*8ca1ca8fSHeiko Stuebner 		return ERR_PTR(-EINVAL);
168*8ca1ca8fSHeiko Stuebner 
169*8ca1ca8fSHeiko Stuebner 	if (child && child->branch_type != branch_mux) {
170*8ca1ca8fSHeiko Stuebner 		pr_err("%s: fractional child clock for %s can only be a mux\n",
171*8ca1ca8fSHeiko Stuebner 		       __func__, name);
172*8ca1ca8fSHeiko Stuebner 		return ERR_PTR(-EINVAL);
173*8ca1ca8fSHeiko Stuebner 	}
174*8ca1ca8fSHeiko Stuebner 
175*8ca1ca8fSHeiko Stuebner 	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
176*8ca1ca8fSHeiko Stuebner 	if (!frac)
177b2155a71SHeiko Stübner 		return ERR_PTR(-ENOMEM);
178b2155a71SHeiko Stübner 
179*8ca1ca8fSHeiko Stuebner 	if (gate_offset >= 0) {
180*8ca1ca8fSHeiko Stuebner 		gate = &frac->gate;
181b2155a71SHeiko Stübner 		gate->flags = gate_flags;
182b2155a71SHeiko Stübner 		gate->reg = base + gate_offset;
183b2155a71SHeiko Stübner 		gate->bit_idx = gate_shift;
184b2155a71SHeiko Stübner 		gate->lock = lock;
185b2155a71SHeiko Stübner 		gate_ops = &clk_gate_ops;
186b2155a71SHeiko Stübner 	}
187b2155a71SHeiko Stübner 
188*8ca1ca8fSHeiko Stuebner 	div = &frac->div;
189b2155a71SHeiko Stübner 	div->flags = div_flags;
190b2155a71SHeiko Stübner 	div->reg = base + muxdiv_offset;
191b2155a71SHeiko Stübner 	div->mshift = 16;
1925d49a6e1SAndy Shevchenko 	div->mwidth = 16;
1935d49a6e1SAndy Shevchenko 	div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift;
194b2155a71SHeiko Stübner 	div->nshift = 0;
1955d49a6e1SAndy Shevchenko 	div->nwidth = 16;
1965d49a6e1SAndy Shevchenko 	div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
197b2155a71SHeiko Stübner 	div->lock = lock;
198b2155a71SHeiko Stübner 	div_ops = &clk_fractional_divider_ops;
199b2155a71SHeiko Stübner 
200b2155a71SHeiko Stübner 	clk = clk_register_composite(NULL, name, parent_names, num_parents,
201b2155a71SHeiko Stübner 				     NULL, NULL,
202b2155a71SHeiko Stübner 				     &div->hw, div_ops,
203b2155a71SHeiko Stübner 				     gate ? &gate->hw : NULL, gate_ops,
204*8ca1ca8fSHeiko Stuebner 				     flags | CLK_SET_RATE_UNGATE);
205*8ca1ca8fSHeiko Stuebner 	if (IS_ERR(clk)) {
206*8ca1ca8fSHeiko Stuebner 		kfree(frac);
207*8ca1ca8fSHeiko Stuebner 		return clk;
208*8ca1ca8fSHeiko Stuebner 	}
209*8ca1ca8fSHeiko Stuebner 
210*8ca1ca8fSHeiko Stuebner 	if (child) {
211*8ca1ca8fSHeiko Stuebner 		struct clk_mux *frac_mux = &frac->mux;
212*8ca1ca8fSHeiko Stuebner 		struct clk_init_data init;
213*8ca1ca8fSHeiko Stuebner 		struct clk *mux_clk;
214*8ca1ca8fSHeiko Stuebner 		int i, ret;
215*8ca1ca8fSHeiko Stuebner 
216*8ca1ca8fSHeiko Stuebner 		frac->mux_frac_idx = -1;
217*8ca1ca8fSHeiko Stuebner 		for (i = 0; i < child->num_parents; i++) {
218*8ca1ca8fSHeiko Stuebner 			if (!strcmp(name, child->parent_names[i])) {
219*8ca1ca8fSHeiko Stuebner 				pr_debug("%s: found fractional parent in mux at pos %d\n",
220*8ca1ca8fSHeiko Stuebner 					 __func__, i);
221*8ca1ca8fSHeiko Stuebner 				frac->mux_frac_idx = i;
222*8ca1ca8fSHeiko Stuebner 				break;
223*8ca1ca8fSHeiko Stuebner 			}
224*8ca1ca8fSHeiko Stuebner 		}
225*8ca1ca8fSHeiko Stuebner 
226*8ca1ca8fSHeiko Stuebner 		frac->mux_ops = &clk_mux_ops;
227*8ca1ca8fSHeiko Stuebner 		frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb;
228*8ca1ca8fSHeiko Stuebner 
229*8ca1ca8fSHeiko Stuebner 		frac_mux->reg = base + child->muxdiv_offset;
230*8ca1ca8fSHeiko Stuebner 		frac_mux->shift = child->mux_shift;
231*8ca1ca8fSHeiko Stuebner 		frac_mux->mask = BIT(child->mux_width) - 1;
232*8ca1ca8fSHeiko Stuebner 		frac_mux->flags = child->mux_flags;
233*8ca1ca8fSHeiko Stuebner 		frac_mux->lock = lock;
234*8ca1ca8fSHeiko Stuebner 		frac_mux->hw.init = &init;
235*8ca1ca8fSHeiko Stuebner 
236*8ca1ca8fSHeiko Stuebner 		init.name = child->name;
237*8ca1ca8fSHeiko Stuebner 		init.flags = child->flags | CLK_SET_RATE_PARENT;
238*8ca1ca8fSHeiko Stuebner 		init.ops = frac->mux_ops;
239*8ca1ca8fSHeiko Stuebner 		init.parent_names = child->parent_names;
240*8ca1ca8fSHeiko Stuebner 		init.num_parents = child->num_parents;
241*8ca1ca8fSHeiko Stuebner 
242*8ca1ca8fSHeiko Stuebner 		mux_clk = clk_register(NULL, &frac_mux->hw);
243*8ca1ca8fSHeiko Stuebner 		if (IS_ERR(mux_clk))
244*8ca1ca8fSHeiko Stuebner 			return clk;
245*8ca1ca8fSHeiko Stuebner 
246*8ca1ca8fSHeiko Stuebner 		rockchip_clk_add_lookup(mux_clk, child->id);
247*8ca1ca8fSHeiko Stuebner 
248*8ca1ca8fSHeiko Stuebner 		/* notifier on the fraction divider to catch rate changes */
249*8ca1ca8fSHeiko Stuebner 		if (frac->mux_frac_idx >= 0) {
250*8ca1ca8fSHeiko Stuebner 			ret = clk_notifier_register(clk, &frac->clk_nb);
251*8ca1ca8fSHeiko Stuebner 			if (ret)
252*8ca1ca8fSHeiko Stuebner 				pr_err("%s: failed to register clock notifier for %s\n",
253*8ca1ca8fSHeiko Stuebner 						__func__, name);
254*8ca1ca8fSHeiko Stuebner 		} else {
255*8ca1ca8fSHeiko Stuebner 			pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n",
256*8ca1ca8fSHeiko Stuebner 				__func__, name, child->name);
257*8ca1ca8fSHeiko Stuebner 		}
258*8ca1ca8fSHeiko Stuebner 	}
259b2155a71SHeiko Stübner 
260b2155a71SHeiko Stübner 	return clk;
261b2155a71SHeiko Stübner }
262b2155a71SHeiko Stübner 
263a245fecbSHeiko Stübner static DEFINE_SPINLOCK(clk_lock);
264a245fecbSHeiko Stübner static struct clk **clk_table;
265a245fecbSHeiko Stübner static void __iomem *reg_base;
266a245fecbSHeiko Stübner static struct clk_onecell_data clk_data;
26790c59025SHeiko Stübner static struct device_node *cru_node;
26890c59025SHeiko Stübner static struct regmap *grf;
269a245fecbSHeiko Stübner 
270a245fecbSHeiko Stübner void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
271a245fecbSHeiko Stübner 			      unsigned long nr_clks)
272a245fecbSHeiko Stübner {
273a245fecbSHeiko Stübner 	reg_base = base;
27490c59025SHeiko Stübner 	cru_node = np;
27590c59025SHeiko Stübner 	grf = ERR_PTR(-EPROBE_DEFER);
276a245fecbSHeiko Stübner 
277a245fecbSHeiko Stübner 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
278a245fecbSHeiko Stübner 	if (!clk_table)
279a245fecbSHeiko Stübner 		pr_err("%s: could not allocate clock lookup table\n", __func__);
280a245fecbSHeiko Stübner 
281a245fecbSHeiko Stübner 	clk_data.clks = clk_table;
282a245fecbSHeiko Stübner 	clk_data.clk_num = nr_clks;
283a245fecbSHeiko Stübner 	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
284a245fecbSHeiko Stübner }
285a245fecbSHeiko Stübner 
28690c59025SHeiko Stübner struct regmap *rockchip_clk_get_grf(void)
28790c59025SHeiko Stübner {
28890c59025SHeiko Stübner 	if (IS_ERR(grf))
28990c59025SHeiko Stübner 		grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf");
29090c59025SHeiko Stübner 	return grf;
29190c59025SHeiko Stübner }
29290c59025SHeiko Stübner 
293a245fecbSHeiko Stübner void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
294a245fecbSHeiko Stübner {
295a245fecbSHeiko Stübner 	if (clk_table && id)
296a245fecbSHeiko Stübner 		clk_table[id] = clk;
297a245fecbSHeiko Stübner }
298a245fecbSHeiko Stübner 
29990c59025SHeiko Stübner void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
30090c59025SHeiko Stübner 				unsigned int nr_pll, int grf_lock_offset)
30190c59025SHeiko Stübner {
30290c59025SHeiko Stübner 	struct clk *clk;
30390c59025SHeiko Stübner 	int idx;
30490c59025SHeiko Stübner 
30590c59025SHeiko Stübner 	for (idx = 0; idx < nr_pll; idx++, list++) {
30690c59025SHeiko Stübner 		clk = rockchip_clk_register_pll(list->type, list->name,
30790c59025SHeiko Stübner 				list->parent_names, list->num_parents,
30890c59025SHeiko Stübner 				reg_base, list->con_offset, grf_lock_offset,
30990c59025SHeiko Stübner 				list->lock_shift, list->mode_offset,
3104f8a7c54SHeiko Stuebner 				list->mode_shift, list->rate_table,
3114f8a7c54SHeiko Stuebner 				list->pll_flags, &clk_lock);
31290c59025SHeiko Stübner 		if (IS_ERR(clk)) {
31390c59025SHeiko Stübner 			pr_err("%s: failed to register clock %s\n", __func__,
31490c59025SHeiko Stübner 				list->name);
31590c59025SHeiko Stübner 			continue;
31690c59025SHeiko Stübner 		}
31790c59025SHeiko Stübner 
31890c59025SHeiko Stübner 		rockchip_clk_add_lookup(clk, list->id);
31990c59025SHeiko Stübner 	}
32090c59025SHeiko Stübner }
32190c59025SHeiko Stübner 
322a245fecbSHeiko Stübner void __init rockchip_clk_register_branches(
323a245fecbSHeiko Stübner 				      struct rockchip_clk_branch *list,
324a245fecbSHeiko Stübner 				      unsigned int nr_clk)
325a245fecbSHeiko Stübner {
326a245fecbSHeiko Stübner 	struct clk *clk = NULL;
327a245fecbSHeiko Stübner 	unsigned int idx;
328a245fecbSHeiko Stübner 	unsigned long flags;
329a245fecbSHeiko Stübner 
330a245fecbSHeiko Stübner 	for (idx = 0; idx < nr_clk; idx++, list++) {
331a245fecbSHeiko Stübner 		flags = list->flags;
332a245fecbSHeiko Stübner 
333a245fecbSHeiko Stübner 		/* catch simple muxes */
334a245fecbSHeiko Stübner 		switch (list->branch_type) {
335a245fecbSHeiko Stübner 		case branch_mux:
336a245fecbSHeiko Stübner 			clk = clk_register_mux(NULL, list->name,
337a245fecbSHeiko Stübner 				list->parent_names, list->num_parents,
338a245fecbSHeiko Stübner 				flags, reg_base + list->muxdiv_offset,
339a245fecbSHeiko Stübner 				list->mux_shift, list->mux_width,
340a245fecbSHeiko Stübner 				list->mux_flags, &clk_lock);
341a245fecbSHeiko Stübner 			break;
342a245fecbSHeiko Stübner 		case branch_divider:
343a245fecbSHeiko Stübner 			if (list->div_table)
344a245fecbSHeiko Stübner 				clk = clk_register_divider_table(NULL,
345a245fecbSHeiko Stübner 					list->name, list->parent_names[0],
346a245fecbSHeiko Stübner 					flags, reg_base + list->muxdiv_offset,
347a245fecbSHeiko Stübner 					list->div_shift, list->div_width,
348a245fecbSHeiko Stübner 					list->div_flags, list->div_table,
349a245fecbSHeiko Stübner 					&clk_lock);
350a245fecbSHeiko Stübner 			else
351a245fecbSHeiko Stübner 				clk = clk_register_divider(NULL, list->name,
352a245fecbSHeiko Stübner 					list->parent_names[0], flags,
353a245fecbSHeiko Stübner 					reg_base + list->muxdiv_offset,
354a245fecbSHeiko Stübner 					list->div_shift, list->div_width,
355a245fecbSHeiko Stübner 					list->div_flags, &clk_lock);
356a245fecbSHeiko Stübner 			break;
357a245fecbSHeiko Stübner 		case branch_fraction_divider:
358b2155a71SHeiko Stübner 			clk = rockchip_clk_register_frac_branch(list->name,
359b2155a71SHeiko Stübner 				list->parent_names, list->num_parents,
360b2155a71SHeiko Stübner 				reg_base, list->muxdiv_offset, list->div_flags,
361b2155a71SHeiko Stübner 				list->gate_offset, list->gate_shift,
362*8ca1ca8fSHeiko Stuebner 				list->gate_flags, flags, list->child,
363*8ca1ca8fSHeiko Stuebner 				&clk_lock);
364a245fecbSHeiko Stübner 			break;
365a245fecbSHeiko Stübner 		case branch_gate:
366a245fecbSHeiko Stübner 			flags |= CLK_SET_RATE_PARENT;
367a245fecbSHeiko Stübner 
368a245fecbSHeiko Stübner 			clk = clk_register_gate(NULL, list->name,
369a245fecbSHeiko Stübner 				list->parent_names[0], flags,
370a245fecbSHeiko Stübner 				reg_base + list->gate_offset,
371a245fecbSHeiko Stübner 				list->gate_shift, list->gate_flags, &clk_lock);
372a245fecbSHeiko Stübner 			break;
373a245fecbSHeiko Stübner 		case branch_composite:
374a245fecbSHeiko Stübner 			clk = rockchip_clk_register_branch(list->name,
375a245fecbSHeiko Stübner 				list->parent_names, list->num_parents,
376a245fecbSHeiko Stübner 				reg_base, list->muxdiv_offset, list->mux_shift,
377a245fecbSHeiko Stübner 				list->mux_width, list->mux_flags,
378a245fecbSHeiko Stübner 				list->div_shift, list->div_width,
379a245fecbSHeiko Stübner 				list->div_flags, list->div_table,
380a245fecbSHeiko Stübner 				list->gate_offset, list->gate_shift,
381a245fecbSHeiko Stübner 				list->gate_flags, flags, &clk_lock);
382a245fecbSHeiko Stübner 			break;
38389bf26cbSAlexandru M Stan 		case branch_mmc:
38489bf26cbSAlexandru M Stan 			clk = rockchip_clk_register_mmc(
38589bf26cbSAlexandru M Stan 				list->name,
38689bf26cbSAlexandru M Stan 				list->parent_names, list->num_parents,
38789bf26cbSAlexandru M Stan 				reg_base + list->muxdiv_offset,
38889bf26cbSAlexandru M Stan 				list->div_shift
38989bf26cbSAlexandru M Stan 			);
39089bf26cbSAlexandru M Stan 			break;
3918a76f443SHeiko Stuebner 		case branch_inverter:
3928a76f443SHeiko Stuebner 			clk = rockchip_clk_register_inverter(
3938a76f443SHeiko Stuebner 				list->name, list->parent_names,
3948a76f443SHeiko Stuebner 				list->num_parents,
3958a76f443SHeiko Stuebner 				reg_base + list->muxdiv_offset,
3968a76f443SHeiko Stuebner 				list->div_shift, list->div_flags, &clk_lock);
3978a76f443SHeiko Stuebner 			break;
398a245fecbSHeiko Stübner 		}
399a245fecbSHeiko Stübner 
400a245fecbSHeiko Stübner 		/* none of the cases above matched */
401a245fecbSHeiko Stübner 		if (!clk) {
402a245fecbSHeiko Stübner 			pr_err("%s: unknown clock type %d\n",
403a245fecbSHeiko Stübner 			       __func__, list->branch_type);
404a245fecbSHeiko Stübner 			continue;
405a245fecbSHeiko Stübner 		}
406a245fecbSHeiko Stübner 
407a245fecbSHeiko Stübner 		if (IS_ERR(clk)) {
408a245fecbSHeiko Stübner 			pr_err("%s: failed to register clock %s: %ld\n",
409a245fecbSHeiko Stübner 			       __func__, list->name, PTR_ERR(clk));
410a245fecbSHeiko Stübner 			continue;
411a245fecbSHeiko Stübner 		}
412a245fecbSHeiko Stübner 
413a245fecbSHeiko Stübner 		rockchip_clk_add_lookup(clk, list->id);
414a245fecbSHeiko Stübner 	}
415a245fecbSHeiko Stübner }
416fe94f974SHeiko Stübner 
417f6fba5f6SHeiko Stuebner void __init rockchip_clk_register_armclk(unsigned int lookup_id,
4184a1caed3SUwe Kleine-König 			const char *name, const char *const *parent_names,
419f6fba5f6SHeiko Stuebner 			u8 num_parents,
420f6fba5f6SHeiko Stuebner 			const struct rockchip_cpuclk_reg_data *reg_data,
421f6fba5f6SHeiko Stuebner 			const struct rockchip_cpuclk_rate_table *rates,
422f6fba5f6SHeiko Stuebner 			int nrates)
423f6fba5f6SHeiko Stuebner {
424f6fba5f6SHeiko Stuebner 	struct clk *clk;
425f6fba5f6SHeiko Stuebner 
426f6fba5f6SHeiko Stuebner 	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
427f6fba5f6SHeiko Stuebner 					   reg_data, rates, nrates, reg_base,
428f6fba5f6SHeiko Stuebner 					   &clk_lock);
429f6fba5f6SHeiko Stuebner 	if (IS_ERR(clk)) {
430f6fba5f6SHeiko Stuebner 		pr_err("%s: failed to register clock %s: %ld\n",
431f6fba5f6SHeiko Stuebner 		       __func__, name, PTR_ERR(clk));
432f6fba5f6SHeiko Stuebner 		return;
433f6fba5f6SHeiko Stuebner 	}
434f6fba5f6SHeiko Stuebner 
435f6fba5f6SHeiko Stuebner 	rockchip_clk_add_lookup(clk, lookup_id);
436f6fba5f6SHeiko Stuebner }
437f6fba5f6SHeiko Stuebner 
438692d8328SUwe Kleine-König void __init rockchip_clk_protect_critical(const char *const clocks[],
439692d8328SUwe Kleine-König 					  int nclocks)
440fe94f974SHeiko Stübner {
441fe94f974SHeiko Stübner 	int i;
442fe94f974SHeiko Stübner 
443fe94f974SHeiko Stübner 	/* Protect the clocks that needs to stay on */
444fe94f974SHeiko Stübner 	for (i = 0; i < nclocks; i++) {
445fe94f974SHeiko Stübner 		struct clk *clk = __clk_lookup(clocks[i]);
446fe94f974SHeiko Stübner 
447fe94f974SHeiko Stübner 		if (clk)
448fe94f974SHeiko Stübner 			clk_prepare_enable(clk);
449fe94f974SHeiko Stübner 	}
450fe94f974SHeiko Stübner }
4516f1294b5SHeiko Stübner 
4526f1294b5SHeiko Stübner static unsigned int reg_restart;
4536f1294b5SHeiko Stübner static int rockchip_restart_notify(struct notifier_block *this,
4546f1294b5SHeiko Stübner 				   unsigned long mode, void *cmd)
4556f1294b5SHeiko Stübner {
4566f1294b5SHeiko Stübner 	writel(0xfdb9, reg_base + reg_restart);
4576f1294b5SHeiko Stübner 	return NOTIFY_DONE;
4586f1294b5SHeiko Stübner }
4596f1294b5SHeiko Stübner 
4606f1294b5SHeiko Stübner static struct notifier_block rockchip_restart_handler = {
4616f1294b5SHeiko Stübner 	.notifier_call = rockchip_restart_notify,
4626f1294b5SHeiko Stübner 	.priority = 128,
4636f1294b5SHeiko Stübner };
4646f1294b5SHeiko Stübner 
4656f1294b5SHeiko Stübner void __init rockchip_register_restart_notifier(unsigned int reg)
4666f1294b5SHeiko Stübner {
4676f1294b5SHeiko Stübner 	int ret;
4686f1294b5SHeiko Stübner 
4696f1294b5SHeiko Stübner 	reg_restart = reg;
4706f1294b5SHeiko Stübner 	ret = register_restart_handler(&rockchip_restart_handler);
4716f1294b5SHeiko Stübner 	if (ret)
4726f1294b5SHeiko Stübner 		pr_err("%s: cannot register restart handler, %d\n",
4736f1294b5SHeiko Stübner 		       __func__, ret);
4746f1294b5SHeiko Stübner }
475