Date: Sat, 17 Apr 2010 17:06:31 GMT From: Alexander Motin <mav@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 177029 for review Message-ID: <201004171706.o3HH6VMl061033@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@177029?ac=10 Change 177029 by mav@mav_mavtest on 2010/04/17 17:05:49 Make ATAPI work (in PIO mode for now). Fix 8-port adapters support. Affected files ... .. //depot/projects/scottl-camlock/src/sys/dev/mvs/mvs.c#3 edit .. //depot/projects/scottl-camlock/src/sys/dev/mvs/mvs.h#3 edit Differences ... ==== //depot/projects/scottl-camlock/src/sys/dev/mvs/mvs.c#3 (text+ko) ==== @@ -162,6 +162,15 @@ ctlr->quirks = mvs_ids[i].quirks; resource_int_value(device_get_name(dev), device_get_unit(dev), "ccc", &ctlr->ccc); + ctlr->cccc = 8; + resource_int_value(device_get_name(dev), + device_get_unit(dev), "cccc", &ctlr->cccc); + if (ctlr->ccc == 0 || ctlr->cccc == 0) { + ctlr->ccc = 0; + ctlr->cccc = 0; + } + if (ctlr->ccc > 100) + ctlr->ccc = 100; /* We should have a memory BAR(0). */ ctlr->r_rid = PCIR_BAR(0); if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, @@ -253,24 +262,19 @@ for (i = 0; i < ctlr->channels / 4; i++) ATA_OUTL(ctlr->r_mem, HC_BASE(i) + HC_IC, 0x00000000); /* Configure CCC */ -/* +#if 0 if (ctlr->ccc) { - ATA_OUTL(ctlr->r_mem, MVS_CCCP, ATA_INL(ctlr->r_mem, MVS_PI)); - ATA_OUTL(ctlr->r_mem, MVS_CCCC, - (ctlr->ccc << MVS_CCCC_TV_SHIFT) | - (4 << MVS_CCCC_CC_SHIFT) | - MVS_CCCC_EN); - ctlr->cccv = (ATA_INL(ctlr->r_mem, MVS_CCCC) & - MVS_CCCC_INT_MASK) >> MVS_CCCC_INT_SHIFT; + ATA_OUTL(ctlr->r_mem, HC_ICT, ctlr->cccc & HC_ICT_SAICOALT_MASK); + ATA_OUTL(ctlr->r_mem, HC_ITT, (ctlr->ccc * 150000) & HC_ITT_SAITMTH_MASK); if (bootverbose) { device_printf(dev, - "CCC with %dms/4cmd enabled on vector %d\n", - ctlr->ccc, ctlr->cccv); + "CCC with %dms/%dcmd enabled\n", + ctlr->ccc, ctlr->cccc); } } -*/ +#endif /* Enable chip interrupts */ - ATA_OUTL(ctlr->r_mem, CHIP_MIM, IC_HC0 | IC_HC1 | IC_ALL_PORTS_COAL_DONE); + ATA_OUTL(ctlr->r_mem, CHIP_MIM, IC_HC0 | IC_HC1 /*| IC_ALL_PORTS_COAL_DONE*/); /* Enable PCI interrupts */ ATA_OUTL(ctlr->r_mem, CHIP_PCIIM, 0x007fffff); return (0); @@ -356,6 +360,7 @@ ic >>= 1; if ((ic & IC_HC0) == 0) { p += 3; + ic >>= 8; continue; } aic = 0; @@ -525,7 +530,7 @@ for (i = 0; i < 16; i++) { ch->user[i].revision = sata_rev; ch->user[i].mode = 0; - ch->user[i].bytecount = 8192; + ch->user[i].bytecount = (ch->quirks & MVS_Q_GENIIE) ? 8192 : 2048; ch->user[i].tags = MVS_MAX_SLOTS; ch->curr[i] = ch->user[i]; } @@ -837,6 +842,7 @@ } ch->curr_mode = mode; ch->fbs_enabled = 0; + ch->fake_busy = 0; if (mode == MVS_EDMA_OFF) return; /* Configure new mode. */ @@ -1141,77 +1147,196 @@ #endif } +static uint8_t +mvs_getstatus(device_t dev, int clear) +{ + struct mvs_channel *ch = device_get_softc(dev); + uint8_t status = ATA_INB(ch->r_mem, clear ? ATA_STATUS : ATA_ALTSTAT); + + if (ch->fake_busy) { + if (status & (ATA_S_BUSY | ATA_S_DRQ | ATA_S_ERROR)) + ch->fake_busy = 0; + else + status |= ATA_S_BUSY; + } + return (status); +} + static void mvs_legacy_intr(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); union ccb *ccb = ch->slot[0].ccb; enum mvs_err_type et = MVS_ERR_NONE; - uint8_t status; + int port = ccb->ccb_h.target_id & 0x0f; + u_int length; + uint8_t status, ireason; /* clear interrupt and get status */ - status = ATA_INB(ch->r_mem, ATA_STATUS); - + status = mvs_getstatus(dev, 1); // device_printf(dev, "Legacy intr status %02x\n", -// ccb->ataio.res.status); - +// status); if (ch->slot[0].state < MVS_SLOT_RUNNING) { // device_printf(dev, "Stray irq\n"); return; } - ccb->ataio.res.status = status; - + /* Wait a bit for late !BUSY status update. */ + if (status & ATA_S_BUSY) { + DELAY(100); + if ((status = mvs_getstatus(dev, 1)) & ATA_S_BUSY) { + DELAY(200); + if ((status = mvs_getstatus(dev, 1)) & ATA_S_BUSY) { +device_printf(dev, "device busy on intr, Status %02x\n", status); + return; + } + } + } /* if we got an error we are done with the HW */ - if (ccb->ataio.res.status & ATA_S_ERROR) { + if (status & ATA_S_ERROR) { et = MVS_ERR_TFE; goto end_finished; } - - /* are we moving data ? */ - if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { - /* if read data get it */ - if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { - if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { - device_printf(dev, "timeout waiting for read DRQ\n"); - et = MVS_ERR_TIMEOUT; - goto end_finished; - } - ATA_INSW_STRM(ch->r_mem, ATA_DATA, - (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), - ch->transfersize / 2); -// device_printf(dev, "After read %d status %02x\n", -// ch->transfersize, ATA_INB(ch->r_mem, ATA_ALTSTAT)); - } - - /* update how far we've gotten */ - ch->donecount += ch->transfersize; - - /* do we need a scoop more ? */ - if (ccb->ataio.dxfer_len > ch->donecount) { - - /* set this transfer size according to HW capabilities */ - ch->transfersize = min(ccb->ataio.dxfer_len - ch->donecount, - ch->curr[ccb->ccb_h.target_id].bytecount); - - /* if data write command, output the data */ - if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + if (ccb->ccb_h.func_code == XPT_ATA_IO) { + ccb->ataio.res.status = status; + /* are we moving data ? */ + if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + /* if read data get it */ + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { - device_printf(dev, "timeout waiting for write DRQ\n"); + device_printf(dev, "timeout waiting for read DRQ\n"); et = MVS_ERR_TIMEOUT; goto end_finished; } - ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, + ATA_INSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), ch->transfersize / 2); -// device_printf(dev, "After write %d status %02x\n", +// device_printf(dev, "After read %d status %02x\n", // ch->transfersize, ATA_INB(ch->r_mem, ATA_ALTSTAT)); - return; + } + + /* update how far we've gotten */ + ch->donecount += ch->transfersize; + + /* do we need a scoop more ? */ + if (ccb->ataio.dxfer_len > ch->donecount) { + + /* set this transfer size according to HW capabilities */ + ch->transfersize = min(ccb->ataio.dxfer_len - ch->donecount, + ch->curr[ccb->ccb_h.target_id].bytecount); + + /* if data write command, output the data */ + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { + device_printf(dev, "timeout waiting for write DRQ\n"); + et = MVS_ERR_TIMEOUT; + goto end_finished; + } + ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), + ch->transfersize / 2); +// device_printf(dev, "After write %d status %02x\n", +// ch->transfersize, ATA_INB(ch->r_mem, ATA_ALTSTAT)); + return; + } + + /* if data read command, return & wait for interrupt */ + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + return; + } } + } else { + length = ATA_INB(ch->r_mem,ATA_CYL_LSB) | (ATA_INB(ch->r_mem,ATA_CYL_MSB) << 8); + ireason = ATA_INB(ch->r_mem,ATA_IREASON); +//device_printf(dev, "status %02x, ireason %02x, length %d\n", status, ireason, length); + switch ((ireason & (ATA_I_CMD | ATA_I_IN)) | + (status & ATA_S_DRQ)) { + + case ATAPI_P_CMDOUT: +device_printf(dev, "ATAPI CMDOUT\n"); + /* this seems to be needed for some (slow) devices */ + DELAY(10); + + if (!(status & ATA_S_DRQ)) { + device_printf(dev, "command interrupt without DRQ\n"); + et = MVS_ERR_TFE; + goto end_finished; + } + ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? + ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes), + ch->curr[port].atapi / 2); + /* return wait for interrupt */ + return; + + case ATAPI_P_WRITE: +//device_printf(dev, "ATAPI WRITE\n"); + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + device_printf(dev, "trying to write on read buffer\n"); + et = MVS_ERR_TFE; + goto end_finished; + break; + } + ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)(ccb->csio.data_ptr + ch->donecount), + length / 2); + ch->donecount += length; + + /* set next transfer size according to HW capabilities */ + ch->transfersize = min(ccb->csio.dxfer_len - ch->donecount, + ch->curr[ccb->ccb_h.target_id].bytecount); + /* return wait for interrupt */ + return; + + case ATAPI_P_READ: +//device_printf(dev, "ATAPI READ\n"); + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + device_printf(dev, "trying to read on write buffer\n"); + et = MVS_ERR_TFE; + goto end_finished; + } + ATA_INSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)(ccb->csio.data_ptr + ch->donecount), + length / 2); + ch->donecount += length; + + /* set next transfer size according to HW capabilities */ + ch->transfersize = min(ccb->csio.dxfer_len - ch->donecount, + ch->curr[ccb->ccb_h.target_id].bytecount); + /* return wait for interrupt */ + return; + + case ATAPI_P_DONEDRQ: +device_printf(dev, "ATAPI DONEDRQ\n"); + device_printf(dev, + "WARNING - DONEDRQ non conformant device\n"); + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + ATA_INSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)(ccb->csio.data_ptr + ch->donecount), + length / 2); + ch->donecount += length; + } + else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)(ccb->csio.data_ptr + ch->donecount), + length / 2); + ch->donecount += length; + } + else + et = MVS_ERR_TFE; + /* FALLTHROUGH */ + + case ATAPI_P_ABORT: + case ATAPI_P_DONE: +//device_printf(dev, "ATAPI ABORT/DONE\n"); + if (status & (ATA_S_ERROR | ATA_S_DWF)) + et = MVS_ERR_TFE; + goto end_finished; - /* if data read command, return & wait for interrupt */ - if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) - return; - } + default: + device_printf(dev, "unknown transfer phase (status %02x, ireason %02x)\n", + status, ireason); + et = MVS_ERR_TFE; + } } end_finished: @@ -1297,6 +1422,10 @@ if (ch->numrslots != 0) return (1); } + } else { /* ATAPI */ + /* ATAPI goes without EDMA, so can't mix it with anything. */ + if (ch->numrslots != 0) + return (1); } /* We have some atomic command running. */ if (ch->aslots != 0) @@ -1399,6 +1528,8 @@ (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT)) { ch->aslots |= (1 << slot->slot); } + } else { + ch->numpslots++; } if (ch->numpslots == 0) { void *buf; @@ -1428,40 +1559,81 @@ struct mvs_channel *ch = device_get_softc(dev); union ccb *ccb = slot->ccb; int port = ccb->ccb_h.target_id & 0x0f; + int timeout; - device_printf(dev, "%d Legacy command %02x size %d\n", - port, ccb->ataio.cmd.command, ccb->ataio.dxfer_len); slot->state = MVS_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); ATA_OUTB(ch->r_mem, SATA_SATAICTL, port << SATA_SATAICTL_PMPTX_SHIFT); - mvs_tfd_write(dev, ccb); - /* device reset doesn't interrupt */ - if (ccb->ataio.cmd.command == ATA_DEVICE_RESET) { - int timeout = 1000000; - do { - 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); - return; - } - ch->donecount = 0; - ch->transfersize = min(ccb->ataio.dxfer_len, - ch->curr[ccb->ccb_h.target_id].bytecount); -// device_printf(dev, "After command (status %02x)\n", -// ATA_INB(ch->r_mem, ATA_ALTSTAT)); - /* if write command output the data */ - if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { - if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { - device_printf(dev, "timeout waiting for write DRQ\n"); -// request->result = EIO; -// goto begin_finished; + if (ccb->ccb_h.func_code == XPT_ATA_IO) { + device_printf(dev, "%d Legacy command %02x size %d\n", + port, ccb->ataio.cmd.command, ccb->ataio.dxfer_len); + mvs_tfd_write(dev, ccb); + /* device reset doesn't interrupt */ + if (ccb->ataio.cmd.command == ATA_DEVICE_RESET) { + int timeout = 1000000; + do { + 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); + return; + } + ch->donecount = 0; + ch->transfersize = min(ccb->ataio.dxfer_len, + ch->curr[port].bytecount); + if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) + ch->fake_busy = 1; + /* if write command output the data */ + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { + device_printf(dev, "timeout waiting for write DRQ\n"); + mvs_end_transaction(slot, MVS_ERR_TIMEOUT); + return; + } + ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, + (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), + ch->transfersize / 2); + } + } else { + device_printf(dev, "%d ATAPI command %02x size %d\n", + port, ccb->csio.cdb_io.cdb_bytes[0], ccb->csio.dxfer_len); + ch->donecount = 0; + ch->transfersize = min(ccb->csio.dxfer_len, + ch->curr[port].bytecount); + ATA_OUTB(ch->r_mem, ATA_FEATURE, 0); + ATA_OUTB(ch->r_mem, ATA_CYL_LSB, ch->transfersize); + ATA_OUTB(ch->r_mem, ATA_CYL_MSB, ch->transfersize >> 8); + ATA_OUTB(ch->r_mem, ATA_COMMAND, ATA_PACKET_CMD); + ch->fake_busy = 1; + /* wait for ready to write ATAPI command block */ + if (mvs_wait(dev, 0, ATA_S_BUSY, 1000) < 0) { + device_printf(dev, "timeout waiting for ATAPI !BUSY\n"); + mvs_end_transaction(slot, MVS_ERR_TIMEOUT); + return; + } + timeout = 5000; + while (timeout--) { + int reason = ATA_INB(ch->r_mem, ATA_IREASON); + int status = ATA_INB(ch->r_mem, ATA_STATUS); + + if (((reason & (ATA_I_CMD | ATA_I_IN)) | + (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT) + break; + DELAY(20); + } + if (timeout <= 0) { + device_printf(dev, "timeout waiting for ATAPI command ready\n"); + mvs_end_transaction(slot, MVS_ERR_TIMEOUT); + return; } + DELAY(20); ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, - (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), - ch->transfersize / 2); -// device_printf(dev, "After write %d status %02x\n", -// ch->transfersize, ATA_INB(ch->r_mem, ATA_ALTSTAT)); + (uint16_t *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? + ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes), + ch->curr[port].atapi / 2); + if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) + ch->fake_busy = 1; + DELAY(10); } } @@ -1828,7 +2000,8 @@ } else { ch->numpslots--; } - } + } else + ch->numpslots--; /* If it was our READ LOG command - process it. */ if (ch->readlog) { mvs_process_read_log(dev, ccb); @@ -1973,18 +2146,17 @@ static int mvs_wait(device_t dev, u_int s, u_int c, int t) { - struct mvs_channel *ch = device_get_softc(dev); int timeout = 0; uint8_t st; - while (((st = ATA_INB(ch->r_mem, ATA_ALTSTAT)) & (s | c)) != s) { + while (((st = mvs_getstatus(dev, 0)) & (s | c)) != s) { DELAY(1000); if (timeout++ > t) { device_printf(dev, "Wait status %02x\n", st); return (-1); } } - device_printf(dev, "Wait status %02x\n", st); +// device_printf(dev, "Wait status %02x\n", st); return (timeout); } @@ -2208,8 +2380,10 @@ d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) d->mode = cts->xport_specific.sata.mode; - if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) - d->bytecount = min(8192, cts->xport_specific.sata.bytecount); + if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) { + d->bytecount = min((ch->quirks & MVS_Q_GENIIE) ? 8192 : 2048, + cts->xport_specific.sata.bytecount); + } if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->tags = min(MVS_MAX_SLOTS, cts->xport_specific.sata.tags); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) ==== //depot/projects/scottl-camlock/src/sys/dev/mvs/mvs.h#3 (text+ko) ==== @@ -206,6 +206,12 @@ #define ATA_A_4BIT 0x08 /* 4 head bits */ #define ATA_A_HOB 0x80 /* High Order Byte enable */ #define ATA_ALTSTAT 0x120 /* (R) alternate status */ +#define ATAPI_P_READ (ATA_S_DRQ | ATA_I_IN) +#define ATAPI_P_WRITE (ATA_S_DRQ) +#define ATAPI_P_CMDOUT (ATA_S_DRQ | ATA_I_CMD) +#define ATAPI_P_DONEDRQ (ATA_S_DRQ | ATA_I_CMD | ATA_I_IN) +#define ATAPI_P_DONE (ATA_I_CMD | ATA_I_IN) +#define ATAPI_P_ABORT 0 /* Basic DMA Registers */ #define DMA_C 0x224 /* Basic DMA Command */ @@ -530,6 +536,7 @@ int in_idx; /* Next read CRPB */ u_int transfersize; /* PIO transfer size */ u_int donecount; /* PIO bytes sent/received */ + u_int fake_busy; /* Fake busy bit after command submission */ union ccb *frozen; /* Frozen command */ struct callout pm_timer; /* Power management events */ @@ -551,7 +558,7 @@ int quirks; int channels; int ccc; /* CCC timeout */ - int cccv; /* CCC vector */ + int cccc; /* CCC commands */ struct { void (*function)(void *); void *argument;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201004171706.o3HH6VMl061033>