1/* 2 * Copyright (c) 2014 Lucas Stach <l.stach@pengutronix.de>, Pengutronix 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * http://www.opensource.org/licenses/gpl-license.html 9 * http://www.gnu.org/copyleft/gpl.html 10 */ 11 12#include <linux/clk.h> 13#include <linux/clk-provider.h> 14#include <linux/slab.h> 15 16struct clk_cpu { 17 struct clk_hw hw; 18 struct clk *div; 19 struct clk *mux; 20 struct clk *pll; 21 struct clk *step; 22}; 23 24static inline struct clk_cpu *to_clk_cpu(struct clk_hw *hw) 25{ 26 return container_of(hw, struct clk_cpu, hw); 27} 28 29static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw, 30 unsigned long parent_rate) 31{ 32 struct clk_cpu *cpu = to_clk_cpu(hw); 33 34 return clk_get_rate(cpu->div); 35} 36 37static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, 38 unsigned long *prate) 39{ 40 struct clk_cpu *cpu = to_clk_cpu(hw); 41 42 return clk_round_rate(cpu->pll, rate); 43} 44 45static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, 46 unsigned long parent_rate) 47{ 48 struct clk_cpu *cpu = to_clk_cpu(hw); 49 int ret; 50 51 /* switch to PLL bypass clock */ 52 ret = clk_set_parent(cpu->mux, cpu->step); 53 if (ret) 54 return ret; 55 56 /* reprogram PLL */ 57 ret = clk_set_rate(cpu->pll, rate); 58 if (ret) { 59 clk_set_parent(cpu->mux, cpu->pll); 60 return ret; 61 } 62 /* switch back to PLL clock */ 63 clk_set_parent(cpu->mux, cpu->pll); 64 65 /* Ensure the divider is what we expect */ 66 clk_set_rate(cpu->div, rate); 67 68 return 0; 69} 70 71static const struct clk_ops clk_cpu_ops = { 72 .recalc_rate = clk_cpu_recalc_rate, 73 .round_rate = clk_cpu_round_rate, 74 .set_rate = clk_cpu_set_rate, 75}; 76 77struct clk *imx_clk_cpu(const char *name, const char *parent_name, 78 struct clk *div, struct clk *mux, struct clk *pll, 79 struct clk *step) 80{ 81 struct clk_cpu *cpu; 82 struct clk *clk; 83 struct clk_init_data init; 84 85 cpu = kzalloc(sizeof(*cpu), GFP_KERNEL); 86 if (!cpu) 87 return ERR_PTR(-ENOMEM); 88 89 cpu->div = div; 90 cpu->mux = mux; 91 cpu->pll = pll; 92 cpu->step = step; 93 94 init.name = name; 95 init.ops = &clk_cpu_ops; 96 init.flags = 0; 97 init.parent_names = &parent_name; 98 init.num_parents = 1; 99 100 cpu->hw.init = &init; 101 102 clk = clk_register(NULL, &cpu->hw); 103 if (IS_ERR(clk)) 104 kfree(cpu); 105 106 return clk; 107} 108