diff options
| author | Venu Byravarasu <vbyravarasu@nvidia.com> | 2013-05-16 10:13:02 -0400 |
|---|---|---|
| committer | Felipe Balbi <balbi@ti.com> | 2013-05-29 19:49:11 -0400 |
| commit | 2d22b42db02fdafeb7b990c2c25caabff4dd46fe (patch) | |
| tree | 5f80c9594098d7d6339d5bb19004c2be79b10d10 /drivers/usb/phy | |
| parent | 6829f92f6e64bfc6a553d7a2203ce1cb2e433c01 (diff) | |
usb: phy: registering Tegra USB PHY as platform driver
Registered Tegra USB PHY as a separate platform driver.
To synchronize host controller and PHY initialization, used deferred
probe mechanism. As PHY should be initialized before EHCI starts running,
deferred probe of Tegra EHCI driver till PHY probe gets completed.
Got rid of instance number based handling in host driver.
Made use of DT params to get the PHY Pad registers.
Signed-off-by: Venu Byravarasu <vbyravarasu@nvidia.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/phy')
| -rw-r--r-- | drivers/usb/phy/phy-tegra-usb.c | 325 |
1 files changed, 180 insertions, 145 deletions
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index f423ae88b741..5d9af11d2731 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2010 Google, Inc. | 2 | * Copyright (C) 2010 Google, Inc. |
| 3 | * Copyright (C) 2013 NVIDIA Corporation | ||
| 3 | * | 4 | * |
| 4 | * Author: | 5 | * Author: |
| 5 | * Erik Gilling <konkers@google.com> | 6 | * Erik Gilling <konkers@google.com> |
| 6 | * Benoit Goby <benoit@android.com> | 7 | * Benoit Goby <benoit@android.com> |
| 8 | * Venu Byravarasu <vbyravarasu@nvidia.com> | ||
| 7 | * | 9 | * |
| 8 | * This software is licensed under the terms of the GNU General Public | 10 | * This software is licensed under the terms of the GNU General Public |
| 9 | * License version 2, as published by the Free Software Foundation, and | 11 | * License version 2, as published by the Free Software Foundation, and |
| @@ -30,9 +32,7 @@ | |||
| 30 | #include <linux/usb/ulpi.h> | 32 | #include <linux/usb/ulpi.h> |
| 31 | #include <asm/mach-types.h> | 33 | #include <asm/mach-types.h> |
| 32 | #include <linux/usb/tegra_usb_phy.h> | 34 | #include <linux/usb/tegra_usb_phy.h> |
| 33 | 35 | #include <linux/module.h> | |
| 34 | #define TEGRA_USB_BASE 0xC5000000 | ||
| 35 | #define TEGRA_USB_SIZE SZ_16K | ||
| 36 | 36 | ||
| 37 | #define ULPI_VIEWPORT 0x170 | 37 | #define ULPI_VIEWPORT 0x170 |
| 38 | 38 | ||
| @@ -198,32 +198,15 @@ static struct tegra_utmip_config utmip_default[] = { | |||
| 198 | 198 | ||
| 199 | static int utmip_pad_open(struct tegra_usb_phy *phy) | 199 | static int utmip_pad_open(struct tegra_usb_phy *phy) |
| 200 | { | 200 | { |
| 201 | phy->pad_clk = clk_get_sys("utmip-pad", NULL); | 201 | phy->pad_clk = devm_clk_get(phy->dev, "utmi-pads"); |
| 202 | if (IS_ERR(phy->pad_clk)) { | 202 | if (IS_ERR(phy->pad_clk)) { |
| 203 | pr_err("%s: can't get utmip pad clock\n", __func__); | 203 | pr_err("%s: can't get utmip pad clock\n", __func__); |
| 204 | return PTR_ERR(phy->pad_clk); | 204 | return PTR_ERR(phy->pad_clk); |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | if (phy->is_legacy_phy) { | ||
| 208 | phy->pad_regs = phy->regs; | ||
| 209 | } else { | ||
| 210 | phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); | ||
| 211 | if (!phy->pad_regs) { | ||
| 212 | pr_err("%s: can't remap usb registers\n", __func__); | ||
| 213 | clk_put(phy->pad_clk); | ||
| 214 | return -ENOMEM; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | return 0; | 207 | return 0; |
| 218 | } | 208 | } |
| 219 | 209 | ||
| 220 | static void utmip_pad_close(struct tegra_usb_phy *phy) | ||
| 221 | { | ||
| 222 | if (!phy->is_legacy_phy) | ||
| 223 | iounmap(phy->pad_regs); | ||
| 224 | clk_put(phy->pad_clk); | ||
| 225 | } | ||
| 226 | |||
| 227 | static void utmip_pad_power_on(struct tegra_usb_phy *phy) | 210 | static void utmip_pad_power_on(struct tegra_usb_phy *phy) |
| 228 | { | 211 | { |
| 229 | unsigned long val, flags; | 212 | unsigned long val, flags; |
| @@ -299,7 +282,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) | |||
| 299 | val &= ~USB_SUSP_SET; | 282 | val &= ~USB_SUSP_SET; |
| 300 | writel(val, base + USB_SUSP_CTRL); | 283 | writel(val, base + USB_SUSP_CTRL); |
| 301 | } else | 284 | } else |
| 302 | phy->set_phcd(&phy->u_phy, true); | 285 | tegra_ehci_set_phcd(&phy->u_phy, true); |
| 303 | 286 | ||
| 304 | if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) | 287 | if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) |
| 305 | pr_err("%s: timeout waiting for phy to stabilize\n", __func__); | 288 | pr_err("%s: timeout waiting for phy to stabilize\n", __func__); |
| @@ -321,7 +304,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) | |||
| 321 | val &= ~USB_SUSP_CLR; | 304 | val &= ~USB_SUSP_CLR; |
| 322 | writel(val, base + USB_SUSP_CTRL); | 305 | writel(val, base + USB_SUSP_CTRL); |
| 323 | } else | 306 | } else |
| 324 | phy->set_phcd(&phy->u_phy, false); | 307 | tegra_ehci_set_phcd(&phy->u_phy, false); |
| 325 | 308 | ||
| 326 | if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, | 309 | if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, |
| 327 | USB_PHY_CLK_VALID)) | 310 | USB_PHY_CLK_VALID)) |
| @@ -444,7 +427,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) | |||
| 444 | utmi_phy_clk_enable(phy); | 427 | utmi_phy_clk_enable(phy); |
| 445 | 428 | ||
| 446 | if (!phy->is_legacy_phy) | 429 | if (!phy->is_legacy_phy) |
| 447 | phy->set_pts(&phy->u_phy, 0); | 430 | tegra_ehci_set_pts(&phy->u_phy, 0); |
| 448 | 431 | ||
| 449 | return 0; | 432 | return 0; |
| 450 | } | 433 | } |
| @@ -614,76 +597,11 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) | |||
| 614 | return gpio_direction_output(phy->reset_gpio, 0); | 597 | return gpio_direction_output(phy->reset_gpio, 0); |
| 615 | } | 598 | } |
| 616 | 599 | ||
| 617 | static int tegra_phy_init(struct usb_phy *x) | ||
| 618 | { | ||
| 619 | struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); | ||
| 620 | struct tegra_ulpi_config *ulpi_config; | ||
| 621 | int err; | ||
| 622 | |||
| 623 | if (phy->is_ulpi_phy) { | ||
| 624 | ulpi_config = phy->config; | ||
| 625 | phy->clk = clk_get_sys(NULL, ulpi_config->clk); | ||
| 626 | if (IS_ERR(phy->clk)) { | ||
| 627 | pr_err("%s: can't get ulpi clock\n", __func__); | ||
| 628 | return PTR_ERR(phy->clk); | ||
| 629 | } | ||
| 630 | |||
| 631 | phy->reset_gpio = | ||
| 632 | of_get_named_gpio(phy->dev->of_node, | ||
| 633 | "nvidia,phy-reset-gpio", 0); | ||
| 634 | if (!gpio_is_valid(phy->reset_gpio)) { | ||
| 635 | dev_err(phy->dev, "invalid gpio: %d\n", | ||
| 636 | phy->reset_gpio); | ||
| 637 | err = phy->reset_gpio; | ||
| 638 | goto cleanup_clk_get; | ||
| 639 | } | ||
| 640 | |||
| 641 | err = gpio_request(phy->reset_gpio, "ulpi_phy_reset_b"); | ||
| 642 | if (err < 0) { | ||
| 643 | dev_err(phy->dev, "request failed for gpio: %d\n", | ||
| 644 | phy->reset_gpio); | ||
| 645 | goto cleanup_clk_get; | ||
| 646 | } | ||
| 647 | |||
| 648 | err = gpio_direction_output(phy->reset_gpio, 0); | ||
| 649 | if (err < 0) { | ||
| 650 | dev_err(phy->dev, "gpio %d direction not set to output\n", | ||
| 651 | phy->reset_gpio); | ||
| 652 | goto cleanup_gpio_req; | ||
| 653 | } | ||
| 654 | |||
| 655 | phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); | ||
| 656 | if (!phy->ulpi) { | ||
| 657 | dev_err(phy->dev, "otg_ulpi_create returned NULL\n"); | ||
| 658 | err = -ENOMEM; | ||
| 659 | goto cleanup_gpio_req; | ||
| 660 | } | ||
| 661 | |||
| 662 | phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; | ||
| 663 | } else { | ||
| 664 | err = utmip_pad_open(phy); | ||
| 665 | if (err < 0) | ||
| 666 | return err; | ||
| 667 | } | ||
| 668 | return 0; | ||
| 669 | cleanup_gpio_req: | ||
| 670 | gpio_free(phy->reset_gpio); | ||
| 671 | cleanup_clk_get: | ||
| 672 | clk_put(phy->clk); | ||
| 673 | return err; | ||
| 674 | } | ||
| 675 | |||
| 676 | static void tegra_usb_phy_close(struct usb_phy *x) | 600 | static void tegra_usb_phy_close(struct usb_phy *x) |
| 677 | { | 601 | { |
| 678 | struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); | 602 | struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); |
| 679 | 603 | ||
| 680 | if (phy->is_ulpi_phy) | ||
| 681 | clk_put(phy->clk); | ||
| 682 | else | ||
| 683 | utmip_pad_close(phy); | ||
| 684 | clk_disable_unprepare(phy->pll_u); | 604 | clk_disable_unprepare(phy->pll_u); |
| 685 | clk_put(phy->pll_u); | ||
| 686 | kfree(phy); | ||
| 687 | } | 605 | } |
| 688 | 606 | ||
| 689 | static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) | 607 | static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) |
| @@ -711,63 +629,63 @@ static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) | |||
| 711 | return tegra_usb_phy_power_on(phy); | 629 | return tegra_usb_phy_power_on(phy); |
| 712 | } | 630 | } |
| 713 | 631 | ||
| 714 | struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, | 632 | static int ulpi_open(struct tegra_usb_phy *phy) |
| 715 | void __iomem *regs, void *config, | ||
| 716 | void (*set_pts)(struct usb_phy *x, u8 pts_val), | ||
| 717 | void (*set_phcd)(struct usb_phy *x, bool enable)) | ||
| 718 | |||
| 719 | { | 633 | { |
| 720 | struct tegra_usb_phy *phy; | ||
| 721 | unsigned long parent_rate; | ||
| 722 | int i; | ||
| 723 | int err; | 634 | int err; |
| 724 | struct device_node *np = dev->of_node; | ||
| 725 | 635 | ||
| 726 | phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); | 636 | phy->clk = devm_clk_get(phy->dev, "ulpi-link"); |
| 727 | if (!phy) | 637 | if (IS_ERR(phy->clk)) { |
| 728 | return ERR_PTR(-ENOMEM); | 638 | pr_err("%s: can't get ulpi clock\n", __func__); |
| 639 | return PTR_ERR(phy->clk); | ||
| 640 | } | ||
| 729 | 641 | ||
| 730 | phy->instance = instance; | 642 | err = devm_gpio_request(phy->dev, phy->reset_gpio, "ulpi_phy_reset_b"); |
| 731 | phy->regs = regs; | 643 | if (err < 0) { |
| 732 | phy->config = config; | 644 | dev_err(phy->dev, "request failed for gpio: %d\n", |
| 733 | phy->dev = dev; | 645 | phy->reset_gpio); |
| 734 | phy->is_legacy_phy = | 646 | return err; |
| 735 | of_property_read_bool(np, "nvidia,has-legacy-mode"); | 647 | } |
| 736 | phy->set_pts = set_pts; | ||
| 737 | phy->set_phcd = set_phcd; | ||
| 738 | err = of_property_match_string(np, "phy_type", "ulpi"); | ||
| 739 | if (err < 0) | ||
| 740 | phy->is_ulpi_phy = false; | ||
| 741 | else | ||
| 742 | phy->is_ulpi_phy = true; | ||
| 743 | 648 | ||
| 744 | err = of_property_match_string(np, "dr_mode", "otg"); | 649 | err = gpio_direction_output(phy->reset_gpio, 0); |
| 745 | if (err < 0) { | 650 | if (err < 0) { |
| 746 | err = of_property_match_string(np, "dr_mode", "peripheral"); | 651 | dev_err(phy->dev, "gpio %d direction not set to output\n", |
| 747 | if (err < 0) | 652 | phy->reset_gpio); |
| 748 | phy->mode = TEGRA_USB_PHY_MODE_HOST; | 653 | return err; |
| 654 | } | ||
| 655 | |||
| 656 | phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); | ||
| 657 | if (!phy->ulpi) { | ||
| 658 | dev_err(phy->dev, "otg_ulpi_create returned NULL\n"); | ||
| 659 | err = -ENOMEM; | ||
| 660 | return err; | ||
| 661 | } | ||
| 662 | |||
| 663 | phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; | ||
| 664 | return 0; | ||
| 665 | } | ||
| 666 | |||
| 667 | static int tegra_usb_phy_init(struct tegra_usb_phy *phy) | ||
| 668 | { | ||
| 669 | unsigned long parent_rate; | ||
| 670 | int i; | ||
| 671 | int err; | ||
| 672 | |||
| 673 | if (!phy->is_ulpi_phy) { | ||
| 674 | if (phy->is_legacy_phy) | ||
| 675 | phy->config = &utmip_default[0]; | ||
| 749 | else | 676 | else |
| 750 | phy->mode = TEGRA_USB_PHY_MODE_DEVICE; | 677 | phy->config = &utmip_default[2]; |
| 751 | } else | ||
| 752 | phy->mode = TEGRA_USB_PHY_MODE_OTG; | ||
| 753 | |||
| 754 | if (!phy->config) { | ||
| 755 | if (phy->is_ulpi_phy) { | ||
| 756 | pr_err("%s: ulpi phy configuration missing", __func__); | ||
| 757 | err = -EINVAL; | ||
| 758 | goto err0; | ||
| 759 | } else { | ||
| 760 | phy->config = &utmip_default[instance]; | ||
| 761 | } | ||
| 762 | } | 678 | } |
| 763 | 679 | ||
| 764 | phy->pll_u = clk_get_sys(NULL, "pll_u"); | 680 | phy->pll_u = devm_clk_get(phy->dev, "pll_u"); |
| 765 | if (IS_ERR(phy->pll_u)) { | 681 | if (IS_ERR(phy->pll_u)) { |
| 766 | pr_err("Can't get pll_u clock\n"); | 682 | pr_err("Can't get pll_u clock\n"); |
| 767 | err = PTR_ERR(phy->pll_u); | 683 | return PTR_ERR(phy->pll_u); |
| 768 | goto err0; | ||
| 769 | } | 684 | } |
| 770 | clk_prepare_enable(phy->pll_u); | 685 | |
| 686 | err = clk_prepare_enable(phy->pll_u); | ||
| 687 | if (err) | ||
| 688 | return err; | ||
| 771 | 689 | ||
| 772 | parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); | 690 | parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); |
| 773 | for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { | 691 | for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { |
| @@ -779,23 +697,22 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, | |||
| 779 | if (!phy->freq) { | 697 | if (!phy->freq) { |
| 780 | pr_err("invalid pll_u parent rate %ld\n", parent_rate); | 698 | pr_err("invalid pll_u parent rate %ld\n", parent_rate); |
| 781 | err = -EINVAL; | 699 | err = -EINVAL; |
| 782 | goto err1; | 700 | goto fail; |
| 783 | } | 701 | } |
| 784 | 702 | ||
| 785 | phy->u_phy.init = tegra_phy_init; | 703 | if (phy->is_ulpi_phy) |
| 786 | phy->u_phy.shutdown = tegra_usb_phy_close; | 704 | err = ulpi_open(phy); |
| 787 | phy->u_phy.set_suspend = tegra_usb_phy_suspend; | 705 | else |
| 706 | err = utmip_pad_open(phy); | ||
| 707 | if (err < 0) | ||
| 708 | goto fail; | ||
| 788 | 709 | ||
| 789 | return phy; | 710 | return 0; |
| 790 | 711 | ||
| 791 | err1: | 712 | fail: |
| 792 | clk_disable_unprepare(phy->pll_u); | 713 | clk_disable_unprepare(phy->pll_u); |
| 793 | clk_put(phy->pll_u); | 714 | return err; |
| 794 | err0: | ||
| 795 | kfree(phy); | ||
| 796 | return ERR_PTR(err); | ||
| 797 | } | 715 | } |
| 798 | EXPORT_SYMBOL_GPL(tegra_usb_phy_open); | ||
| 799 | 716 | ||
| 800 | void tegra_usb_phy_preresume(struct usb_phy *x) | 717 | void tegra_usb_phy_preresume(struct usb_phy *x) |
| 801 | { | 718 | { |
| @@ -834,3 +751,121 @@ void tegra_ehci_phy_restore_end(struct usb_phy *x) | |||
| 834 | } | 751 | } |
| 835 | EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); | 752 | EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); |
| 836 | 753 | ||
| 754 | static int tegra_usb_phy_probe(struct platform_device *pdev) | ||
| 755 | { | ||
| 756 | struct resource *res; | ||
| 757 | struct tegra_usb_phy *tegra_phy = NULL; | ||
| 758 | struct device_node *np = pdev->dev.of_node; | ||
| 759 | int err; | ||
| 760 | |||
| 761 | tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL); | ||
| 762 | if (!tegra_phy) { | ||
| 763 | dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n"); | ||
| 764 | return -ENOMEM; | ||
| 765 | } | ||
| 766 | |||
| 767 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 768 | if (!res) { | ||
| 769 | dev_err(&pdev->dev, "Failed to get I/O memory\n"); | ||
| 770 | return -ENXIO; | ||
| 771 | } | ||
| 772 | |||
| 773 | tegra_phy->regs = devm_ioremap(&pdev->dev, res->start, | ||
| 774 | resource_size(res)); | ||
| 775 | if (!tegra_phy->regs) { | ||
| 776 | dev_err(&pdev->dev, "Failed to remap I/O memory\n"); | ||
| 777 | return -ENOMEM; | ||
| 778 | } | ||
| 779 | |||
| 780 | tegra_phy->is_legacy_phy = | ||
| 781 | of_property_read_bool(np, "nvidia,has-legacy-mode"); | ||
| 782 | |||
| 783 | err = of_property_match_string(np, "phy_type", "ulpi"); | ||
| 784 | if (err < 0) { | ||
| 785 | tegra_phy->is_ulpi_phy = false; | ||
| 786 | |||
| 787 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 788 | if (!res) { | ||
| 789 | dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n"); | ||
| 790 | return -ENXIO; | ||
| 791 | } | ||
| 792 | |||
| 793 | tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start, | ||
| 794 | resource_size(res)); | ||
| 795 | if (!tegra_phy->regs) { | ||
| 796 | dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n"); | ||
| 797 | return -ENOMEM; | ||
| 798 | } | ||
| 799 | } else { | ||
| 800 | tegra_phy->is_ulpi_phy = true; | ||
| 801 | |||
| 802 | tegra_phy->reset_gpio = | ||
| 803 | of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0); | ||
| 804 | if (!gpio_is_valid(tegra_phy->reset_gpio)) { | ||
| 805 | dev_err(&pdev->dev, "invalid gpio: %d\n", | ||
| 806 | tegra_phy->reset_gpio); | ||
| 807 | return tegra_phy->reset_gpio; | ||
| 808 | } | ||
| 809 | } | ||
| 810 | |||
| 811 | err = of_property_match_string(np, "dr_mode", "otg"); | ||
| 812 | if (err < 0) { | ||
| 813 | err = of_property_match_string(np, "dr_mode", "peripheral"); | ||
| 814 | if (err < 0) | ||
| 815 | tegra_phy->mode = TEGRA_USB_PHY_MODE_HOST; | ||
| 816 | else | ||
| 817 | tegra_phy->mode = TEGRA_USB_PHY_MODE_DEVICE; | ||
| 818 | } else | ||
| 819 | tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG; | ||
| 820 | |||
| 821 | tegra_phy->dev = &pdev->dev; | ||
| 822 | err = tegra_usb_phy_init(tegra_phy); | ||
| 823 | if (err < 0) | ||
| 824 | return err; | ||
| 825 | |||
| 826 | tegra_phy->u_phy.shutdown = tegra_usb_phy_close; | ||
| 827 | tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; | ||
| 828 | |||
| 829 | dev_set_drvdata(&pdev->dev, tegra_phy); | ||
| 830 | return 0; | ||
| 831 | } | ||
| 832 | |||
| 833 | static struct of_device_id tegra_usb_phy_id_table[] = { | ||
| 834 | { .compatible = "nvidia,tegra20-usb-phy", }, | ||
| 835 | { }, | ||
| 836 | }; | ||
| 837 | MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); | ||
| 838 | |||
| 839 | static struct platform_driver tegra_usb_phy_driver = { | ||
| 840 | .probe = tegra_usb_phy_probe, | ||
| 841 | .driver = { | ||
| 842 | .name = "tegra-phy", | ||
| 843 | .owner = THIS_MODULE, | ||
| 844 | .of_match_table = of_match_ptr(tegra_usb_phy_id_table), | ||
| 845 | }, | ||
| 846 | }; | ||
| 847 | module_platform_driver(tegra_usb_phy_driver); | ||
| 848 | |||
| 849 | static int tegra_usb_phy_match(struct device *dev, void *data) | ||
| 850 | { | ||
| 851 | struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev); | ||
| 852 | struct device_node *dn = data; | ||
| 853 | |||
| 854 | return (tegra_phy->dev->of_node == dn) ? 1 : 0; | ||
| 855 | } | ||
| 856 | |||
| 857 | struct usb_phy *tegra_usb_get_phy(struct device_node *dn) | ||
| 858 | { | ||
| 859 | struct device *dev; | ||
| 860 | struct tegra_usb_phy *tegra_phy; | ||
| 861 | |||
| 862 | dev = driver_find_device(&tegra_usb_phy_driver.driver, NULL, dn, | ||
| 863 | tegra_usb_phy_match); | ||
| 864 | if (!dev) | ||
| 865 | return ERR_PTR(-EPROBE_DEFER); | ||
| 866 | |||
| 867 | tegra_phy = dev_get_drvdata(dev); | ||
| 868 | |||
| 869 | return &tegra_phy->u_phy; | ||
| 870 | } | ||
| 871 | EXPORT_SYMBOL_GPL(tegra_usb_get_phy); | ||
