mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-26 07:21:37 +00:00
DM USB: xHCI: re-design the S3 logic to speed up resuming process
This patch unbinds the kernel driver usbfs with device before suspending is completed and binds them again during resuming process, this could avoid a busy loop in SOS due to VBUS drop. This patch could save ~200ms for resuming process. Tracked-On: #2576 Signed-off-by: Conghui Chen <conghui.chen@intel.com> Signed-off-by: Xiaoguang Wu <xiaoguang.wu@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
parent
b77755cd58
commit
9844ff0266
@ -255,12 +255,17 @@ struct pci_xhci_rtsregs {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* this is used to describe the VBus Drop state */
|
/* this is used to describe the VBus Drop state */
|
||||||
enum pci_xhci_vbdp_state {
|
enum pci_xhci_vbdp_shift {
|
||||||
S3_VBDP_NONE = 0,
|
S3_VBDP_SHIFT_CSS,
|
||||||
S3_VBDP_START,
|
S3_VBDP_SHIFT_DIS_SLOT,
|
||||||
S3_VBDP_END
|
S3_VBDP_SHIFT_CONN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define S3_VBDP_NONE 0
|
||||||
|
#define S3_VBDP_CSS (1 << S3_VBDP_SHIFT_CSS)
|
||||||
|
#define S3_VBDP_DIS_SLOT (1 << S3_VBDP_SHIFT_DIS_SLOT)
|
||||||
|
#define S3_VBDP_CONN (1 << S3_VBDP_SHIFT_CONN)
|
||||||
|
|
||||||
struct pci_xhci_excap_ptr {
|
struct pci_xhci_excap_ptr {
|
||||||
uint8_t cap_id;
|
uint8_t cap_id;
|
||||||
uint8_t cap_ptr;
|
uint8_t cap_ptr;
|
||||||
@ -541,7 +546,8 @@ pci_xhci_get_free_vport(struct pci_xhci_vdev *xdev,
|
|||||||
struct usb_native_devinfo *di)
|
struct usb_native_devinfo *di)
|
||||||
{
|
{
|
||||||
int ports, porte;
|
int ports, porte;
|
||||||
int i, j, k;
|
int i, j, found = false;
|
||||||
|
uint8_t mask = (S3_VBDP_CSS | S3_VBDP_DIS_SLOT | S3_VBDP_CONN);
|
||||||
|
|
||||||
assert(xdev);
|
assert(xdev);
|
||||||
assert(di);
|
assert(di);
|
||||||
@ -554,18 +560,36 @@ pci_xhci_get_free_vport(struct pci_xhci_vdev *xdev,
|
|||||||
porte = ports + (XHCI_MAX_DEVS / 2);
|
porte = ports + (XHCI_MAX_DEVS / 2);
|
||||||
|
|
||||||
for (i = ports; i <= porte; i++) {
|
for (i = ports; i <= porte; i++) {
|
||||||
for (j = 0; j < XHCI_MAX_VIRT_PORTS; j++) {
|
for (j = 0; j < XHCI_MAX_VIRT_PORTS; j++)
|
||||||
if (xdev->native_ports[j].vport == i)
|
if (xdev->native_ports[j].vport == i) {
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
k = xdev->vbdp_dev_num;
|
if (found) {
|
||||||
if (k > 0 && xdev->vbdp_devs[j].state == S3_VBDP_START
|
found = false;
|
||||||
&& xdev->vbdp_devs[j].vport == i)
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (j >= XHCI_MAX_VIRT_PORTS)
|
|
||||||
|
if (xdev->vbdp_dev_num <= 0)
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
|
for (j = 0; j < XHCI_MAX_VIRT_PORTS; j++)
|
||||||
|
if (xdev->vbdp_devs[j].vport == i &&
|
||||||
|
(xdev->vbdp_devs[j].state & mask)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
found = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +752,6 @@ pci_xhci_unassign_hub_ports(struct pci_xhci_vdev *xdev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
xhci_vbdp_thread(void *data)
|
xhci_vbdp_thread(void *data)
|
||||||
{
|
{
|
||||||
@ -736,6 +759,7 @@ xhci_vbdp_thread(void *data)
|
|||||||
int speed;
|
int speed;
|
||||||
struct pci_xhci_vdev *xdev;
|
struct pci_xhci_vdev *xdev;
|
||||||
struct pci_xhci_native_port *p;
|
struct pci_xhci_native_port *p;
|
||||||
|
uint8_t mask = (S3_VBDP_DIS_SLOT | S3_VBDP_CONN);
|
||||||
|
|
||||||
xdev = data;
|
xdev = data;
|
||||||
assert(xdev);
|
assert(xdev);
|
||||||
@ -743,28 +767,41 @@ xhci_vbdp_thread(void *data)
|
|||||||
while (xdev->vbdp_polling) {
|
while (xdev->vbdp_polling) {
|
||||||
|
|
||||||
sem_wait(&xdev->vbdp_sem);
|
sem_wait(&xdev->vbdp_sem);
|
||||||
|
UPRINTF(LINF, "vbdp: thread triggered once\r\n");
|
||||||
for (i = 0; i < XHCI_MAX_VIRT_PORTS; ++i)
|
for (i = 0; i < XHCI_MAX_VIRT_PORTS; ++i)
|
||||||
if (xdev->vbdp_devs[i].state == S3_VBDP_END) {
|
if ((xdev->vbdp_devs[i].state & mask) == mask)
|
||||||
xdev->vbdp_devs[i].state = S3_VBDP_NONE;
|
/* FIXME: if an USB device is disconnected
|
||||||
|
* after suspending is done, this condition
|
||||||
|
* will never hit. Need to provide a proper
|
||||||
|
* solution to fix it in near future.
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= XHCI_MAX_VIRT_PORTS)
|
if (i >= XHCI_MAX_VIRT_PORTS) {
|
||||||
|
UPRINTF(LINF, "vbdp: fail to find proper state\r\n");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
j = pci_xhci_get_native_port_index_by_path(xdev,
|
j = pci_xhci_get_native_port_index_by_path(xdev,
|
||||||
&xdev->vbdp_devs[i].path);
|
&xdev->vbdp_devs[i].path);
|
||||||
if (j < 0)
|
if (j < 0) {
|
||||||
|
UPRINTF(LINF, "vbdp: fail to find proper device\r\n");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
p = &xdev->native_ports[j];
|
p = &xdev->native_ports[j];
|
||||||
if (p->state != VPORT_CONNECTED)
|
if (p->state != VPORT_CONNECTED) {
|
||||||
|
UPRINTF(LINF, "vbdp: device is not connected\r\n");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
speed = pci_xhci_convert_speed(p->info.speed);
|
speed = pci_xhci_convert_speed(p->info.speed);
|
||||||
pci_xhci_connect_port(xdev, p->vport, speed, 1);
|
pci_xhci_connect_port(xdev, p->vport, speed, 1);
|
||||||
UPRINTF(LINF, "change portsc for %d-%s\r\n", p->info.path.bus,
|
UPRINTF(LINF, "change portsc for %d-%s\r\n", p->info.path.bus,
|
||||||
usb_dev_path(&p->info.path));
|
usb_dev_path(&p->info.path));
|
||||||
|
|
||||||
|
xdev->vbdp_devs[i].state = S3_VBDP_NONE;
|
||||||
|
xdev->vbdp_dev_num--;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -813,13 +850,11 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
|
|||||||
di->pid, di->path.bus, usb_dev_path(&di->path));
|
di->pid, di->path.bus, usb_dev_path(&di->path));
|
||||||
|
|
||||||
for (i = 0; xdev->vbdp_dev_num && i < XHCI_MAX_VIRT_PORTS; ++i) {
|
for (i = 0; xdev->vbdp_dev_num && i < XHCI_MAX_VIRT_PORTS; ++i) {
|
||||||
if (xdev->vbdp_devs[i].state != S3_VBDP_START)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!usb_dev_path_cmp(&di->path, &xdev->vbdp_devs[i].path))
|
if (!usb_dev_path_cmp(&di->path, &xdev->vbdp_devs[i].path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
s3_conn = 1;
|
s3_conn = 1;
|
||||||
|
xdev->vbdp_devs[i].state |= S3_VBDP_CONN;
|
||||||
vport = xdev->vbdp_devs[i].vport;
|
vport = xdev->vbdp_devs[i].vport;
|
||||||
UPRINTF(LINF, "Skip and cache connect event for %d-%s\r\n",
|
UPRINTF(LINF, "Skip and cache connect event for %d-%s\r\n",
|
||||||
di->path.bus, usb_dev_path(&di->path));
|
di->path.bus, usb_dev_path(&di->path));
|
||||||
@ -847,8 +882,10 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
|
|||||||
/* we will report connecting event in xhci_vbdp_thread for
|
/* we will report connecting event in xhci_vbdp_thread for
|
||||||
* device that hasn't complete the S3 process
|
* device that hasn't complete the S3 process
|
||||||
*/
|
*/
|
||||||
if (s3_conn)
|
if (s3_conn) {
|
||||||
|
sem_post(&xdev->vbdp_sem);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Trigger port change event for the arriving device */
|
/* Trigger port change event for the arriving device */
|
||||||
if (pci_xhci_connect_port(xdev, vport, di->speed, 1))
|
if (pci_xhci_connect_port(xdev, vport, di->speed, 1))
|
||||||
@ -871,6 +908,7 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
|
|||||||
int index;
|
int index;
|
||||||
int rc;
|
int rc;
|
||||||
int i;
|
int i;
|
||||||
|
uint8_t mask;
|
||||||
|
|
||||||
assert(hci_data);
|
assert(hci_data);
|
||||||
assert(dev_data);
|
assert(dev_data);
|
||||||
@ -925,20 +963,21 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
for (i = 0; xdev->vbdp_dev_num && i < XHCI_MAX_VIRT_PORTS; ++i) {
|
for (i = 0; xdev->vbdp_dev_num && i < XHCI_MAX_VIRT_PORTS; ++i) {
|
||||||
if (xdev->vbdp_devs[i].state != S3_VBDP_START)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!usb_dev_path_cmp(&xdev->vbdp_devs[i].path, &di->path))
|
if (!usb_dev_path_cmp(&xdev->vbdp_devs[i].path, &di->path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
mask = (S3_VBDP_DIS_SLOT | S3_VBDP_CSS);
|
||||||
* we do nothing here for device that is in the middle of
|
if (xdev->vbdp_devs[i].state & mask) {
|
||||||
* S3 resuming process.
|
/*
|
||||||
*/
|
* we do nothing here for device that is in the middle
|
||||||
UPRINTF(LINF, "disconnect device %d-%s on vport %d with "
|
* of S3 resuming process.
|
||||||
"state %d and return.\r\n", di->path.bus,
|
*/
|
||||||
usb_dev_path(&di->path), vport, state);
|
UPRINTF(LINF, "disconnect device %d-%s on vport %d "
|
||||||
return 0;
|
"with state %d and return.\r\n",
|
||||||
|
di->path.bus, usb_dev_path(&di->path),
|
||||||
|
vport, state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(state == VPORT_EMULATED || state == VPORT_CONNECTED);
|
assert(state == VPORT_EMULATED || state == VPORT_CONNECTED);
|
||||||
@ -1116,6 +1155,9 @@ pci_xhci_dev_destroy(struct pci_xhci_dev_emu *de, bool full)
|
|||||||
|
|
||||||
if (full)
|
if (full)
|
||||||
free(de);
|
free(de);
|
||||||
|
|
||||||
|
UPRINTF(LINF, "%s: deinit %d-%s mode %d\r\n", __func__,
|
||||||
|
ud->info.path.bus, usb_dev_path(&ud->info.path), full);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
@ -1260,6 +1302,27 @@ pci_xhci_usbcmd_write(struct pci_xhci_vdev *xdev, uint32_t cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cmd & XHCI_CMD_CSS) {
|
if (cmd & XHCI_CMD_CSS) {
|
||||||
|
/*
|
||||||
|
* Here gives a simple introduction for the following code.
|
||||||
|
* On some platforms, the USB VBus will drop after suspending,
|
||||||
|
* and re-enumerate during subsequent resuming process. Hence
|
||||||
|
* the DM emulation is divided into two parts:
|
||||||
|
*
|
||||||
|
* Suspending: if the following code is executed, it means UOS
|
||||||
|
* started the xHCI suspending process and trapped here. The DM
|
||||||
|
* will save the states of current connected devices and clear
|
||||||
|
* the PORTSC for those devices, which will emulate a
|
||||||
|
* 'disconnect' state in UOS.
|
||||||
|
*
|
||||||
|
* Resuming: The SOS will do re-emulate for the connected
|
||||||
|
* devices and report a connect event to DM. At the same time
|
||||||
|
* the DM will receive disable slot cmd from UOS for the
|
||||||
|
* 'virtually disconnected' devices. After those things
|
||||||
|
* completed, DM will emulate a connect event to trigger UOS
|
||||||
|
* do re-emulate for those devices although they are always
|
||||||
|
* connected physically.
|
||||||
|
*/
|
||||||
|
|
||||||
/* TODO: should think about what happen if system S3 fail
|
/* TODO: should think about what happen if system S3 fail
|
||||||
* and under that situation, the vbdp_devs and se_dev_num
|
* and under that situation, the vbdp_devs and se_dev_num
|
||||||
* should also need to be cleared
|
* should also need to be cleared
|
||||||
@ -1274,7 +1337,7 @@ pci_xhci_usbcmd_write(struct pci_xhci_vdev *xdev, uint32_t cmd)
|
|||||||
j = xdev->vbdp_dev_num;
|
j = xdev->vbdp_dev_num;
|
||||||
xdev->vbdp_devs[j].path = p->info.path;
|
xdev->vbdp_devs[j].path = p->info.path;
|
||||||
xdev->vbdp_devs[j].vport = p->vport;
|
xdev->vbdp_devs[j].vport = p->vport;
|
||||||
xdev->vbdp_devs[j].state = S3_VBDP_START;
|
xdev->vbdp_devs[j].state |= S3_VBDP_CSS;
|
||||||
xdev->vbdp_dev_num++;
|
xdev->vbdp_dev_num++;
|
||||||
|
|
||||||
/* clear PORTSC register */
|
/* clear PORTSC register */
|
||||||
@ -1283,11 +1346,23 @@ pci_xhci_usbcmd_write(struct pci_xhci_vdev *xdev, uint32_t cmd)
|
|||||||
/* clear other information for this device*/
|
/* clear other information for this device*/
|
||||||
p->vport = 0;
|
p->vport = 0;
|
||||||
p->state = VPORT_ASSIGNED;
|
p->state = VPORT_ASSIGNED;
|
||||||
UPRINTF(LINF, "s3: save %d-%s state\r\n",
|
UPRINTF(LINF, "s3: save %d-%s state: %08x\r\n",
|
||||||
p->info.path.bus,
|
p->info.path.bus,
|
||||||
usb_dev_path(&p->info.path));
|
usb_dev_path(&p->info.path),
|
||||||
|
xdev->vbdp_devs[j].state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XHCI_MAX_DEVS; ++i) {
|
||||||
|
if (!xdev->devices[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Unbind all connected native USB devices to avoid
|
||||||
|
* dead-loop poll in libusb service during S3 resume
|
||||||
|
* which impact UOS S3 resume latency.
|
||||||
|
*/
|
||||||
|
pci_xhci_dev_destroy(xdev->devices[i], false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd &= ~(XHCI_CMD_CSS | XHCI_CMD_CRS);
|
cmd &= ~(XHCI_CMD_CSS | XHCI_CMD_CRS);
|
||||||
@ -1802,7 +1877,7 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
|
|||||||
struct usb_native_devinfo *di = NULL;
|
struct usb_native_devinfo *di = NULL;
|
||||||
struct usb_devpath *path;
|
struct usb_devpath *path;
|
||||||
uint32_t cmderr;
|
uint32_t cmderr;
|
||||||
int i, j, index;
|
int i, j;
|
||||||
|
|
||||||
UPRINTF(LINF, "pci_xhci disable slot %u\r\n", slot);
|
UPRINTF(LINF, "pci_xhci disable slot %u\r\n", slot);
|
||||||
|
|
||||||
@ -1842,27 +1917,13 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
|
|||||||
xdev->slot_allocated[slot] = false;
|
xdev->slot_allocated[slot] = false;
|
||||||
|
|
||||||
di = &udev->info;
|
di = &udev->info;
|
||||||
index = pci_xhci_get_native_port_index_by_path(xdev, &di->path);
|
|
||||||
if (index < 0) {
|
|
||||||
/*
|
|
||||||
* one possible reason for failing to find the device is
|
|
||||||
* it is plugged out during the resuming process. we
|
|
||||||
* should give the xhci_vbdp_thread an opportunity to
|
|
||||||
* try.
|
|
||||||
*/
|
|
||||||
sem_post(&xdev->vbdp_sem);
|
|
||||||
cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < XHCI_MAX_VIRT_PORTS; ++j) {
|
for (j = 0; j < XHCI_MAX_VIRT_PORTS; ++j) {
|
||||||
path = &xdev->vbdp_devs[j].path;
|
path = &xdev->vbdp_devs[j].path;
|
||||||
|
|
||||||
if (!usb_dev_path_cmp(path, &di->path))
|
if (!usb_dev_path_cmp(path, &di->path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
xdev->vbdp_devs[j].state = S3_VBDP_END;
|
xdev->vbdp_devs[j].state |= S3_VBDP_DIS_SLOT;
|
||||||
xdev->vbdp_dev_num--;
|
|
||||||
sem_post(&xdev->vbdp_sem);
|
sem_post(&xdev->vbdp_sem);
|
||||||
UPRINTF(LINF, "signal device %d-%s to connect\r\n",
|
UPRINTF(LINF, "signal device %d-%s to connect\r\n",
|
||||||
di->path.bus, usb_dev_path(&di->path));
|
di->path.bus, usb_dev_path(&di->path));
|
||||||
|
@ -592,9 +592,10 @@ usb_dev_native_toggle_if_drivers(struct usb_dev *udev, int attach)
|
|||||||
|
|
||||||
c = config->bConfigurationValue;
|
c = config->bConfigurationValue;
|
||||||
for (i = 0; i < config->bNumInterfaces; i++) {
|
for (i = 0; i < config->bNumInterfaces; i++) {
|
||||||
if (attach == 1)
|
if (attach == 1) {
|
||||||
|
usb_dev_native_toggle_if(udev, 0);
|
||||||
r = libusb_attach_kernel_driver(udev->handle, i);
|
r = libusb_attach_kernel_driver(udev->handle, i);
|
||||||
else {
|
} else {
|
||||||
if (libusb_kernel_driver_active(udev->handle, i) == 1)
|
if (libusb_kernel_driver_active(udev->handle, i) == 1)
|
||||||
r = libusb_detach_kernel_driver(udev->handle,
|
r = libusb_detach_kernel_driver(udev->handle,
|
||||||
i);
|
i);
|
||||||
|
Loading…
Reference in New Issue
Block a user