aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@csr.com>2008-11-25 09:23:40 -0500
committerDavid Vrabel <david.vrabel@csr.com>2008-11-25 09:23:40 -0500
commit56968d0c1a920eb165c06318f5c458724e1df0af (patch)
tree9516fdc8ef8bc67bf6a51a1e4940d3ae59008dfa /drivers
parent65d76f368295973a35d195c9b13053502a67b6bc (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/whci/asl.c21
-rw-r--r--drivers/usb/host/whci/pzl.c21
-rw-r--r--drivers/usb/wusbcore/devconnect.c24
-rw-r--r--drivers/usb/wusbcore/mmc.c37
4 files changed, 72 insertions, 31 deletions
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c
index 4d7078e50572..ba99a7a3f81a 100644
--- a/drivers/usb/host/whci/asl.c
+++ b/drivers/usb/host/whci/asl.c
@@ -179,11 +179,26 @@ void asl_stop(struct whc *whc)
179 1000, "stop ASL"); 179 1000, "stop ASL");
180} 180}
181 181
182/**
183 * asl_update - request an ASL update and wait for the hardware to be synced
184 * @whc: the WHCI HC
185 * @wusbcmd: WUSBCMD value to start the update.
186 *
187 * If the WUSB HC is inactive (i.e., the ASL is stopped) then the
188 * update must be skipped as the hardware may not respond to update
189 * requests.
190 */
182void asl_update(struct whc *whc, uint32_t wusbcmd) 191void asl_update(struct whc *whc, uint32_t wusbcmd)
183{ 192{
184 whc_write_wusbcmd(whc, wusbcmd, wusbcmd); 193 struct wusbhc *wusbhc = &whc->wusbhc;
185 wait_event(whc->async_list_wq, 194
186 (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0); 195 mutex_lock(&wusbhc->mutex);
196 if (wusbhc->active) {
197 whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
198 wait_event(whc->async_list_wq,
199 (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0);
200 }
201 mutex_unlock(&wusbhc->mutex);
187} 202}
188 203
189/** 204/**
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
index 8d62df0c330b..34d3a0aeab2b 100644
--- a/drivers/usb/host/whci/pzl.c
+++ b/drivers/usb/host/whci/pzl.c
@@ -195,11 +195,26 @@ void pzl_stop(struct whc *whc)
195 1000, "stop PZL"); 195 1000, "stop PZL");
196} 196}
197 197
198/**
199 * pzl_update - request a PZL update and wait for the hardware to be synced
200 * @whc: the WHCI HC
201 * @wusbcmd: WUSBCMD value to start the update.
202 *
203 * If the WUSB HC is inactive (i.e., the PZL is stopped) then the
204 * update must be skipped as the hardware may not respond to update
205 * requests.
206 */
198void pzl_update(struct whc *whc, uint32_t wusbcmd) 207void pzl_update(struct whc *whc, uint32_t wusbcmd)
199{ 208{
200 whc_write_wusbcmd(whc, wusbcmd, wusbcmd); 209 struct wusbhc *wusbhc = &whc->wusbhc;
201 wait_event(whc->periodic_list_wq, 210
202 (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0); 211 mutex_lock(&wusbhc->mutex);
212 if (wusbhc->active) {
213 whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
214 wait_event(whc->periodic_list_wq,
215 (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0);
216 }
217 mutex_unlock(&wusbhc->mutex);
203} 218}
204 219
205static void update_pzl_hw_view(struct whc *whc) 220static void update_pzl_hw_view(struct whc *whc)
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c
index 08a1ec903867..26cbc89ea281 100644
--- a/drivers/usb/wusbcore/devconnect.c
+++ b/drivers/usb/wusbcore/devconnect.c
@@ -484,21 +484,15 @@ static void __wusbhc_keep_alive(struct wusbhc *wusbhc)
484 */ 484 */
485static void wusbhc_keep_alive_run(struct work_struct *ws) 485static void wusbhc_keep_alive_run(struct work_struct *ws)
486{ 486{
487 struct delayed_work *dw = 487 struct delayed_work *dw = container_of(ws, struct delayed_work, work);
488 container_of(ws, struct delayed_work, work); 488 struct wusbhc *wusbhc = container_of(dw, struct wusbhc, keep_alive_timer);
489 struct wusbhc *wusbhc = 489
490 container_of(dw, struct wusbhc, keep_alive_timer); 490 mutex_lock(&wusbhc->mutex);
491 491 __wusbhc_keep_alive(wusbhc);
492 d_fnstart(5, wusbhc->dev, "(wusbhc %p)\n", wusbhc); 492 mutex_unlock(&wusbhc->mutex);
493 if (wusbhc->active) { 493
494 mutex_lock(&wusbhc->mutex); 494 queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
495 __wusbhc_keep_alive(wusbhc); 495 msecs_to_jiffies(wusbhc->trust_timeout / 2));
496 mutex_unlock(&wusbhc->mutex);
497 queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
498 (wusbhc->trust_timeout * CONFIG_HZ)/1000/2);
499 }
500 d_fnend(5, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
501 return;
502} 496}
503 497
504/* 498/*
diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c
index 5463ecebafdf..3b52161e6e9c 100644
--- a/drivers/usb/wusbcore/mmc.c
+++ b/drivers/usb/wusbcore/mmc.c
@@ -159,6 +159,27 @@ found:
159} 159}
160EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm); 160EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm);
161 161
162static int wusbhc_mmc_start(struct wusbhc *wusbhc)
163{
164 int ret;
165
166 mutex_lock(&wusbhc->mutex);
167 ret = wusbhc->start(wusbhc);
168 if (ret >= 0)
169 wusbhc->active = 1;
170 mutex_unlock(&wusbhc->mutex);
171
172 return ret;
173}
174
175static void wusbhc_mmc_stop(struct wusbhc *wusbhc)
176{
177 mutex_lock(&wusbhc->mutex);
178 wusbhc->active = 0;
179 wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS);
180 mutex_unlock(&wusbhc->mutex);
181}
182
162/* 183/*
163 * wusbhc_start - start transmitting MMCs and accepting connections 184 * wusbhc_start - start transmitting MMCs and accepting connections
164 * @wusbhc: the HC to start 185 * @wusbhc: the HC to start
@@ -198,12 +219,12 @@ int wusbhc_start(struct wusbhc *wusbhc)
198 dev_err(dev, "Cannot set DNTS parameters: %d\n", result); 219 dev_err(dev, "Cannot set DNTS parameters: %d\n", result);
199 goto error_set_num_dnts; 220 goto error_set_num_dnts;
200 } 221 }
201 result = wusbhc->start(wusbhc); 222 result = wusbhc_mmc_start(wusbhc);
202 if (result < 0) { 223 if (result < 0) {
203 dev_err(dev, "error starting wusbch: %d\n", result); 224 dev_err(dev, "error starting wusbch: %d\n", result);
204 goto error_wusbhc_start; 225 goto error_wusbhc_start;
205 } 226 }
206 wusbhc->active = 1; 227
207 return 0; 228 return 0;
208 229
209error_wusbhc_start: 230error_wusbhc_start:
@@ -225,15 +246,11 @@ error_rsv_establish:
225 */ 246 */
226void wusbhc_stop(struct wusbhc *wusbhc) 247void wusbhc_stop(struct wusbhc *wusbhc)
227{ 248{
228 if (wusbhc->active) { 249 wusbhc_mmc_stop(wusbhc);
229 wusbhc->active = 0; 250 wusbhc_sec_stop(wusbhc);
230 wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS); 251 wusbhc_devconnect_stop(wusbhc);
231 wusbhc_sec_stop(wusbhc); 252 wusbhc_rsv_terminate(wusbhc);
232 wusbhc_devconnect_stop(wusbhc);
233 wusbhc_rsv_terminate(wusbhc);
234 }
235} 253}
236EXPORT_SYMBOL_GPL(wusbhc_stop);
237 254
238/* 255/*
239 * Set/reset/update a new CHID 256 * Set/reset/update a new CHID