summaryrefslogtreecommitdiff
path: root/sys/dev/ic
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 06:51:26 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 06:51:26 +0000
commit437dbd92fd7d36954c7a8ec5777d3c2fa0bbcd9d (patch)
treef91c9292860e1e0fdeb2027ab25583fa2823a415 /sys/dev/ic
parent8011445ddc68deee97ebbc93e05aa11dbe79e30f (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.c153
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)
{