static int usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
+#define USB_VENDOR_XIAOMI 0x2717
+#define USB_PRODUCT_XIAOMI_HEADSET 0x3801
+
+bool is_xiaomi_headset = false;
+
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
if (hub_is_superspeed(hub->hdev))
dev_info(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
+ if (is_xiaomi_headset) {
+ dev_info(&udev->dev, "xiaomi headset removed, devnum %d\n", udev->devnum);
+ is_xiaomi_headset = false;
+ }
+
/*
* Ensure that the pm runtime code knows that the USB device
* is in the process of being disconnected.
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
+ if (USB_VENDOR_XIAOMI == le16_to_cpu(udev->descriptor.idVendor)
+ && USB_PRODUCT_XIAOMI_HEADSET == le16_to_cpu(udev->descriptor.idProduct)) {
+ dev_info(&udev->dev, "xiaomi headset identified, devnum %d\n", udev->devnum);
+ is_xiaomi_headset = true;
+ }
+
/* Tell the world! */
announce_device(udev);
#include <linux/slab.h>
#include "xhci.h"
#include "xhci-trace.h"
+extern void kick_usbpd_vbus_sm(void);
+extern bool is_xiaomi_headset;
/*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
{
+ if (is_xiaomi_headset)
+ delay = msecs_to_jiffies(1000);
+
return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
}
{
u64 temp_64;
int ret;
+ int delay;
xhci_dbg(xhci, "Abort command ring\n");
* CRR negated in a timely manner, then it should assume
* that the there are larger problems with the xHC and assert HCRST.
*/
+ if (is_xiaomi_headset) {
+ delay = 500 * 1000;
+ } else {
+ delay = 5000 * 1000;
+ }
+
ret = xhci_handshake_check_state(xhci, &xhci->op_regs->cmd_ring,
CMD_RING_RUNNING, 0, 1000 * 1000);
if (ret < 0) {
+ if (is_xiaomi_headset)
+ return -EPERM;
xhci_err(xhci,
"Stop command ring failed, maybe the host is dead\n");
xhci->xhc_state |= XHCI_STATE_DYING;
xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
xhci_dbg(xhci, "Command timeout\n");
ret = xhci_abort_cmd_ring(xhci, flags);
+ if (ret == -EPERM) {
+ xhci_err(xhci, "Abort command ring failed reset usb device\n");
+ xhci_cleanup_command_queue(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ kick_usbpd_vbus_sm();
+ return;
+ }
+
if (unlikely(ret == -ESHUTDOWN)) {
xhci_err(xhci, "Abort command ring failed\n");
xhci_cleanup_command_queue(xhci);
struct device dev;
struct workqueue_struct *wq;
struct work_struct sm_work;
+ struct delayed_work vbus_work;
struct hrtimer timer;
bool sm_queued;
}
static DEVICE_ATTR_RW(get_battery_status);
+struct usbpd *pd_lobal;
+void pd_vbus_reset(struct usbpd *pd)
+{
+ if (!pd) {
+ return;
+ }
+
+ if (pd->vbus_enabled) {
+ regulator_disable(pd->vbus);
+ pd->vbus_enabled = false;
+ stop_usb_host(pd);
+ msleep(500);
+ start_usb_host(pd, true);
+ enable_vbus(pd);
+ }
+}
+
+/* Handles VBUS off on */
+void usbpd_vbus_sm(struct work_struct *w)
+{
+ struct usbpd *pd = pd_lobal;
+ pd_vbus_reset(pd);
+}
+ void kick_usbpd_vbus_sm(void)
+{
+ pm_stay_awake(&pd_lobal->dev);
+ queue_delayed_work(pd_lobal->wq, &(pd_lobal->vbus_work), msecs_to_jiffies(200));
+}
+
static struct attribute *usbpd_attrs[] = {
&dev_attr_contract.attr,
&dev_attr_initial_pr.attr,
goto del_pd;
}
INIT_WORK(&pd->sm_work, usbpd_sm);
+ INIT_DELAYED_WORK(&pd->vbus_work, usbpd_vbus_sm);
hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pd->timer.function = pd_timeout;
mutex_init(&pd->swap_lock);
/* force read initial power_supply values */
psy_changed(&pd->psy_nb, PSY_EVENT_PROP_CHANGED, pd->usb_psy);
+ pd_lobal = pd;
+
return pd;
del_inst:
return 0;
}
+#define USB_VENDOR_XIAOMI 0x2717
+#define USB_PRODUCT_XIAOMI_HEADSET 0x3801
+
+extern void kick_usbpd_vbus_sm(void);
+
/*
* find a matching format and set up the interface
*/
dev_err(&dev->dev,
"%d:%d: usb_set_interface failed (%d)\n",
fmt->iface, fmt->altsetting, err);
+
+ if (USB_VENDOR_XIAOMI == USB_ID_VENDOR(subs->stream->chip->usb_id) &&
+ USB_PRODUCT_XIAOMI_HEADSET == USB_ID_PRODUCT(subs->stream->chip->usb_id)) {
+ kick_usbpd_vbus_sm();
+ }
+
return -EIO;
}
dev_dbg(&dev->dev, "setting usb interface %d:%d\n",