diff options
-rw-r--r-- | sys/dev/mvs/mvs.c | 100 | ||||
-rw-r--r-- | sys/dev/mvs/mvs.h | 3 |
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 */ |