mirror of https://gitee.com/openkylin/linux.git
wusb: whci-hcd shouldn't do ASL/PZL updates while channel is inactive
ASL/PZL updates while the WUSB channel is inactive (i.e., the PZL and ASL are stopped) may not complete. This causes hangs when removing the whci-hcd module if a device is still connected (removing the device does an endpoint_disable which results in an ASL update to remove the qset). If the WUSB channel is inactive the update can simply be skipped as the WHC doesn't care about the state of the ASL/PZL. Signed-off-by: David Vrabel <david.vrabel@csr.com>
This commit is contained in:
parent
65d76f3682
commit
56968d0c1a
|
@ -179,11 +179,26 @@ void asl_stop(struct whc *whc)
|
|||
1000, "stop ASL");
|
||||
}
|
||||
|
||||
/**
|
||||
* asl_update - request an ASL update and wait for the hardware to be synced
|
||||
* @whc: the WHCI HC
|
||||
* @wusbcmd: WUSBCMD value to start the update.
|
||||
*
|
||||
* If the WUSB HC is inactive (i.e., the ASL is stopped) then the
|
||||
* update must be skipped as the hardware may not respond to update
|
||||
* requests.
|
||||
*/
|
||||
void asl_update(struct whc *whc, uint32_t wusbcmd)
|
||||
{
|
||||
whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
|
||||
wait_event(whc->async_list_wq,
|
||||
(le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0);
|
||||
struct wusbhc *wusbhc = &whc->wusbhc;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
if (wusbhc->active) {
|
||||
whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
|
||||
wait_event(whc->async_list_wq,
|
||||
(le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0);
|
||||
}
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -195,11 +195,26 @@ void pzl_stop(struct whc *whc)
|
|||
1000, "stop PZL");
|
||||
}
|
||||
|
||||
/**
|
||||
* pzl_update - request a PZL update and wait for the hardware to be synced
|
||||
* @whc: the WHCI HC
|
||||
* @wusbcmd: WUSBCMD value to start the update.
|
||||
*
|
||||
* If the WUSB HC is inactive (i.e., the PZL is stopped) then the
|
||||
* update must be skipped as the hardware may not respond to update
|
||||
* requests.
|
||||
*/
|
||||
void pzl_update(struct whc *whc, uint32_t wusbcmd)
|
||||
{
|
||||
whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
|
||||
wait_event(whc->periodic_list_wq,
|
||||
(le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0);
|
||||
struct wusbhc *wusbhc = &whc->wusbhc;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
if (wusbhc->active) {
|
||||
whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
|
||||
wait_event(whc->periodic_list_wq,
|
||||
(le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0);
|
||||
}
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
}
|
||||
|
||||
static void update_pzl_hw_view(struct whc *whc)
|
||||
|
|
|
@ -484,21 +484,15 @@ static void __wusbhc_keep_alive(struct wusbhc *wusbhc)
|
|||
*/
|
||||
static void wusbhc_keep_alive_run(struct work_struct *ws)
|
||||
{
|
||||
struct delayed_work *dw =
|
||||
container_of(ws, struct delayed_work, work);
|
||||
struct wusbhc *wusbhc =
|
||||
container_of(dw, struct wusbhc, keep_alive_timer);
|
||||
struct delayed_work *dw = container_of(ws, struct delayed_work, work);
|
||||
struct wusbhc *wusbhc = container_of(dw, struct wusbhc, keep_alive_timer);
|
||||
|
||||
d_fnstart(5, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
|
||||
if (wusbhc->active) {
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
__wusbhc_keep_alive(wusbhc);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
|
||||
(wusbhc->trust_timeout * CONFIG_HZ)/1000/2);
|
||||
}
|
||||
d_fnend(5, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
|
||||
return;
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
__wusbhc_keep_alive(wusbhc);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
|
||||
msecs_to_jiffies(wusbhc->trust_timeout / 2));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -159,6 +159,27 @@ void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm);
|
||||
|
||||
static int wusbhc_mmc_start(struct wusbhc *wusbhc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
ret = wusbhc->start(wusbhc);
|
||||
if (ret >= 0)
|
||||
wusbhc->active = 1;
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wusbhc_mmc_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
wusbhc->active = 0;
|
||||
wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* wusbhc_start - start transmitting MMCs and accepting connections
|
||||
* @wusbhc: the HC to start
|
||||
|
@ -198,12 +219,12 @@ int wusbhc_start(struct wusbhc *wusbhc)
|
|||
dev_err(dev, "Cannot set DNTS parameters: %d\n", result);
|
||||
goto error_set_num_dnts;
|
||||
}
|
||||
result = wusbhc->start(wusbhc);
|
||||
result = wusbhc_mmc_start(wusbhc);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error starting wusbch: %d\n", result);
|
||||
goto error_wusbhc_start;
|
||||
}
|
||||
wusbhc->active = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
error_wusbhc_start:
|
||||
|
@ -225,15 +246,11 @@ int wusbhc_start(struct wusbhc *wusbhc)
|
|||
*/
|
||||
void wusbhc_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
if (wusbhc->active) {
|
||||
wusbhc->active = 0;
|
||||
wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS);
|
||||
wusbhc_sec_stop(wusbhc);
|
||||
wusbhc_devconnect_stop(wusbhc);
|
||||
wusbhc_rsv_terminate(wusbhc);
|
||||
}
|
||||
wusbhc_mmc_stop(wusbhc);
|
||||
wusbhc_sec_stop(wusbhc);
|
||||
wusbhc_devconnect_stop(wusbhc);
|
||||
wusbhc_rsv_terminate(wusbhc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wusbhc_stop);
|
||||
|
||||
/*
|
||||
* Set/reset/update a new CHID
|
||||
|
|
Loading…
Reference in New Issue