Skip to content

Commit

Permalink
USB: ohci-pxa27x: Add support for external vbus regulators
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Laurent Pinchart authored and Greg Kroah-Hartman committed Apr 24, 2014
1 parent 3776993 commit cecabe5
Showing 1 changed file with 68 additions and 0 deletions.
68 changes: 68 additions & 0 deletions drivers/usb/host/ohci-pxa27x.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <linux/platform_data/usb-pxa3xx-ulpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/signal.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
Expand Down Expand Up @@ -120,6 +121,8 @@ static struct hc_driver __read_mostly ohci_pxa27x_hc_driver;
struct pxa27x_ohci {
struct clk *clk;
void __iomem *mmio_base;
struct regulator *vbus[3];
bool vbus_enabled[3];
};

#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv)
Expand Down Expand Up @@ -166,6 +169,52 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode)
return 0;
}

static int pxa27x_ohci_set_vbus_power(struct pxa27x_ohci *pxa_ohci,
unsigned int port, bool enable)
{
struct regulator *vbus = pxa_ohci->vbus[port];
int ret = 0;

if (IS_ERR_OR_NULL(vbus))
return 0;

if (enable && !pxa_ohci->vbus_enabled[port])
ret = regulator_enable(vbus);
else if (!enable && pxa_ohci->vbus_enabled[port])
ret = regulator_disable(vbus);

if (ret < 0)
return ret;

pxa_ohci->vbus_enabled[port] = enable;

return 0;
}

static int pxa27x_ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
int ret;

switch (typeReq) {
case SetPortFeature:
case ClearPortFeature:
if (!wIndex || wIndex > 3)
return -EPIPE;

if (wValue != USB_PORT_FEAT_POWER)
break;

ret = pxa27x_ohci_set_vbus_power(pxa_ohci, wIndex - 1,
typeReq == SetPortFeature);
if (ret)
return ret;
break;
}

return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
}
/*-------------------------------------------------------------------------*/

static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci,
Expand Down Expand Up @@ -372,6 +421,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
struct ohci_hcd *ohci;
struct resource *r;
struct clk *usb_clk;
unsigned int i;

retval = ohci_pxa_of_init(pdev);
if (retval)
Expand Down Expand Up @@ -417,6 +467,16 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
pxa_ohci->clk = usb_clk;
pxa_ohci->mmio_base = (void __iomem *)hcd->regs;

for (i = 0; i < 3; ++i) {
char name[6];

if (!(inf->flags & (ENABLE_PORT1 << i)))
continue;

sprintf(name, "vbus%u", i + 1);
pxa_ohci->vbus[i] = devm_regulator_get(&pdev->dev, name);
}

retval = pxa27x_start_hc(pxa_ohci, &pdev->dev);
if (retval < 0) {
pr_debug("pxa27x_start_hc failed");
Expand Down Expand Up @@ -462,9 +522,14 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
unsigned int i;

usb_remove_hcd(hcd);
pxa27x_stop_hc(pxa_ohci, &pdev->dev);

for (i = 0; i < 3; ++i)
pxa27x_ohci_set_vbus_power(pxa_ohci, i, false);

usb_put_hcd(hcd);
}

Expand Down Expand Up @@ -563,7 +628,10 @@ static int __init ohci_pxa27x_init(void)
return -ENODEV;

pr_info("%s: " DRIVER_DESC "\n", hcd_name);

ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides);
ohci_pxa27x_hc_driver.hub_control = pxa27x_ohci_hub_control;

return platform_driver_register(&ohci_hcd_pxa27x_driver);
}
module_init(ohci_pxa27x_init);
Expand Down

0 comments on commit cecabe5

Please sign in to comment.