summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ohci-pxa27x.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-08 22:31:16 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-08 22:31:16 +0400
commit3f17ea6dea8ba5668873afa54628a91aaa3fb1c0 (patch)
treeafbeb2accd4c2199ddd705ae943995b143a0af02 /drivers/usb/host/ohci-pxa27x.c
parent1860e379875dfe7271c649058aeddffe5afd9d0d (diff)
parent1a5700bc2d10cd379a795fd2bb377a190af5acd4 (diff)
downloadlinux-3f17ea6dea8ba5668873afa54628a91aaa3fb1c0.tar.xz
Merge branch 'next' (accumulated 3.16 merge window patches) into master
Now that 3.15 is released, this merges the 'next' branch into 'master', bringing us to the normal situation where my 'master' branch is the merge window. * accumulated work in next: (6809 commits) ufs: sb mutex merge + mutex_destroy powerpc: update comments for generic idle conversion cris: update comments for generic idle conversion idle: remove cpu_idle() forward declarations nbd: zero from and len fields in NBD_CMD_DISCONNECT. mm: convert some level-less printks to pr_* MAINTAINERS: adi-buildroot-devel is moderated MAINTAINERS: add linux-api for review of API/ABI changes mm/kmemleak-test.c: use pr_fmt for logging fs/dlm/debug_fs.c: replace seq_printf by seq_puts fs/dlm/lockspace.c: convert simple_str to kstr fs/dlm/config.c: convert simple_str to kstr mm: mark remap_file_pages() syscall as deprecated mm: memcontrol: remove unnecessary memcg argument from soft limit functions mm: memcontrol: clean up memcg zoneinfo lookup mm/memblock.c: call kmemleak directly from memblock_(alloc|free) mm/mempool.c: update the kmemleak stack trace for mempool allocations lib/radix-tree.c: update the kmemleak stack trace for radix tree allocations mm: introduce kmemleak_update_trace() mm/kmemleak.c: use %u to print ->checksum ...
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 @@
#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>
@@ -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)
@@ -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,
@@ -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)
@@ -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");
@@ -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);
}
@@ -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);