aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-pxa27x.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2014-04-16 12:00:11 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-04-24 16:05:32 -0400
commitcecabe5c349b613614fbbf822e3aeb33758a22f0 (patch)
treec3b8c7f2db7a24f94a3f854598195cd2047d59a6 /drivers/usb/host/ohci-pxa27x.c
parent37769939082ae0749405133e09eac2c3ccb8fcf0 (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.c68
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;
120struct pxa27x_ohci { 121struct 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
172static 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
194static 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
171static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci, 220static 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
462void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) 522void 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}
569module_init(ohci_pxa27x_init); 637module_init(ohci_pxa27x_init);