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