diff options
author | Ulf Hansson <ulf.hansson@linaro.org> | 2013-04-03 08:26:57 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2013-04-10 14:27:56 -0400 |
commit | 5b82d03b74cadb681377e1c2494c477185bf6619 (patch) | |
tree | c6342e1461c7a35a098c236b3ee5e307472712fe /drivers/clk/ux500 | |
parent | 4cb24e68a5af427651b3529cd3fe382fb9ba1544 (diff) |
clk: ux500: Add support for sysctrl clocks
The abx500 sysctrl clocks are using the ab8500 sysctrl driver to
modify the clock hardware. Sysctrl clocks are represented by a
ab8500 sysctrl register and with a corresponding bitmask.
The sysctrl clocks are slow path clocks, which means clk_prepare
and clk_unprepare will be used to gate|ungate these clocks.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/ux500')
-rw-r--r-- | drivers/clk/ux500/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/ux500/clk-sysctrl.c | 221 | ||||
-rw-r--r-- | drivers/clk/ux500/clk.h | 29 |
3 files changed, 251 insertions, 0 deletions
diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile index bcc0c11a507c..c6a806ed0e8c 100644 --- a/drivers/clk/ux500/Makefile +++ b/drivers/clk/ux500/Makefile | |||
@@ -5,6 +5,7 @@ | |||
5 | # Clock types | 5 | # Clock types |
6 | obj-y += clk-prcc.o | 6 | obj-y += clk-prcc.o |
7 | obj-y += clk-prcmu.o | 7 | obj-y += clk-prcmu.o |
8 | obj-y += clk-sysctrl.o | ||
8 | 9 | ||
9 | # Clock definitions | 10 | # Clock definitions |
10 | obj-y += u8500_clk.o | 11 | obj-y += u8500_clk.o |
diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c new file mode 100644 index 000000000000..bc7e9bde792b --- /dev/null +++ b/drivers/clk/ux500/clk-sysctrl.c | |||
@@ -0,0 +1,221 @@ | |||
1 | /* | ||
2 | * Sysctrl clock implementation for ux500 platform. | ||
3 | * | ||
4 | * Copyright (C) 2013 ST-Ericsson SA | ||
5 | * Author: Ulf Hansson <ulf.hansson@linaro.org> | ||
6 | * | ||
7 | * License terms: GNU General Public License (GPL) version 2 | ||
8 | */ | ||
9 | |||
10 | #include <linux/clk-provider.h> | ||
11 | #include <linux/mfd/abx500/ab8500-sysctrl.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/err.h> | ||
17 | #include "clk.h" | ||
18 | |||
19 | #define SYSCTRL_MAX_NUM_PARENTS 4 | ||
20 | |||
21 | #define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw) | ||
22 | |||
23 | struct clk_sysctrl { | ||
24 | struct clk_hw hw; | ||
25 | struct device *dev; | ||
26 | u8 parent_index; | ||
27 | u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS]; | ||
28 | u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS]; | ||
29 | u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS]; | ||
30 | unsigned long rate; | ||
31 | unsigned long enable_delay_us; | ||
32 | }; | ||
33 | |||
34 | /* Sysctrl clock operations. */ | ||
35 | |||
36 | static int clk_sysctrl_prepare(struct clk_hw *hw) | ||
37 | { | ||
38 | int ret; | ||
39 | struct clk_sysctrl *clk = to_clk_sysctrl(hw); | ||
40 | |||
41 | ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0], | ||
42 | clk->reg_bits[0]); | ||
43 | |||
44 | if (!ret && clk->enable_delay_us) | ||
45 | usleep_range(clk->enable_delay_us, clk->enable_delay_us); | ||
46 | |||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | static void clk_sysctrl_unprepare(struct clk_hw *hw) | ||
51 | { | ||
52 | struct clk_sysctrl *clk = to_clk_sysctrl(hw); | ||
53 | if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0])) | ||
54 | dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n", | ||
55 | __func__, __clk_get_name(hw->clk)); | ||
56 | } | ||
57 | |||
58 | static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw, | ||
59 | unsigned long parent_rate) | ||
60 | { | ||
61 | struct clk_sysctrl *clk = to_clk_sysctrl(hw); | ||
62 | return clk->rate; | ||
63 | } | ||
64 | |||
65 | static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index) | ||
66 | { | ||
67 | struct clk_sysctrl *clk = to_clk_sysctrl(hw); | ||
68 | u8 old_index = clk->parent_index; | ||
69 | int ret = 0; | ||
70 | |||
71 | if (clk->reg_sel[old_index]) { | ||
72 | ret = ab8500_sysctrl_clear(clk->reg_sel[old_index], | ||
73 | clk->reg_mask[old_index]); | ||
74 | if (ret) | ||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | if (clk->reg_sel[index]) { | ||
79 | ret = ab8500_sysctrl_write(clk->reg_sel[index], | ||
80 | clk->reg_mask[index], | ||
81 | clk->reg_bits[index]); | ||
82 | if (ret) { | ||
83 | if (clk->reg_sel[old_index]) | ||
84 | ab8500_sysctrl_write(clk->reg_sel[old_index], | ||
85 | clk->reg_mask[old_index], | ||
86 | clk->reg_bits[old_index]); | ||
87 | return ret; | ||
88 | } | ||
89 | } | ||
90 | clk->parent_index = index; | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | static u8 clk_sysctrl_get_parent(struct clk_hw *hw) | ||
96 | { | ||
97 | struct clk_sysctrl *clk = to_clk_sysctrl(hw); | ||
98 | return clk->parent_index; | ||
99 | } | ||
100 | |||
101 | static struct clk_ops clk_sysctrl_gate_ops = { | ||
102 | .prepare = clk_sysctrl_prepare, | ||
103 | .unprepare = clk_sysctrl_unprepare, | ||
104 | }; | ||
105 | |||
106 | static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { | ||
107 | .prepare = clk_sysctrl_prepare, | ||
108 | .unprepare = clk_sysctrl_unprepare, | ||
109 | .recalc_rate = clk_sysctrl_recalc_rate, | ||
110 | }; | ||
111 | |||
112 | static struct clk_ops clk_sysctrl_set_parent_ops = { | ||
113 | .set_parent = clk_sysctrl_set_parent, | ||
114 | .get_parent = clk_sysctrl_get_parent, | ||
115 | }; | ||
116 | |||
117 | static struct clk *clk_reg_sysctrl(struct device *dev, | ||
118 | const char *name, | ||
119 | const char **parent_names, | ||
120 | u8 num_parents, | ||
121 | u16 *reg_sel, | ||
122 | u8 *reg_mask, | ||
123 | u8 *reg_bits, | ||
124 | unsigned long rate, | ||
125 | unsigned long enable_delay_us, | ||
126 | unsigned long flags, | ||
127 | struct clk_ops *clk_sysctrl_ops) | ||
128 | { | ||
129 | struct clk_sysctrl *clk; | ||
130 | struct clk_init_data clk_sysctrl_init; | ||
131 | struct clk *clk_reg; | ||
132 | int i; | ||
133 | |||
134 | if (!dev) | ||
135 | return ERR_PTR(-EINVAL); | ||
136 | |||
137 | if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) { | ||
138 | dev_err(dev, "clk_sysctrl: invalid arguments passed\n"); | ||
139 | return ERR_PTR(-EINVAL); | ||
140 | } | ||
141 | |||
142 | clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL); | ||
143 | if (!clk) { | ||
144 | dev_err(dev, "clk_sysctrl: could not allocate clk\n"); | ||
145 | return ERR_PTR(-ENOMEM); | ||
146 | } | ||
147 | |||
148 | for (i = 0; i < num_parents; i++) { | ||
149 | clk->reg_sel[i] = reg_sel[i]; | ||
150 | clk->reg_bits[i] = reg_bits[i]; | ||
151 | clk->reg_mask[i] = reg_mask[i]; | ||
152 | } | ||
153 | |||
154 | clk->parent_index = 0; | ||
155 | clk->rate = rate; | ||
156 | clk->enable_delay_us = enable_delay_us; | ||
157 | clk->dev = dev; | ||
158 | |||
159 | clk_sysctrl_init.name = name; | ||
160 | clk_sysctrl_init.ops = clk_sysctrl_ops; | ||
161 | clk_sysctrl_init.flags = flags; | ||
162 | clk_sysctrl_init.parent_names = parent_names; | ||
163 | clk_sysctrl_init.num_parents = num_parents; | ||
164 | clk->hw.init = &clk_sysctrl_init; | ||
165 | |||
166 | clk_reg = devm_clk_register(clk->dev, &clk->hw); | ||
167 | if (IS_ERR(clk_reg)) | ||
168 | dev_err(dev, "clk_sysctrl: clk_register failed\n"); | ||
169 | |||
170 | return clk_reg; | ||
171 | } | ||
172 | |||
173 | struct clk *clk_reg_sysctrl_gate(struct device *dev, | ||
174 | const char *name, | ||
175 | const char *parent_name, | ||
176 | u16 reg_sel, | ||
177 | u8 reg_mask, | ||
178 | u8 reg_bits, | ||
179 | unsigned long enable_delay_us, | ||
180 | unsigned long flags) | ||
181 | { | ||
182 | const char **parent_names = (parent_name ? &parent_name : NULL); | ||
183 | u8 num_parents = (parent_name ? 1 : 0); | ||
184 | |||
185 | return clk_reg_sysctrl(dev, name, parent_names, num_parents, | ||
186 | ®_sel, ®_mask, ®_bits, 0, enable_delay_us, | ||
187 | flags, &clk_sysctrl_gate_ops); | ||
188 | } | ||
189 | |||
190 | struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev, | ||
191 | const char *name, | ||
192 | const char *parent_name, | ||
193 | u16 reg_sel, | ||
194 | u8 reg_mask, | ||
195 | u8 reg_bits, | ||
196 | unsigned long rate, | ||
197 | unsigned long enable_delay_us, | ||
198 | unsigned long flags) | ||
199 | { | ||
200 | const char **parent_names = (parent_name ? &parent_name : NULL); | ||
201 | u8 num_parents = (parent_name ? 1 : 0); | ||
202 | |||
203 | return clk_reg_sysctrl(dev, name, parent_names, num_parents, | ||
204 | ®_sel, ®_mask, ®_bits, | ||
205 | rate, enable_delay_us, flags, | ||
206 | &clk_sysctrl_gate_fixed_rate_ops); | ||
207 | } | ||
208 | |||
209 | struct clk *clk_reg_sysctrl_set_parent(struct device *dev, | ||
210 | const char *name, | ||
211 | const char **parent_names, | ||
212 | u8 num_parents, | ||
213 | u16 *reg_sel, | ||
214 | u8 *reg_mask, | ||
215 | u8 *reg_bits, | ||
216 | unsigned long flags) | ||
217 | { | ||
218 | return clk_reg_sysctrl(dev, name, parent_names, num_parents, | ||
219 | reg_sel, reg_mask, reg_bits, 0, 0, flags, | ||
220 | &clk_sysctrl_set_parent_ops); | ||
221 | } | ||
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h index c3e449169a83..3d2dfdc71290 100644 --- a/drivers/clk/ux500/clk.h +++ b/drivers/clk/ux500/clk.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #define __UX500_CLK_H | 11 | #define __UX500_CLK_H |
12 | 12 | ||
13 | #include <linux/clk.h> | 13 | #include <linux/clk.h> |
14 | #include <linux/device.h> | ||
14 | 15 | ||
15 | struct clk *clk_reg_prcc_pclk(const char *name, | 16 | struct clk *clk_reg_prcc_pclk(const char *name, |
16 | const char *parent_name, | 17 | const char *parent_name, |
@@ -57,4 +58,32 @@ struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name, | |||
57 | unsigned long rate, | 58 | unsigned long rate, |
58 | unsigned long flags); | 59 | unsigned long flags); |
59 | 60 | ||
61 | struct clk *clk_reg_sysctrl_gate(struct device *dev, | ||
62 | const char *name, | ||
63 | const char *parent_name, | ||
64 | u16 reg_sel, | ||
65 | u8 reg_mask, | ||
66 | u8 reg_bits, | ||
67 | unsigned long enable_delay_us, | ||
68 | unsigned long flags); | ||
69 | |||
70 | struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev, | ||
71 | const char *name, | ||
72 | const char *parent_name, | ||
73 | u16 reg_sel, | ||
74 | u8 reg_mask, | ||
75 | u8 reg_bits, | ||
76 | unsigned long rate, | ||
77 | unsigned long enable_delay_us, | ||
78 | unsigned long flags); | ||
79 | |||
80 | struct clk *clk_reg_sysctrl_set_parent(struct device *dev, | ||
81 | const char *name, | ||
82 | const char **parent_names, | ||
83 | u8 num_parents, | ||
84 | u16 *reg_sel, | ||
85 | u8 *reg_mask, | ||
86 | u8 *reg_bits, | ||
87 | unsigned long flags); | ||
88 | |||
60 | #endif /* __UX500_CLK_H */ | 89 | #endif /* __UX500_CLK_H */ |