diff options
author | Arun Ramamurthy <arunrama@broadcom.com> | 2015-01-19 19:05:29 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-01-25 08:05:10 -0500 |
commit | 7e7a0e67f2c2104fb7515008fb2ba72ffb10b493 (patch) | |
tree | abc2414404d44d4b8da4064b6d1d02c849e9e9cc | |
parent | 85cd690d7e08159e8e4d737317a5e8a96140d687 (diff) |
usb: ehci-platform: add support for multiple phys per controller
Added support for cases where one controller is connected
to multiple phys.
Signed-off-by: Arun Ramamurthy <arunrama@broadcom.com>
Reviewed-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
Tested-by: Scott Branden <sbranden@broadcom.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/host/ehci-platform.c | 82 |
1 files changed, 60 insertions, 22 deletions
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 63f2622926c4..d8a75a51d6d4 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c | |||
@@ -43,7 +43,8 @@ | |||
43 | struct ehci_platform_priv { | 43 | struct ehci_platform_priv { |
44 | struct clk *clks[EHCI_MAX_CLKS]; | 44 | struct clk *clks[EHCI_MAX_CLKS]; |
45 | struct reset_control *rst; | 45 | struct reset_control *rst; |
46 | struct phy *phy; | 46 | struct phy **phys; |
47 | int num_phys; | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | static const char hcd_name[] = "ehci-platform"; | 50 | static const char hcd_name[] = "ehci-platform"; |
@@ -78,7 +79,7 @@ static int ehci_platform_power_on(struct platform_device *dev) | |||
78 | { | 79 | { |
79 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 80 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
80 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | 81 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); |
81 | int clk, ret; | 82 | int clk, ret, phy_num; |
82 | 83 | ||
83 | for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { | 84 | for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { |
84 | ret = clk_prepare_enable(priv->clks[clk]); | 85 | ret = clk_prepare_enable(priv->clks[clk]); |
@@ -86,20 +87,28 @@ static int ehci_platform_power_on(struct platform_device *dev) | |||
86 | goto err_disable_clks; | 87 | goto err_disable_clks; |
87 | } | 88 | } |
88 | 89 | ||
89 | if (priv->phy) { | 90 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { |
90 | ret = phy_init(priv->phy); | 91 | if (priv->phys[phy_num]) { |
91 | if (ret) | 92 | ret = phy_init(priv->phys[phy_num]); |
92 | goto err_disable_clks; | 93 | if (ret) |
93 | 94 | goto err_exit_phy; | |
94 | ret = phy_power_on(priv->phy); | 95 | ret = phy_power_on(priv->phys[phy_num]); |
95 | if (ret) | 96 | if (ret) { |
96 | goto err_exit_phy; | 97 | phy_exit(priv->phys[phy_num]); |
98 | goto err_exit_phy; | ||
99 | } | ||
100 | } | ||
97 | } | 101 | } |
98 | 102 | ||
99 | return 0; | 103 | return 0; |
100 | 104 | ||
101 | err_exit_phy: | 105 | err_exit_phy: |
102 | phy_exit(priv->phy); | 106 | while (--phy_num >= 0) { |
107 | if (priv->phys[phy_num]) { | ||
108 | phy_power_off(priv->phys[phy_num]); | ||
109 | phy_exit(priv->phys[phy_num]); | ||
110 | } | ||
111 | } | ||
103 | err_disable_clks: | 112 | err_disable_clks: |
104 | while (--clk >= 0) | 113 | while (--clk >= 0) |
105 | clk_disable_unprepare(priv->clks[clk]); | 114 | clk_disable_unprepare(priv->clks[clk]); |
@@ -111,11 +120,13 @@ static void ehci_platform_power_off(struct platform_device *dev) | |||
111 | { | 120 | { |
112 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 121 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
113 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | 122 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); |
114 | int clk; | 123 | int clk, phy_num; |
115 | 124 | ||
116 | if (priv->phy) { | 125 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { |
117 | phy_power_off(priv->phy); | 126 | if (priv->phys[phy_num]) { |
118 | phy_exit(priv->phy); | 127 | phy_power_off(priv->phys[phy_num]); |
128 | phy_exit(priv->phys[phy_num]); | ||
129 | } | ||
119 | } | 130 | } |
120 | 131 | ||
121 | for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) | 132 | for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) |
@@ -143,7 +154,8 @@ static int ehci_platform_probe(struct platform_device *dev) | |||
143 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); | 154 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); |
144 | struct ehci_platform_priv *priv; | 155 | struct ehci_platform_priv *priv; |
145 | struct ehci_hcd *ehci; | 156 | struct ehci_hcd *ehci; |
146 | int err, irq, clk = 0; | 157 | const char *phy_name; |
158 | int err, irq, phy_num, clk = 0; | ||
147 | 159 | ||
148 | if (usb_disabled()) | 160 | if (usb_disabled()) |
149 | return -ENODEV; | 161 | return -ENODEV; |
@@ -190,12 +202,38 @@ static int ehci_platform_probe(struct platform_device *dev) | |||
190 | "needs-reset-on-resume")) | 202 | "needs-reset-on-resume")) |
191 | pdata->reset_on_resume = 1; | 203 | pdata->reset_on_resume = 1; |
192 | 204 | ||
193 | priv->phy = devm_phy_get(&dev->dev, "usb"); | 205 | priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, |
194 | if (IS_ERR(priv->phy)) { | 206 | "phys", "#phy-cells"); |
195 | err = PTR_ERR(priv->phy); | 207 | priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; |
196 | if (err == -EPROBE_DEFER) | 208 | |
197 | goto err_put_hcd; | 209 | priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, |
198 | priv->phy = NULL; | 210 | sizeof(struct phy *), GFP_KERNEL); |
211 | if (!priv->phys) | ||
212 | return -ENOMEM; | ||
213 | |||
214 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { | ||
215 | err = of_property_read_string_index( | ||
216 | dev->dev.of_node, | ||
217 | "phy-names", phy_num, | ||
218 | &phy_name); | ||
219 | |||
220 | if (err < 0) { | ||
221 | if (priv->num_phys > 1) { | ||
222 | dev_err(&dev->dev, "phy-names not provided"); | ||
223 | goto err_put_hcd; | ||
224 | } else | ||
225 | phy_name = "usb"; | ||
226 | } | ||
227 | |||
228 | priv->phys[phy_num] = devm_phy_get(&dev->dev, | ||
229 | phy_name); | ||
230 | if (IS_ERR(priv->phys[phy_num])) { | ||
231 | err = PTR_ERR(priv->phys[phy_num]); | ||
232 | if ((priv->num_phys > 1) || | ||
233 | (err == -EPROBE_DEFER)) | ||
234 | goto err_put_hcd; | ||
235 | priv->phys[phy_num] = NULL; | ||
236 | } | ||
199 | } | 237 | } |
200 | 238 | ||
201 | for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { | 239 | for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { |