Here is what I have currently to make the Lemote Yeeloong resume without
that long 31 second delay.
What happens is that the reset code waits until each valid channel
reports itself as ready, giving up after 31 seconds.
However, as hinted by the comments in wdactivate(), we need to reset
twice, and the first reset will only warm up the controller, not the
drives. So we do not have to wait here, this only matters during the
second reset.
The wd_get_params() added after the reset was a leftover from dlg@'s
tinkering, and is definitely necessary to prevent the dreaded `spurious
interrupt' situation after reset.
I tried to make the diff as non-intrusive as possible. Which means other
wd attachments than wd@pciide and wd@atapiscsi ought to be unaffected by
this change.
Please test this on x86 systems as well and let me know if it breaks
suspend/resume (or even regular operation).
Miod
Index: dev/ata/atavar.h
===================================================================
RCS file: /cvs/src/sys/dev/ata/atavar.h,v
retrieving revision 1.19
diff -u -p -r1.19 atavar.h
--- dev/ata/atavar.h 23 Jul 2010 07:47:13 -0000 1.19
+++ dev/ata/atavar.h 7 Apr 2011 20:09:22 -0000
@@ -164,7 +164,7 @@ void wdc_probe_caps(struct ata_drive_dat
void wdc_print_caps(struct ata_drive_datas*);
int wdc_downgrade_mode(struct ata_drive_datas*);
-void wdc_reset_channel(struct ata_drive_datas *);
+void wdc_reset_channel(struct ata_drive_datas *, int);
int wdc_ata_addref(struct ata_drive_datas *);
void wdc_ata_delref(struct ata_drive_datas *);
Index: dev/ata/wd.c
===================================================================
RCS file: /cvs/src/sys/dev/ata/wd.c,v
retrieving revision 1.98
diff -u -p -r1.98 wd.c
--- dev/ata/wd.c 5 Apr 2011 19:57:40 -0000 1.98
+++ dev/ata/wd.c 7 Apr 2011 20:09:22 -0000
@@ -355,13 +355,14 @@ wdactivate(struct device *self, int act)
/*
* Do two resets separated by a small delay. The
* first wakes the controller, the second resets
- * the channel
+ * the channel.
*/
wdc_disable_intr(wd->drvp->chnl_softc);
- wdc_reset_channel(wd->drvp);
+ wdc_reset_channel(wd->drvp, 1);
delay(10000);
- wdc_reset_channel(wd->drvp);
+ wdc_reset_channel(wd->drvp, 0);
wdc_enable_intr(wd->drvp->chnl_softc);
+ wd_get_params(wd, at_poll, &wd->sc_params);
break;
}
return (rv);
@@ -582,7 +583,7 @@ wddone(void *v)
sizeof buf);
retry:
/* Just reset and retry. Can we do more ? */
- wdc_reset_channel(wd->drvp);
+ wdc_reset_channel(wd->drvp, 0);
diskerr(bp, "wd", errbuf, LOG_PRINTF,
wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label);
if (wd->retries++ < WDIORETRIES) {
Index: dev/atapiscsi/atapiscsi.c
===================================================================
RCS file: /cvs/src/sys/dev/atapiscsi/atapiscsi.c,v
retrieving revision 1.96
diff -u -p -r1.96 atapiscsi.c
--- dev/atapiscsi/atapiscsi.c 18 Nov 2010 21:13:19 -0000 1.96
+++ dev/atapiscsi/atapiscsi.c 7 Apr 2011 20:09:22 -0000
@@ -311,9 +311,9 @@ atapiscsi_activate(struct device *self,
* the channel
*/
wdc_disable_intr(chp);
- wdc_reset_channel(drvp);
+ wdc_reset_channel(drvp, 1);
delay(10000);
- wdc_reset_channel(drvp);
+ wdc_reset_channel(drvp, 0);
wdc_enable_intr(chp);
break;
}
@@ -1626,7 +1626,7 @@ wdc_atapi_reset_2(chp, xfer, timeout, re
chp->wdc->sc_dev.dv_xname, chp->channel,
xfer->drive);
sc_xfer->error = XS_SELTIMEOUT;
- wdc_reset_channel(drvp);
+ wdc_reset_channel(drvp, 0);
xfer->next = wdc_atapi_done;
return;
Index: dev/ic/wdc.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/wdc.c,v
retrieving revision 1.111
diff -u -p -r1.111 wdc.c
--- dev/ic/wdc.c 5 Apr 2011 19:57:40 -0000 1.111
+++ dev/ic/wdc.c 7 Apr 2011 20:09:22 -0000
@@ -934,7 +934,7 @@ wdcintr(void *arg)
/* Put all disk in RESET state */
void
-wdc_reset_channel(struct ata_drive_datas *drvp)
+wdc_reset_channel(struct ata_drive_datas *drvp, int nowait)
{
struct channel_softc *chp = drvp->chnl_softc;
int drive;
@@ -942,14 +942,14 @@ wdc_reset_channel(struct ata_drive_datas
WDCDEBUG_PRINT(("ata_reset_channel %s:%d for drive %d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive),
DEBUG_FUNCS);
- (void) wdcreset(chp, VERBOSE);
+ (void) wdcreset(chp, nowait ? NOWAIT : VERBOSE);
for (drive = 0; drive < 2; drive++) {
chp->ch_drive[drive].state = 0;
}
}
int
-wdcreset(struct channel_softc *chp, int verb)
+wdcreset(struct channel_softc *chp, int flags)
{
int drv_mask1, drv_mask2;
@@ -958,10 +958,14 @@ wdcreset(struct channel_softc *chp, int
chp->wdc->reset(chp);
+ if (flags & NOWAIT)
+ return 0;
+
drv_mask1 = (chp->ch_drive[0].drive_flags & DRIVE) ? 0x01:0x00;
drv_mask1 |= (chp->ch_drive[1].drive_flags & DRIVE) ? 0x02:0x00;
drv_mask2 = __wdcwait_reset(chp, drv_mask1);
- if (verb && drv_mask2 != drv_mask1) {
+
+ if ((flags & VERBOSE) && drv_mask2 != drv_mask1) {
printf("%s channel %d: reset failed for",
chp->wdc->sc_dev.dv_xname, chp->channel);
if ((drv_mask1 & 0x01) != 0 && (drv_mask2 & 0x01) == 0)
@@ -1554,7 +1558,7 @@ wdc_downgrade_mode(struct ata_drive_data
wdc->set_modes(chp);
/* reset the channel, which will schedule all drives for setup */
- wdc_reset_channel(drvp);
+ wdc_reset_channel(drvp, 0);
return 1;
}
Index: dev/ic/wdcvar.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/wdcvar.h,v
retrieving revision 1.47
diff -u -p -r1.47 wdcvar.h
--- dev/ic/wdcvar.h 5 Apr 2011 19:57:40 -0000 1.47
+++ dev/ic/wdcvar.h 7 Apr 2011 20:09:22 -0000
@@ -271,8 +271,9 @@ struct wdc_xfer *wdc_get_xfer(int); /* i
void wdc_free_xfer(struct channel_softc *, struct wdc_xfer *);
void wdcstart(struct channel_softc *);
int wdcreset(struct channel_softc *, int);
-#define VERBOSE 1
-#define SILENT 0 /* wdcreset will not print errors */
+#define NOWAIT 0x02
+#define VERBOSE 0x01
+#define SILENT 0x00 /* wdcreset will not print errors */
int wdc_wait_for_status(struct channel_softc *, int, int, int);
int wdc_dmawait(struct channel_softc *, struct wdc_xfer *, int);
void wdcbit_bucket(struct channel_softc *, int);