diff options
Diffstat (limited to 'drivers/phy/phy-exynos5-usbdrd.c')
-rw-r--r-- | drivers/phy/phy-exynos5-usbdrd.c | 139 |
1 files changed, 117 insertions, 22 deletions
diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index f756aca871db..04374018425f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c | |||
@@ -141,6 +141,7 @@ struct exynos5_usbdrd_phy_drvdata { | |||
141 | const struct exynos5_usbdrd_phy_config *phy_cfg; | 141 | const struct exynos5_usbdrd_phy_config *phy_cfg; |
142 | u32 pmu_offset_usbdrd0_phy; | 142 | u32 pmu_offset_usbdrd0_phy; |
143 | u32 pmu_offset_usbdrd1_phy; | 143 | u32 pmu_offset_usbdrd1_phy; |
144 | bool has_common_clk_gate; | ||
144 | }; | 145 | }; |
145 | 146 | ||
146 | /** | 147 | /** |
@@ -148,6 +149,9 @@ struct exynos5_usbdrd_phy_drvdata { | |||
148 | * @dev: pointer to device instance of this platform device | 149 | * @dev: pointer to device instance of this platform device |
149 | * @reg_phy: usb phy controller register memory base | 150 | * @reg_phy: usb phy controller register memory base |
150 | * @clk: phy clock for register access | 151 | * @clk: phy clock for register access |
152 | * @pipeclk: clock for pipe3 phy | ||
153 | * @utmiclk: clock for utmi+ phy | ||
154 | * @itpclk: clock for ITP generation | ||
151 | * @drv_data: pointer to SoC level driver data structure | 155 | * @drv_data: pointer to SoC level driver data structure |
152 | * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY | 156 | * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY |
153 | * instances each with its 'phy' and 'phy_cfg'. | 157 | * instances each with its 'phy' and 'phy_cfg'. |
@@ -155,12 +159,16 @@ struct exynos5_usbdrd_phy_drvdata { | |||
155 | * reference clocks' for SS and HS operations | 159 | * reference clocks' for SS and HS operations |
156 | * @ref_clk: reference clock to PHY block from which PHY's | 160 | * @ref_clk: reference clock to PHY block from which PHY's |
157 | * operational clocks are derived | 161 | * operational clocks are derived |
158 | * @ref_rate: rate of above reference clock | 162 | * vbus: VBUS regulator for phy |
163 | * vbus_boost: Boost regulator for VBUS present on few Exynos boards | ||
159 | */ | 164 | */ |
160 | struct exynos5_usbdrd_phy { | 165 | struct exynos5_usbdrd_phy { |
161 | struct device *dev; | 166 | struct device *dev; |
162 | void __iomem *reg_phy; | 167 | void __iomem *reg_phy; |
163 | struct clk *clk; | 168 | struct clk *clk; |
169 | struct clk *pipeclk; | ||
170 | struct clk *utmiclk; | ||
171 | struct clk *itpclk; | ||
164 | const struct exynos5_usbdrd_phy_drvdata *drv_data; | 172 | const struct exynos5_usbdrd_phy_drvdata *drv_data; |
165 | struct phy_usb_instance { | 173 | struct phy_usb_instance { |
166 | struct phy *phy; | 174 | struct phy *phy; |
@@ -172,6 +180,7 @@ struct exynos5_usbdrd_phy { | |||
172 | u32 extrefclk; | 180 | u32 extrefclk; |
173 | struct clk *ref_clk; | 181 | struct clk *ref_clk; |
174 | struct regulator *vbus; | 182 | struct regulator *vbus; |
183 | struct regulator *vbus_boost; | ||
175 | }; | 184 | }; |
176 | 185 | ||
177 | static inline | 186 | static inline |
@@ -447,13 +456,27 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) | |||
447 | dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); | 456 | dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); |
448 | 457 | ||
449 | clk_prepare_enable(phy_drd->ref_clk); | 458 | clk_prepare_enable(phy_drd->ref_clk); |
459 | if (!phy_drd->drv_data->has_common_clk_gate) { | ||
460 | clk_prepare_enable(phy_drd->pipeclk); | ||
461 | clk_prepare_enable(phy_drd->utmiclk); | ||
462 | clk_prepare_enable(phy_drd->itpclk); | ||
463 | } | ||
450 | 464 | ||
451 | /* Enable VBUS supply */ | 465 | /* Enable VBUS supply */ |
466 | if (phy_drd->vbus_boost) { | ||
467 | ret = regulator_enable(phy_drd->vbus_boost); | ||
468 | if (ret) { | ||
469 | dev_err(phy_drd->dev, | ||
470 | "Failed to enable VBUS boost supply\n"); | ||
471 | goto fail_vbus; | ||
472 | } | ||
473 | } | ||
474 | |||
452 | if (phy_drd->vbus) { | 475 | if (phy_drd->vbus) { |
453 | ret = regulator_enable(phy_drd->vbus); | 476 | ret = regulator_enable(phy_drd->vbus); |
454 | if (ret) { | 477 | if (ret) { |
455 | dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); | 478 | dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); |
456 | goto fail_vbus; | 479 | goto fail_vbus_boost; |
457 | } | 480 | } |
458 | } | 481 | } |
459 | 482 | ||
@@ -462,8 +485,17 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) | |||
462 | 485 | ||
463 | return 0; | 486 | return 0; |
464 | 487 | ||
488 | fail_vbus_boost: | ||
489 | if (phy_drd->vbus_boost) | ||
490 | regulator_disable(phy_drd->vbus_boost); | ||
491 | |||
465 | fail_vbus: | 492 | fail_vbus: |
466 | clk_disable_unprepare(phy_drd->ref_clk); | 493 | clk_disable_unprepare(phy_drd->ref_clk); |
494 | if (!phy_drd->drv_data->has_common_clk_gate) { | ||
495 | clk_disable_unprepare(phy_drd->itpclk); | ||
496 | clk_disable_unprepare(phy_drd->utmiclk); | ||
497 | clk_disable_unprepare(phy_drd->pipeclk); | ||
498 | } | ||
467 | 499 | ||
468 | return ret; | 500 | return ret; |
469 | } | 501 | } |
@@ -481,8 +513,15 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy) | |||
481 | /* Disable VBUS supply */ | 513 | /* Disable VBUS supply */ |
482 | if (phy_drd->vbus) | 514 | if (phy_drd->vbus) |
483 | regulator_disable(phy_drd->vbus); | 515 | regulator_disable(phy_drd->vbus); |
516 | if (phy_drd->vbus_boost) | ||
517 | regulator_disable(phy_drd->vbus_boost); | ||
484 | 518 | ||
485 | clk_disable_unprepare(phy_drd->ref_clk); | 519 | clk_disable_unprepare(phy_drd->ref_clk); |
520 | if (!phy_drd->drv_data->has_common_clk_gate) { | ||
521 | clk_disable_unprepare(phy_drd->itpclk); | ||
522 | clk_disable_unprepare(phy_drd->pipeclk); | ||
523 | clk_disable_unprepare(phy_drd->utmiclk); | ||
524 | } | ||
486 | 525 | ||
487 | return 0; | 526 | return 0; |
488 | } | 527 | } |
@@ -506,6 +545,57 @@ static struct phy_ops exynos5_usbdrd_phy_ops = { | |||
506 | .owner = THIS_MODULE, | 545 | .owner = THIS_MODULE, |
507 | }; | 546 | }; |
508 | 547 | ||
548 | static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) | ||
549 | { | ||
550 | unsigned long ref_rate; | ||
551 | int ret; | ||
552 | |||
553 | phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); | ||
554 | if (IS_ERR(phy_drd->clk)) { | ||
555 | dev_err(phy_drd->dev, "Failed to get phy clock\n"); | ||
556 | return PTR_ERR(phy_drd->clk); | ||
557 | } | ||
558 | |||
559 | phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); | ||
560 | if (IS_ERR(phy_drd->ref_clk)) { | ||
561 | dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); | ||
562 | return PTR_ERR(phy_drd->ref_clk); | ||
563 | } | ||
564 | ref_rate = clk_get_rate(phy_drd->ref_clk); | ||
565 | |||
566 | ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); | ||
567 | if (ret) { | ||
568 | dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", | ||
569 | ref_rate); | ||
570 | return ret; | ||
571 | } | ||
572 | |||
573 | if (!phy_drd->drv_data->has_common_clk_gate) { | ||
574 | phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); | ||
575 | if (IS_ERR(phy_drd->pipeclk)) { | ||
576 | dev_info(phy_drd->dev, | ||
577 | "PIPE3 phy operational clock not specified\n"); | ||
578 | phy_drd->pipeclk = NULL; | ||
579 | } | ||
580 | |||
581 | phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); | ||
582 | if (IS_ERR(phy_drd->utmiclk)) { | ||
583 | dev_info(phy_drd->dev, | ||
584 | "UTMI phy operational clock not specified\n"); | ||
585 | phy_drd->utmiclk = NULL; | ||
586 | } | ||
587 | |||
588 | phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); | ||
589 | if (IS_ERR(phy_drd->itpclk)) { | ||
590 | dev_info(phy_drd->dev, | ||
591 | "ITP clock from main OSC not specified\n"); | ||
592 | phy_drd->itpclk = NULL; | ||
593 | } | ||
594 | } | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
509 | static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { | 599 | static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { |
510 | { | 600 | { |
511 | .id = EXYNOS5_DRDPHY_UTMI, | 601 | .id = EXYNOS5_DRDPHY_UTMI, |
@@ -525,11 +615,19 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { | |||
525 | .phy_cfg = phy_cfg_exynos5, | 615 | .phy_cfg = phy_cfg_exynos5, |
526 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, | 616 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
527 | .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, | 617 | .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, |
618 | .has_common_clk_gate = true, | ||
528 | }; | 619 | }; |
529 | 620 | ||
530 | static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { | 621 | static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { |
531 | .phy_cfg = phy_cfg_exynos5, | 622 | .phy_cfg = phy_cfg_exynos5, |
532 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, | 623 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, |
624 | .has_common_clk_gate = true, | ||
625 | }; | ||
626 | |||
627 | static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { | ||
628 | .phy_cfg = phy_cfg_exynos5, | ||
629 | .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, | ||
630 | .has_common_clk_gate = false, | ||
533 | }; | 631 | }; |
534 | 632 | ||
535 | static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { | 633 | static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { |
@@ -539,6 +637,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { | |||
539 | }, { | 637 | }, { |
540 | .compatible = "samsung,exynos5420-usbdrd-phy", | 638 | .compatible = "samsung,exynos5420-usbdrd-phy", |
541 | .data = &exynos5420_usbdrd_phy | 639 | .data = &exynos5420_usbdrd_phy |
640 | }, { | ||
641 | .compatible = "samsung,exynos7-usbdrd-phy", | ||
642 | .data = &exynos7_usbdrd_phy | ||
542 | }, | 643 | }, |
543 | { }, | 644 | { }, |
544 | }; | 645 | }; |
@@ -555,7 +656,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) | |||
555 | const struct exynos5_usbdrd_phy_drvdata *drv_data; | 656 | const struct exynos5_usbdrd_phy_drvdata *drv_data; |
556 | struct regmap *reg_pmu; | 657 | struct regmap *reg_pmu; |
557 | u32 pmu_offset; | 658 | u32 pmu_offset; |
558 | unsigned long ref_rate; | ||
559 | int i, ret; | 659 | int i, ret; |
560 | int channel; | 660 | int channel; |
561 | 661 | ||
@@ -576,23 +676,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) | |||
576 | drv_data = match->data; | 676 | drv_data = match->data; |
577 | phy_drd->drv_data = drv_data; | 677 | phy_drd->drv_data = drv_data; |
578 | 678 | ||
579 | phy_drd->clk = devm_clk_get(dev, "phy"); | 679 | ret = exynos5_usbdrd_phy_clk_handle(phy_drd); |
580 | if (IS_ERR(phy_drd->clk)) { | ||
581 | dev_err(dev, "Failed to get clock of phy controller\n"); | ||
582 | return PTR_ERR(phy_drd->clk); | ||
583 | } | ||
584 | |||
585 | phy_drd->ref_clk = devm_clk_get(dev, "ref"); | ||
586 | if (IS_ERR(phy_drd->ref_clk)) { | ||
587 | dev_err(dev, "Failed to get reference clock of usbdrd phy\n"); | ||
588 | return PTR_ERR(phy_drd->ref_clk); | ||
589 | } | ||
590 | ref_rate = clk_get_rate(phy_drd->ref_clk); | ||
591 | |||
592 | ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); | ||
593 | if (ret) { | 680 | if (ret) { |
594 | dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", | 681 | dev_err(dev, "Failed to initialize clocks\n"); |
595 | ref_rate); | ||
596 | return ret; | 682 | return ret; |
597 | } | 683 | } |
598 | 684 | ||
@@ -622,7 +708,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) | |||
622 | break; | 708 | break; |
623 | } | 709 | } |
624 | 710 | ||
625 | /* Get Vbus regulator */ | 711 | /* Get Vbus regulators */ |
626 | phy_drd->vbus = devm_regulator_get(dev, "vbus"); | 712 | phy_drd->vbus = devm_regulator_get(dev, "vbus"); |
627 | if (IS_ERR(phy_drd->vbus)) { | 713 | if (IS_ERR(phy_drd->vbus)) { |
628 | ret = PTR_ERR(phy_drd->vbus); | 714 | ret = PTR_ERR(phy_drd->vbus); |
@@ -633,12 +719,21 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) | |||
633 | phy_drd->vbus = NULL; | 719 | phy_drd->vbus = NULL; |
634 | } | 720 | } |
635 | 721 | ||
722 | phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); | ||
723 | if (IS_ERR(phy_drd->vbus_boost)) { | ||
724 | ret = PTR_ERR(phy_drd->vbus_boost); | ||
725 | if (ret == -EPROBE_DEFER) | ||
726 | return ret; | ||
727 | |||
728 | dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); | ||
729 | phy_drd->vbus_boost = NULL; | ||
730 | } | ||
731 | |||
636 | dev_vdbg(dev, "Creating usbdrd_phy phy\n"); | 732 | dev_vdbg(dev, "Creating usbdrd_phy phy\n"); |
637 | 733 | ||
638 | for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { | 734 | for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { |
639 | struct phy *phy = devm_phy_create(dev, NULL, | 735 | struct phy *phy = devm_phy_create(dev, NULL, |
640 | &exynos5_usbdrd_phy_ops, | 736 | &exynos5_usbdrd_phy_ops); |
641 | NULL); | ||
642 | if (IS_ERR(phy)) { | 737 | if (IS_ERR(phy)) { |
643 | dev_err(dev, "Failed to create usbdrd_phy phy\n"); | 738 | dev_err(dev, "Failed to create usbdrd_phy phy\n"); |
644 | return PTR_ERR(phy); | 739 | return PTR_ERR(phy); |