diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2006-08-05 00:55:36 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2006-08-05 00:55:36 +0000 |
commit | b8c1078169e0fde878766f1fb39dbeee4426d4ed (patch) | |
tree | 000f642d2289500239cf83c056c112807e780c7b /sys/dev | |
parent | 6cb84f36c2a847181d7433ef9c825aef1128c4a9 (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.c | 159 |
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); } |