| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
|---|
| 2 | 2 | /* |
|---|
| 3 | | - * SAMSUNG EXYNOS USB HOST EHCI Controller |
|---|
| 3 | + * Samsung Exynos USB HOST EHCI Controller |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * Copyright (C) 2011 Samsung Electronics Co.Ltd |
|---|
| 6 | 6 | * Author: Jingoo Han <jg1.han@samsung.com> |
|---|
| .. | .. |
|---|
| 21 | 21 | |
|---|
| 22 | 22 | #include "ehci.h" |
|---|
| 23 | 23 | |
|---|
| 24 | | -#define DRIVER_DESC "EHCI EXYNOS driver" |
|---|
| 24 | +#define DRIVER_DESC "EHCI Exynos driver" |
|---|
| 25 | 25 | |
|---|
| 26 | 26 | #define EHCI_INSNREG00(base) (base + 0x90) |
|---|
| 27 | 27 | #define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25) |
|---|
| .. | .. |
|---|
| 39 | 39 | |
|---|
| 40 | 40 | struct exynos_ehci_hcd { |
|---|
| 41 | 41 | struct clk *clk; |
|---|
| 42 | + struct device_node *of_node; |
|---|
| 42 | 43 | struct phy *phy[PHY_NUMBER]; |
|---|
| 44 | + bool legacy_phy; |
|---|
| 43 | 45 | }; |
|---|
| 44 | 46 | |
|---|
| 45 | 47 | #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv) |
|---|
| .. | .. |
|---|
| 49 | 51 | { |
|---|
| 50 | 52 | struct device_node *child; |
|---|
| 51 | 53 | struct phy *phy; |
|---|
| 52 | | - int phy_number; |
|---|
| 54 | + int phy_number, num_phys; |
|---|
| 53 | 55 | int ret; |
|---|
| 54 | 56 | |
|---|
| 55 | 57 | /* Get PHYs for the controller */ |
|---|
| 58 | + num_phys = of_count_phandle_with_args(dev->of_node, "phys", |
|---|
| 59 | + "#phy-cells"); |
|---|
| 60 | + for (phy_number = 0; phy_number < num_phys; phy_number++) { |
|---|
| 61 | + phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number); |
|---|
| 62 | + if (IS_ERR(phy)) |
|---|
| 63 | + return PTR_ERR(phy); |
|---|
| 64 | + exynos_ehci->phy[phy_number] = phy; |
|---|
| 65 | + } |
|---|
| 66 | + if (num_phys > 0) |
|---|
| 67 | + return 0; |
|---|
| 68 | + |
|---|
| 69 | + /* Get PHYs using legacy bindings */ |
|---|
| 56 | 70 | for_each_available_child_of_node(dev->of_node, child) { |
|---|
| 57 | 71 | ret = of_property_read_u32(child, "reg", &phy_number); |
|---|
| 58 | 72 | if (ret) { |
|---|
| .. | .. |
|---|
| 83 | 97 | } |
|---|
| 84 | 98 | } |
|---|
| 85 | 99 | |
|---|
| 100 | + exynos_ehci->legacy_phy = true; |
|---|
| 86 | 101 | return 0; |
|---|
| 87 | 102 | } |
|---|
| 88 | 103 | |
|---|
| .. | .. |
|---|
| 202 | 217 | ehci = hcd_to_ehci(hcd); |
|---|
| 203 | 218 | ehci->caps = hcd->regs; |
|---|
| 204 | 219 | |
|---|
| 220 | + /* |
|---|
| 221 | + * Workaround: reset of_node pointer to avoid conflict between legacy |
|---|
| 222 | + * Exynos EHCI port subnodes and generic USB device bindings |
|---|
| 223 | + */ |
|---|
| 224 | + exynos_ehci->of_node = pdev->dev.of_node; |
|---|
| 225 | + if (exynos_ehci->legacy_phy) |
|---|
| 226 | + pdev->dev.of_node = NULL; |
|---|
| 227 | + |
|---|
| 205 | 228 | /* DMA burst Enable */ |
|---|
| 206 | 229 | writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); |
|---|
| 207 | 230 | |
|---|
| .. | .. |
|---|
| 218 | 241 | |
|---|
| 219 | 242 | fail_add_hcd: |
|---|
| 220 | 243 | exynos_ehci_phy_disable(&pdev->dev); |
|---|
| 244 | + pdev->dev.of_node = exynos_ehci->of_node; |
|---|
| 221 | 245 | fail_io: |
|---|
| 222 | 246 | clk_disable_unprepare(exynos_ehci->clk); |
|---|
| 223 | 247 | fail_clk: |
|---|
| .. | .. |
|---|
| 230 | 254 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
|---|
| 231 | 255 | struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); |
|---|
| 232 | 256 | |
|---|
| 257 | + pdev->dev.of_node = exynos_ehci->of_node; |
|---|
| 258 | + |
|---|
| 233 | 259 | usb_remove_hcd(hcd); |
|---|
| 234 | 260 | |
|---|
| 235 | 261 | exynos_ehci_phy_disable(&pdev->dev); |
|---|