diff options
author | Hoan Tran <hotran@apm.com> | 2016-09-12 14:23:24 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2016-09-14 16:54:35 -0400 |
commit | 1a85b50bef13b44016c206272785a746adae7833 (patch) | |
tree | 7495fc987a795c2132b11d88592e402a874cb634 | |
parent | 405f27be6182362e343c25ae6ec2b2933919ef30 (diff) |
clk: xgene: Add PMD clock
Add X-Gene PMD clock support.
PMD clock is implemented for a single register field.
Output rate = parent_rate * (denominator - scale) / denominator
with
- denominator = bitmask of register field + 1
- scale = values of register field
For example, for bitmask is 0x7, denominator will be 8 and scale
will be computed and programmed accordingly.
Signed-off-by: Hoan Tran <hotran@apm.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r-- | drivers/clk/clk-xgene.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 343313250c58..5daddf5ecc4b 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c | |||
@@ -217,6 +217,226 @@ static void xgene_pcppllclk_init(struct device_node *np) | |||
217 | xgene_pllclk_init(np, PLL_TYPE_PCP); | 217 | xgene_pllclk_init(np, PLL_TYPE_PCP); |
218 | } | 218 | } |
219 | 219 | ||
220 | /** | ||
221 | * struct xgene_clk_pmd - PMD clock | ||
222 | * | ||
223 | * @hw: handle between common and hardware-specific interfaces | ||
224 | * @reg: register containing the fractional scale multiplier (scaler) | ||
225 | * @shift: shift to the unit bit field | ||
226 | * @denom: 1/denominator unit | ||
227 | * @lock: register lock | ||
228 | * Flags: | ||
229 | * XGENE_CLK_PMD_SCALE_INVERTED - By default the scaler is the value read | ||
230 | * from the register plus one. For example, | ||
231 | * 0 for (0 + 1) / denom, | ||
232 | * 1 for (1 + 1) / denom and etc. | ||
233 | * If this flag is set, it is | ||
234 | * 0 for (denom - 0) / denom, | ||
235 | * 1 for (denom - 1) / denom and etc. | ||
236 | * | ||
237 | */ | ||
238 | struct xgene_clk_pmd { | ||
239 | struct clk_hw hw; | ||
240 | void __iomem *reg; | ||
241 | u8 shift; | ||
242 | u32 mask; | ||
243 | u64 denom; | ||
244 | u32 flags; | ||
245 | spinlock_t *lock; | ||
246 | }; | ||
247 | |||
248 | #define to_xgene_clk_pmd(_hw) container_of(_hw, struct xgene_clk_pmd, hw) | ||
249 | |||
250 | #define XGENE_CLK_PMD_SCALE_INVERTED BIT(0) | ||
251 | #define XGENE_CLK_PMD_SHIFT 8 | ||
252 | #define XGENE_CLK_PMD_WIDTH 3 | ||
253 | |||
254 | static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw, | ||
255 | unsigned long parent_rate) | ||
256 | { | ||
257 | struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); | ||
258 | unsigned long flags = 0; | ||
259 | u64 ret, scale; | ||
260 | u32 val; | ||
261 | |||
262 | if (fd->lock) | ||
263 | spin_lock_irqsave(fd->lock, flags); | ||
264 | else | ||
265 | __acquire(fd->lock); | ||
266 | |||
267 | val = clk_readl(fd->reg); | ||
268 | |||
269 | if (fd->lock) | ||
270 | spin_unlock_irqrestore(fd->lock, flags); | ||
271 | else | ||
272 | __release(fd->lock); | ||
273 | |||
274 | ret = (u64)parent_rate; | ||
275 | |||
276 | scale = (val & fd->mask) >> fd->shift; | ||
277 | if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) | ||
278 | scale = fd->denom - scale; | ||
279 | else | ||
280 | scale++; | ||
281 | |||
282 | /* freq = parent_rate * scaler / denom */ | ||
283 | do_div(ret, fd->denom); | ||
284 | ret *= scale; | ||
285 | if (ret == 0) | ||
286 | ret = (u64)parent_rate; | ||
287 | |||
288 | return ret; | ||
289 | } | ||
290 | |||
291 | static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate, | ||
292 | unsigned long *parent_rate) | ||
293 | { | ||
294 | struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); | ||
295 | u64 ret, scale; | ||
296 | |||
297 | if (!rate || rate >= *parent_rate) | ||
298 | return *parent_rate; | ||
299 | |||
300 | /* freq = parent_rate * scaler / denom */ | ||
301 | ret = rate * fd->denom; | ||
302 | scale = DIV_ROUND_UP_ULL(ret, *parent_rate); | ||
303 | |||
304 | ret = (u64)*parent_rate * scale; | ||
305 | do_div(ret, fd->denom); | ||
306 | |||
307 | return ret; | ||
308 | } | ||
309 | |||
310 | static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate, | ||
311 | unsigned long parent_rate) | ||
312 | { | ||
313 | struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); | ||
314 | unsigned long flags = 0; | ||
315 | u64 scale, ret; | ||
316 | u32 val; | ||
317 | |||
318 | /* | ||
319 | * Compute the scaler: | ||
320 | * | ||
321 | * freq = parent_rate * scaler / denom, or | ||
322 | * scaler = freq * denom / parent_rate | ||
323 | */ | ||
324 | ret = rate * fd->denom; | ||
325 | scale = DIV_ROUND_UP_ULL(ret, (u64)parent_rate); | ||
326 | |||
327 | /* Check if inverted */ | ||
328 | if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) | ||
329 | scale = fd->denom - scale; | ||
330 | else | ||
331 | scale--; | ||
332 | |||
333 | if (fd->lock) | ||
334 | spin_lock_irqsave(fd->lock, flags); | ||
335 | else | ||
336 | __acquire(fd->lock); | ||
337 | |||
338 | val = clk_readl(fd->reg); | ||
339 | val &= ~fd->mask; | ||
340 | val |= (scale << fd->shift); | ||
341 | clk_writel(val, fd->reg); | ||
342 | |||
343 | if (fd->lock) | ||
344 | spin_unlock_irqrestore(fd->lock, flags); | ||
345 | else | ||
346 | __release(fd->lock); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static const struct clk_ops xgene_clk_pmd_ops = { | ||
352 | .recalc_rate = xgene_clk_pmd_recalc_rate, | ||
353 | .round_rate = xgene_clk_pmd_round_rate, | ||
354 | .set_rate = xgene_clk_pmd_set_rate, | ||
355 | }; | ||
356 | |||
357 | static struct clk * | ||
358 | xgene_register_clk_pmd(struct device *dev, | ||
359 | const char *name, const char *parent_name, | ||
360 | unsigned long flags, void __iomem *reg, u8 shift, | ||
361 | u8 width, u64 denom, u32 clk_flags, spinlock_t *lock) | ||
362 | { | ||
363 | struct xgene_clk_pmd *fd; | ||
364 | struct clk_init_data init; | ||
365 | struct clk *clk; | ||
366 | |||
367 | fd = kzalloc(sizeof(*fd), GFP_KERNEL); | ||
368 | if (!fd) | ||
369 | return ERR_PTR(-ENOMEM); | ||
370 | |||
371 | init.name = name; | ||
372 | init.ops = &xgene_clk_pmd_ops; | ||
373 | init.flags = flags; | ||
374 | init.parent_names = parent_name ? &parent_name : NULL; | ||
375 | init.num_parents = parent_name ? 1 : 0; | ||
376 | |||
377 | fd->reg = reg; | ||
378 | fd->shift = shift; | ||
379 | fd->mask = (BIT(width) - 1) << shift; | ||
380 | fd->denom = denom; | ||
381 | fd->flags = clk_flags; | ||
382 | fd->lock = lock; | ||
383 | fd->hw.init = &init; | ||
384 | |||
385 | clk = clk_register(dev, &fd->hw); | ||
386 | if (IS_ERR(clk)) { | ||
387 | pr_err("%s: could not register clk %s\n", __func__, name); | ||
388 | kfree(fd); | ||
389 | return NULL; | ||
390 | } | ||
391 | |||
392 | return clk; | ||
393 | } | ||
394 | |||
395 | static void xgene_pmdclk_init(struct device_node *np) | ||
396 | { | ||
397 | const char *clk_name = np->full_name; | ||
398 | void __iomem *csr_reg; | ||
399 | struct resource res; | ||
400 | struct clk *clk; | ||
401 | u64 denom; | ||
402 | u32 flags = 0; | ||
403 | int rc; | ||
404 | |||
405 | /* Check if the entry is disabled */ | ||
406 | if (!of_device_is_available(np)) | ||
407 | return; | ||
408 | |||
409 | /* Parse the DTS register for resource */ | ||
410 | rc = of_address_to_resource(np, 0, &res); | ||
411 | if (rc != 0) { | ||
412 | pr_err("no DTS register for %s\n", np->full_name); | ||
413 | return; | ||
414 | } | ||
415 | csr_reg = of_iomap(np, 0); | ||
416 | if (!csr_reg) { | ||
417 | pr_err("Unable to map resource for %s\n", np->full_name); | ||
418 | return; | ||
419 | } | ||
420 | of_property_read_string(np, "clock-output-names", &clk_name); | ||
421 | |||
422 | denom = BIT(XGENE_CLK_PMD_WIDTH); | ||
423 | flags |= XGENE_CLK_PMD_SCALE_INVERTED; | ||
424 | |||
425 | clk = xgene_register_clk_pmd(NULL, clk_name, | ||
426 | of_clk_get_parent_name(np, 0), 0, | ||
427 | csr_reg, XGENE_CLK_PMD_SHIFT, | ||
428 | XGENE_CLK_PMD_WIDTH, denom, | ||
429 | flags, &clk_lock); | ||
430 | if (!IS_ERR(clk)) { | ||
431 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
432 | clk_register_clkdev(clk, clk_name, NULL); | ||
433 | pr_debug("Add %s clock\n", clk_name); | ||
434 | } else { | ||
435 | if (csr_reg) | ||
436 | iounmap(csr_reg); | ||
437 | } | ||
438 | } | ||
439 | |||
220 | /* IP Clock */ | 440 | /* IP Clock */ |
221 | struct xgene_dev_parameters { | 441 | struct xgene_dev_parameters { |
222 | void __iomem *csr_reg; /* CSR for IP clock */ | 442 | void __iomem *csr_reg; /* CSR for IP clock */ |
@@ -543,6 +763,7 @@ err: | |||
543 | 763 | ||
544 | CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); | 764 | CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); |
545 | CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); | 765 | CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); |
766 | CLK_OF_DECLARE(xgene_pmd_clock, "apm,xgene-pmd-clock", xgene_pmdclk_init); | ||
546 | CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock", | 767 | CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock", |
547 | xgene_socpllclk_init); | 768 | xgene_socpllclk_init); |
548 | CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock", | 769 | CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock", |