.. | .. |
---|
1 | 1 | /* |
---|
2 | 2 | * SCI Clock driver for keystone based devices |
---|
3 | 3 | * |
---|
4 | | - * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ |
---|
| 4 | + * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ |
---|
5 | 5 | * Tero Kristo <t-kristo@ti.com> |
---|
6 | 6 | * |
---|
7 | 7 | * This program is free software; you can redistribute it and/or modify |
---|
.. | .. |
---|
23 | 23 | #include <linux/slab.h> |
---|
24 | 24 | #include <linux/soc/ti/ti_sci_protocol.h> |
---|
25 | 25 | #include <linux/bsearch.h> |
---|
| 26 | +#include <linux/list_sort.h> |
---|
26 | 27 | |
---|
27 | 28 | #define SCI_CLK_SSC_ENABLE BIT(0) |
---|
28 | 29 | #define SCI_CLK_ALLOW_FREQ_CHANGE BIT(1) |
---|
.. | .. |
---|
52 | 53 | * @num_parents: Number of parents for this clock |
---|
53 | 54 | * @provider: Master clock provider |
---|
54 | 55 | * @flags: Flags for the clock |
---|
| 56 | + * @node: Link for handling clocks probed via DT |
---|
| 57 | + * @cached_req: Cached requested freq for determine rate calls |
---|
| 58 | + * @cached_res: Cached result freq for determine rate calls |
---|
55 | 59 | */ |
---|
56 | 60 | struct sci_clk { |
---|
57 | 61 | struct clk_hw hw; |
---|
58 | 62 | u16 dev_id; |
---|
59 | | - u8 clk_id; |
---|
60 | | - u8 num_parents; |
---|
| 63 | + u32 clk_id; |
---|
| 64 | + u32 num_parents; |
---|
61 | 65 | struct sci_clk_provider *provider; |
---|
62 | 66 | u8 flags; |
---|
| 67 | + struct list_head node; |
---|
| 68 | + unsigned long cached_req; |
---|
| 69 | + unsigned long cached_res; |
---|
63 | 70 | }; |
---|
64 | 71 | |
---|
65 | 72 | #define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw) |
---|
.. | .. |
---|
172 | 179 | int ret; |
---|
173 | 180 | u64 new_rate; |
---|
174 | 181 | |
---|
| 182 | + if (clk->cached_req && clk->cached_req == req->rate) { |
---|
| 183 | + req->rate = clk->cached_res; |
---|
| 184 | + return 0; |
---|
| 185 | + } |
---|
| 186 | + |
---|
175 | 187 | ret = clk->provider->ops->get_best_match_freq(clk->provider->sci, |
---|
176 | 188 | clk->dev_id, |
---|
177 | 189 | clk->clk_id, |
---|
.. | .. |
---|
185 | 197 | clk->dev_id, clk->clk_id, ret); |
---|
186 | 198 | return ret; |
---|
187 | 199 | } |
---|
| 200 | + |
---|
| 201 | + clk->cached_req = req->rate; |
---|
| 202 | + clk->cached_res = new_rate; |
---|
188 | 203 | |
---|
189 | 204 | req->rate = new_rate; |
---|
190 | 205 | |
---|
.. | .. |
---|
206 | 221 | struct sci_clk *clk = to_sci_clk(hw); |
---|
207 | 222 | |
---|
208 | 223 | return clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id, |
---|
209 | | - clk->clk_id, rate, rate, rate); |
---|
| 224 | + clk->clk_id, rate / 10 * 9, rate, |
---|
| 225 | + rate / 10 * 11); |
---|
210 | 226 | } |
---|
211 | 227 | |
---|
212 | 228 | /** |
---|
.. | .. |
---|
218 | 234 | static u8 sci_clk_get_parent(struct clk_hw *hw) |
---|
219 | 235 | { |
---|
220 | 236 | struct sci_clk *clk = to_sci_clk(hw); |
---|
221 | | - u8 parent_id; |
---|
| 237 | + u32 parent_id = 0; |
---|
222 | 238 | int ret; |
---|
223 | 239 | |
---|
224 | 240 | ret = clk->provider->ops->get_parent(clk->provider->sci, clk->dev_id, |
---|
225 | | - clk->clk_id, &parent_id); |
---|
| 241 | + clk->clk_id, (void *)&parent_id); |
---|
226 | 242 | if (ret) { |
---|
227 | 243 | dev_err(clk->provider->dev, |
---|
228 | 244 | "get-parent failed for dev=%d, clk=%d, ret=%d\n", |
---|
.. | .. |
---|
230 | 246 | return 0; |
---|
231 | 247 | } |
---|
232 | 248 | |
---|
233 | | - return parent_id - clk->clk_id - 1; |
---|
| 249 | + parent_id = parent_id - clk->clk_id - 1; |
---|
| 250 | + |
---|
| 251 | + return (u8)parent_id; |
---|
234 | 252 | } |
---|
235 | 253 | |
---|
236 | 254 | /** |
---|
.. | .. |
---|
243 | 261 | static int sci_clk_set_parent(struct clk_hw *hw, u8 index) |
---|
244 | 262 | { |
---|
245 | 263 | struct sci_clk *clk = to_sci_clk(hw); |
---|
| 264 | + |
---|
| 265 | + clk->cached_req = 0; |
---|
246 | 266 | |
---|
247 | 267 | return clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id, |
---|
248 | 268 | clk->clk_id, |
---|
.. | .. |
---|
280 | 300 | int i; |
---|
281 | 301 | int ret = 0; |
---|
282 | 302 | |
---|
283 | | - name = kasprintf(GFP_KERNEL, "%s:%d:%d", dev_name(provider->dev), |
---|
284 | | - sci_clk->dev_id, sci_clk->clk_id); |
---|
| 303 | + name = kasprintf(GFP_KERNEL, "clk:%d:%d", sci_clk->dev_id, |
---|
| 304 | + sci_clk->clk_id); |
---|
| 305 | + if (!name) |
---|
| 306 | + return -ENOMEM; |
---|
285 | 307 | |
---|
286 | 308 | init.name = name; |
---|
287 | 309 | |
---|
.. | .. |
---|
306 | 328 | for (i = 0; i < sci_clk->num_parents; i++) { |
---|
307 | 329 | char *parent_name; |
---|
308 | 330 | |
---|
309 | | - parent_name = kasprintf(GFP_KERNEL, "%s:%d:%d", |
---|
310 | | - dev_name(provider->dev), |
---|
| 331 | + parent_name = kasprintf(GFP_KERNEL, "clk:%d:%d", |
---|
311 | 332 | sci_clk->dev_id, |
---|
312 | 333 | sci_clk->clk_id + 1 + i); |
---|
313 | 334 | if (!parent_name) { |
---|
.. | .. |
---|
404 | 425 | }; |
---|
405 | 426 | MODULE_DEVICE_TABLE(of, ti_sci_clk_of_match); |
---|
406 | 427 | |
---|
407 | | -/** |
---|
408 | | - * ti_sci_clk_probe - Probe function for the TI SCI clock driver |
---|
409 | | - * @pdev: platform device pointer to be probed |
---|
410 | | - * |
---|
411 | | - * Probes the TI SCI clock device. Allocates a new clock provider |
---|
412 | | - * and registers this to the common clock framework. Also applies |
---|
413 | | - * any required flags to the identified clocks via clock lists |
---|
414 | | - * supplied from DT. Returns 0 for success, negative error value |
---|
415 | | - * for failure. |
---|
416 | | - */ |
---|
417 | | -static int ti_sci_clk_probe(struct platform_device *pdev) |
---|
| 428 | +#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW |
---|
| 429 | +static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider) |
---|
418 | 430 | { |
---|
419 | | - struct device *dev = &pdev->dev; |
---|
420 | | - struct device_node *np = dev->of_node; |
---|
421 | | - struct sci_clk_provider *provider; |
---|
422 | | - const struct ti_sci_handle *handle; |
---|
423 | 431 | int ret; |
---|
424 | 432 | int num_clks = 0; |
---|
425 | 433 | struct sci_clk **clks = NULL; |
---|
.. | .. |
---|
428 | 436 | int max_clks = 0; |
---|
429 | 437 | int clk_id = 0; |
---|
430 | 438 | int dev_id = 0; |
---|
431 | | - u8 num_parents; |
---|
| 439 | + u32 num_parents = 0; |
---|
432 | 440 | int gap_size = 0; |
---|
433 | | - |
---|
434 | | - handle = devm_ti_sci_get_handle(dev); |
---|
435 | | - if (IS_ERR(handle)) |
---|
436 | | - return PTR_ERR(handle); |
---|
437 | | - |
---|
438 | | - provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL); |
---|
439 | | - if (!provider) |
---|
440 | | - return -ENOMEM; |
---|
441 | | - |
---|
442 | | - provider->sci = handle; |
---|
443 | | - provider->ops = &handle->ops.clk_ops; |
---|
444 | | - provider->dev = dev; |
---|
| 441 | + struct device *dev = provider->dev; |
---|
445 | 442 | |
---|
446 | 443 | while (1) { |
---|
447 | 444 | ret = provider->ops->get_num_parents(provider->sci, dev_id, |
---|
448 | | - clk_id, &num_parents); |
---|
| 445 | + clk_id, |
---|
| 446 | + (void *)&num_parents); |
---|
449 | 447 | if (ret) { |
---|
450 | 448 | gap_size++; |
---|
451 | 449 | if (!clk_id) { |
---|
.. | .. |
---|
502 | 500 | |
---|
503 | 501 | devm_kfree(dev, clks); |
---|
504 | 502 | |
---|
| 503 | + return 0; |
---|
| 504 | +} |
---|
| 505 | + |
---|
| 506 | +#else |
---|
| 507 | + |
---|
| 508 | +static int _cmp_sci_clk_list(void *priv, struct list_head *a, |
---|
| 509 | + struct list_head *b) |
---|
| 510 | +{ |
---|
| 511 | + struct sci_clk *ca = container_of(a, struct sci_clk, node); |
---|
| 512 | + struct sci_clk *cb = container_of(b, struct sci_clk, node); |
---|
| 513 | + |
---|
| 514 | + return _cmp_sci_clk(ca, &cb); |
---|
| 515 | +} |
---|
| 516 | + |
---|
| 517 | +static int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider) |
---|
| 518 | +{ |
---|
| 519 | + struct device *dev = provider->dev; |
---|
| 520 | + struct device_node *np = NULL; |
---|
| 521 | + int ret; |
---|
| 522 | + int index; |
---|
| 523 | + struct of_phandle_args args; |
---|
| 524 | + struct list_head clks; |
---|
| 525 | + struct sci_clk *sci_clk, *prev; |
---|
| 526 | + int num_clks = 0; |
---|
| 527 | + int num_parents; |
---|
| 528 | + int clk_id; |
---|
| 529 | + const char * const clk_names[] = { |
---|
| 530 | + "clocks", "assigned-clocks", "assigned-clock-parents", NULL |
---|
| 531 | + }; |
---|
| 532 | + const char * const *clk_name; |
---|
| 533 | + |
---|
| 534 | + INIT_LIST_HEAD(&clks); |
---|
| 535 | + |
---|
| 536 | + clk_name = clk_names; |
---|
| 537 | + |
---|
| 538 | + while (*clk_name) { |
---|
| 539 | + np = of_find_node_with_property(np, *clk_name); |
---|
| 540 | + if (!np) { |
---|
| 541 | + clk_name++; |
---|
| 542 | + continue; |
---|
| 543 | + } |
---|
| 544 | + |
---|
| 545 | + if (!of_device_is_available(np)) |
---|
| 546 | + continue; |
---|
| 547 | + |
---|
| 548 | + index = 0; |
---|
| 549 | + |
---|
| 550 | + do { |
---|
| 551 | + ret = of_parse_phandle_with_args(np, *clk_name, |
---|
| 552 | + "#clock-cells", index, |
---|
| 553 | + &args); |
---|
| 554 | + if (ret) |
---|
| 555 | + break; |
---|
| 556 | + |
---|
| 557 | + if (args.args_count == 2 && args.np == dev->of_node) { |
---|
| 558 | + sci_clk = devm_kzalloc(dev, sizeof(*sci_clk), |
---|
| 559 | + GFP_KERNEL); |
---|
| 560 | + if (!sci_clk) |
---|
| 561 | + return -ENOMEM; |
---|
| 562 | + |
---|
| 563 | + sci_clk->dev_id = args.args[0]; |
---|
| 564 | + sci_clk->clk_id = args.args[1]; |
---|
| 565 | + sci_clk->provider = provider; |
---|
| 566 | + provider->ops->get_num_parents(provider->sci, |
---|
| 567 | + sci_clk->dev_id, |
---|
| 568 | + sci_clk->clk_id, |
---|
| 569 | + (void *)&sci_clk->num_parents); |
---|
| 570 | + list_add_tail(&sci_clk->node, &clks); |
---|
| 571 | + |
---|
| 572 | + num_clks++; |
---|
| 573 | + |
---|
| 574 | + num_parents = sci_clk->num_parents; |
---|
| 575 | + if (num_parents == 1) |
---|
| 576 | + num_parents = 0; |
---|
| 577 | + |
---|
| 578 | + /* |
---|
| 579 | + * Linux kernel has inherent limitation |
---|
| 580 | + * of 255 clock parents at the moment. |
---|
| 581 | + * Right now, it is not expected that |
---|
| 582 | + * any mux clock from sci-clk driver |
---|
| 583 | + * would exceed that limit either, but |
---|
| 584 | + * the ABI basically provides that |
---|
| 585 | + * possibility. Print out a warning if |
---|
| 586 | + * this happens for any clock. |
---|
| 587 | + */ |
---|
| 588 | + if (num_parents >= 255) { |
---|
| 589 | + dev_warn(dev, "too many parents for dev=%d, clk=%d (%d), cropping to 255.\n", |
---|
| 590 | + sci_clk->dev_id, |
---|
| 591 | + sci_clk->clk_id, num_parents); |
---|
| 592 | + num_parents = 255; |
---|
| 593 | + } |
---|
| 594 | + |
---|
| 595 | + clk_id = args.args[1] + 1; |
---|
| 596 | + |
---|
| 597 | + while (num_parents--) { |
---|
| 598 | + sci_clk = devm_kzalloc(dev, |
---|
| 599 | + sizeof(*sci_clk), |
---|
| 600 | + GFP_KERNEL); |
---|
| 601 | + if (!sci_clk) |
---|
| 602 | + return -ENOMEM; |
---|
| 603 | + sci_clk->dev_id = args.args[0]; |
---|
| 604 | + sci_clk->clk_id = clk_id++; |
---|
| 605 | + sci_clk->provider = provider; |
---|
| 606 | + list_add_tail(&sci_clk->node, &clks); |
---|
| 607 | + |
---|
| 608 | + num_clks++; |
---|
| 609 | + } |
---|
| 610 | + } |
---|
| 611 | + |
---|
| 612 | + index++; |
---|
| 613 | + } while (args.np); |
---|
| 614 | + } |
---|
| 615 | + |
---|
| 616 | + list_sort(NULL, &clks, _cmp_sci_clk_list); |
---|
| 617 | + |
---|
| 618 | + provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk), |
---|
| 619 | + GFP_KERNEL); |
---|
| 620 | + if (!provider->clocks) |
---|
| 621 | + return -ENOMEM; |
---|
| 622 | + |
---|
| 623 | + num_clks = 0; |
---|
| 624 | + prev = NULL; |
---|
| 625 | + |
---|
| 626 | + list_for_each_entry(sci_clk, &clks, node) { |
---|
| 627 | + if (prev && prev->dev_id == sci_clk->dev_id && |
---|
| 628 | + prev->clk_id == sci_clk->clk_id) |
---|
| 629 | + continue; |
---|
| 630 | + |
---|
| 631 | + provider->clocks[num_clks++] = sci_clk; |
---|
| 632 | + prev = sci_clk; |
---|
| 633 | + } |
---|
| 634 | + |
---|
| 635 | + provider->num_clocks = num_clks; |
---|
| 636 | + |
---|
| 637 | + return 0; |
---|
| 638 | +} |
---|
| 639 | +#endif |
---|
| 640 | + |
---|
| 641 | +/** |
---|
| 642 | + * ti_sci_clk_probe - Probe function for the TI SCI clock driver |
---|
| 643 | + * @pdev: platform device pointer to be probed |
---|
| 644 | + * |
---|
| 645 | + * Probes the TI SCI clock device. Allocates a new clock provider |
---|
| 646 | + * and registers this to the common clock framework. Also applies |
---|
| 647 | + * any required flags to the identified clocks via clock lists |
---|
| 648 | + * supplied from DT. Returns 0 for success, negative error value |
---|
| 649 | + * for failure. |
---|
| 650 | + */ |
---|
| 651 | +static int ti_sci_clk_probe(struct platform_device *pdev) |
---|
| 652 | +{ |
---|
| 653 | + struct device *dev = &pdev->dev; |
---|
| 654 | + struct device_node *np = dev->of_node; |
---|
| 655 | + struct sci_clk_provider *provider; |
---|
| 656 | + const struct ti_sci_handle *handle; |
---|
| 657 | + int ret; |
---|
| 658 | + |
---|
| 659 | + handle = devm_ti_sci_get_handle(dev); |
---|
| 660 | + if (IS_ERR(handle)) |
---|
| 661 | + return PTR_ERR(handle); |
---|
| 662 | + |
---|
| 663 | + provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL); |
---|
| 664 | + if (!provider) |
---|
| 665 | + return -ENOMEM; |
---|
| 666 | + |
---|
| 667 | + provider->sci = handle; |
---|
| 668 | + provider->ops = &handle->ops.clk_ops; |
---|
| 669 | + provider->dev = dev; |
---|
| 670 | + |
---|
| 671 | +#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW |
---|
| 672 | + ret = ti_sci_scan_clocks_from_fw(provider); |
---|
| 673 | + if (ret) { |
---|
| 674 | + dev_err(dev, "scan clocks from FW failed: %d\n", ret); |
---|
| 675 | + return ret; |
---|
| 676 | + } |
---|
| 677 | +#else |
---|
| 678 | + ret = ti_sci_scan_clocks_from_dt(provider); |
---|
| 679 | + if (ret) { |
---|
| 680 | + dev_err(dev, "scan clocks from DT failed: %d\n", ret); |
---|
| 681 | + return ret; |
---|
| 682 | + } |
---|
| 683 | +#endif |
---|
| 684 | + |
---|
505 | 685 | ret = ti_sci_init_clocks(provider); |
---|
506 | 686 | if (ret) { |
---|
507 | 687 | pr_err("ti-sci-init-clocks failed.\n"); |
---|