]>
Commit | Line | Data |
---|---|---|
e0fed513 LS |
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> | |
a39973a4 | 15 | #include "clk.h" |
e0fed513 LS |
16 | |
17 | struct clk_cpu { | |
18 | struct clk_hw hw; | |
19 | struct clk *div; | |
20 | struct clk *mux; | |
21 | struct clk *pll; | |
22 | struct clk *step; | |
23 | }; | |
24 | ||
25 | static inline struct clk_cpu *to_clk_cpu(struct clk_hw *hw) | |
26 | { | |
27 | return container_of(hw, struct clk_cpu, hw); | |
28 | } | |
29 | ||
30 | static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw, | |
31 | unsigned long parent_rate) | |
32 | { | |
33 | struct clk_cpu *cpu = to_clk_cpu(hw); | |
34 | ||
35 | return clk_get_rate(cpu->div); | |
36 | } | |
37 | ||
38 | static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, | |
39 | unsigned long *prate) | |
40 | { | |
41 | struct clk_cpu *cpu = to_clk_cpu(hw); | |
42 | ||
43 | return clk_round_rate(cpu->pll, rate); | |
44 | } | |
45 | ||
46 | static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, | |
47 | unsigned long parent_rate) | |
48 | { | |
49 | struct clk_cpu *cpu = to_clk_cpu(hw); | |
50 | int ret; | |
51 | ||
52 | /* switch to PLL bypass clock */ | |
53 | ret = clk_set_parent(cpu->mux, cpu->step); | |
54 | if (ret) | |
55 | return ret; | |
56 | ||
57 | /* reprogram PLL */ | |
58 | ret = clk_set_rate(cpu->pll, rate); | |
59 | if (ret) { | |
60 | clk_set_parent(cpu->mux, cpu->pll); | |
61 | return ret; | |
62 | } | |
63 | /* switch back to PLL clock */ | |
64 | clk_set_parent(cpu->mux, cpu->pll); | |
65 | ||
66 | /* Ensure the divider is what we expect */ | |
67 | clk_set_rate(cpu->div, rate); | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
72 | static const struct clk_ops clk_cpu_ops = { | |
73 | .recalc_rate = clk_cpu_recalc_rate, | |
74 | .round_rate = clk_cpu_round_rate, | |
75 | .set_rate = clk_cpu_set_rate, | |
76 | }; | |
77 | ||
78 | struct clk *imx_clk_cpu(const char *name, const char *parent_name, | |
79 | struct clk *div, struct clk *mux, struct clk *pll, | |
80 | struct clk *step) | |
81 | { | |
82 | struct clk_cpu *cpu; | |
83 | struct clk *clk; | |
84 | struct clk_init_data init; | |
85 | ||
86 | cpu = kzalloc(sizeof(*cpu), GFP_KERNEL); | |
87 | if (!cpu) | |
88 | return ERR_PTR(-ENOMEM); | |
89 | ||
90 | cpu->div = div; | |
91 | cpu->mux = mux; | |
92 | cpu->pll = pll; | |
93 | cpu->step = step; | |
94 | ||
95 | init.name = name; | |
96 | init.ops = &clk_cpu_ops; | |
97 | init.flags = 0; | |
98 | init.parent_names = &parent_name; | |
99 | init.num_parents = 1; | |
100 | ||
101 | cpu->hw.init = &init; | |
102 | ||
103 | clk = clk_register(NULL, &cpu->hw); | |
104 | if (IS_ERR(clk)) | |
105 | kfree(cpu); | |
106 | ||
107 | return clk; | |
108 | } |