diff options
Diffstat (limited to 'drivers/clk/clk-composite.c')
-rw-r--r-- | drivers/clk/clk-composite.c | 93 |
1 files changed, 81 insertions, 12 deletions
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 1f903e1f86a2..00269de2f390 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c | |||
@@ -151,6 +151,33 @@ static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, | |||
151 | return rate_ops->set_rate(rate_hw, rate, parent_rate); | 151 | return rate_ops->set_rate(rate_hw, rate, parent_rate); |
152 | } | 152 | } |
153 | 153 | ||
154 | static int clk_composite_set_rate_and_parent(struct clk_hw *hw, | ||
155 | unsigned long rate, | ||
156 | unsigned long parent_rate, | ||
157 | u8 index) | ||
158 | { | ||
159 | struct clk_composite *composite = to_clk_composite(hw); | ||
160 | const struct clk_ops *rate_ops = composite->rate_ops; | ||
161 | const struct clk_ops *mux_ops = composite->mux_ops; | ||
162 | struct clk_hw *rate_hw = composite->rate_hw; | ||
163 | struct clk_hw *mux_hw = composite->mux_hw; | ||
164 | unsigned long temp_rate; | ||
165 | |||
166 | __clk_hw_set_clk(rate_hw, hw); | ||
167 | __clk_hw_set_clk(mux_hw, hw); | ||
168 | |||
169 | temp_rate = rate_ops->recalc_rate(rate_hw, parent_rate); | ||
170 | if (temp_rate > rate) { | ||
171 | rate_ops->set_rate(rate_hw, rate, parent_rate); | ||
172 | mux_ops->set_parent(mux_hw, index); | ||
173 | } else { | ||
174 | mux_ops->set_parent(mux_hw, index); | ||
175 | rate_ops->set_rate(rate_hw, rate, parent_rate); | ||
176 | } | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
154 | static int clk_composite_is_enabled(struct clk_hw *hw) | 181 | static int clk_composite_is_enabled(struct clk_hw *hw) |
155 | { | 182 | { |
156 | struct clk_composite *composite = to_clk_composite(hw); | 183 | struct clk_composite *composite = to_clk_composite(hw); |
@@ -184,17 +211,18 @@ static void clk_composite_disable(struct clk_hw *hw) | |||
184 | gate_ops->disable(gate_hw); | 211 | gate_ops->disable(gate_hw); |
185 | } | 212 | } |
186 | 213 | ||
187 | struct clk *clk_register_composite(struct device *dev, const char *name, | 214 | struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, |
188 | const char * const *parent_names, int num_parents, | 215 | const char * const *parent_names, int num_parents, |
189 | struct clk_hw *mux_hw, const struct clk_ops *mux_ops, | 216 | struct clk_hw *mux_hw, const struct clk_ops *mux_ops, |
190 | struct clk_hw *rate_hw, const struct clk_ops *rate_ops, | 217 | struct clk_hw *rate_hw, const struct clk_ops *rate_ops, |
191 | struct clk_hw *gate_hw, const struct clk_ops *gate_ops, | 218 | struct clk_hw *gate_hw, const struct clk_ops *gate_ops, |
192 | unsigned long flags) | 219 | unsigned long flags) |
193 | { | 220 | { |
194 | struct clk *clk; | 221 | struct clk_hw *hw; |
195 | struct clk_init_data init; | 222 | struct clk_init_data init; |
196 | struct clk_composite *composite; | 223 | struct clk_composite *composite; |
197 | struct clk_ops *clk_composite_ops; | 224 | struct clk_ops *clk_composite_ops; |
225 | int ret; | ||
198 | 226 | ||
199 | composite = kzalloc(sizeof(*composite), GFP_KERNEL); | 227 | composite = kzalloc(sizeof(*composite), GFP_KERNEL); |
200 | if (!composite) | 228 | if (!composite) |
@@ -204,12 +232,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name, | |||
204 | init.flags = flags | CLK_IS_BASIC; | 232 | init.flags = flags | CLK_IS_BASIC; |
205 | init.parent_names = parent_names; | 233 | init.parent_names = parent_names; |
206 | init.num_parents = num_parents; | 234 | init.num_parents = num_parents; |
235 | hw = &composite->hw; | ||
207 | 236 | ||
208 | clk_composite_ops = &composite->ops; | 237 | clk_composite_ops = &composite->ops; |
209 | 238 | ||
210 | if (mux_hw && mux_ops) { | 239 | if (mux_hw && mux_ops) { |
211 | if (!mux_ops->get_parent) { | 240 | if (!mux_ops->get_parent) { |
212 | clk = ERR_PTR(-EINVAL); | 241 | hw = ERR_PTR(-EINVAL); |
213 | goto err; | 242 | goto err; |
214 | } | 243 | } |
215 | 244 | ||
@@ -224,7 +253,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, | |||
224 | 253 | ||
225 | if (rate_hw && rate_ops) { | 254 | if (rate_hw && rate_ops) { |
226 | if (!rate_ops->recalc_rate) { | 255 | if (!rate_ops->recalc_rate) { |
227 | clk = ERR_PTR(-EINVAL); | 256 | hw = ERR_PTR(-EINVAL); |
228 | goto err; | 257 | goto err; |
229 | } | 258 | } |
230 | clk_composite_ops->recalc_rate = clk_composite_recalc_rate; | 259 | clk_composite_ops->recalc_rate = clk_composite_recalc_rate; |
@@ -250,10 +279,16 @@ struct clk *clk_register_composite(struct device *dev, const char *name, | |||
250 | composite->rate_ops = rate_ops; | 279 | composite->rate_ops = rate_ops; |
251 | } | 280 | } |
252 | 281 | ||
282 | if (mux_hw && mux_ops && rate_hw && rate_ops) { | ||
283 | if (mux_ops->set_parent && rate_ops->set_rate) | ||
284 | clk_composite_ops->set_rate_and_parent = | ||
285 | clk_composite_set_rate_and_parent; | ||
286 | } | ||
287 | |||
253 | if (gate_hw && gate_ops) { | 288 | if (gate_hw && gate_ops) { |
254 | if (!gate_ops->is_enabled || !gate_ops->enable || | 289 | if (!gate_ops->is_enabled || !gate_ops->enable || |
255 | !gate_ops->disable) { | 290 | !gate_ops->disable) { |
256 | clk = ERR_PTR(-EINVAL); | 291 | hw = ERR_PTR(-EINVAL); |
257 | goto err; | 292 | goto err; |
258 | } | 293 | } |
259 | 294 | ||
@@ -267,22 +302,56 @@ struct clk *clk_register_composite(struct device *dev, const char *name, | |||
267 | init.ops = clk_composite_ops; | 302 | init.ops = clk_composite_ops; |
268 | composite->hw.init = &init; | 303 | composite->hw.init = &init; |
269 | 304 | ||
270 | clk = clk_register(dev, &composite->hw); | 305 | ret = clk_hw_register(dev, hw); |
271 | if (IS_ERR(clk)) | 306 | if (ret) { |
307 | hw = ERR_PTR(ret); | ||
272 | goto err; | 308 | goto err; |
309 | } | ||
273 | 310 | ||
274 | if (composite->mux_hw) | 311 | if (composite->mux_hw) |
275 | composite->mux_hw->clk = clk; | 312 | composite->mux_hw->clk = hw->clk; |
276 | 313 | ||
277 | if (composite->rate_hw) | 314 | if (composite->rate_hw) |
278 | composite->rate_hw->clk = clk; | 315 | composite->rate_hw->clk = hw->clk; |
279 | 316 | ||
280 | if (composite->gate_hw) | 317 | if (composite->gate_hw) |
281 | composite->gate_hw->clk = clk; | 318 | composite->gate_hw->clk = hw->clk; |
282 | 319 | ||
283 | return clk; | 320 | return hw; |
284 | 321 | ||
285 | err: | 322 | err: |
286 | kfree(composite); | 323 | kfree(composite); |
287 | return clk; | 324 | return hw; |
325 | } | ||
326 | |||
327 | struct clk *clk_register_composite(struct device *dev, const char *name, | ||
328 | const char * const *parent_names, int num_parents, | ||
329 | struct clk_hw *mux_hw, const struct clk_ops *mux_ops, | ||
330 | struct clk_hw *rate_hw, const struct clk_ops *rate_ops, | ||
331 | struct clk_hw *gate_hw, const struct clk_ops *gate_ops, | ||
332 | unsigned long flags) | ||
333 | { | ||
334 | struct clk_hw *hw; | ||
335 | |||
336 | hw = clk_hw_register_composite(dev, name, parent_names, num_parents, | ||
337 | mux_hw, mux_ops, rate_hw, rate_ops, gate_hw, gate_ops, | ||
338 | flags); | ||
339 | if (IS_ERR(hw)) | ||
340 | return ERR_CAST(hw); | ||
341 | return hw->clk; | ||
342 | } | ||
343 | |||
344 | void clk_unregister_composite(struct clk *clk) | ||
345 | { | ||
346 | struct clk_composite *composite; | ||
347 | struct clk_hw *hw; | ||
348 | |||
349 | hw = __clk_get_hw(clk); | ||
350 | if (!hw) | ||
351 | return; | ||
352 | |||
353 | composite = to_clk_composite(hw); | ||
354 | |||
355 | clk_unregister(clk); | ||
356 | kfree(composite); | ||
288 | } | 357 | } |