PERFORCE change 177029 for review
Alexander Motin
mav at FreeBSD.org
Sat Apr 17 17:06:32 UTC 2010
http://p4web.freebsd.org/@@177029?ac=10
Change 177029 by mav at 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;
More information about the p4-projects
mailing list