diff options
author | Arun Ramamurthy <arunrama@broadcom.com> | 2015-01-19 19:05:30 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-01-25 08:04:21 -0500 |
commit | 85cd690d7e08159e8e4d737317a5e8a96140d687 (patch) | |
tree | 7003ef0210b95b174382bafc6205fb12d121616f | |
parent | 6d2a5663297a0489e7ade3303bba37e544da66e5 (diff) |
usb: ohci-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/ohci-platform.c | 82 |
1 files changed, 60 insertions, 22 deletions
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 9c06b0197eb9..185ceee52d47 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c | |||
@@ -38,7 +38,8 @@ | |||
38 | struct ohci_platform_priv { | 38 | struct ohci_platform_priv { |
39 | struct clk *clks[OHCI_MAX_CLKS]; | 39 | struct clk *clks[OHCI_MAX_CLKS]; |
40 | struct reset_control *rst; | 40 | struct reset_control *rst; |
41 | struct phy *phy; | 41 | struct phy **phys; |
42 | int num_phys; | ||
42 | }; | 43 | }; |
43 | 44 | ||
44 | static const char hcd_name[] = "ohci-platform"; | 45 | static const char hcd_name[] = "ohci-platform"; |
@@ -47,7 +48,7 @@ static int ohci_platform_power_on(struct platform_device *dev) | |||
47 | { | 48 | { |
48 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 49 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
49 | struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); | 50 | struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); |
50 | int clk, ret; | 51 | int clk, ret, phy_num; |
51 | 52 | ||
52 | for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { | 53 | for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { |
53 | ret = clk_prepare_enable(priv->clks[clk]); | 54 | ret = clk_prepare_enable(priv->clks[clk]); |
@@ -55,20 +56,28 @@ static int ohci_platform_power_on(struct platform_device *dev) | |||
55 | goto err_disable_clks; | 56 | goto err_disable_clks; |
56 | } | 57 | } |
57 | 58 | ||
58 | if (priv->phy) { | 59 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { |
59 | ret = phy_init(priv->phy); | 60 | if (priv->phys[phy_num]) { |
60 | if (ret) | 61 | ret = phy_init(priv->phys[phy_num]); |
61 | goto err_disable_clks; | 62 | if (ret) |
62 | 63 | goto err_exit_phy; | |
63 | ret = phy_power_on(priv->phy); | 64 | ret = phy_power_on(priv->phys[phy_num]); |
64 | if (ret) | 65 | if (ret) { |
65 | goto err_exit_phy; | 66 | phy_exit(priv->phys[phy_num]); |
67 | goto err_exit_phy; | ||
68 | } | ||
69 | } | ||
66 | } | 70 | } |
67 | 71 | ||
68 | return 0; | 72 | return 0; |
69 | 73 | ||
70 | err_exit_phy: | 74 | err_exit_phy: |
71 | phy_exit(priv->phy); | 75 | while (--phy_num >= 0) { |
76 | if (priv->phys[phy_num]) { | ||
77 | phy_power_off(priv->phys[phy_num]); | ||
78 | phy_exit(priv->phys[phy_num]); | ||
79 | } | ||
80 | } | ||
72 | err_disable_clks: | 81 | err_disable_clks: |
73 | while (--clk >= 0) | 82 | while (--clk >= 0) |
74 | clk_disable_unprepare(priv->clks[clk]); | 83 | clk_disable_unprepare(priv->clks[clk]); |
@@ -80,11 +89,13 @@ static void ohci_platform_power_off(struct platform_device *dev) | |||
80 | { | 89 | { |
81 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 90 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
82 | struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); | 91 | struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); |
83 | int clk; | 92 | int clk, phy_num; |
84 | 93 | ||
85 | if (priv->phy) { | 94 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { |
86 | phy_power_off(priv->phy); | 95 | if (priv->phys[phy_num]) { |
87 | phy_exit(priv->phy); | 96 | phy_power_off(priv->phys[phy_num]); |
97 | phy_exit(priv->phys[phy_num]); | ||
98 | } | ||
88 | } | 99 | } |
89 | 100 | ||
90 | for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) | 101 | for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) |
@@ -112,7 +123,8 @@ static int ohci_platform_probe(struct platform_device *dev) | |||
112 | struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); | 123 | struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); |
113 | struct ohci_platform_priv *priv; | 124 | struct ohci_platform_priv *priv; |
114 | struct ohci_hcd *ohci; | 125 | struct ohci_hcd *ohci; |
115 | int err, irq, clk = 0; | 126 | const char *phy_name; |
127 | int err, irq, phy_num, clk = 0; | ||
116 | 128 | ||
117 | if (usb_disabled()) | 129 | if (usb_disabled()) |
118 | return -ENODEV; | 130 | return -ENODEV; |
@@ -160,12 +172,38 @@ static int ohci_platform_probe(struct platform_device *dev) | |||
160 | of_property_read_u32(dev->dev.of_node, "num-ports", | 172 | of_property_read_u32(dev->dev.of_node, "num-ports", |
161 | &ohci->num_ports); | 173 | &ohci->num_ports); |
162 | 174 | ||
163 | priv->phy = devm_phy_get(&dev->dev, "usb"); | 175 | priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, |
164 | if (IS_ERR(priv->phy)) { | 176 | "phys", "#phy-cells"); |
165 | err = PTR_ERR(priv->phy); | 177 | priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; |
166 | if (err == -EPROBE_DEFER) | 178 | |
167 | goto err_put_hcd; | 179 | priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, |
168 | priv->phy = NULL; | 180 | sizeof(struct phy *), GFP_KERNEL); |
181 | if (!priv->phys) | ||
182 | return -ENOMEM; | ||
183 | |||
184 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { | ||
185 | err = of_property_read_string_index( | ||
186 | dev->dev.of_node, | ||
187 | "phy-names", phy_num, | ||
188 | &phy_name); | ||
189 | |||
190 | if (err < 0) { | ||
191 | if (priv->num_phys > 1) { | ||
192 | dev_err(&dev->dev, "phy-names not provided"); | ||
193 | goto err_put_hcd; | ||
194 | } else | ||
195 | phy_name = "usb"; | ||
196 | } | ||
197 | |||
198 | priv->phys[phy_num] = devm_phy_get(&dev->dev, | ||
199 | phy_name); | ||
200 | if (IS_ERR(priv->phys[phy_num])) { | ||
201 | err = PTR_ERR(priv->phys[phy_num]); | ||
202 | if ((priv->num_phys > 1) || | ||
203 | (err == -EPROBE_DEFER)) | ||
204 | goto err_put_hcd; | ||
205 | priv->phys[phy_num] = NULL; | ||
206 | } | ||
169 | } | 207 | } |
170 | 208 | ||
171 | for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { | 209 | for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { |