diff options
Diffstat (limited to 'drivers/usb/host/ehci-platform.c')
-rw-r--r-- | drivers/usb/host/ehci-platform.c | 182 |
1 files changed, 156 insertions, 26 deletions
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 01536cfd361d..b3a0e11073aa 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright 2007 Steven Brown <sbrown@cortland.com> | 4 | * Copyright 2007 Steven Brown <sbrown@cortland.com> |
5 | * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> | 5 | * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> |
6 | * Copyright 2014 Hans de Goede <hdegoede@redhat.com> | ||
6 | * | 7 | * |
7 | * Derived from the ohci-ssb driver | 8 | * Derived from the ohci-ssb driver |
8 | * Copyright 2007 Michael Buesch <m@bues.ch> | 9 | * Copyright 2007 Michael Buesch <m@bues.ch> |
@@ -18,6 +19,7 @@ | |||
18 | * | 19 | * |
19 | * Licensed under the GNU/GPL. See COPYING for details. | 20 | * Licensed under the GNU/GPL. See COPYING for details. |
20 | */ | 21 | */ |
22 | #include <linux/clk.h> | ||
21 | #include <linux/dma-mapping.h> | 23 | #include <linux/dma-mapping.h> |
22 | #include <linux/err.h> | 24 | #include <linux/err.h> |
23 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
@@ -25,6 +27,7 @@ | |||
25 | #include <linux/io.h> | 27 | #include <linux/io.h> |
26 | #include <linux/module.h> | 28 | #include <linux/module.h> |
27 | #include <linux/of.h> | 29 | #include <linux/of.h> |
30 | #include <linux/phy/phy.h> | ||
28 | #include <linux/platform_device.h> | 31 | #include <linux/platform_device.h> |
29 | #include <linux/usb.h> | 32 | #include <linux/usb.h> |
30 | #include <linux/usb/hcd.h> | 33 | #include <linux/usb/hcd.h> |
@@ -33,6 +36,13 @@ | |||
33 | #include "ehci.h" | 36 | #include "ehci.h" |
34 | 37 | ||
35 | #define DRIVER_DESC "EHCI generic platform driver" | 38 | #define DRIVER_DESC "EHCI generic platform driver" |
39 | #define EHCI_MAX_CLKS 3 | ||
40 | #define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) | ||
41 | |||
42 | struct ehci_platform_priv { | ||
43 | struct clk *clks[EHCI_MAX_CLKS]; | ||
44 | struct phy *phy; | ||
45 | }; | ||
36 | 46 | ||
37 | static const char hcd_name[] = "ehci-platform"; | 47 | static const char hcd_name[] = "ehci-platform"; |
38 | 48 | ||
@@ -45,8 +55,6 @@ static int ehci_platform_reset(struct usb_hcd *hcd) | |||
45 | 55 | ||
46 | hcd->has_tt = pdata->has_tt; | 56 | hcd->has_tt = pdata->has_tt; |
47 | ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; | 57 | ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; |
48 | ehci->big_endian_desc = pdata->big_endian_desc; | ||
49 | ehci->big_endian_mmio = pdata->big_endian_mmio; | ||
50 | 58 | ||
51 | if (pdata->pre_setup) { | 59 | if (pdata->pre_setup) { |
52 | retval = pdata->pre_setup(hcd); | 60 | retval = pdata->pre_setup(hcd); |
@@ -64,38 +72,91 @@ static int ehci_platform_reset(struct usb_hcd *hcd) | |||
64 | return 0; | 72 | return 0; |
65 | } | 73 | } |
66 | 74 | ||
75 | static int ehci_platform_power_on(struct platform_device *dev) | ||
76 | { | ||
77 | struct usb_hcd *hcd = platform_get_drvdata(dev); | ||
78 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | ||
79 | int clk, ret; | ||
80 | |||
81 | for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { | ||
82 | ret = clk_prepare_enable(priv->clks[clk]); | ||
83 | if (ret) | ||
84 | goto err_disable_clks; | ||
85 | } | ||
86 | |||
87 | if (priv->phy) { | ||
88 | ret = phy_init(priv->phy); | ||
89 | if (ret) | ||
90 | goto err_disable_clks; | ||
91 | |||
92 | ret = phy_power_on(priv->phy); | ||
93 | if (ret) | ||
94 | goto err_exit_phy; | ||
95 | } | ||
96 | |||
97 | return 0; | ||
98 | |||
99 | err_exit_phy: | ||
100 | phy_exit(priv->phy); | ||
101 | err_disable_clks: | ||
102 | while (--clk >= 0) | ||
103 | clk_disable_unprepare(priv->clks[clk]); | ||
104 | |||
105 | return ret; | ||
106 | } | ||
107 | |||
108 | static void ehci_platform_power_off(struct platform_device *dev) | ||
109 | { | ||
110 | struct usb_hcd *hcd = platform_get_drvdata(dev); | ||
111 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | ||
112 | int clk; | ||
113 | |||
114 | if (priv->phy) { | ||
115 | phy_power_off(priv->phy); | ||
116 | phy_exit(priv->phy); | ||
117 | } | ||
118 | |||
119 | for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) | ||
120 | if (priv->clks[clk]) | ||
121 | clk_disable_unprepare(priv->clks[clk]); | ||
122 | } | ||
123 | |||
67 | static struct hc_driver __read_mostly ehci_platform_hc_driver; | 124 | static struct hc_driver __read_mostly ehci_platform_hc_driver; |
68 | 125 | ||
69 | static const struct ehci_driver_overrides platform_overrides __initconst = { | 126 | static const struct ehci_driver_overrides platform_overrides __initconst = { |
70 | .reset = ehci_platform_reset, | 127 | .reset = ehci_platform_reset, |
128 | .extra_priv_size = sizeof(struct ehci_platform_priv), | ||
71 | }; | 129 | }; |
72 | 130 | ||
73 | static struct usb_ehci_pdata ehci_platform_defaults; | 131 | static struct usb_ehci_pdata ehci_platform_defaults = { |
132 | .power_on = ehci_platform_power_on, | ||
133 | .power_suspend = ehci_platform_power_off, | ||
134 | .power_off = ehci_platform_power_off, | ||
135 | }; | ||
74 | 136 | ||
75 | static int ehci_platform_probe(struct platform_device *dev) | 137 | static int ehci_platform_probe(struct platform_device *dev) |
76 | { | 138 | { |
77 | struct usb_hcd *hcd; | 139 | struct usb_hcd *hcd; |
78 | struct resource *res_mem; | 140 | struct resource *res_mem; |
79 | struct usb_ehci_pdata *pdata; | 141 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); |
80 | int irq; | 142 | struct ehci_platform_priv *priv; |
81 | int err; | 143 | struct ehci_hcd *ehci; |
144 | int err, irq, clk = 0; | ||
82 | 145 | ||
83 | if (usb_disabled()) | 146 | if (usb_disabled()) |
84 | return -ENODEV; | 147 | return -ENODEV; |
85 | 148 | ||
86 | /* | 149 | /* |
87 | * use reasonable defaults so platforms don't have to provide these. | 150 | * Use reasonable defaults so platforms don't have to provide these |
88 | * with DT probing on ARM, none of these are set. | 151 | * with DT probing on ARM. |
89 | */ | 152 | */ |
90 | if (!dev_get_platdata(&dev->dev)) | 153 | if (!pdata) |
91 | dev->dev.platform_data = &ehci_platform_defaults; | 154 | pdata = &ehci_platform_defaults; |
92 | 155 | ||
93 | err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); | 156 | err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); |
94 | if (err) | 157 | if (err) |
95 | return err; | 158 | return err; |
96 | 159 | ||
97 | pdata = dev_get_platdata(&dev->dev); | ||
98 | |||
99 | irq = platform_get_irq(dev, 0); | 160 | irq = platform_get_irq(dev, 0); |
100 | if (irq < 0) { | 161 | if (irq < 0) { |
101 | dev_err(&dev->dev, "no irq provided"); | 162 | dev_err(&dev->dev, "no irq provided"); |
@@ -107,17 +168,72 @@ static int ehci_platform_probe(struct platform_device *dev) | |||
107 | return -ENXIO; | 168 | return -ENXIO; |
108 | } | 169 | } |
109 | 170 | ||
171 | hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, | ||
172 | dev_name(&dev->dev)); | ||
173 | if (!hcd) | ||
174 | return -ENOMEM; | ||
175 | |||
176 | platform_set_drvdata(dev, hcd); | ||
177 | dev->dev.platform_data = pdata; | ||
178 | priv = hcd_to_ehci_priv(hcd); | ||
179 | ehci = hcd_to_ehci(hcd); | ||
180 | |||
181 | if (pdata == &ehci_platform_defaults && dev->dev.of_node) { | ||
182 | if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) | ||
183 | ehci->big_endian_mmio = 1; | ||
184 | |||
185 | if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) | ||
186 | ehci->big_endian_desc = 1; | ||
187 | |||
188 | if (of_property_read_bool(dev->dev.of_node, "big-endian")) | ||
189 | ehci->big_endian_mmio = ehci->big_endian_desc = 1; | ||
190 | |||
191 | priv->phy = devm_phy_get(&dev->dev, "usb"); | ||
192 | if (IS_ERR(priv->phy)) { | ||
193 | err = PTR_ERR(priv->phy); | ||
194 | if (err == -EPROBE_DEFER) | ||
195 | goto err_put_hcd; | ||
196 | priv->phy = NULL; | ||
197 | } | ||
198 | |||
199 | for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { | ||
200 | priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); | ||
201 | if (IS_ERR(priv->clks[clk])) { | ||
202 | err = PTR_ERR(priv->clks[clk]); | ||
203 | if (err == -EPROBE_DEFER) | ||
204 | goto err_put_clks; | ||
205 | priv->clks[clk] = NULL; | ||
206 | break; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | if (pdata->big_endian_desc) | ||
212 | ehci->big_endian_desc = 1; | ||
213 | if (pdata->big_endian_mmio) | ||
214 | ehci->big_endian_mmio = 1; | ||
215 | |||
216 | #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO | ||
217 | if (ehci->big_endian_mmio) { | ||
218 | dev_err(&dev->dev, | ||
219 | "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n"); | ||
220 | err = -EINVAL; | ||
221 | goto err_put_clks; | ||
222 | } | ||
223 | #endif | ||
224 | #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC | ||
225 | if (ehci->big_endian_desc) { | ||
226 | dev_err(&dev->dev, | ||
227 | "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n"); | ||
228 | err = -EINVAL; | ||
229 | goto err_put_clks; | ||
230 | } | ||
231 | #endif | ||
232 | |||
110 | if (pdata->power_on) { | 233 | if (pdata->power_on) { |
111 | err = pdata->power_on(dev); | 234 | err = pdata->power_on(dev); |
112 | if (err < 0) | 235 | if (err < 0) |
113 | return err; | 236 | goto err_put_clks; |
114 | } | ||
115 | |||
116 | hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, | ||
117 | dev_name(&dev->dev)); | ||
118 | if (!hcd) { | ||
119 | err = -ENOMEM; | ||
120 | goto err_power; | ||
121 | } | 237 | } |
122 | 238 | ||
123 | hcd->rsrc_start = res_mem->start; | 239 | hcd->rsrc_start = res_mem->start; |
@@ -126,22 +242,28 @@ static int ehci_platform_probe(struct platform_device *dev) | |||
126 | hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); | 242 | hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); |
127 | if (IS_ERR(hcd->regs)) { | 243 | if (IS_ERR(hcd->regs)) { |
128 | err = PTR_ERR(hcd->regs); | 244 | err = PTR_ERR(hcd->regs); |
129 | goto err_put_hcd; | 245 | goto err_power; |
130 | } | 246 | } |
131 | err = usb_add_hcd(hcd, irq, IRQF_SHARED); | 247 | err = usb_add_hcd(hcd, irq, IRQF_SHARED); |
132 | if (err) | 248 | if (err) |
133 | goto err_put_hcd; | 249 | goto err_power; |
134 | 250 | ||
135 | device_wakeup_enable(hcd->self.controller); | 251 | device_wakeup_enable(hcd->self.controller); |
136 | platform_set_drvdata(dev, hcd); | 252 | platform_set_drvdata(dev, hcd); |
137 | 253 | ||
138 | return err; | 254 | return err; |
139 | 255 | ||
140 | err_put_hcd: | ||
141 | usb_put_hcd(hcd); | ||
142 | err_power: | 256 | err_power: |
143 | if (pdata->power_off) | 257 | if (pdata->power_off) |
144 | pdata->power_off(dev); | 258 | pdata->power_off(dev); |
259 | err_put_clks: | ||
260 | while (--clk >= 0) | ||
261 | clk_put(priv->clks[clk]); | ||
262 | err_put_hcd: | ||
263 | if (pdata == &ehci_platform_defaults) | ||
264 | dev->dev.platform_data = NULL; | ||
265 | |||
266 | usb_put_hcd(hcd); | ||
145 | 267 | ||
146 | return err; | 268 | return err; |
147 | } | 269 | } |
@@ -150,13 +272,19 @@ static int ehci_platform_remove(struct platform_device *dev) | |||
150 | { | 272 | { |
151 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 273 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
152 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); | 274 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); |
275 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | ||
276 | int clk; | ||
153 | 277 | ||
154 | usb_remove_hcd(hcd); | 278 | usb_remove_hcd(hcd); |
155 | usb_put_hcd(hcd); | ||
156 | 279 | ||
157 | if (pdata->power_off) | 280 | if (pdata->power_off) |
158 | pdata->power_off(dev); | 281 | pdata->power_off(dev); |
159 | 282 | ||
283 | for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) | ||
284 | clk_put(priv->clks[clk]); | ||
285 | |||
286 | usb_put_hcd(hcd); | ||
287 | |||
160 | if (pdata == &ehci_platform_defaults) | 288 | if (pdata == &ehci_platform_defaults) |
161 | dev->dev.platform_data = NULL; | 289 | dev->dev.platform_data = NULL; |
162 | 290 | ||
@@ -207,8 +335,10 @@ static int ehci_platform_resume(struct device *dev) | |||
207 | static const struct of_device_id vt8500_ehci_ids[] = { | 335 | static const struct of_device_id vt8500_ehci_ids[] = { |
208 | { .compatible = "via,vt8500-ehci", }, | 336 | { .compatible = "via,vt8500-ehci", }, |
209 | { .compatible = "wm,prizm-ehci", }, | 337 | { .compatible = "wm,prizm-ehci", }, |
338 | { .compatible = "generic-ehci", }, | ||
210 | {} | 339 | {} |
211 | }; | 340 | }; |
341 | MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); | ||
212 | 342 | ||
213 | static const struct platform_device_id ehci_platform_table[] = { | 343 | static const struct platform_device_id ehci_platform_table[] = { |
214 | { "ehci-platform", 0 }, | 344 | { "ehci-platform", 0 }, |