diff options
Diffstat (limited to 'drivers/clk/imx/clk-cpu.c')
-rw-r--r-- | drivers/clk/imx/clk-cpu.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/drivers/clk/imx/clk-cpu.c b/drivers/clk/imx/clk-cpu.c new file mode 100644 index 000000000000..9d46eac87f45 --- /dev/null +++ b/drivers/clk/imx/clk-cpu.c | |||
@@ -0,0 +1,108 @@ | |||
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 | #include "clk.h" | ||
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 | } | ||