xref: /openbmc/linux/drivers/clk/imx/clk-cpu.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1acee2e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
211f68120SShawn Guo /*
311f68120SShawn Guo  * Copyright (c) 2014 Lucas Stach <l.stach@pengutronix.de>, Pengutronix
411f68120SShawn Guo  */
511f68120SShawn Guo 
611f68120SShawn Guo #include <linux/clk.h>
711f68120SShawn Guo #include <linux/clk-provider.h>
8*870ed5e2SAnson Huang #include <linux/export.h>
911f68120SShawn Guo #include <linux/slab.h>
10a39973a4SFabio Estevam #include "clk.h"
1111f68120SShawn Guo 
1211f68120SShawn Guo struct clk_cpu {
1311f68120SShawn Guo 	struct clk_hw	hw;
1411f68120SShawn Guo 	struct clk	*div;
1511f68120SShawn Guo 	struct clk	*mux;
1611f68120SShawn Guo 	struct clk	*pll;
1711f68120SShawn Guo 	struct clk	*step;
1811f68120SShawn Guo };
1911f68120SShawn Guo 
to_clk_cpu(struct clk_hw * hw)2011f68120SShawn Guo static inline struct clk_cpu *to_clk_cpu(struct clk_hw *hw)
2111f68120SShawn Guo {
2211f68120SShawn Guo 	return container_of(hw, struct clk_cpu, hw);
2311f68120SShawn Guo }
2411f68120SShawn Guo 
clk_cpu_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)2511f68120SShawn Guo static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw,
2611f68120SShawn Guo 					 unsigned long parent_rate)
2711f68120SShawn Guo {
2811f68120SShawn Guo 	struct clk_cpu *cpu = to_clk_cpu(hw);
2911f68120SShawn Guo 
3011f68120SShawn Guo 	return clk_get_rate(cpu->div);
3111f68120SShawn Guo }
3211f68120SShawn Guo 
clk_cpu_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)3311f68120SShawn Guo static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
3411f68120SShawn Guo 			       unsigned long *prate)
3511f68120SShawn Guo {
3611f68120SShawn Guo 	struct clk_cpu *cpu = to_clk_cpu(hw);
3711f68120SShawn Guo 
3811f68120SShawn Guo 	return clk_round_rate(cpu->pll, rate);
3911f68120SShawn Guo }
4011f68120SShawn Guo 
clk_cpu_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)4111f68120SShawn Guo static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
4211f68120SShawn Guo 			    unsigned long parent_rate)
4311f68120SShawn Guo {
4411f68120SShawn Guo 	struct clk_cpu *cpu = to_clk_cpu(hw);
4511f68120SShawn Guo 	int ret;
4611f68120SShawn Guo 
4711f68120SShawn Guo 	/* switch to PLL bypass clock */
4811f68120SShawn Guo 	ret = clk_set_parent(cpu->mux, cpu->step);
4911f68120SShawn Guo 	if (ret)
5011f68120SShawn Guo 		return ret;
5111f68120SShawn Guo 
5211f68120SShawn Guo 	/* reprogram PLL */
5311f68120SShawn Guo 	ret = clk_set_rate(cpu->pll, rate);
5411f68120SShawn Guo 	if (ret) {
5511f68120SShawn Guo 		clk_set_parent(cpu->mux, cpu->pll);
5611f68120SShawn Guo 		return ret;
5711f68120SShawn Guo 	}
5811f68120SShawn Guo 	/* switch back to PLL clock */
5911f68120SShawn Guo 	clk_set_parent(cpu->mux, cpu->pll);
6011f68120SShawn Guo 
6111f68120SShawn Guo 	/* Ensure the divider is what we expect */
6211f68120SShawn Guo 	clk_set_rate(cpu->div, rate);
6311f68120SShawn Guo 
6411f68120SShawn Guo 	return 0;
6511f68120SShawn Guo }
6611f68120SShawn Guo 
6711f68120SShawn Guo static const struct clk_ops clk_cpu_ops = {
6811f68120SShawn Guo 	.recalc_rate	= clk_cpu_recalc_rate,
6911f68120SShawn Guo 	.round_rate	= clk_cpu_round_rate,
7011f68120SShawn Guo 	.set_rate	= clk_cpu_set_rate,
7111f68120SShawn Guo };
7211f68120SShawn Guo 
imx_clk_hw_cpu(const char * name,const char * parent_name,struct clk * div,struct clk * mux,struct clk * pll,struct clk * step)732bc7e9dcSAbel Vesa struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name,
7411f68120SShawn Guo 		struct clk *div, struct clk *mux, struct clk *pll,
7511f68120SShawn Guo 		struct clk *step)
7611f68120SShawn Guo {
7711f68120SShawn Guo 	struct clk_cpu *cpu;
782bc7e9dcSAbel Vesa 	struct clk_hw *hw;
7911f68120SShawn Guo 	struct clk_init_data init;
802bc7e9dcSAbel Vesa 	int ret;
8111f68120SShawn Guo 
8211f68120SShawn Guo 	cpu = kzalloc(sizeof(*cpu), GFP_KERNEL);
8311f68120SShawn Guo 	if (!cpu)
8411f68120SShawn Guo 		return ERR_PTR(-ENOMEM);
8511f68120SShawn Guo 
8611f68120SShawn Guo 	cpu->div = div;
8711f68120SShawn Guo 	cpu->mux = mux;
8811f68120SShawn Guo 	cpu->pll = pll;
8911f68120SShawn Guo 	cpu->step = step;
9011f68120SShawn Guo 
9111f68120SShawn Guo 	init.name = name;
9211f68120SShawn Guo 	init.ops = &clk_cpu_ops;
93ec189392SAnson Huang 	init.flags = CLK_IS_CRITICAL;
9411f68120SShawn Guo 	init.parent_names = &parent_name;
9511f68120SShawn Guo 	init.num_parents = 1;
9611f68120SShawn Guo 
9711f68120SShawn Guo 	cpu->hw.init = &init;
982bc7e9dcSAbel Vesa 	hw = &cpu->hw;
9911f68120SShawn Guo 
1002bc7e9dcSAbel Vesa 	ret = clk_hw_register(NULL, hw);
1012bc7e9dcSAbel Vesa 	if (ret) {
10211f68120SShawn Guo 		kfree(cpu);
1032bc7e9dcSAbel Vesa 		return ERR_PTR(ret);
1042bc7e9dcSAbel Vesa 	}
10511f68120SShawn Guo 
1062bc7e9dcSAbel Vesa 	return hw;
10711f68120SShawn Guo }
108*870ed5e2SAnson Huang EXPORT_SYMBOL_GPL(imx_clk_hw_cpu);
109