Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2014 Lucas Stach <l.stach@pengutronix.de>, Pengutronix
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/clk-provider.h>
0008 #include <linux/export.h>
0009 #include <linux/slab.h>
0010 #include "clk.h"
0011 
0012 struct clk_cpu {
0013     struct clk_hw   hw;
0014     struct clk  *div;
0015     struct clk  *mux;
0016     struct clk  *pll;
0017     struct clk  *step;
0018 };
0019 
0020 static inline struct clk_cpu *to_clk_cpu(struct clk_hw *hw)
0021 {
0022     return container_of(hw, struct clk_cpu, hw);
0023 }
0024 
0025 static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw,
0026                      unsigned long parent_rate)
0027 {
0028     struct clk_cpu *cpu = to_clk_cpu(hw);
0029 
0030     return clk_get_rate(cpu->div);
0031 }
0032 
0033 static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
0034                    unsigned long *prate)
0035 {
0036     struct clk_cpu *cpu = to_clk_cpu(hw);
0037 
0038     return clk_round_rate(cpu->pll, rate);
0039 }
0040 
0041 static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
0042                 unsigned long parent_rate)
0043 {
0044     struct clk_cpu *cpu = to_clk_cpu(hw);
0045     int ret;
0046 
0047     /* switch to PLL bypass clock */
0048     ret = clk_set_parent(cpu->mux, cpu->step);
0049     if (ret)
0050         return ret;
0051 
0052     /* reprogram PLL */
0053     ret = clk_set_rate(cpu->pll, rate);
0054     if (ret) {
0055         clk_set_parent(cpu->mux, cpu->pll);
0056         return ret;
0057     }
0058     /* switch back to PLL clock */
0059     clk_set_parent(cpu->mux, cpu->pll);
0060 
0061     /* Ensure the divider is what we expect */
0062     clk_set_rate(cpu->div, rate);
0063 
0064     return 0;
0065 }
0066 
0067 static const struct clk_ops clk_cpu_ops = {
0068     .recalc_rate    = clk_cpu_recalc_rate,
0069     .round_rate = clk_cpu_round_rate,
0070     .set_rate   = clk_cpu_set_rate,
0071 };
0072 
0073 struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name,
0074         struct clk *div, struct clk *mux, struct clk *pll,
0075         struct clk *step)
0076 {
0077     struct clk_cpu *cpu;
0078     struct clk_hw *hw;
0079     struct clk_init_data init;
0080     int ret;
0081 
0082     cpu = kzalloc(sizeof(*cpu), GFP_KERNEL);
0083     if (!cpu)
0084         return ERR_PTR(-ENOMEM);
0085 
0086     cpu->div = div;
0087     cpu->mux = mux;
0088     cpu->pll = pll;
0089     cpu->step = step;
0090 
0091     init.name = name;
0092     init.ops = &clk_cpu_ops;
0093     init.flags = CLK_IS_CRITICAL;
0094     init.parent_names = &parent_name;
0095     init.num_parents = 1;
0096 
0097     cpu->hw.init = &init;
0098     hw = &cpu->hw;
0099 
0100     ret = clk_hw_register(NULL, hw);
0101     if (ret) {
0102         kfree(cpu);
0103         return ERR_PTR(ret);
0104     }
0105 
0106     return hw;
0107 }
0108 EXPORT_SYMBOL_GPL(imx_clk_hw_cpu);