summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2006-08-05 00:55:36 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2006-08-05 00:55:36 +0000
commitb8c1078169e0fde878766f1fb39dbeee4426d4ed (patch)
tree000f642d2289500239cf83c056c112807e780c7b /sys/dev
parent6cb84f36c2a847181d7433ef9c825aef1128c4a9 (diff)
implement reply and post queue handling, but only polled for now. io should
work with this, but i cant test without disks.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/arc.c159
1 files changed, 134 insertions, 25 deletions
diff --git a/sys/dev/pci/arc.c b/sys/dev/pci/arc.c
index 5763d8e6815..b2f29e9258a 100644
--- a/sys/dev/pci/arc.c
+++ b/sys/dev/pci/arc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: arc.c,v 1.7 2006/08/03 08:29:26 dlg Exp $ */
+/* $OpenBSD: arc.c,v 1.8 2006/08/05 00:55:35 dlg Exp $ */
/*
* Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
@@ -112,8 +112,19 @@ static const struct pci_matchid arc_devices[] = {
#define ARC_REG_OUTB_INTRMASK_DOORBELL (1<<2)
#define ARC_REG_OUTB_INTRMASK_POSTQUEUE (1<<3)
#define ARC_REG_OUTB_INTRMASK_PCI (1<<4)
-#define ARC_REG_INB_QUEUE 0x0040
-#define ARC_REG_OUTB_QUEUE 0x0044
+#define ARC_REG_POST_QUEUE 0x0040
+#define ARC_REG_POST_QUEUE_ADDR_SHIFT 5
+#define ARC_REG_POST_QUEUE_ADDR(r) \
+ ((r) >> ARC_REG_REPLY_QUEUE_ADDR_SHIFT)
+#define ARC_REG_POST_QUEUE_IAMBIOS (1<<30)
+#define ARC_REG_POST_QUEUE_BIGFRAME (1<<31)
+#define ARC_REG_REPLY_QUEUE 0x0044
+#define ARC_REG_REPLY_QUEUE_ADDR_MASK (0x07ffffff)
+#define ARC_REG_REPLY_QUEUE_ADDR_SHIFT 5
+#define ARC_REG_REPLY_QUEUE_ADDR(r) \
+ (((r) & ARC_REG_REPLY_QUEUE_ADDR_MASK) << ARC_REG_REPLY_QUEUE_ADDR_SHIFT)
+#define ARC_REG_REPLY_QUEUE_ERR (1<<28)
+#define ARC_REG_REPLY_QUEUE_IAMBIOS (1<<30)
#define ARC_REG_MSGBUF 0x0a00
#define ARC_REG_MSGBUF_LEN 256 /* dwords */
#define ARC_REG_IOC_WBUF 0x0e00
@@ -157,8 +168,11 @@ struct arc_msg_scsicmd {
#define ARC_MSG_CDBLEN 16
u_int8_t cdb[ARC_MSG_CDBLEN];
- u_int8_t dev_stat;
- u_int8_t sense_data[15];
+#define ARC_MSG_SENSELEN 16
+ u_int8_t sense_data[ARC_MSG_SENSELEN];
+#define ARC_MSG_SENSE_TIMEOUT 0xf0
+#define ARC_MSG_SENSE_ABORTED 0xf1
+#define ARC_MSG_SENSE_INIT_FAIL 0xf2
/* followed by an sgl */
} __packed;
@@ -242,6 +256,9 @@ int arc_wait_eq(struct arc_softc *, bus_size_t,
int arc_wait_ne(struct arc_softc *, bus_size_t,
u_int32_t, u_int32_t);
+#define arc_push(_s, _r) arc_write((_s), ARC_REG_POST_QUEUE, (_r))
+#define arc_pop(_s) arc_read((_s), ARC_REG_REPLY_QUEUE)
+
/* wrap up the bus_dma api */
struct arc_dmamem {
bus_dmamap_t adm_map;
@@ -267,7 +284,7 @@ struct arc_ccb {
bus_dmamap_t ccb_dmamap;
bus_addr_t ccb_offset;
void *ccb_cmd;
- bus_addr_t ccb_cmd_dva;
+ u_int32_t ccb_cmd_post;
TAILQ_ENTRY(arc_ccb) ccb_link;
};
@@ -276,6 +293,10 @@ int arc_alloc_ccbs(struct arc_softc *);
struct arc_ccb *arc_get_ccb(struct arc_softc *);
void arc_put_ccb(struct arc_softc *, struct arc_ccb *);
int arc_load_xs(struct arc_ccb *);
+int arc_complete(struct arc_softc *, struct arc_ccb *,
+ int);
+void arc_scsi_cmd_done(struct arc_softc *, struct arc_ccb *,
+ u_int32_t);
/* real stuff for dealing with the hardware */
int arc_map_pci_resources(struct arc_softc *,
@@ -342,11 +363,12 @@ arc_intr(void *arg)
int
arc_scsi_cmd(struct scsi_xfer *xs)
{
-#if 0 /* XXX this is real code */
struct scsi_link *link = xs->sc_link;
struct arc_softc *sc = link->adapter_softc;
struct arc_ccb *ccb;
struct arc_msg_scsicmd *cmd;
+ u_int32_t reg;
+ int rv = SUCCESSFULLY_QUEUED;
int s;
if (xs->cmdlen > ARC_MSG_CDBLEN) {
@@ -355,7 +377,9 @@ arc_scsi_cmd(struct scsi_xfer *xs)
xs->sense.flags = SKEY_ILLEGAL_REQUEST;
xs->sense.add_sense_code = 0x20;
xs->error = XS_SENSE;
+ s = splbio();
scsi_done(xs);
+ splx(s);
return (COMPLETE);
}
@@ -364,12 +388,25 @@ arc_scsi_cmd(struct scsi_xfer *xs)
splx(s);
if (ccb == NULL) {
xs->error = XS_DRIVER_STUFFUP;
+ s = splbio();
scsi_done(xs);
+ splx(s);
return (COMPLETE);
}
ccb->ccb_xs = xs;
+
+ if (arc_load_xs(ccb) != 0) {
+ xs->error = XS_DRIVER_STUFFUP;
+ s = splbio();
+ arc_put_ccb(sc, ccb);
+ scsi_done(xs);
+ splx(s);
+ return (COMPLETE);
+ }
+
cmd = ccb->ccb_cmd;
+ reg = ccb->ccb_cmd_post;
/* bus is always 0 */
cmd->target = link->target;
@@ -377,28 +414,40 @@ arc_scsi_cmd(struct scsi_xfer *xs)
cmd->function = 1; /* XXX magic number */
cmd->cdb_len = xs->cmdlen;
- /* sgl_len is set in load_xs */
+ cmd->sgl_len = ccb->ccb_dmamap->dm_nsegs;
if (xs->flags & SCSI_DATA_OUT)
cmd->flags = ARC_MSG_SCSICMD_FLAG_WRITE;
+ if (ccb->ccb_dmamap->dm_nsegs > ARC_SGL_256LEN) {
+ cmd->flags |= ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512;
+ reg |= ARC_REG_POST_QUEUE_BIGFRAME;
+ }
cmd->context = htole32(ccb->ccb_id);
cmd->data_len = htole32(xs->datalen);
bcopy(xs->cmd, cmd->cdb, xs->cmdlen);
- if (arc_load_xs(ccb) != 0) {
- s = splbio();
- arc_put_ccb(sc, ccb);
- splx(s);
- xs->error = XS_DRIVER_STUFFUP;
- scsi_done(xs);
- return (COMPLETE);
+ /* we've built the command, lets put it on the hw */
+ bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
+ ccb->ccb_offset, sc->sc_req_size,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ s = splbio();
+ arc_push(sc, reg);
+#if 0
+ if (xs->flags & SCSI_POLL) {
+#endif
+ rv = COMPLETE;
+ if (arc_complete(sc, ccb, xs->timeout) != 0) {
+ xs->error = XS_DRIVER_STUFFUP;
+ scsi_done(xs);
+ }
+#if 0
}
-#endif /* XXX this is real code */
+#endif
+ splx(s);
- xs->error = XS_DRIVER_STUFFUP;
- scsi_done(xs);
- return (COMPLETE);
+ return (rv);
}
int
@@ -435,10 +484,6 @@ arc_load_xs(struct arc_ccb *ccb)
sge->sg_lo_addr = htole32((u_int32_t)addr);
}
- if (dmap->dm_nsegs > ARC_SGL_256LEN)
- bundle->cmd.flags |= ARC_MSG_SCSICMD_FLAG_SGL_BSIZE_512;
- bundle->cmd.sgl_len = dmap->dm_nsegs;
-
bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
(xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
BUS_DMASYNC_PREWRITE);
@@ -447,6 +492,70 @@ arc_load_xs(struct arc_ccb *ccb)
}
void
+arc_scsi_cmd_done(struct arc_softc *sc, struct arc_ccb *ccb, u_int32_t reg)
+{
+ struct scsi_xfer *xs = ccb->ccb_xs;
+
+ if (xs->datalen != 0) {
+ bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
+ ccb->ccb_dmamap->dm_mapsize, (xs->flags & SCSI_DATA_IN) ?
+ BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
+ }
+
+ /* timeout_del */
+ xs->flags |= ITSDONE;
+
+ if (reg & ARC_REG_REPLY_QUEUE_ERR) {
+ // printf("%s: something went wrong\n", DEVNAME(sc));
+ xs->error = XS_DRIVER_STUFFUP;
+ } else {
+ xs->status = SCSI_OK;
+ xs->error = XS_NOERROR;
+ xs->resid = 0;
+ }
+
+ arc_put_ccb(sc, ccb);
+ scsi_done(xs);
+}
+
+int
+arc_complete(struct arc_softc *sc, struct arc_ccb *nccb, int timeout)
+{
+ struct arc_ccb *ccb = NULL;
+ char *kva = ARC_DMA_KVA(sc->sc_requests);
+ struct arc_msg_scsicmd *cmd;
+ int diff;
+ u_int32_t reg;
+
+ do {
+
+ reg = arc_pop(sc);
+ if (reg == 0xffffffff) {
+ if (timeout-- == 0)
+ return (1);
+
+ delay(1000);
+ continue;
+ }
+
+ diff = (reg << 5) - ARC_DMA_DVA(sc->sc_requests);
+ cmd = (struct arc_msg_scsicmd *)(kva + diff);
+
+ ccb = &sc->sc_ccbs[cmd->context];
+
+ bus_dmamap_sync(sc->sc_dmat, ARC_DMA_MAP(sc->sc_requests),
+ ccb->ccb_offset, sc->sc_req_size,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ arc_scsi_cmd_done(sc, ccb, reg);
+
+ } while (nccb != ccb);
+
+ return (0);
+}
+
+void
arc_minphys(struct buf *bp)
{
if (bp->b_bcount > MAXPHYS)
@@ -727,8 +836,8 @@ arc_alloc_ccbs(struct arc_softc *sc)
ccb->ccb_offset = sc->sc_req_size * i;
ccb->ccb_cmd = &cmd[ccb->ccb_offset];
- ccb->ccb_cmd_dva = (u_int32_t)ARC_DMA_DVA(sc->sc_requests) +
- ccb->ccb_offset;
+ ccb->ccb_cmd_post = (ARC_DMA_DVA(sc->sc_requests) +
+ ccb->ccb_offset) >> 5; /* XXX magic number */
arc_put_ccb(sc, ccb);
}