File Index
Files and directories
Sign in
FILE

clk.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/rockchip/clk.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2014 MundoReader S.L.
  4 * Author: Heiko Stuebner <heiko@sntech.de>
  5 *
  6 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
  7 * Author: Xing Zheng <zhengxing@rock-chips.com>
  8 *
  9 * based on
 10 *
 11 * samsung/clk.c
 12 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
 13 * Copyright (c) 2013 Linaro Ltd.
 14 * Author: Thomas Abraham <thomas.ab@samsung.com>
 15 */
 16
 17#include <linux/slab.h>
 18#include <linux/clk.h>
 19#include <linux/clk-provider.h>
 20#include <linux/io.h>
 21#include <linux/mfd/syscon.h>
 22#include <linux/platform_device.h>
 23#include <linux/regmap.h>
 24#include <linux/reboot.h>
 25
 26#include "../clk-fractional-divider.h"
 27#include "clk.h"
 28
 29/*
 30 * Register a clock branch.
 31 * Most clock branches have a form like
 32 *
 33 * src1 --|--\
 34 *        |M |--[GATE]-[DIV]-
 35 * src2 --|--/
 36 *
 37 * sometimes without one of those components.
 38 */
 39static struct clk *rockchip_clk_register_branch(const char *name,
 40		const char *const *parent_names, u8 num_parents,
 41		void __iomem *base,
 42		int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
 43		u32 *mux_table,
 44		int div_offset, u8 div_shift, u8 div_width, u8 div_flags,
 45		struct clk_div_table *div_table, int gate_offset,
 46		u8 gate_shift, u8 gate_flags, unsigned long flags,
 47		spinlock_t *lock)
 48{
 49	struct clk_hw *hw;
 50	struct clk_mux *mux = NULL;
 51	struct clk_gate *gate = NULL;
 52	struct clk_divider *div = NULL;
 53	const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
 54			     *gate_ops = NULL;
 55	int ret;
 56
 57	if (num_parents > 1) {
 58		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
 59		if (!mux)
 60			return ERR_PTR(-ENOMEM);
 61
 62		mux->reg = base + muxdiv_offset;
 63		mux->shift = mux_shift;
 64		mux->mask = BIT(mux_width) - 1;
 65		mux->flags = mux_flags;
 66		mux->table = mux_table;
 67		mux->lock = lock;
 68		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
 69							: &clk_mux_ops;
 70	}
 71
 72	if (gate_offset >= 0) {
 73		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 74		if (!gate) {
 75			ret = -ENOMEM;
 76			goto err_gate;
 77		}
 78
 79		gate->flags = gate_flags;
 80		gate->reg = base + gate_offset;
 81		gate->bit_idx = gate_shift;
 82		gate->lock = lock;
 83		gate_ops = &clk_gate_ops;
 84	}
 85
 86	if (div_width > 0) {
 87		div = kzalloc(sizeof(*div), GFP_KERNEL);
 88		if (!div) {
 89			ret = -ENOMEM;
 90			goto err_div;
 91		}
 92
 93		div->flags = div_flags;
 94		if (div_offset)
 95			div->reg = base + div_offset;
 96		else
 97			div->reg = base + muxdiv_offset;
 98		div->shift = div_shift;
 99		div->width = div_width;
100		div->lock = lock;
101		div->table = div_table;
102		div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
103						? &clk_divider_ro_ops
104						: &clk_divider_ops;
105	}
106
107	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
108				       mux ? &mux->hw : NULL, mux_ops,
109				       div ? &div->hw : NULL, div_ops,
110				       gate ? &gate->hw : NULL, gate_ops,
111				       flags);
112	if (IS_ERR(hw)) {
113		kfree(div);
114		kfree(gate);
115		return ERR_CAST(hw);
116	}
117
118	return hw->clk;
119err_div:
120	kfree(gate);
121err_gate:
122	kfree(mux);
123	return ERR_PTR(ret);
124}
125
126struct rockchip_clk_frac {
127	struct notifier_block			clk_nb;
128	struct clk_fractional_divider		div;
129	struct clk_gate				gate;
130
131	struct clk_mux				mux;
132	const struct clk_ops			*mux_ops;
133	int					mux_frac_idx;
134
135	bool					rate_change_remuxed;
136	int					rate_change_idx;
137};
138
139#define to_rockchip_clk_frac_nb(nb) \
140			container_of(nb, struct rockchip_clk_frac, clk_nb)
141
142static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
143					 unsigned long event, void *data)
144{
145	struct clk_notifier_data *ndata = data;
146	struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb);
147	struct clk_mux *frac_mux = &frac->mux;
148	int ret = 0;
149
150	pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
151		 __func__, event, ndata->old_rate, ndata->new_rate);
152	if (event == PRE_RATE_CHANGE) {
153		frac->rate_change_idx =
154				frac->mux_ops->get_parent(&frac_mux->hw);
155		if (frac->rate_change_idx != frac->mux_frac_idx) {
156			frac->mux_ops->set_parent(&frac_mux->hw,
157						  frac->mux_frac_idx);
158			frac->rate_change_remuxed = 1;
159		}
160	} else if (event == POST_RATE_CHANGE) {
161		/*
162		 * The POST_RATE_CHANGE notifier runs directly after the
163		 * divider clock is set in clk_change_rate, so we'll have
164		 * remuxed back to the original parent before clk_change_rate
165		 * reaches the mux itself.
166		 */
167		if (frac->rate_change_remuxed) {
168			frac->mux_ops->set_parent(&frac_mux->hw,
169						  frac->rate_change_idx);
170			frac->rate_change_remuxed = 0;
171		}
172	}
173
174	return notifier_from_errno(ret);
175}
176
177/*
178 * fractional divider must set that denominator is 20 times larger than
179 * numerator to generate precise clock frequency.
180 */
181static void rockchip_fractional_approximation(struct clk_hw *hw,
182		unsigned long rate, unsigned long *parent_rate,
183		unsigned long *m, unsigned long *n)
184{
185	struct clk_fractional_divider *fd = to_clk_fd(hw);
186	unsigned long p_rate, p_parent_rate;
187	struct clk_hw *p_parent;
188
189	p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
190	if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
191		p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
192		p_parent_rate = clk_hw_get_rate(p_parent);
193		*parent_rate = p_parent_rate;
194	}
195
196	fd->flags |= CLK_FRAC_DIVIDER_POWER_OF_TWO_PS;
197
198	clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
199}
200
201static struct clk *rockchip_clk_register_frac_branch(
202		struct rockchip_clk_provider *ctx, const char *name,
203		const char *const *parent_names, u8 num_parents,
204		void __iomem *base, int muxdiv_offset, u8 div_flags,
205		int gate_offset, u8 gate_shift, u8 gate_flags,
206		unsigned long flags, struct rockchip_clk_branch *child,
207		spinlock_t *lock)
208{
209	struct clk_hw *hw;
210	struct rockchip_clk_frac *frac;
211	struct clk_gate *gate = NULL;
212	struct clk_fractional_divider *div = NULL;
213	const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
214
215	if (muxdiv_offset < 0)
216		return ERR_PTR(-EINVAL);
217
218	if (child && child->branch_type != branch_mux) {
219		pr_err("%s: fractional child clock for %s can only be a mux\n",
220		       __func__, name);
221		return ERR_PTR(-EINVAL);
222	}
223
224	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
225	if (!frac)
226		return ERR_PTR(-ENOMEM);
227
228	if (gate_offset >= 0) {
229		gate = &frac->gate;
230		gate->flags = gate_flags;
231		gate->reg = base + gate_offset;
232		gate->bit_idx = gate_shift;
233		gate->lock = lock;
234		gate_ops = &clk_gate_ops;
235	}
236
237	div = &frac->div;
238	div->flags = div_flags;
239	div->reg = base + muxdiv_offset;
240	div->mshift = 16;
241	div->mwidth = 16;
242	div->nshift = 0;
243	div->nwidth = 16;
244	div->lock = lock;
245	div->approximation = rockchip_fractional_approximation;
246	div_ops = &clk_fractional_divider_ops;
247
248	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
249				       NULL, NULL,
250				       &div->hw, div_ops,
251				       gate ? &gate->hw : NULL, gate_ops,
252				       flags | CLK_SET_RATE_UNGATE);
253	if (IS_ERR(hw)) {
254		kfree(frac);
255		return ERR_CAST(hw);
256	}
257
258	if (child) {
259		struct clk_mux *frac_mux = &frac->mux;
260		struct clk_init_data init;
261		struct clk *mux_clk;
262		int ret;
263
264		frac->mux_frac_idx = match_string(child->parent_names,
265						  child->num_parents, name);
266		frac->mux_ops = &clk_mux_ops;
267		frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb;
268
269		frac_mux->reg = base + child->muxdiv_offset;
270		frac_mux->shift = child->mux_shift;
271		frac_mux->mask = BIT(child->mux_width) - 1;
272		frac_mux->flags = child->mux_flags;
273		if (child->mux_table)
274			frac_mux->table = child->mux_table;
275		frac_mux->lock = lock;
276		frac_mux->hw.init = &init;
277
278		init.name = child->name;
279		init.flags = child->flags | CLK_SET_RATE_PARENT;
280		init.ops = frac->mux_ops;
281		init.parent_names = child->parent_names;
282		init.num_parents = child->num_parents;
283
284		mux_clk = clk_register(NULL, &frac_mux->hw);
285		if (IS_ERR(mux_clk)) {
286			kfree(frac);
287			return mux_clk;
288		}
289
290		rockchip_clk_set_lookup(ctx, mux_clk, child->id);
291
292		/* notifier on the fraction divider to catch rate changes */
293		if (frac->mux_frac_idx >= 0) {
294			pr_debug("%s: found fractional parent in mux at pos %d\n",
295				 __func__, frac->mux_frac_idx);
296			ret = clk_notifier_register(hw->clk, &frac->clk_nb);
297			if (ret)
298				pr_err("%s: failed to register clock notifier for %s\n",
299						__func__, name);
300		} else {
301			pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n",
302				__func__, name, child->name);
303		}
304	}
305
306	return hw->clk;
307}
308
309static struct clk *rockchip_clk_register_factor_branch(const char *name,
310		const char *const *parent_names, u8 num_parents,
311		void __iomem *base, unsigned int mult, unsigned int div,
312		int gate_offset, u8 gate_shift, u8 gate_flags,
313		unsigned long flags, spinlock_t *lock)
314{
315	struct clk_hw *hw;
316	struct clk_gate *gate = NULL;
317	struct clk_fixed_factor *fix = NULL;
318
319	/* without gate, register a simple factor clock */
320	if (gate_offset == 0) {
321		return clk_register_fixed_factor(NULL, name,
322				parent_names[0], flags, mult,
323				div);
324	}
325
326	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
327	if (!gate)
328		return ERR_PTR(-ENOMEM);
329
330	gate->flags = gate_flags;
331	gate->reg = base + gate_offset;
332	gate->bit_idx = gate_shift;
333	gate->lock = lock;
334
335	fix = kzalloc(sizeof(*fix), GFP_KERNEL);
336	if (!fix) {
337		kfree(gate);
338		return ERR_PTR(-ENOMEM);
339	}
340
341	fix->mult = mult;
342	fix->div = div;
343
344	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
345				       NULL, NULL,
346				       &fix->hw, &clk_fixed_factor_ops,
347				       &gate->hw, &clk_gate_ops, flags);
348	if (IS_ERR(hw)) {
349		kfree(fix);
350		kfree(gate);
351		return ERR_CAST(hw);
352	}
353
354	return hw->clk;
355}
356
357static struct rockchip_clk_provider *rockchip_clk_init_base(
358		struct device_node *np, void __iomem *base,
359		unsigned long nr_clks, bool has_late_clocks)
360{
361	struct rockchip_clk_provider *ctx;
362	struct clk **clk_table;
363	struct clk *default_clk_val;
364	int i;
365
366	default_clk_val = ERR_PTR(has_late_clocks ? -EPROBE_DEFER : -ENOENT);
367
368	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
369	if (!ctx)
370		return ERR_PTR(-ENOMEM);
371
372	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
373	if (!clk_table)
374		goto err_free;
375
376	for (i = 0; i < nr_clks; ++i)
377		clk_table[i] = default_clk_val;
378
379	ctx->reg_base = base;
380	ctx->clk_data.clks = clk_table;
381	ctx->clk_data.clk_num = nr_clks;
382	ctx->cru_node = np;
383	spin_lock_init(&ctx->lock);
384
385	hash_init(ctx->aux_grf_table);
386
387	ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
388						   "rockchip,grf");
389
390	return ctx;
391
392err_free:
393	kfree(ctx);
394	return ERR_PTR(-ENOMEM);
395}
396
397struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
398						void __iomem *base,
399						unsigned long nr_clks)
400{
401	return rockchip_clk_init_base(np, base, nr_clks, false);
402}
403EXPORT_SYMBOL_GPL(rockchip_clk_init);
404
405struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np,
406						      void __iomem *base,
407						      unsigned long nr_clks)
408{
409	return rockchip_clk_init_base(np, base, nr_clks, true);
410}
411EXPORT_SYMBOL_GPL(rockchip_clk_init_early);
412
413void rockchip_clk_finalize(struct rockchip_clk_provider *ctx)
414{
415	int i;
416
417	for (i = 0; i < ctx->clk_data.clk_num; ++i)
418		if (ctx->clk_data.clks[i] == ERR_PTR(-EPROBE_DEFER))
419			ctx->clk_data.clks[i] = ERR_PTR(-ENOENT);
420}
421EXPORT_SYMBOL_GPL(rockchip_clk_finalize);
422
423void rockchip_clk_of_add_provider(struct device_node *np,
424				  struct rockchip_clk_provider *ctx)
425{
426	if (of_clk_add_provider(np, of_clk_src_onecell_get,
427				&ctx->clk_data))
428		pr_err("%s: could not register clk provider\n", __func__);
429}
430EXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider);
431
432void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
433				struct rockchip_pll_clock *list,
434				unsigned int nr_pll, int grf_lock_offset)
435{
436	struct clk *clk;
437	int idx;
438
439	for (idx = 0; idx < nr_pll; idx++, list++) {
440		clk = rockchip_clk_register_pll(ctx, list->type, list->name,
441				list->parent_names, list->num_parents,
442				list->con_offset, grf_lock_offset,
443				list->lock_shift, list->mode_offset,
444				list->mode_shift, list->rate_table,
445				list->flags, list->pll_flags);
446		if (IS_ERR(clk)) {
447			pr_err("%s: failed to register clock %s\n", __func__,
448				list->name);
449			continue;
450		}
451
452		rockchip_clk_set_lookup(ctx, clk, list->id);
453	}
454}
455EXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
456
457unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
458					   unsigned int nr_clk)
459{
460	unsigned long max = 0;
461	unsigned int idx;
462
463	for (idx = 0; idx < nr_clk; idx++, list++) {
464		if (list->id > max)
465			max = list->id;
466		if (list->child && list->child->id > max)
467			max = list->child->id;
468	}
469
470	return max;
471}
472EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id);
473
474static struct platform_device *rockchip_clk_register_gate_link(
475		struct device *parent_dev,
476		struct rockchip_clk_provider *ctx,
477		struct rockchip_clk_branch *clkbr)
478{
479	struct rockchip_gate_link_platdata gate_link_pdata = {
480		.ctx = ctx,
481		.clkbr = clkbr,
482	};
483
484	struct platform_device_info pdevinfo = {
485		.parent = parent_dev,
486		.name = "rockchip-gate-link-clk",
487		.id = clkbr->id,
488		.fwnode = dev_fwnode(parent_dev),
489		.of_node_reused = true,
490		.data = &gate_link_pdata,
491		.size_data = sizeof(gate_link_pdata),
492	};
493
494	return platform_device_register_full(&pdevinfo);
495}
496
497void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
498				    struct rockchip_clk_branch *list,
499				    unsigned int nr_clk)
500{
501	struct regmap *grf = ctx->grf;
502	struct rockchip_aux_grf *agrf;
503	struct clk *clk;
504	unsigned int idx;
505	unsigned long flags;
506
507	for (idx = 0; idx < nr_clk; idx++, list++) {
508		flags = list->flags;
509		clk = NULL;
510
511		/* for GRF-dependent branches, choose the right grf first */
512		if ((list->branch_type == branch_grf_mux ||
513		     list->branch_type == branch_grf_gate ||
514		     list->branch_type == branch_grf_mmc) &&
515		    list->grf_type != grf_type_sys) {
516			hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) {
517				if (agrf->type == list->grf_type) {
518					grf = agrf->grf;
519					break;
520				}
521			}
522		}
523
524		/* catch simple muxes */
525		switch (list->branch_type) {
526		case branch_mux:
527			if (list->mux_table)
528				clk = clk_register_mux_table(NULL, list->name,
529					list->parent_names, list->num_parents,
530					flags,
531					ctx->reg_base + list->muxdiv_offset,
532					list->mux_shift, list->mux_width,
533					list->mux_flags, list->mux_table,
534					&ctx->lock);
535			else
536				clk = clk_register_mux(NULL, list->name,
537					list->parent_names, list->num_parents,
538					flags,
539					ctx->reg_base + list->muxdiv_offset,
540					list->mux_shift, list->mux_width,
541					list->mux_flags, &ctx->lock);
542			break;
543		case branch_grf_mux:
544			clk = rockchip_clk_register_muxgrf(list->name,
545				list->parent_names, list->num_parents,
546				flags, grf, list->muxdiv_offset,
547				list->mux_shift, list->mux_width,
548				list->mux_flags);
549			break;
550		case branch_divider:
551			if (list->div_table)
552				clk = clk_register_divider_table(NULL,
553					list->name, list->parent_names[0],
554					flags,
555					ctx->reg_base + list->muxdiv_offset,
556					list->div_shift, list->div_width,
557					list->div_flags, list->div_table,
558					&ctx->lock);
559			else
560				clk = clk_register_divider(NULL, list->name,
561					list->parent_names[0], flags,
562					ctx->reg_base + list->muxdiv_offset,
563					list->div_shift, list->div_width,
564					list->div_flags, &ctx->lock);
565			break;
566		case branch_fraction_divider:
567			clk = rockchip_clk_register_frac_branch(ctx, list->name,
568				list->parent_names, list->num_parents,
569				ctx->reg_base, list->muxdiv_offset,
570				list->div_flags,
571				list->gate_offset, list->gate_shift,
572				list->gate_flags, flags, list->child,
573				&ctx->lock);
574			break;
575		case branch_half_divider:
576			clk = rockchip_clk_register_halfdiv(list->name,
577				list->parent_names, list->num_parents,
578				ctx->reg_base, list->muxdiv_offset,
579				list->mux_shift, list->mux_width,
580				list->mux_flags, list->div_shift,
581				list->div_width, list->div_flags,
582				list->gate_offset, list->gate_shift,
583				list->gate_flags, flags, &ctx->lock);
584			break;
585		case branch_gate:
586			flags |= CLK_SET_RATE_PARENT;
587
588			clk = clk_register_gate(NULL, list->name,
589				list->parent_names[0], flags,
590				ctx->reg_base + list->gate_offset,
591				list->gate_shift, list->gate_flags, &ctx->lock);
592			break;
593		case branch_grf_gate:
594			flags |= CLK_SET_RATE_PARENT;
595			clk = rockchip_clk_register_gate_grf(list->name,
596				list->parent_names[0], flags, grf,
597				list->gate_offset, list->gate_shift,
598				list->gate_flags);
599			break;
600		case branch_composite:
601			clk = rockchip_clk_register_branch(list->name,
602				list->parent_names, list->num_parents,
603				ctx->reg_base, list->muxdiv_offset,
604				list->mux_shift,
605				list->mux_width, list->mux_flags,
606				list->mux_table, list->div_offset,
607				list->div_shift, list->div_width,
608				list->div_flags, list->div_table,
609				list->gate_offset, list->gate_shift,
610				list->gate_flags, flags, &ctx->lock);
611			break;
612		case branch_mmc:
613			clk = rockchip_clk_register_mmc(
614				list->name,
615				list->parent_names, list->num_parents,
616				ctx->reg_base + list->muxdiv_offset,
617				NULL, 0,
618				list->div_shift
619			);
620			break;
621		case branch_grf_mmc:
622			clk = rockchip_clk_register_mmc(
623				list->name,
624				list->parent_names, list->num_parents,
625				NULL,
626				grf, list->muxdiv_offset,
627				list->div_shift
628			);
629			break;
630		case branch_inverter:
631			clk = rockchip_clk_register_inverter(
632				list->name, list->parent_names,
633				list->num_parents,
634				ctx->reg_base + list->muxdiv_offset,
635				list->div_shift, list->div_flags, &ctx->lock);
636			break;
637		case branch_factor:
638			clk = rockchip_clk_register_factor_branch(
639				list->name, list->parent_names,
640				list->num_parents, ctx->reg_base,
641				list->div_shift, list->div_width,
642				list->gate_offset, list->gate_shift,
643				list->gate_flags, flags, &ctx->lock);
644			break;
645		case branch_ddrclk:
646			clk = rockchip_clk_register_ddrclk(
647				list->name, list->flags,
648				list->parent_names, list->num_parents,
649				list->muxdiv_offset, list->mux_shift,
650				list->mux_width, list->div_shift,
651				list->div_width, list->div_flags,
652				ctx->reg_base, &ctx->lock);
653			break;
654		case branch_linked_gate:
655			/* must be registered late, fall-through for error message */
656			break;
657		}
658
659		/* none of the cases above matched */
660		if (!clk) {
661			pr_err("%s: unknown clock type %d\n",
662			       __func__, list->branch_type);
663			continue;
664		}
665
666		if (IS_ERR(clk)) {
667			pr_err("%s: failed to register clock %s: %ld\n",
668			       __func__, list->name, PTR_ERR(clk));
669			continue;
670		}
671
672		rockchip_clk_set_lookup(ctx, clk, list->id);
673	}
674}
675EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
676
677void rockchip_clk_register_late_branches(struct device *dev,
678					 struct rockchip_clk_provider *ctx,
679					 struct rockchip_clk_branch *list,
680					 unsigned int nr_clk)
681{
682	unsigned int idx;
683
684	for (idx = 0; idx < nr_clk; idx++, list++) {
685		struct platform_device *pdev = NULL;
686
687		switch (list->branch_type) {
688		case branch_linked_gate:
689			pdev = rockchip_clk_register_gate_link(dev, ctx, list);
690			break;
691		default:
692			dev_err(dev, "unknown clock type %d\n", list->branch_type);
693			break;
694		}
695
696		if (IS_ERR_OR_NULL(pdev))
697			dev_err(dev, "failed to register device for clock %s\n", list->name);
698	}
699}
700EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches);
701
702void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
703				  unsigned int lookup_id,
704				  const char *name, const char *const *parent_names,
705				  u8 num_parents,
706				  const struct rockchip_cpuclk_reg_data *reg_data,
707				  const struct rockchip_cpuclk_rate_table *rates,
708				  int nrates)
709{
710	struct clk *clk;
711
712	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
713					   reg_data, rates, nrates,
714					   ctx->reg_base, &ctx->lock);
715	if (IS_ERR(clk)) {
716		pr_err("%s: failed to register clock %s: %ld\n",
717		       __func__, name, PTR_ERR(clk));
718		return;
719	}
720
721	rockchip_clk_set_lookup(ctx, clk, lookup_id);
722}
723EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
724
725void rockchip_clk_register_armclk_multi_pll(struct rockchip_clk_provider *ctx,
726					    struct rockchip_clk_branch *list,
727					    const struct rockchip_cpuclk_rate_table *rates,
728					    int nrates)
729{
730	struct clk *clk;
731
732	clk = rockchip_clk_register_cpuclk_multi_pll(list->name, list->parent_names,
733						     list->num_parents, ctx->reg_base,
734						     list->muxdiv_offset, list->mux_shift,
735						     list->mux_width, list->mux_flags,
736						     list->div_offset, list->div_shift,
737						     list->div_width, list->div_flags,
738						     list->flags, &ctx->lock, rates, nrates);
739	if (IS_ERR(clk)) {
740		pr_err("%s: failed to register clock %s: %ld\n",
741		       __func__, list->name, PTR_ERR(clk));
742		return;
743	}
744
745	rockchip_clk_set_lookup(ctx, clk, list->id);
746}
747EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk_multi_pll);
748
749void rockchip_clk_protect_critical(const char *const clocks[],
750				   int nclocks)
751{
752	int i;
753
754	/* Protect the clocks that needs to stay on */
755	for (i = 0; i < nclocks; i++) {
756		struct clk *clk = __clk_lookup(clocks[i]);
757
758		clk_prepare_enable(clk);
759	}
760}
761EXPORT_SYMBOL_GPL(rockchip_clk_protect_critical);
762
763static void __iomem *rst_base;
764static unsigned int reg_restart;
765static void (*cb_restart)(void);
766static int rockchip_restart_notify(struct notifier_block *this,
767				   unsigned long mode, void *cmd)
768{
769	if (cb_restart)
770		cb_restart();
771
772	writel(0xfdb9, rst_base + reg_restart);
773	return NOTIFY_DONE;
774}
775
776static struct notifier_block rockchip_restart_handler = {
777	.notifier_call = rockchip_restart_notify,
778	.priority = 128,
779};
780
781void
782rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
783				   unsigned int reg,
784				   void (*cb)(void))
785{
786	int ret;
787
788	rst_base = ctx->reg_base;
789	reg_restart = reg;
790	cb_restart = cb;
791	ret = register_restart_handler(&rockchip_restart_handler);
792	if (ret)
793		pr_err("%s: cannot register restart handler, %d\n",
794		       __func__, ret);
795}
796EXPORT_SYMBOL_GPL(rockchip_register_restart_notifier);
~/ Read-only file preview.