diff options
author | Philippe Langlais <philippe.langlais@linaro.org> | 2012-03-16 10:45:50 +0100 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:03:18 +0200 |
commit | 1d46676256fe722fc11c266728f1f09de0943aa8 (patch) | |
tree | 0984e3accce1a38b4396ba5fa928349e428ef1e7 /drivers/usb | |
parent | 56d0696232fa2ba60006fcd05888e933ef9dc085 (diff) |
USB: Add support for USB OTG 2.0
OTG 2.0 support is provided in Kernel 3.0.
Srp related modifications are done in ab8500-usb.
ST-Ericsson ID: 401192
ST-Ericsson Linux next: NA
ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10054
Change-Id: I1bf52c8d6f6c4b0bedf5e51004dc72bf52a68020
Signed-off-by: Avinash Kumar <avinash.kumar@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49006
Reviewed-by: QATOOLS
Reviewed-by: Praveena NADAHALLY <praveen.nadahally@stericsson.com>
Conflicts:
drivers/usb/otg/ab8500-usb.c
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/driver.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 135 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 7 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 6 | ||||
-rw-r--r-- | drivers/usb/musb/musb_gadget.c | 2 | ||||
-rw-r--r-- | drivers/usb/musb/musb_gadget_ep0.c | 54 | ||||
-rw-r--r-- | drivers/usb/musb/musb_regs.h | 4 | ||||
-rw-r--r-- | drivers/usb/musb/ux500.c | 9 | ||||
-rw-r--r-- | drivers/usb/otg/ab8500-usb.c | 23 |
9 files changed, 208 insertions, 33 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b3948206024..7ce5080df46 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1306,6 +1306,7 @@ void usb_hnp_polling_work(struct work_struct *work) if (ret < 0) { /* Peripheral may not be supporting HNP polling */ dev_vdbg(&udev->dev, "HNP polling failed. status %d\n", ret); + ret = usb_suspend_both(udev, PMSG_USER_SUSPEND); goto out; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f4038c53b61..f78df1d3163 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -639,7 +639,7 @@ static int hub_hub_status(struct usb_hub *hub, "%s failed (err = %d)\n", __func__, ret); else { *status = le16_to_cpu(hub->status->hub.wHubStatus); - *change = le16_to_cpu(hub->status->hub.wHubChange); + *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; } mutex_unlock(&hub->status_mutex); @@ -1693,7 +1693,7 @@ void usb_disconnect(struct usb_device **pdev) dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); -#ifdef CONFIG_USB_OTG +#ifdef CONFIG_USB_OTG_20 if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) { cancel_delayed_work_sync(&udev->bus->hnp_polling); udev->bus->hnp_support = 0; @@ -1775,11 +1775,13 @@ static inline void announce_device(struct usb_device *udev) { } * * Finish enumeration for On-The-Go devices */ + +#ifdef CONFIG_USB_OTG_20 + static int usb_enumerate_device_otg(struct usb_device *udev) { int err = 0; -#ifdef CONFIG_USB_OTG /* * OTG-aware devices on OTG-capable root hubs may be able to use SRP, * to wake us after we've powered off VBUS; and HNP, switching roles @@ -1803,6 +1805,10 @@ static int usb_enumerate_device_otg(struct usb_device *udev) (port1 == bus->otg_port) ? "" : "non-"); + if (port1 != bus->otg_port) + goto out; + bus->hnp_support = 1; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, 0, @@ -1832,8 +1838,6 @@ out: if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } - err = -ENOTSUPP; - goto fail; } else if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) { /* HNP polling is introduced in OTG supplement Rev 2.0 @@ -1845,10 +1849,82 @@ out: msecs_to_jiffies(THOST_REQ_POLL)); } fail: + + return err; +} + +#else + +static int usb_enumerate_device_otg(struct usb_device *udev) +{ + int err = 0; + +#ifdef CONFIG_USB_OTG + /* + * OTG-aware devices on OTG-capable root hubs may be able to use SRP, + * to wake us after we've powered off VBUS; and HNP, switching roles + * "host" to "peripheral". The OTG descriptor helps figure this out. + */ + if (!udev->bus->is_b_host + && udev->config + && udev->parent == udev->bus->root_hub) { + struct usb_otg_descriptor *desc = NULL; + struct usb_bus *bus = udev->bus; + + /* descriptor may appear anywhere in config */ + if (__usb_get_extra_descriptor(udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + USB_DT_OTG, (void **) &desc) == 0) { + if (desc->bmAttributes & USB_OTG_HNP) { + unsigned port1 = udev->portnum; + + dev_info(&udev->dev, + "Dual-Role OTG device on %sHNP port\n", + (port1 == bus->otg_port) + ? "" : "non-"); + + /* enable HNP before suspend, it's simpler */ + if (port1 == bus->otg_port) + bus->b_hnp_enable = 1; + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + bus->b_hnp_enable + ? USB_DEVICE_B_HNP_ENABLE + : USB_DEVICE_A_ALT_HNP_SUPPORT, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (err < 0) { + /* OTG MESSAGE: report errors here, + * customize to match your product. + */ + dev_info(&udev->dev, + "can't set HNP mode: %d\n", + err); + bus->b_hnp_enable = 0; + } + } + } + } + + if (!is_targeted(udev)) { + + /* Maybe it can talk to us, though we can't talk to it. + * (Includes HNP test device.) + */ + if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { + err = usb_port_suspend(udev, PMSG_SUSPEND); + if (err < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } + err = -ENOTSUPP; + goto fail; + } +fail: #endif return err; } +#endif /** * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal) @@ -2508,7 +2584,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); -#ifdef CONFIG_USB_OTG +#ifdef CONFIG_USB_OTG_20 if (!udev->bus->is_b_host && udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -2522,6 +2598,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) udev->bus->b_hnp_enable = 1; } #endif + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2676,7 +2753,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) int status; u16 portchange, portstatus; -#ifdef CONFIG_USB_OTG +#ifdef CONFIG_USB_OTG_20 if (!udev->bus->is_b_host && udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) udev->bus->b_hnp_enable = 0; @@ -2885,7 +2962,7 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); * Between connect detection and reset signaling there must be a delay * of 100ms at least for debounce and power-settling. The corresponding * timer shall restart whenever the downstream port detects a disconnect. - * + * * Apparently there are some bluetooth and irda-dongles and a number of * low-speed devices for which this debounce period may last over a second. * Not covered by the spec - but easy to deal with. @@ -3083,7 +3160,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, udev->tt = &hub->tt; udev->ttport = port1; } - + /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? * Because device hardware and firmware is sometimes buggy in * this area, and this is how Linux has done it for ages. @@ -3243,7 +3320,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i); usb_ep0_reinit(udev); } - + retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); if (retval < (signed)sizeof(udev->descriptor)) { dev_err(&udev->dev, "device descriptor read/all, error %d\n", @@ -3528,7 +3605,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, goto loop_disable; } } - + /* check for devices running slower than they could */ if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 && udev->speed == USB_SPEED_FULL @@ -3570,6 +3647,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, dev_dbg(hub_dev, "%dmA power budget left\n", status); #ifdef CONFIG_USB_OTG_20 + struct usb_otg_descriptor *desc = NULL; + int ret; + /* descriptor may appear anywhere in config */ + __usb_get_extra_descriptor(udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + USB_DT_OTG, (void **) &desc); + + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_B_HNP_ENABLE, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + dev_dbg(hub_dev, "set feature error\n"); u16 idVendor = le16_to_cpu(udev->descriptor.idVendor); if (idVendor == USB_OTG_TEST_MODE_VID) { @@ -3651,8 +3742,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, break; default: /* is_targeted() will take care for wrong PID */ - dev_dbg(&udev->dev, "OTG TEST_MODE:Wrong - PID %d\n" idProduct); + dev_dbg(&udev->dev, "OTG TEST_MODE:Wrong" + "PID %d\n", idProduct); break; } @@ -3681,7 +3772,7 @@ loop: !(hcd->driver->port_handed_over)(hcd, port1)) dev_err(hub_dev, "unable to enumerate USB device on port %d\n", port1); - + done: hub_port_disable(hub, port1, 1); if (hcd->driver->relinquish_port && !hub->hdev->parent) @@ -3848,7 +3939,7 @@ static void hub_events(void) * EM interference sometimes causes badly * shielded USB devices to be shutdown by * the hub, this hack enables them again. - * Works at least with mouse driver. + * Works at least with mouse driver. */ if (!(portstatus & USB_PORT_STAT_ENABLE) && !connect_change @@ -4188,7 +4279,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) if (ret < 0) goto re_enumerate; - + /* Device might have changed firmware (DFU or similar) */ if (descriptors_changed(udev, &descriptor)) { dev_info(&udev->dev, "device firmware changed\n"); @@ -4221,6 +4312,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev) goto re_enumerate; } mutex_unlock(hcd->bandwidth_mutex); + +#ifdef CONFIG_USB_OTG_20 + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_A_HNP_SUPPORT, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + dev_err(&udev->dev, "set feature error\n"); +#endif usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Put interfaces back into the same altsettings as before. @@ -4261,7 +4362,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) done: return 0; - + re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 840afb3b8a0..1ab2fd8c3e9 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -714,6 +714,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, b_host: musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; +#ifdef CONFIG_USB_OTG_20 + musb->g.otg_hnp_reqd = 0; +#endif musb->ignore_disconnect = 0; del_timer(&musb->otg_timer); break; @@ -1748,7 +1751,9 @@ musb_srp_store(struct device *dev, struct device_attribute *attr, { struct musb *musb = dev_to_musb(dev); unsigned short srp; - +#ifdef CONFIG_USB_OTG_20 + musb->xceiv->start_srp(musb->xceiv); +#endif if (sscanf(buf, "%hu", &srp) != 1 || (srp != 1)) { dev_err(dev, "SRP: Value must be 1\n"); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index f7a37fa1bfe..2d52530d3e4 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -153,7 +153,10 @@ enum musb_g_ep0_state { #define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */ #define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */ #define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */ - +#ifdef CONFIG_USB_OTG_20 +#define USB_SUSP_DET_DURATION 5 /* suspend time 5ms */ +#define TTST_SRP 3000 /* max 5 sec */ +#endif /*************************** REGISTER ACCESS ********************************/ @@ -432,7 +435,6 @@ struct musb { unsigned set_address:1; unsigned test_mode:1; unsigned softconnect:1; - u8 address; u8 test_mode_nr; u16 ackpend; /* ep0 */ diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 461b5f2d889..b2abef69dc3 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1662,7 +1662,9 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) } spin_unlock_irqrestore(&musb->lock, flags); +#ifndef CONFIG_USB_OTG_20 otg_start_srp(musb->xceiv->otg); +#endif spin_lock_irqsave(&musb->lock, flags); /* Block idling for at least 1s */ diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index e40d7647caf..631aab86240 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -45,6 +45,11 @@ /* ep0 is always musb->endpoints[0].ep_in */ #define next_ep0_request(musb) next_in_request(&(musb)->endpoints[0]) +/* OTG 2.0 Specification 6.2.3 GetStatus commands */ +#ifdef CONFIG_USB_OTG_20 +#define OTG_STATUS_SELECT 0xF +#endif + /* * locking note: we use only the controller lock, for simpler correctness. * It's always held with IRQs blocked. @@ -80,21 +85,33 @@ static int service_tx_status_request( int handled = 1; u8 result[2], epnum = 0; const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK; - +#ifdef CONFIG_USB_OTG_20 + unsigned int otg_recip = ctrlrequest->wIndex >> 12; +#endif result[1] = 0; switch (recip) { case USB_RECIP_DEVICE: - result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED; - result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; - if (musb->g.is_otg) { - result[0] |= musb->g.b_hnp_enable - << USB_DEVICE_B_HNP_ENABLE; - result[0] |= musb->g.a_alt_hnp_support - << USB_DEVICE_A_ALT_HNP_SUPPORT; - result[0] |= musb->g.a_hnp_support - << USB_DEVICE_A_HNP_SUPPORT; +#ifdef CONFIG_USB_OTG_20 + if (!(otg_recip == OTG_STATUS_SELECT)) { +#endif + result[0] = musb->is_self_powered << + USB_DEVICE_SELF_POWERED; + result[0] |= musb->may_wakeup << + USB_DEVICE_REMOTE_WAKEUP; + if (musb->g.is_otg) { + result[0] |= musb->g.b_hnp_enable + << USB_DEVICE_B_HNP_ENABLE; + result[0] |= musb->g.a_alt_hnp_support + << USB_DEVICE_A_ALT_HNP_SUPPORT; + result[0] |= musb->g.a_hnp_support + << USB_DEVICE_A_HNP_SUPPORT; + } +#ifdef CONFIG_USB_OTG_20 + } else { + result[0] = 1 & musb->g.otg_hnp_reqd; } +#endif break; case USB_RECIP_INTERFACE: @@ -356,7 +373,22 @@ __acquires(musb->lock) musb->test_mode_nr = MUSB_TEST_PACKET; break; - +#ifdef CONFIG_USB_OTG_20 + case 6: + if (!musb->g.is_otg) + goto stall; + musb->g.otg_srp_reqd = 1; + + mod_timer(&musb->otg_timer, + jiffies + + msecs_to_jiffies(TTST_SRP)); + break; + case 7: + if (!musb->g.is_otg) + goto stall; + musb->g.otg_hnp_reqd = 1; + break; +#endif case 0xc0: /* TEST_FORCE_HS */ pr_debug("TEST_FORCE_HS\n"); diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 03f2655af29..1c96cf60907 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -246,7 +246,9 @@ */ #define MUSB_DEVCTL 0x60 /* 8 bit */ - +#ifdef CONFIG_USB_OTG_20 +#define MUSB_MISC 0x61 /* 8 bit */ +#endif /* These are always controlled through the INDEX register */ #define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ #define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */ diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index fe322cf541c..89ef846d32c 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -311,11 +311,18 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on) u8 devctl; unsigned long timeout = jiffies + msecs_to_jiffies(1000); int ret = 1; +#ifdef CONFIG_USB_OTG_20 + int val = 0; +#endif /* HDRC controls CPEN, but beware current surges during device * connect. They can trigger transient overcurrent conditions * that must be ignored. */ - +#ifdef CONFIG_USB_OTG_20 + val = musb_readb(musb->mregs, MUSB_MISC); + val |= 0x1C; + musb_writeb(musb->mregs, MUSB_MISC, val); +#endif devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (is_on) { diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index a698165135e..7efd64b3bc5 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -484,6 +484,26 @@ static unsigned ab8500_eyediagram_workaroud(struct ab8500_usb *ab, unsigned mA) return mA; } +#ifdef CONFIG_USB_OTG_20 +static int ab8500_usb_start_srp(struct usb_phy *phy, unsigned mA) +{ + struct ab8500_usb *ab; + + if (!phy) + return -ENODEV; + + ab = phy_to_ab(phy); + + atomic_notifier_call_chain(&ab->phy.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); + + ab8500_usb_peri_phy_en(ab); + + return 0; +} +#endif + static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) { struct ab8500_usb *ab; @@ -838,6 +858,9 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) otg->phy = &ab->phy; otg->set_host = ab8500_usb_set_host; otg->set_peripheral = ab8500_usb_set_peripheral; +#ifdef CONFIG_USB_OTG_20 + ab->otg.start_srp = ab8500_usb_start_srp; +#endif ab->sysfs_flag = true; platform_set_drvdata(pdev, ab); |