diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2014-04-16 12:00:11 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-04-24 16:05:32 -0400 |
commit | cecabe5c349b613614fbbf822e3aeb33758a22f0 (patch) | |
tree | c3b8c7f2db7a24f94a3f854598195cd2047d59a6 /drivers/usb/host/ohci-pxa27x.c | |
parent | 37769939082ae0749405133e09eac2c3ccb8fcf0 (diff) |
USB: ohci-pxa27x: Add support for external vbus regulators
Override the hub control operation to enable and disable external
regulators for the ports vbus power supply in response to clear/set
USB_PORT_FEAT_POWER requests.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ohci-pxa27x.c')
-rw-r--r-- | drivers/usb/host/ohci-pxa27x.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index d21d5fefa76c..e68f3d02cd1a 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/platform_data/usb-ohci-pxa27x.h> | 30 | #include <linux/platform_data/usb-ohci-pxa27x.h> |
31 | #include <linux/platform_data/usb-pxa3xx-ulpi.h> | 31 | #include <linux/platform_data/usb-pxa3xx-ulpi.h> |
32 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
33 | #include <linux/regulator/consumer.h> | ||
33 | #include <linux/signal.h> | 34 | #include <linux/signal.h> |
34 | #include <linux/usb.h> | 35 | #include <linux/usb.h> |
35 | #include <linux/usb/hcd.h> | 36 | #include <linux/usb/hcd.h> |
@@ -120,6 +121,8 @@ static struct hc_driver __read_mostly ohci_pxa27x_hc_driver; | |||
120 | struct pxa27x_ohci { | 121 | struct pxa27x_ohci { |
121 | struct clk *clk; | 122 | struct clk *clk; |
122 | void __iomem *mmio_base; | 123 | void __iomem *mmio_base; |
124 | struct regulator *vbus[3]; | ||
125 | bool vbus_enabled[3]; | ||
123 | }; | 126 | }; |
124 | 127 | ||
125 | #define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv) | 128 | #define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv) |
@@ -166,6 +169,52 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode) | |||
166 | return 0; | 169 | return 0; |
167 | } | 170 | } |
168 | 171 | ||
172 | static int pxa27x_ohci_set_vbus_power(struct pxa27x_ohci *pxa_ohci, | ||
173 | unsigned int port, bool enable) | ||
174 | { | ||
175 | struct regulator *vbus = pxa_ohci->vbus[port]; | ||
176 | int ret = 0; | ||
177 | |||
178 | if (IS_ERR_OR_NULL(vbus)) | ||
179 | return 0; | ||
180 | |||
181 | if (enable && !pxa_ohci->vbus_enabled[port]) | ||
182 | ret = regulator_enable(vbus); | ||
183 | else if (!enable && pxa_ohci->vbus_enabled[port]) | ||
184 | ret = regulator_disable(vbus); | ||
185 | |||
186 | if (ret < 0) | ||
187 | return ret; | ||
188 | |||
189 | pxa_ohci->vbus_enabled[port] = enable; | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int pxa27x_ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | ||
195 | u16 wIndex, char *buf, u16 wLength) | ||
196 | { | ||
197 | struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); | ||
198 | int ret; | ||
199 | |||
200 | switch (typeReq) { | ||
201 | case SetPortFeature: | ||
202 | case ClearPortFeature: | ||
203 | if (!wIndex || wIndex > 3) | ||
204 | return -EPIPE; | ||
205 | |||
206 | if (wValue != USB_PORT_FEAT_POWER) | ||
207 | break; | ||
208 | |||
209 | ret = pxa27x_ohci_set_vbus_power(pxa_ohci, wIndex - 1, | ||
210 | typeReq == SetPortFeature); | ||
211 | if (ret) | ||
212 | return ret; | ||
213 | break; | ||
214 | } | ||
215 | |||
216 | return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); | ||
217 | } | ||
169 | /*-------------------------------------------------------------------------*/ | 218 | /*-------------------------------------------------------------------------*/ |
170 | 219 | ||
171 | static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci, | 220 | static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci, |
@@ -372,6 +421,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device | |||
372 | struct ohci_hcd *ohci; | 421 | struct ohci_hcd *ohci; |
373 | struct resource *r; | 422 | struct resource *r; |
374 | struct clk *usb_clk; | 423 | struct clk *usb_clk; |
424 | unsigned int i; | ||
375 | 425 | ||
376 | retval = ohci_pxa_of_init(pdev); | 426 | retval = ohci_pxa_of_init(pdev); |
377 | if (retval) | 427 | if (retval) |
@@ -417,6 +467,16 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device | |||
417 | pxa_ohci->clk = usb_clk; | 467 | pxa_ohci->clk = usb_clk; |
418 | pxa_ohci->mmio_base = (void __iomem *)hcd->regs; | 468 | pxa_ohci->mmio_base = (void __iomem *)hcd->regs; |
419 | 469 | ||
470 | for (i = 0; i < 3; ++i) { | ||
471 | char name[6]; | ||
472 | |||
473 | if (!(inf->flags & (ENABLE_PORT1 << i))) | ||
474 | continue; | ||
475 | |||
476 | sprintf(name, "vbus%u", i + 1); | ||
477 | pxa_ohci->vbus[i] = devm_regulator_get(&pdev->dev, name); | ||
478 | } | ||
479 | |||
420 | retval = pxa27x_start_hc(pxa_ohci, &pdev->dev); | 480 | retval = pxa27x_start_hc(pxa_ohci, &pdev->dev); |
421 | if (retval < 0) { | 481 | if (retval < 0) { |
422 | pr_debug("pxa27x_start_hc failed"); | 482 | pr_debug("pxa27x_start_hc failed"); |
@@ -462,9 +522,14 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device | |||
462 | void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) | 522 | void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) |
463 | { | 523 | { |
464 | struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); | 524 | struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); |
525 | unsigned int i; | ||
465 | 526 | ||
466 | usb_remove_hcd(hcd); | 527 | usb_remove_hcd(hcd); |
467 | pxa27x_stop_hc(pxa_ohci, &pdev->dev); | 528 | pxa27x_stop_hc(pxa_ohci, &pdev->dev); |
529 | |||
530 | for (i = 0; i < 3; ++i) | ||
531 | pxa27x_ohci_set_vbus_power(pxa_ohci, i, false); | ||
532 | |||
468 | usb_put_hcd(hcd); | 533 | usb_put_hcd(hcd); |
469 | } | 534 | } |
470 | 535 | ||
@@ -563,7 +628,10 @@ static int __init ohci_pxa27x_init(void) | |||
563 | return -ENODEV; | 628 | return -ENODEV; |
564 | 629 | ||
565 | pr_info("%s: " DRIVER_DESC "\n", hcd_name); | 630 | pr_info("%s: " DRIVER_DESC "\n", hcd_name); |
631 | |||
566 | ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides); | 632 | ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides); |
633 | ohci_pxa27x_hc_driver.hub_control = pxa27x_ohci_hub_control; | ||
634 | |||
567 | return platform_driver_register(&ohci_hcd_pxa27x_driver); | 635 | return platform_driver_register(&ohci_hcd_pxa27x_driver); |
568 | } | 636 | } |
569 | module_init(ohci_pxa27x_init); | 637 | module_init(ohci_pxa27x_init); |