summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/mvme88k/dev/vs.c556
-rw-r--r--sys/arch/mvme88k/dev/vsvar.h113
2 files changed, 340 insertions, 329 deletions
diff --git a/sys/arch/mvme88k/dev/vs.c b/sys/arch/mvme88k/dev/vs.c
index 1c36dd5fbe3..2ab3481bacb 100644
--- a/sys/arch/mvme88k/dev/vs.c
+++ b/sys/arch/mvme88k/dev/vs.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: vs.c,v 1.72 2009/01/29 22:16:34 miod Exp $ */
+/* $OpenBSD: vs.c,v 1.73 2009/02/01 00:44:36 miod Exp $ */
/*
- * Copyright (c) 2004, Miodrag Vallat.
+ * Copyright (c) 2004, 2009, Miodrag Vallat.
* Copyright (c) 1999 Steve Murphree, Jr.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@@ -64,11 +64,12 @@
int vsmatch(struct device *, void *, void *);
void vsattach(struct device *, struct device *, void *);
+void vs_minphys(struct buf *);
int vs_scsicmd(struct scsi_xfer *);
struct scsi_adapter vs_scsiswitch = {
vs_scsicmd,
- minphys,
+ vs_minphys,
0, /* no lun support */
0, /* no lun support */
};
@@ -91,18 +92,18 @@ struct cfdriver vs_cd = {
int do_vspoll(struct vs_softc *, struct scsi_xfer *, int);
void thaw_queue(struct vs_softc *, int);
void thaw_all_queues(struct vs_softc *);
-M328_SG vs_alloc_scatter_gather(void);
-M328_SG vs_build_memory_structure(struct vs_softc *, struct scsi_xfer *,
- bus_addr_t);
-void vs_chksense(struct scsi_xfer *);
-void vs_dealloc_scatter_gather(M328_SG);
+int vs_alloc_sg(struct vs_softc *);
+int vs_alloc_wq(struct vs_softc *);
+void vs_build_sg_list(struct vs_softc *, struct vs_cb *, bus_addr_t);
+void vs_chksense(struct vs_cb *, struct scsi_xfer *);
int vs_eintr(void *);
int vs_getcqe(struct vs_softc *, bus_addr_t *, bus_addr_t *);
int vs_identify(struct vs_channel *, int);
int vs_initialize(struct vs_softc *);
int vs_intr(struct vs_softc *);
-void vs_link_sg_element(sg_list_element_t *, vaddr_t, int);
-void vs_link_sg_list(sg_list_element_t *, vaddr_t, int);
+int vs_load_command(struct vs_softc *, struct vs_cb *, bus_addr_t,
+ bus_addr_t, struct scsi_link *, int, struct scsi_generic *, int,
+ uint8_t *, int);
int vs_nintr(void *);
int vs_poll(struct vs_softc *, struct vs_cb *);
void vs_print_addr(struct vs_softc *, struct scsi_xfer *);
@@ -114,7 +115,6 @@ int vs_unit_value(int, int, int);
static __inline__ void vs_free(struct vs_softc *, struct vs_cb *);
static __inline__ void vs_clear_return_info(struct vs_softc *);
-static __inline__ paddr_t kvtop(vaddr_t);
int
vsmatch(struct device *device, void *cf, void *args)
@@ -168,9 +168,9 @@ vsattach(struct device *parent, struct device *self, void *args)
printf(" vec 0x%x: ", evec);
- sc->sc_paddr = ca->ca_paddr;
+ sc->sc_dmat = ca->ca_dmat;
sc->sc_iot = ca->ca_iot;
- if (bus_space_map(sc->sc_iot, sc->sc_paddr, S_SHORTIO, 0,
+ if (bus_space_map(sc->sc_iot, ca->ca_paddr, S_SHORTIO, 0,
&sc->sc_ioh) != 0) {
printf("can't map registers!\n");
return;
@@ -204,7 +204,7 @@ vsattach(struct device *parent, struct device *self, void *args)
* (see device_register).
*/
tmp = bootpart;
- if (sc->sc_paddr != bootaddr)
+ if (ca->ca_paddr != bootaddr)
bootpart = -1; /* invalid flag to device_register */
for (bus = 0; bus < 2; bus++) {
@@ -254,6 +254,14 @@ vsattach(struct device *parent, struct device *self, void *args)
}
void
+vs_minphys(struct buf *bp)
+{
+ if (bp->b_bcount > ptoa(MAX_SG_ELEMENTS))
+ bp->b_bcount = ptoa(MAX_SG_ELEMENTS);
+ minphys(bp);
+}
+
+void
vs_print_addr(struct vs_softc *sc, struct scsi_xfer *xs)
{
if (xs == NULL)
@@ -378,15 +386,18 @@ vs_scsidone(struct vs_softc *sc, struct vs_cb *cb)
xs->error = XS_SELTIMEOUT;
xs->status = -1;
} else {
- if (xs->flags & SCSI_DATA_IN)
- dma_cachectl(pmap_kernel(), (vaddr_t)xs->data,
- xs->datalen, DMA_CACHE_INV);
+ if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ bus_dmamap_sync(sc->sc_dmat, cb->cb_dmamap, 0,
+ cb->cb_dmalen, (xs->flags & SCSI_DATA_IN) ?
+ BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, cb->cb_dmamap);
+ }
xs->status = error >> 8;
}
while (xs->status == SCSI_CHECK) {
- vs_chksense(xs);
+ vs_chksense(cb, xs);
}
xs->flags |= ITSDONE;
@@ -399,16 +410,15 @@ vs_scsicmd(struct scsi_xfer *xs)
{
struct scsi_link *slp = xs->sc_link;
struct vs_softc *sc = slp->adapter_softc;
- int flags, option;
- unsigned int iopb_len;
+ int flags;
bus_addr_t cqep, iopb;
struct vs_cb *cb;
- u_int queue;
int s;
+ int rc;
flags = xs->flags;
if (flags & SCSI_POLL) {
- cb = &sc->sc_cb[0];
+ cb = sc->sc_cb;
cqep = sh_MCE;
iopb = sh_MCE_IOPB;
@@ -449,16 +459,56 @@ vs_scsicmd(struct scsi_xfer *xs)
}
}
- queue = cb->cb_q;
+#ifdef VS_DEBUG
+ printf("%s: sending SCSI command %02x (length %d) on queue %d\n",
+ __func__, xs->cmd->opcode, xs->cmdlen, cb->cb_q);
+#endif
+ rc = vs_load_command(sc, cb, cqep, iopb, slp, xs->flags,
+ xs->cmd, xs->cmdlen, xs->data, xs->datalen);
+ if (rc != 0) {
+ printf("%s: unable to load DMA map: error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ xs->error = XS_DRIVER_STUFFUP;
+ scsi_done(xs);
+ splx(s);
+ return (COMPLETE);
+ }
+
+ vs_write(1, cqep + CQE_WORK_QUEUE, cb->cb_q);
- vs_bzero(iopb, IOPB_LONG_SIZE);
+ cb->cb_xs = xs;
+ splx(s);
+
+ vs_write(4, cqep + CQE_CTAG, (u_int32_t)cb);
+
+ if (crb_read(2, CRB_CRSW) & M_CRSW_AQ)
+ vs_write(2, cqep + CQE_QECR, M_QECR_AA | M_QECR_GO);
+ else
+ vs_write(2, cqep + CQE_QECR, M_QECR_GO);
+
+ if (flags & SCSI_POLL) {
+ /* poll for the command to complete */
+ return vs_poll(sc, cb);
+ }
+
+ return (SUCCESSFULLY_QUEUED);
+}
+
+int
+vs_load_command(struct vs_softc *sc, struct vs_cb *cb, bus_addr_t cqep,
+ bus_addr_t iopb, struct scsi_link *slp, int flags,
+ struct scsi_generic *cmd, int cmdlen, uint8_t *data, int datalen)
+{
+ unsigned int iopb_len;
+ int option;
+ int rc;
/*
* We should only provide the iopb len if the controller is not
* able to compute it from the SCSI command group.
* Note that Jaguar has no knowledge of group 2.
*/
- switch ((xs->cmd->opcode) >> 5) {
+ switch ((cmd->opcode) >> 5) {
case 0:
case 1:
case 5:
@@ -470,17 +520,13 @@ vs_scsicmd(struct scsi_xfer *xs)
else
/* FALLTHROUGH */
default:
- iopb_len = IOPB_SHORT_SIZE + ((xs->cmdlen + 1) >> 1);
+ iopb_len = IOPB_SHORT_SIZE + ((cmdlen + 1) >> 1);
break;
}
-#ifdef VS_DEBUG
- printf("%s: sending SCSI command %02x (length %d, iopb length %d) on queue %d\n",
- __func__, xs->cmd->opcode, xs->cmdlen, iopb_len, queue);
-#endif
+ vs_bzero(iopb, IOPB_LONG_SIZE);
bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, iopb + IOPB_SCSI_DATA,
- (u_int8_t *)xs->cmd, xs->cmdlen);
-
+ (u_int8_t *)cmd, cmdlen);
vs_write(2, iopb + IOPB_CMD, IOPB_PASSTHROUGH);
vs_write(2, iopb + IOPB_UNIT,
vs_unit_value(slp->flags & SDEV_2NDBUS, slp->target, slp->lun));
@@ -493,13 +539,22 @@ vs_scsicmd(struct scsi_xfer *xs)
vs_write(1, iopb + IOPB_EVCT, sc->sc_evec);
/*
- * Since the 88k doesn't support cache snooping, we have
- * to flush the cache for a write and flush with inval for
- * a read, prior to starting the IO.
+ * Setup DMA map for data transfer
*/
- dma_cachectl(pmap_kernel(), (vaddr_t)xs->data, xs->datalen,
- flags & SCSI_DATA_IN ? DMA_CACHE_SYNC_INVAL : DMA_CACHE_SYNC);
-
+ if (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ cb->cb_dmalen = (bus_size_t)datalen;
+ rc = bus_dmamap_load(sc->sc_dmat, cb->cb_dmamap,
+ data, cb->cb_dmalen, NULL,
+ BUS_DMA_NOWAIT | BUS_DMA_STREAMING |
+ ((flags & SCSI_DATA_IN) ? BUS_DMA_READ : BUS_DMA_WRITE));
+ if (rc != 0)
+ return rc;
+
+ bus_dmamap_sync(sc->sc_dmat, cb->cb_dmamap, 0,
+ cb->cb_dmalen, (flags & SCSI_DATA_IN) ?
+ BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+ }
+
option = 0;
if (flags & SCSI_DATA_OUT)
option |= M_OPT_DIR;
@@ -515,40 +570,25 @@ vs_scsicmd(struct scsi_xfer *xs)
}
vs_write(2, iopb + IOPB_ADDR, ADDR_MOD);
+ if (flags & (SCSI_DATA_IN | SCSI_DATA_OUT))
+ vs_build_sg_list(sc, cb, iopb);
+
+ vs_bzero(cqep, CQE_SIZE);
vs_write(2, cqep + CQE_IOPB_ADDR, iopb);
vs_write(1, cqep + CQE_IOPB_LENGTH, iopb_len);
- vs_write(1, cqep + CQE_WORK_QUEUE, queue);
-
- cb->cb_xs = xs;
- splx(s);
-
- if (xs->datalen != 0)
- cb->cb_sg = vs_build_memory_structure(sc, xs, iopb);
- else
- cb->cb_sg = NULL;
-
- vs_write(4, cqep + CQE_CTAG, (u_int32_t)cb);
-
- if (crb_read(2, CRB_CRSW) & M_CRSW_AQ)
- vs_write(2, cqep + CQE_QECR, M_QECR_AA | M_QECR_GO);
- else
- vs_write(2, cqep + CQE_QECR, M_QECR_GO);
+ /* CQE_WORK_QUEUE to be filled by the caller */
- if (flags & SCSI_POLL) {
- /* poll for the command to complete */
- return vs_poll(sc, cb);
- }
-
- return (SUCCESSFULLY_QUEUED);
+ return 0;
}
void
-vs_chksense(struct scsi_xfer *xs)
+vs_chksense(struct vs_cb *cb, struct scsi_xfer *xs)
{
- int s;
struct scsi_link *slp = xs->sc_link;
struct vs_softc *sc = slp->adapter_softc;
- struct scsi_sense *ss;
+ struct scsi_sense ss;
+ int rc;
+ int s;
#ifdef VS_DEBUG
printf("%s: target %d\n", slp->target);
@@ -563,31 +603,27 @@ vs_chksense(struct scsi_xfer *xs)
while (mce_read(2, CQE_QECR) & M_QECR_GO)
;
- vs_bzero(sh_MCE_IOPB, IOPB_LONG_SIZE);
- /* This is a command, so point to it */
- ss = (void *)(bus_space_vaddr(sc->sc_iot, sc->sc_ioh) +
- sh_MCE_IOPB + IOPB_SCSI_DATA);
- ss->opcode = REQUEST_SENSE;
- ss->byte2 = slp->lun << 5;
- ss->length = sizeof(struct scsi_sense_data);
-
- mce_iopb_write(2, IOPB_CMD, IOPB_PASSTHROUGH);
- mce_iopb_write(2, IOPB_OPTION, 0);
- mce_iopb_write(1, IOPB_NVCT, sc->sc_nvec);
- mce_iopb_write(1, IOPB_EVCT, sc->sc_evec);
- mce_iopb_write(2, IOPB_LEVEL, 0 /* sc->sc_ipl */);
- mce_iopb_write(2, IOPB_ADDR, ADDR_MOD);
- mce_iopb_write(4, IOPB_BUFF, kvtop((vaddr_t)&xs->sense));
- mce_iopb_write(4, IOPB_LENGTH, sizeof(struct scsi_sense_data));
- mce_iopb_write(2, IOPB_UNIT,
- vs_unit_value(slp->flags & SDEV_2NDBUS, slp->target, slp->lun));
+ bzero(&ss, sizeof ss);
+ ss.opcode = REQUEST_SENSE;
+ ss.byte2 = slp->lun << 5;
+ ss.length = sizeof(xs->sense);
- dma_cachectl(pmap_kernel(), (vaddr_t)&xs->sense,
- sizeof(struct scsi_sense_data), DMA_CACHE_SYNC_INVAL);
+#ifdef VS_DEBUG
+ printf("%s: sending SCSI command %02x (length %d) on queue %d\n",
+ __func__, ss.opcode, sizeof ss, 0);
+#endif
+ rc = vs_load_command(sc, cb, sh_MCE, sh_MCE_IOPB, slp,
+ SCSI_DATA_IN | SCSI_POLL,
+ (struct scsi_generic *)&ss, sizeof ss, (uint8_t *)&xs->sense,
+ sizeof(xs->sense));
+ if (rc != 0) {
+ printf("%s: unable to load DMA map: error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->status = 0;
+ return;
+ }
- vs_bzero(sh_MCE, CQE_SIZE);
- mce_write(2, CQE_IOPB_ADDR, sh_MCE_IOPB);
- mce_write(1, CQE_IOPB_LENGTH, 0);
mce_write(1, CQE_WORK_QUEUE, 0);
mce_write(2, CQE_QECR, M_QECR_GO);
@@ -661,7 +697,7 @@ vs_identify(struct vs_channel *vc, int cid)
int
vs_initialize(struct vs_softc *sc)
{
- int i, msr, id;
+ int i, msr, id, rc;
u_int targets;
/*
@@ -824,13 +860,17 @@ vs_initialize(struct vs_softc *sc)
/* poll for the command to complete */
do_vspoll(sc, NULL, 1);
+ if ((rc = vs_alloc_sg(sc)) != 0)
+ return rc;
+
+ if ((rc = vs_alloc_wq(sc)) != 0)
+ return rc;
+
/* initialize work queues */
#ifdef VS_DEBUG
printf("%s: initializing %d work queues\n",
__func__, sc->sc_nwq);
#endif
- for (i = 0; i <= sc->sc_nwq; i++)
- sc->sc_cb[i].cb_q = i;
for (i = 1; i <= sc->sc_nwq; i++) {
/* Wait until we can use the command queue entry. */
@@ -862,9 +902,10 @@ vs_initialize(struct vs_softc *sc)
/* poll for the command to complete */
do_vspoll(sc, NULL, 1);
if (CRSW & M_CRSW_ER) {
- printf("work queue %d initialization error 0x%x\n",
- i, vs_read(2, sh_RET_IOPB + IOPB_STATUS));
- return 1;
+ printf("%s: work queue %d initialization error 0x%x\n",
+ sc->sc_dev.dv_xname, i,
+ vs_read(2, sh_RET_IOPB + IOPB_STATUS));
+ return ENXIO;
}
CRB_CLR_DONE;
}
@@ -880,6 +921,109 @@ vs_initialize(struct vs_softc *sc)
return 0;
}
+/*
+ * Allocate memory for the scatter/gather lists.
+ *
+ * Since vs_minphys() makes sure we won't need more than flat lists of
+ * up to MAX_SG_ELEMENTS entries, we need to allocate storage for one
+ * such list per work queue.
+ */
+int
+vs_alloc_sg(struct vs_softc *sc)
+{
+ size_t sglen;
+ int nseg;
+ int rc;
+
+ sglen = sc->sc_nwq * MAX_SG_ELEMENTS * sizeof(struct vs_sg_entry);
+ sglen = round_page(sglen);
+
+ rc = bus_dmamem_alloc(sc->sc_dmat, sglen, 0, 0,
+ &sc->sc_sgseg, 1, &nseg, BUS_DMA_NOWAIT);
+ if (rc != 0) {
+ printf("%s: unable to allocate s/g memory: error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto fail1;
+ }
+ rc = bus_dmamem_map(sc->sc_dmat, &sc->sc_sgseg, nseg, sglen,
+ (caddr_t *)&sc->sc_sgva, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
+ if (rc != 0) {
+ printf("%s: unable to map s/g memory: error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto fail2;
+ }
+ rc = bus_dmamap_create(sc->sc_dmat, sglen, 1, sglen, 0,
+ BUS_DMA_NOWAIT /* | BUS_DMA_ALLOCNOW */, &sc->sc_sgmap);
+ if (rc != 0) {
+ printf("%s: unable to create s/g dma map: error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto fail3;
+ }
+ rc = bus_dmamap_load(sc->sc_dmat, sc->sc_sgmap, sc->sc_sgva,
+ sglen, NULL, BUS_DMA_NOWAIT);
+ if (rc != 0) {
+ printf("%s: unable to load s/g dma map: error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto fail4;
+ }
+
+ return 0;
+
+fail4:
+ bus_dmamap_destroy(sc->sc_dmat, sc->sc_sgmap);
+fail3:
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_sgva, PAGE_SIZE);
+fail2:
+ bus_dmamem_free(sc->sc_dmat, &sc->sc_sgseg, 1);
+fail1:
+ return rc;
+}
+
+/*
+ * Allocate one command block per work qeue.
+ */
+int
+vs_alloc_wq(struct vs_softc *sc)
+{
+ struct vs_cb *cb;
+ u_int i;
+ int rc;
+
+ sc->sc_cb = malloc(sc->sc_nwq * sizeof(struct vs_cb), M_DEVBUF,
+ M_ZERO | M_NOWAIT);
+ if (sc->sc_cb == NULL) {
+ printf("%s: unable to allocate %d work queues\n",
+ sc->sc_dev.dv_xname, sc->sc_nwq);
+ return ENOMEM;
+ }
+
+ for (i = 0, cb = sc->sc_cb; i <= sc->sc_nwq; i++, cb++) {
+ cb->cb_q = i;
+
+ rc = bus_dmamap_create(sc->sc_dmat, ptoa(MAX_SG_ELEMENTS),
+ MAX_SG_ELEMENTS, MAX_SG_ELEMENT_SIZE, 0,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &cb->cb_dmamap);
+ if (rc != 0) {
+ printf("%s: unable to create dma map for queue %d"
+ ": error %d\n",
+ sc->sc_dev.dv_xname, i, rc);
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ while (i != 0) {
+ i--; cb--;
+ bus_dmamap_destroy(sc->sc_dmat, cb->cb_dmamap);
+ }
+ free(sc->sc_cb, M_DEVBUF);
+ sc->sc_cb = NULL;
+
+ return rc;
+}
+
void
vs_resync(struct vs_softc *sc)
{
@@ -976,10 +1120,14 @@ vs_free(struct vs_softc *sc, struct vs_cb *cb)
{
if (cb->cb_q != 0)
thaw_queue(sc, cb->cb_q);
+#if 0
if (cb->cb_sg != NULL) {
vs_dealloc_scatter_gather(cb->cb_sg);
cb->cb_sg = NULL;
}
+#else
+ /* nothing to do, really */
+#endif
cb->cb_xs = NULL;
}
@@ -1130,7 +1278,7 @@ vs_find_queue(struct scsi_link *sl, struct vs_softc *sc)
if (sl->flags & SDEV_2NDBUS)
q += sc->sc_channel[0].vc_width - 1; /* map to 8-14 or 16-30 */
- if ((cb = &sc->sc_cb[q])->cb_xs == NULL)
+ if ((cb = sc->sc_cb + q)->cb_xs == NULL)
return (cb);
return (NULL);
@@ -1161,198 +1309,60 @@ vs_unit_value(int bus, int tgt, int lun)
}
/*
- * Useful functions for scatter/gather list
+ * Build the scatter/gather list for the given control block and update
+ * its IOPB.
*/
-
-M328_SG
-vs_alloc_scatter_gather(void)
-{
- M328_SG sg;
-
- sg = malloc(sizeof(struct m328_sg), M_DEVBUF, M_WAITOK | M_ZERO);
-
- return (sg);
-}
-
void
-vs_dealloc_scatter_gather(M328_SG sg)
+vs_build_sg_list(struct vs_softc *sc, struct vs_cb *cb, bus_addr_t iopb)
{
- int i;
-
- if (sg->level > 0) {
- for (i = 0; sg->down[i] && i < MAX_SG_ELEMENTS; i++) {
- vs_dealloc_scatter_gather(sg->down[i]);
- }
- }
- free(sg, M_DEVBUF);
-}
-
-void
-vs_link_sg_element(sg_list_element_t *element, vaddr_t phys_add, int len)
-{
- element->count.bytes = len;
- element->addrlo = phys_add;
- element->addrhi = phys_add >> 16;
- element->link = 0; /* FALSE */
- element->transfer_type = NORMAL_TYPE;
- element->memory_type = LONG_TRANSFER;
- element->address_modifier = ADRM_EXT_S_D;
-}
-
-void
-vs_link_sg_list(sg_list_element_t *list, vaddr_t phys_add, int elements)
-{
- list->count.scatter.gather = elements;
- list->addrlo = phys_add;
- list->addrhi = phys_add >> 16;
- list->link = 1; /* TRUE */
- list->transfer_type = NORMAL_TYPE;
- list->memory_type = LONG_TRANSFER;
- list->address_modifier = ADRM_EXT_S_D;
-}
-
-M328_SG
-vs_build_memory_structure(struct vs_softc *sc, struct scsi_xfer *xs,
- bus_addr_t iopb)
-{
- M328_SG sg;
- vaddr_t starting_point_virt, starting_point_phys, point_virt,
- point1_phys, point2_phys, virt;
- unsigned int len;
- int level;
-
- sg = NULL; /* Hopefully we need no scatter/gather list */
-
- /*
- * We have the following things:
- * virt va of the virtual memory block
- * len length of the virtual memory block
- * starting_point_virt va of the physical memory block
- * starting_point_phys pa of the physical memory block
- * point_virt va of the virtual memory
- * we are checking at the moment
- * point1_phys pa of the physical memory
- * we are checking at the moment
- * point2_phys pa of another physical memory
- * we are checking at the moment
- */
-
- level = 0;
- virt = starting_point_virt = (vaddr_t)xs->data;
- point1_phys = starting_point_phys = kvtop((vaddr_t)xs->data);
- len = xs->datalen;
+ struct vs_sg_entry *sgentry;
+ int segno;
+ bus_dma_segment_t *seg = cb->cb_dmamap->dm_segs;
+ bus_size_t sgoffs;
+ bus_size_t len;
/*
- * Check if we need scatter/gather
+ * No need to build a scatter/gather chain if there is only
+ * one contiguous physical area.
*/
- if (trunc_page(virt + len - 1) != trunc_page(virt)) {
- for (point_virt = round_page(starting_point_virt + 1);
- /* if we do already scatter/gather we have to stay in the loop and jump */
- point_virt < virt + len || sg != NULL;
- point_virt += PAGE_SIZE) { /* out later */
-
- point2_phys = kvtop(point_virt);
-
- if ((point2_phys != trunc_page(point1_phys) + PAGE_SIZE) || /* physical memory is not contiguous */
- (point_virt - starting_point_virt >= MAX_SG_BLOCK_SIZE && sg)) { /* we only can access (1<<16)-1 bytes in scatter/gather_mode */
- if (point_virt - starting_point_virt >= MAX_SG_BLOCK_SIZE) { /* We were walking too far for one scatter/gather block ... */
- point_virt = trunc_page(starting_point_virt+MAX_SG_BLOCK_SIZE-1); /* So go back to the beginning of the last matching page */
- /* and generate the physical address of
- * this location for the next time. */
- point2_phys = kvtop(point_virt);
- }
-
- if (sg == NULL)
- sg = vs_alloc_scatter_gather();
-
-#if 1 /* broken firmware */
- if (sg->elements >= MAX_SG_ELEMENTS) {
- vs_dealloc_scatter_gather(sg);
- printf("%s: scatter/gather list too large\n",
- sc->sc_dev.dv_xname);
- return (NULL);
- }
-#else /* if the firmware will ever get fixed */
- while (sg->elements >= MAX_SG_ELEMENTS) {
- if (!sg->up) { /* If the list full in this layer ? */
- sg->up = vs_alloc_scatter_gather();
- sg->up->level = sg->level+1;
- sg->up->down[0] = sg;
- sg->up->elements = 1;
- }
- /* link this full list also in physical memory */
- vs_link_sg_list(&(sg->up->list[sg->up->elements-1]),
- kvtop((vaddr_t)sg->list),
- sg->elements);
- sg = sg->up; /* Climb up */
- }
- while (sg->level) { /* As long as we are not a the base level */
- int i;
-
- i = sg->elements;
- /* We need a new element */
- sg->down[i] = vs_alloc_scatter_gather();
- sg->down[i]->level = sg->level - 1;
- sg->down[i]->up = sg;
- sg->elements++;
- sg = sg->down[i]; /* Climb down */
- }
-#endif /* 1 */
- if (point_virt < virt + len) {
- /* linking element */
- vs_link_sg_element(&(sg->list[sg->elements]),
- starting_point_phys,
- point_virt - starting_point_virt);
- sg->elements++;
- } else {
- /* linking last element */
- vs_link_sg_element(&(sg->list[sg->elements]),
- starting_point_phys,
- virt + len - starting_point_virt);
- sg->elements++;
- break; /* We have now collected all blocks */
- }
- starting_point_virt = point_virt;
- starting_point_phys = point2_phys;
- }
- point1_phys = point2_phys;
- }
+ if (cb->cb_dmamap->dm_nsegs == 1) {
+ vs_write(4, iopb + IOPB_BUFF, seg->ds_addr);
+ vs_write(4, iopb + IOPB_LENGTH, cb->cb_dmalen);
+ return;
}
/*
- * Climb up along the right side of the tree until we reach the top.
+ * Otherwise, we need to build the flat s/g list.
*/
- if (sg != NULL) {
- while (sg->up) {
- /* link this list also in physical memory */
- vs_link_sg_list(&(sg->up->list[sg->up->elements-1]),
- kvtop((vaddr_t)sg->list),
- sg->elements);
- sg = sg->up; /* Climb up */
+ sgentry = sc->sc_sgva + cb->cb_q * MAX_SG_ELEMENTS;
+ sgoffs = (vaddr_t)sgentry - (vaddr_t)sc->sc_sgva;
+
+ len = cb->cb_dmalen;
+ for (segno = 0; segno < cb->cb_dmamap->dm_nsegs; seg++, segno++) {
+ if (seg->ds_len > len) {
+ sgentry->count.bytes = len;
+ len = 0;
+ } else {
+ sgentry->count.bytes = seg->ds_len;
+ len -= seg->ds_len;
}
-
- vs_write(2, iopb + IOPB_OPTION,
- vs_read(2, iopb + IOPB_OPTION) | M_OPT_SG);
- vs_write(2, iopb + IOPB_ADDR,
- vs_read(2, iopb + IOPB_ADDR) | M_ADR_SG_LINK);
- vs_write(4, iopb + IOPB_BUFF, kvtop((vaddr_t)sg->list));
- vs_write(4, iopb + IOPB_LENGTH, sg->elements);
- vs_write(4, iopb + IOPB_SGTTL, len);
- } else {
- /* no scatter/gather necessary */
- vs_write(4, iopb + IOPB_BUFF, starting_point_phys);
- vs_write(4, iopb + IOPB_LENGTH, len);
+ sgentry->pa_high = seg->ds_addr >> 16;
+ sgentry->pa_low = seg->ds_addr & 0xffff;
+ sgentry->addr = ADDR_MOD;
+ sgentry++;
}
- return sg;
-}
-
-static paddr_t
-kvtop(vaddr_t va)
-{
- paddr_t pa;
- pmap_extract(pmap_kernel(), va, &pa);
- /* XXX check for failure */
- return pa;
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_sgmap, sgoffs,
+ cb->cb_dmamap->dm_nsegs * sizeof(struct vs_sg_entry),
+ BUS_DMASYNC_PREREAD);
+
+ vs_write(2, iopb + IOPB_OPTION,
+ vs_read(2, iopb + IOPB_OPTION) | M_OPT_SG);
+ vs_write(2, iopb + IOPB_ADDR,
+ vs_read(2, iopb + IOPB_ADDR) | M_ADR_SG_LINK);
+ vs_write(4, iopb + IOPB_BUFF,
+ sc->sc_sgmap->dm_segs[0].ds_addr + sgoffs);
+ vs_write(4, iopb + IOPB_LENGTH, cb->cb_dmamap->dm_nsegs);
+ vs_write(4, iopb + IOPB_SGTTL, cb->cb_dmalen);
}
diff --git a/sys/arch/mvme88k/dev/vsvar.h b/sys/arch/mvme88k/dev/vsvar.h
index 8c6b32f7bb2..eb3787ad639 100644
--- a/sys/arch/mvme88k/dev/vsvar.h
+++ b/sys/arch/mvme88k/dev/vsvar.h
@@ -1,6 +1,6 @@
-/* $OpenBSD: vsvar.h,v 1.20 2008/01/05 00:34:07 miod Exp $ */
+/* $OpenBSD: vsvar.h,v 1.21 2009/02/01 00:44:36 miod Exp $ */
/*
- * Copyright (c) 2004, Miodrag Vallat.
+ * Copyright (c) 2004, 2009, Miodrag Vallat.
* Copyright (c) 1999 Steve Murphree, Jr.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@@ -36,60 +36,51 @@
#define _VSVAR_H_
/*
- * The largest single request will be MAXPHYS bytes which will require
- * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of
- * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the
- * buffer is not page aligned (+1).
+ * Scatter/gather structures
+ *
+ * Each s/g element is 8 bytes long; a s/g list is limited to 64
+ * entries. To afford larger lists, it is possible to use ``link''
+ * entries, which can add up to 64 new entries (and contain link
+ * entries themselves, too), by setting the M_ADR_SG_LINK bit in
+ * the address modifier field.
+ *
+ * To keep things simple, this driver will use only use a flat list
+ * of up to 64 entries, thereby limiting the maximum transfer to
+ * 64 pages (worst case situation).
*/
-#define DMAMAXIO (MAXPHYS/NBPG+1)
-#define OFF(x) (u_int16_t)(x)
-/*
- * scatter/gather structures
- */
+#define MAX_SG_ELEMENTS 64
-typedef struct {
+struct vs_sg_entry {
union {
- unsigned short bytes :16;
-#define MAX_SG_BLOCK_SIZE (1<<16)
+ uint16_t bytes; /* entry byte count */
struct {
- unsigned short :8;
- unsigned short gather:8;
- } scatter;
+ uint8_t gather; /* count of linked entries */
+ uint8_t reserved;
+ } link;
} count;
- u_int16_t addrhi, addrlo; /* split due to alignment */
- unsigned short link:1;
- unsigned short :3;
- unsigned short transfer_type:2;
-#define SHORT_TRANSFER 0x1
-#define LONG_TRANSFER 0x2
-#define SCATTER_GATTER_LIST_IN_SHORT_IO 0x3
- unsigned short memory_type:2;
-#define NORMAL_TYPE 0x0
-#define BLOCK_MODE 0x1
- unsigned short address_modifier:8;
-} sg_list_element_t;
-
-typedef sg_list_element_t * scatter_gather_list_t;
-
-#define MAX_SG_ELEMENTS 64
-
-struct m328_sg {
- struct m328_sg *up;
- int elements;
- int level;
- struct m328_sg *down[MAX_SG_ELEMENTS];
- sg_list_element_t list[MAX_SG_ELEMENTS];
+ uint16_t pa_high; /* 32-bit address field split... */
+ uint16_t pa_low; /* ... due to alignment */
+ uint16_t addr; /* address type and modifier */
};
-typedef struct m328_sg *M328_SG;
+/* largest power of two for the bytes field */
+#define MAX_SG_ELEMENT_SIZE (1U << 15)
+/*
+ * Command control block
+ */
struct vs_cb {
- struct scsi_xfer *cb_xs;
- u_int cb_q;
- M328_SG cb_sg;
+ struct scsi_xfer *cb_xs; /* current command */
+ u_int cb_q; /* controller queue it's in */
+
+ bus_dmamap_t cb_dmamap;
+ bus_size_t cb_dmalen;
};
+/*
+ * Per-channel information
+ */
struct vs_channel {
int vc_id; /* host id */
int vc_width; /* number of targets */
@@ -101,18 +92,28 @@ struct vs_channel {
};
struct vs_softc {
- struct device sc_dev;
- int sc_bid; /* board id */
- paddr_t sc_paddr;
- bus_space_tag_t sc_iot;
- bus_space_handle_t sc_ioh;
- struct intrhand sc_ih_e, sc_ih_n;
- char sc_intrname_e[16 + 4];
- int sc_ipl;
- int sc_evec, sc_nvec;
- u_int sc_nwq; /* number of work queues */
- struct vs_channel sc_channel[2];
- struct vs_cb sc_cb[1 + NUM_WQ];
+ struct device sc_dev;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_dma_tag_t sc_dmat;
+
+ int sc_bid; /* board id */
+ struct vs_channel sc_channel[2];
+
+ struct intrhand sc_ih_n; /* normal interrupt handler */
+ int sc_nvec;
+ struct intrhand sc_ih_e; /* error interrupt handler */
+ int sc_evec;
+ char sc_intrname_e[16 + 4];
+ int sc_ipl;
+
+ u_int sc_nwq; /* number of work queues */
+ struct vs_cb *sc_cb;
+
+ bus_dmamap_t sc_sgmap;
+ bus_dma_segment_t sc_sgseg;
+ struct vs_sg_entry *sc_sgva;
};
/* Access macros */