| /* | 
 |  * Copyright (c) 2014 Lucas Stach <l.stach@pengutronix.de>, Pengutronix | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 as | 
 |  * published by the Free Software Foundation. | 
 |  * | 
 |  * http://www.opensource.org/licenses/gpl-license.html | 
 |  * http://www.gnu.org/copyleft/gpl.html | 
 |  */ | 
 |  | 
 | #include <linux/clk.h> | 
 | #include <linux/clk-provider.h> | 
 | #include <linux/slab.h> | 
 | #include "clk.h" | 
 |  | 
 | struct clk_cpu { | 
 | 	struct clk_hw	hw; | 
 | 	struct clk	*div; | 
 | 	struct clk	*mux; | 
 | 	struct clk	*pll; | 
 | 	struct clk	*step; | 
 | }; | 
 |  | 
 | static inline struct clk_cpu *to_clk_cpu(struct clk_hw *hw) | 
 | { | 
 | 	return container_of(hw, struct clk_cpu, hw); | 
 | } | 
 |  | 
 | static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw, | 
 | 					 unsigned long parent_rate) | 
 | { | 
 | 	struct clk_cpu *cpu = to_clk_cpu(hw); | 
 |  | 
 | 	return clk_get_rate(cpu->div); | 
 | } | 
 |  | 
 | static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, | 
 | 			       unsigned long *prate) | 
 | { | 
 | 	struct clk_cpu *cpu = to_clk_cpu(hw); | 
 |  | 
 | 	return clk_round_rate(cpu->pll, rate); | 
 | } | 
 |  | 
 | static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, | 
 | 			    unsigned long parent_rate) | 
 | { | 
 | 	struct clk_cpu *cpu = to_clk_cpu(hw); | 
 | 	int ret; | 
 |  | 
 | 	/* switch to PLL bypass clock */ | 
 | 	ret = clk_set_parent(cpu->mux, cpu->step); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	/* reprogram PLL */ | 
 | 	ret = clk_set_rate(cpu->pll, rate); | 
 | 	if (ret) { | 
 | 		clk_set_parent(cpu->mux, cpu->pll); | 
 | 		return ret; | 
 | 	} | 
 | 	/* switch back to PLL clock */ | 
 | 	clk_set_parent(cpu->mux, cpu->pll); | 
 |  | 
 | 	/* Ensure the divider is what we expect */ | 
 | 	clk_set_rate(cpu->div, rate); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct clk_ops clk_cpu_ops = { | 
 | 	.recalc_rate	= clk_cpu_recalc_rate, | 
 | 	.round_rate	= clk_cpu_round_rate, | 
 | 	.set_rate	= clk_cpu_set_rate, | 
 | }; | 
 |  | 
 | struct clk *imx_clk_cpu(const char *name, const char *parent_name, | 
 | 		struct clk *div, struct clk *mux, struct clk *pll, | 
 | 		struct clk *step) | 
 | { | 
 | 	struct clk_cpu *cpu; | 
 | 	struct clk *clk; | 
 | 	struct clk_init_data init; | 
 |  | 
 | 	cpu = kzalloc(sizeof(*cpu), GFP_KERNEL); | 
 | 	if (!cpu) | 
 | 		return ERR_PTR(-ENOMEM); | 
 |  | 
 | 	cpu->div = div; | 
 | 	cpu->mux = mux; | 
 | 	cpu->pll = pll; | 
 | 	cpu->step = step; | 
 |  | 
 | 	init.name = name; | 
 | 	init.ops = &clk_cpu_ops; | 
 | 	init.flags = 0; | 
 | 	init.parent_names = &parent_name; | 
 | 	init.num_parents = 1; | 
 |  | 
 | 	cpu->hw.init = &init; | 
 |  | 
 | 	clk = clk_register(NULL, &cpu->hw); | 
 | 	if (IS_ERR(clk)) | 
 | 		kfree(cpu); | 
 |  | 
 | 	return clk; | 
 | } |