summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/mvs/mvs.c100
-rw-r--r--sys/dev/mvs/mvs.h3
2 files changed, 86 insertions, 17 deletions
diff --git a/sys/dev/mvs/mvs.c b/sys/dev/mvs/mvs.c
index 9a84e82..0ab60cd 100644
--- a/sys/dev/mvs/mvs.c
+++ b/sys/dev/mvs/mvs.c
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/ata.h>
#include <sys/bus.h>
+#include <sys/conf.h>
#include <sys/endian.h>
#include <sys/malloc.h>
#include <sys/lock.h>
@@ -75,7 +76,7 @@ static int mvs_sata_phy_reset(device_t dev);
static int mvs_wait(device_t dev, u_int s, u_int c, int t);
static void mvs_tfd_read(device_t dev, union ccb *ccb);
static void mvs_tfd_write(device_t dev, union ccb *ccb);
-static void mvs_legacy_intr(device_t dev);
+static void mvs_legacy_intr(device_t dev, int poll);
static void mvs_crbq_intr(device_t dev);
static void mvs_begin_transaction(device_t dev, union ccb *ccb);
static void mvs_legacy_execute_transaction(struct mvs_slot *slot);
@@ -125,6 +126,7 @@ mvs_ch_attach(device_t dev)
device_get_unit(dev), "pm_level", &ch->pm_level);
if (ch->pm_level > 3)
callout_init_mtx(&ch->pm_timer, &ch->mtx, 0);
+ callout_init_mtx(&ch->reset_timer, &ch->mtx, 0);
resource_int_value(device_get_name(dev),
device_get_unit(dev), "sata_rev", &sata_rev);
for (i = 0; i < 16; i++) {
@@ -218,6 +220,11 @@ mvs_ch_detach(device_t dev)
mtx_lock(&ch->mtx);
xpt_async(AC_LOST_DEVICE, ch->path, NULL);
+ /* Forget about reset. */
+ if (ch->resetting) {
+ ch->resetting = 0;
+ xpt_release_simq(ch->sim, TRUE);
+ }
xpt_free_path(ch->path);
xpt_bus_deregister(cam_sim_path(ch->sim));
cam_sim_free(ch->sim, /*free_devq*/TRUE);
@@ -225,6 +232,7 @@ mvs_ch_detach(device_t dev)
if (ch->pm_level > 3)
callout_drain(&ch->pm_timer);
+ callout_drain(&ch->reset_timer);
bus_teardown_intr(dev, ch->r_irq, ch->ih);
bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq);
@@ -286,6 +294,12 @@ mvs_ch_suspend(device_t dev)
xpt_freeze_simq(ch->sim, 1);
while (ch->oslots)
msleep(ch, &ch->mtx, PRIBIO, "mvssusp", hz/100);
+ /* Forget about reset. */
+ if (ch->resetting) {
+ ch->resetting = 0;
+ callout_stop(&ch->reset_timer);
+ xpt_release_simq(ch->sim, TRUE);
+ }
mvs_ch_deinit(dev);
mtx_unlock(&ch->mtx);
return (0);
@@ -804,7 +818,7 @@ mvs_ch_intr(void *data)
}
/* Legacy mode device interrupt. */
if ((arg->cause & 2) && !edma)
- mvs_legacy_intr(dev);
+ mvs_legacy_intr(dev, arg->cause & 4);
}
static uint8_t
@@ -823,7 +837,7 @@ mvs_getstatus(device_t dev, int clear)
}
static void
-mvs_legacy_intr(device_t dev)
+mvs_legacy_intr(device_t dev, int poll)
{
struct mvs_channel *ch = device_get_softc(dev);
struct mvs_slot *slot = &ch->slot[0]; /* PIO is always in slot 0. */
@@ -841,6 +855,8 @@ mvs_legacy_intr(device_t dev)
port = ccb->ccb_h.target_id & 0x0f;
/* Wait a bit for late !BUSY status update. */
if (status & ATA_S_BUSY) {
+ if (poll)
+ return;
DELAY(100);
if ((status = mvs_getstatus(dev, 1)) & ATA_S_BUSY) {
DELAY(1000);
@@ -1317,7 +1333,7 @@ mvs_legacy_execute_transaction(struct mvs_slot *slot)
DELAY(10);
ccb->ataio.res.status = ATA_INB(ch->r_mem, ATA_STATUS);
} while (ccb->ataio.res.status & ATA_S_BUSY && timeout--);
- mvs_legacy_intr(dev);
+ mvs_legacy_intr(dev, 1);
return;
}
ch->donecount = 0;
@@ -1910,11 +1926,13 @@ mvs_wait(device_t dev, u_int s, u_int c, int t)
uint8_t st;
while (((st = mvs_getstatus(dev, 0)) & (s | c)) != s) {
- DELAY(1000);
- if (timeout++ > t) {
- device_printf(dev, "Wait status %02x\n", st);
+ if (timeout >= t) {
+ if (t != 0)
+ device_printf(dev, "Wait status %02x\n", st);
return (-1);
}
+ DELAY(1000);
+ timeout++;
}
return (timeout);
}
@@ -1937,6 +1955,35 @@ mvs_requeue_frozen(device_t dev)
}
static void
+mvs_reset_to(void *arg)
+{
+ device_t dev = arg;
+ struct mvs_channel *ch = device_get_softc(dev);
+ int t;
+
+ if (ch->resetting == 0)
+ return;
+ ch->resetting--;
+ if ((t = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, 0)) >= 0) {
+ if (bootverbose) {
+ device_printf(dev,
+ "MVS reset: device ready after %dms\n",
+ (310 - ch->resetting) * 100);
+ }
+ ch->resetting = 0;
+ xpt_release_simq(ch->sim, TRUE);
+ return;
+ }
+ if (ch->resetting == 0) {
+ device_printf(dev,
+ "MVS reset: device not ready after 31000ms\n");
+ xpt_release_simq(ch->sim, TRUE);
+ return;
+ }
+ callout_schedule(&ch->reset_timer, hz / 10);
+}
+
+static void
mvs_reset(device_t dev)
{
struct mvs_channel *ch = device_get_softc(dev);
@@ -1945,6 +1992,12 @@ mvs_reset(device_t dev)
xpt_freeze_simq(ch->sim, 1);
if (bootverbose)
device_printf(dev, "MVS reset...\n");
+ /* Forget about previous reset. */
+ if (ch->resetting) {
+ ch->resetting = 0;
+ callout_stop(&ch->reset_timer);
+ xpt_release_simq(ch->sim, TRUE);
+ }
/* Requeue freezed command. */
mvs_requeue_frozen(dev);
/* Kill the engine and requeue all running commands. */
@@ -1969,6 +2022,7 @@ mvs_reset(device_t dev)
ch->eslots = 0;
ch->toslots = 0;
ch->fatalerr = 0;
+ ch->fake_busy = 0;
/* Tell the XPT about the event */
xpt_async(AC_BUS_RESET, ch->path, NULL);
ATA_OUTL(ch->r_mem, EDMA_IEM, 0);
@@ -1978,8 +2032,7 @@ mvs_reset(device_t dev)
/* Reset and reconnect PHY, */
if (!mvs_sata_phy_reset(dev)) {
if (bootverbose)
- device_printf(dev,
- "MVS reset done: phy reset found no device\n");
+ device_printf(dev, "MVS reset: device not found\n");
ch->devices = 0;
ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff);
ATA_OUTL(ch->r_mem, EDMA_IEC, 0);
@@ -1987,18 +2040,26 @@ mvs_reset(device_t dev)
xpt_release_simq(ch->sim, TRUE);
return;
}
+ if (bootverbose)
+ device_printf(dev, "MVS reset: device found\n");
/* Wait for clearing busy status. */
- if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, 15000)) < 0)
- device_printf(dev, "device is not ready\n");
- else if (bootverbose)
- device_printf(dev, "ready wait time=%dms\n", i);
+ if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ,
+ dumping ? 31000 : 0)) < 0) {
+ if (dumping) {
+ device_printf(dev,
+ "MVS reset: device not ready after 31000ms\n");
+ } else
+ ch->resetting = 310;
+ } else if (bootverbose)
+ device_printf(dev, "MVS reset: device ready after %dms\n", i);
ch->devices = 1;
ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff);
ATA_OUTL(ch->r_mem, EDMA_IEC, 0);
ATA_OUTL(ch->r_mem, EDMA_IEM, ~EDMA_IE_TRANSIENT);
- if (bootverbose)
- device_printf(dev, "MVS reset done: device found\n");
- xpt_release_simq(ch->sim, TRUE);
+ if (ch->resetting)
+ callout_reset(&ch->reset_timer, hz / 10, mvs_reset_to, dev);
+ else
+ xpt_release_simq(ch->sim, TRUE);
}
static void
@@ -2307,7 +2368,12 @@ mvspoll(struct cam_sim *sim)
struct mvs_intr_arg arg;
arg.arg = ch->dev;
- arg.cause = 2; /* XXX */
+ arg.cause = 2 | 4; /* XXX */
mvs_ch_intr(&arg);
+ if (ch->resetting != 0 &&
+ (--ch->resetpolldiv <= 0 || !callout_pending(&ch->reset_timer))) {
+ ch->resetpolldiv = 1000;
+ mvs_reset_to(ch->dev);
+ }
}
diff --git a/sys/dev/mvs/mvs.h b/sys/dev/mvs/mvs.h
index 1d26722..d634003 100644
--- a/sys/dev/mvs/mvs.h
+++ b/sys/dev/mvs/mvs.h
@@ -561,6 +561,8 @@ struct mvs_channel {
int fatalerr; /* Fatal error happend */
int lastslot; /* Last used slot */
int taggedtarget; /* Last tagged target */
+ int resetting; /* Hard-reset in progress. */
+ int resetpolldiv; /* Hard-reset poll divider. */
int out_idx; /* Next written CRQB */
int in_idx; /* Next read CRPB */
u_int transfersize; /* PIO transfer size */
@@ -569,6 +571,7 @@ struct mvs_channel {
u_int fake_busy; /* Fake busy bit after command submission */
union ccb *frozen; /* Frozen command */
struct callout pm_timer; /* Power management events */
+ struct callout reset_timer; /* Hard-reset timeout */
struct mvs_device user[16]; /* User-specified settings */
struct mvs_device curr[16]; /* Current settings */
OpenPOWER on IntegriCloud