diff options
author | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2007-04-08 06:51:26 +0000 |
---|---|---|
committer | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2007-04-08 06:51:26 +0000 |
commit | 437dbd92fd7d36954c7a8ec5777d3c2fa0bbcd9d (patch) | |
tree | f91c9292860e1e0fdeb2027ab25583fa2823a415 /sys/dev/ic | |
parent | 8011445ddc68deee97ebbc93e05aa11dbe79e30f (diff) |
Add support for NCQ error recovery and enable NCQ command submission.
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/sili.c | 153 |
1 files changed, 131 insertions, 22 deletions
diff --git a/sys/dev/ic/sili.c b/sys/dev/ic/sili.c index 941f6280cec..0ffa5ba784d 100644 --- a/sys/dev/ic/sili.c +++ b/sys/dev/ic/sili.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sili.c,v 1.28 2007/04/08 04:45:40 pascoe Exp $ */ +/* $OpenBSD: sili.c,v 1.29 2007/04/08 06:51:25 pascoe Exp $ */ /* * Copyright (c) 2007 David Gwynne <dlg@openbsd.org> @@ -67,12 +67,16 @@ void sili_dmamem_free(struct sili_softc *, /* per port goo */ struct sili_ccb; +/* size of scratch space for use in error recovery. */ +#define SILI_SCRATCH_LEN 512 /* must be at least 1 sector */ + struct sili_port { struct sili_softc *sp_sc; bus_space_handle_t sp_ioh; struct sili_ccb *sp_ccbs; struct sili_dmamem *sp_cmds; + struct sili_dmamem *sp_scratch; TAILQ_HEAD(, sili_ccb) sp_free_ccbs; @@ -145,6 +149,7 @@ int sili_load(struct sili_ccb *, struct sili_sge *, int); void sili_unload(struct sili_ccb *); int sili_poll(struct sili_ccb *, int, void (*)(void *)); void sili_start(struct sili_port *, struct sili_ccb *); +int sili_read_ncq_error(struct sili_port *, int *); /* port interrupt handler */ u_int32_t sili_port_intr(struct sili_port *, u_int32_t); @@ -187,6 +192,7 @@ sili_attach(struct sili_softc *sc) aaa.aaa_minphys = minphys; aaa.aaa_nports = sc->sc_nports; aaa.aaa_ncmds = SILI_MAX_CMDS; + aaa.aaa_capability = ASAA_CAP_NCQ; sc->sc_atascsi = atascsi_attach(&sc->sc_dev, &aaa); @@ -203,8 +209,8 @@ u_int32_t sili_port_intr(struct sili_port *sp, u_int32_t slotmask) { u_int32_t is, pss_saved, pss_masked; - u_int32_t processed = 0; - int slot, need_restart = 0; + u_int32_t processed = 0, need_restart = 0; + int slot; struct sili_ccb *ccb; is = sili_pread(sp, SILI_PREG_IS); @@ -227,19 +233,14 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask) pss_saved &= SILI_PREG_PSS_ALL_SLOTS; if (is & SILI_PREG_IS_CMDERR) { - int err_slot; + int err_slot, err_code; + u_int32_t sactive = 0; bus_size_t r; - /* XXX Non-NCQ error path. */ err_slot = SILI_PREG_PCS_ACTIVE(sili_pread(sp, SILI_PREG_PCS)); + err_code = sili_pread(sp, SILI_PREG_CE); - ccb = &sp->sp_ccbs[err_slot]; - - DPRINTF(SILI_D_VERBOSE, "%s: error, code %d, slot %d, " - "active %08x\n", PORTNAME(sp), sili_pread(sp, SILI_PREG_CE), - err_slot, sp->sp_active); - - switch (sili_pread(sp, SILI_PREG_CE)) { + switch (err_code) { case SILI_PREG_CE_DEVICEERROR: case SILI_PREG_CE_DATAFISERROR: /* Extract error from command slot in LRAM. */ @@ -251,24 +252,51 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask) sp->sp_ioh, r, &ccb->ccb_xa.rfis, sizeof(struct ata_fis_d2h)); break; + case SILI_PREG_CE_SDBERROR: - /* An NCQ error? */ - if (1 /* no_ncq_commands_outstanding */) - need_restart = 1; - break; + /* No NCQ commands active? Treat as a normal error. */ + sactive = sili_pread(sp, SILI_PREG_SACT);/* XXX Pmult */ + if (sactive == 0) + break; + + /* Extract real NCQ error slot & RFIS from log page. */ + if (!sili_read_ncq_error(sp, &err_slot)) + break; + + /* failed to get error or not NCQ */ + + /* FALLTHROUGH */ default: - printf("%s: fatal error\n", PORTNAME(sp)); - break; + /* All other error types are fatal. */ + printf("%s: fatal error (%d), aborting active slots " + "(%08x) and resetting device.\n", PORTNAME(sp), + err_code, pss_saved); + while (pss_saved) { + slot = ffs(pss_saved) - 1; + pss_saved &= ~(1 << slot); + + ccb = &sp->sp_ccbs[slot]; + KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP); + ccb->ccb_xa.state = ATA_S_ERROR; + } + need_restart = SILI_PREG_PCS_DEVRESET; + goto fatal; } + DPRINTF(SILI_D_VERBOSE, "%s: %serror, code %d, slot %d, " + "active %08x\n", PORTNAME(sp), sactive ? "NCQ " : "", + err_code, err_slot, sp->sp_active); + /* Clear the failed commmand in saved PSS so cmd_done runs. */ pss_saved &= ~(1 << err_slot); + ccb = &sp->sp_ccbs[err_slot]; KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP); ccb->ccb_xa.state = ATA_S_ERROR; - need_restart = 1; + need_restart = SILI_PREG_PCS_PORTINIT; } +fatal: /* Command slot is complete if its bit in PSS is 0 but 1 in active. */ pss_masked = ~pss_saved & sp->sp_active; @@ -288,7 +316,7 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask) if (need_restart) { /* Re-enable transfers on port. */ - sili_pwrite(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTINIT); + sili_pwrite(sp, SILI_PREG_PCS, need_restart); if (!sili_pwait_eq(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTRDY, SILI_PREG_PCS_PORTRDY, 1000)) { printf("%s: couldn't restart port after error\n", @@ -328,7 +356,7 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask) } } - return processed; + return (processed); } int @@ -424,6 +452,9 @@ sili_ccb_alloc(struct sili_port *sp) SILI_PRB_ALIGN); if (sp->sp_cmds == NULL) goto free_ccbs; + sp->sp_scratch = sili_dmamem_alloc(sc, SILI_SCRATCH_LEN, PAGE_SIZE); + if (sp->sp_scratch == NULL) + goto free_cmds; bzero(sp->sp_ccbs, sizeof(struct sili_ccb) * SILI_MAX_CMDS); @@ -435,7 +466,7 @@ sili_ccb_alloc(struct sili_port *sp) if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, SILI_DMA_SEGS, MAXPHYS, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &ccb->ccb_dmamap) != 0) - goto free_cmds; + goto free_scratch; prb = ccb->ccb_cmd; ccb->ccb_xa.fis = (struct ata_fis_h2d *)&prb->fis; @@ -449,6 +480,8 @@ sili_ccb_alloc(struct sili_port *sp) return (0); +free_scratch: + sili_dmamem_free(sc, sp->sp_scratch); free_cmds: sili_dmamem_free(sc, sp->sp_cmds); free_ccbs: @@ -956,6 +989,82 @@ sili_start(struct sili_port *sp, struct sili_ccb *ccb) sili_post_indirect(sp, ccb); } +int +sili_read_ncq_error(struct sili_port *sp, int *err_slotp) +{ + struct sili_softc *sc = sp->sp_sc; + struct sili_prb_ata read_10h; + u_int64_t addr; + struct ata_fis_h2d *fis; + struct ata_log_page_10h *log; + struct sili_ccb *ccb; + int rc; + + sili_pwrite(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTINIT); + if (!sili_pwait_eq(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTRDY, + SILI_PREG_PCS_PORTRDY, 1000)) { + printf("%s: couldn't ready port during log page read\n", + PORTNAME(sp)); + return (1); + } + + /* READ LOG EXT 10h into scratch space */ + bzero(&read_10h, sizeof(read_10h)); + read_10h.control = htole16(SILI_PRB_INTERRUPT_MASK); + + addr = SILI_DMA_DVA(sp->sp_scratch); + read_10h.sgl[0].addr_lo = htole32((u_int32_t)addr); + read_10h.sgl[0].addr_hi = htole32((u_int32_t)(addr >> 32)); + read_10h.sgl[0].data_count = htole32(512); + read_10h.sgl[0].flags = htole32(SILI_SGE_TRM); + + fis = (struct ata_fis_h2d *)read_10h.fis; + fis->type = ATA_FIS_TYPE_H2D; + fis->flags = ATA_H2D_FLAGS_CMD; /* XXX fis pmp field */ + fis->command = ATA_C_READ_LOG_EXT; + fis->lba_low = 0x10; /* queued error log page (10h) */ + fis->sector_count = 1; /* number of sectors (1) */ + fis->sector_count_exp = 0; + fis->lba_mid = 0; /* starting offset */ + fis->lba_mid_exp = 0; + fis->device = 0; + + bus_dmamap_sync(sc->sc_dmat, SILI_DMA_MAP(sp->sp_scratch), 0, + 512, BUS_DMASYNC_PREREAD); + + /* issue read and poll for completion */ + sili_post_direct(sp, 0, &read_10h, sizeof(read_10h)); + rc = sili_pwait_eq(sp, SILI_PREG_PSS, (1 << 0), 0, 1000); + + bus_dmamap_sync(sc->sc_dmat, SILI_DMA_MAP(sp->sp_scratch), 0, + 512, BUS_DMASYNC_POSTREAD); + + if (!rc) { + DPRINTF(SILI_D_VERBOSE, "%s: timed out while waiting for log " + "page read\n", PORTNAME(sp)); + return (1); + } + + /* Extract failed register set and tags from the scratch space. */ + log = (struct ata_log_page_10h *)SILI_DMA_KVA(sp->sp_scratch); + if (ISSET(log->err_regs.type, ATA_LOG_10H_TYPE_NOTQUEUED)) { + /* Not queued bit was set - wasn't an NCQ error? */ + printf("%s: read NCQ error page, but not an NCQ error?\n", + PORTNAME(sp)); + return (1); + } + + /* Copy back the log record as a D2H register FIS. */ + *err_slotp = log->err_regs.type & ATA_LOG_10H_TYPE_TAG_MASK; + + ccb = &sp->sp_ccbs[*err_slotp]; + memcpy(&ccb->ccb_xa.rfis, &log->err_regs, sizeof(struct ata_fis_d2h)); + ccb->ccb_xa.rfis.type = ATA_FIS_TYPE_D2H; + ccb->ccb_xa.rfis.flags = 0; + + return (0); +} + struct ata_xfer * sili_ata_get_xfer(void *xsc, int port) { |