Subject: Hooking MCA DMA to bus_dma framework
To: None <port-i386@netbsd.org>
From: Jaromir Dolecek <jdolecek@netbsd.org>
List: port-i386
Date: 11/17/2001 00:00:38
--ELM713407792-10865-0_
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=US-ASCII
Hi,
I've implemented the (hopefull) better way to deal with DMA on MCA
bus. I moved the MCA DMA controller setup related code to MD location
from edc(4), and exposed the functionality via bus_dma + mca_dmamap_create()
to associate DRQ with the dmamap.
The primary reason was to make the code usable also to other drivers
besides edc, but this was also to deal with buffer bouncing on >16MB RAM
machines properly.
Since MCA shares the same 16MB DMA limit as ISA, I've used mainly
the _isa_dmamap_* routines and just hooked the necessary MCA bits.
I'm appending the changes as a diff to current i386/mca/mca_machdep.c.
I'm not quite sure about two things in the code:
* is it 'right' to abuse the bus_dmamap_t the way I do it in
_mca_dmamap_* ? (most imminent in _mca_dmamap_create())
* is it okay to kick the MCA DMA controller off _mca_dmamap_sync()?
The latter one has good side effect in that the drivers would _need_
to have appropriate sync calls even on i386, thus hopefully less
work would be required to make the drivers working on other archs
such as RS/6000. The former one is mainly because I did not find
any better way to cleanly hook to the _isa_dmamap_* routines.
Your comments would be greatly appreciated,
Jaromir
--
Jaromir Dolecek <jdolecek@NetBSD.org> http://www.NetBSD.org/Ports/i386/ps2.html
-= Those who would give up liberty for a little temporary safety deserve =-
-= neither liberty nor safety, and will lose both. -- Benjamin Franklin =-
--ELM713407792-10865-0_
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=ISO-8859-2
Content-Disposition: attachment; filename=mcadma.diff
Index: mca_machdep.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/i386/mca/mca_machdep.c,v
retrieving revision 1.10
diff -u -p -r1.10 mca_machdep.c
--- mca_machdep.c 2001/11/15 07:03:33 1.10
+++ mca_machdep.c 2001/11/16 22:37:12
@@ -46,10 +46,12 @@ __KERNEL_RCSID(0, "$NetBSD: mca_machdep.
#include <sys/types.h>
#include <sys/param.h>
-#include <sys/time.h>
-#include <sys/systm.h>
#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
#include <machine/bioscall.h>
#include <machine/psl.h>
@@ -84,33 +86,85 @@ struct bios_config {
u_int8_t pad[10];
} __attribute__ ((packed));
+/* Used as MCA DMA context, passed up as a fake bus_dmamap_t */
+struct i386_mca_dmactx {
+ bus_dmamap_t dm;
+ int drq;
+};
+
+/* ISA DMA stuff - see i386/isa/isa_machdep.c */
+int _isa_bus_dmamap_create __P((bus_dma_tag_t, bus_size_t, int,
+ bus_size_t, bus_size_t, int, bus_dmamap_t *));
+void _isa_bus_dmamap_destroy __P((bus_dma_tag_t, bus_dmamap_t));
+int _isa_bus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *,
+ bus_size_t, struct proc *, int));
+void _isa_bus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t));
+void _isa_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t,
+ bus_addr_t, bus_size_t, int));
+
+int _isa_bus_dmamem_alloc __P((bus_dma_tag_t, bus_size_t, bus_size_t,
+ bus_size_t, bus_dma_segment_t *, int, int *, int));
+
+static int _mca_bus_dmamap_create __P((bus_dma_tag_t, bus_size_t, int,
+ bus_size_t, bus_size_t, int, bus_dmamap_t *));
+static void _mca_bus_dmamap_destroy __P((bus_dma_tag_t, bus_dmamap_t));
+static int _mca_bus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *,
+ bus_size_t, struct proc *, int));
+static void _mca_bus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t));
+static void _mca_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t,
+ bus_addr_t, bus_size_t, int));
+static int _mca_bus_dmamap_load_mbuf __P((bus_dma_tag_t, bus_dmamap_t,
+ struct mbuf *, int));
+static int _mca_bus_dmamap_load_uio __P((bus_dma_tag_t, bus_dmamap_t,
+ struct uio *, int));
+static int _mca_bus_dmamap_load_raw __P((bus_dma_tag_t, bus_dmamap_t,
+ bus_dma_segment_t *, int, bus_size_t, int));
+/*
+ * For now, we use MCA DMA to 0-16M always. Some IBM PS/2 have 32bit MCA bus,
+ * but majority of them have 24bit only.
+ */
+#define MCA_DMA_BOUNCE_THRESHOLD (16 * 1024 * 1024)
+
struct i386_bus_dma_tag mca_bus_dma_tag = {
- NULL, /* _cookie */
- _bus_dmamap_create,
- _bus_dmamap_destroy,
- _bus_dmamap_load,
- _bus_dmamap_load_mbuf,
- _bus_dmamap_load_uio,
- _bus_dmamap_load_raw,
- _bus_dmamap_unload,
- NULL, /* _dmamap_sync */
- _bus_dmamem_alloc,
+ MCA_DMA_BOUNCE_THRESHOLD, /* _bounce_thresh */
+ _mca_bus_dmamap_create,
+ _mca_bus_dmamap_destroy,
+ _mca_bus_dmamap_load,
+ _mca_bus_dmamap_load_mbuf,
+ _mca_bus_dmamap_load_uio,
+ _mca_bus_dmamap_load_raw,
+ _mca_bus_dmamap_unload,
+ _mca_bus_dmamap_sync,
+ _isa_bus_dmamem_alloc,
_bus_dmamem_free,
_bus_dmamem_map,
_bus_dmamem_unmap,
_bus_dmamem_mmap,
};
-/* setup by mca_busprobe() */
-int MCA_system = 0; /* Updated in mca_busprobe() if appropriate. */
+/* Updated in mca_busprobe() if appropriate. */
+int MCA_system = 0;
+/* Used to kick MCA DMA controller */
+#define DMA_EXTCMD 0x18
+#define DMA_EXEC 0x1A
+static bus_space_handle_t dmaiot, dmaextcmdh, dmaexech;
+
+/*
+ * Map the MCA DMA controller registers.
+ */
void
mca_attach_hook(parent, self, mba)
struct device *parent, *self;
struct mcabus_attach_args *mba;
{
- /* Nothing */
+ dmaiot = mba->mba_iot;
+
+ if (bus_space_map(dmaiot, DMA_EXTCMD, 1, 0, &dmaextcmdh)
+ || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech))
+ panic("%s: couldn't map DMA registers",
+ mba->mba_busname);
}
/*
@@ -208,11 +262,9 @@ mca_nmi()
outb(MCA_MB_SETUP_REG, 0xff);
/* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */
- for(slot=0; slot<MCA_MAX_SLOTS; slot++)
- {
+ for(slot=0; slot<MCA_MAX_SLOTS; slot++) {
outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
- if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0)
- {
+ if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0) {
mcanmi = 1;
/* find if CHCK status is available in POS 6/7 */
if((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK_STAT) == 0)
@@ -226,12 +278,10 @@ mca_nmi()
outb(MCA_ADAP_SETUP_REG, 0);
out:
-#if NISA > 0
if (!mcanmi) {
/* no CHCK bits asserted, assume ISA NMI */
return (isa_nmi());
} else
-#endif
return(0);
}
@@ -307,4 +357,241 @@ void
mca_disk_unbusy(void)
{
outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON);
+}
+
+/*
+ * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ * MCA DMA specific stuff. We use ISA routines for bulk of the work,
+ * since MCA shares much of the charasteristics with it. We just hook
+ * the DRQ initialization and kick MCA DMA controller appropriately.
+ * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ */
+
+/*
+ * Create a MCA DMA map.
+ */
+static int
+_mca_bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, flags, dmamp)
+ bus_dma_tag_t t;
+ bus_size_t size;
+ int nsegments;
+ bus_size_t maxsegsz;
+ bus_size_t boundary;
+ int flags;
+ bus_dmamap_t *dmamp;
+{
+ struct i386_mca_dmactx *ctx;
+ int error;
+
+ MALLOC(ctx, struct i386_mca_dmactx *, sizeof(*ctx), M_DEVBUF,
+ cold ? M_NOWAIT : M_WAITOK);
+ if (ctx == NULL)
+ return (ENOMEM);
+
+ if ((error = _isa_bus_dmamap_create(t, size, nsegments, maxsegsz,
+ boundary, flags, &ctx->dm))) {
+ FREE(ctx, M_DEVBUF);
+ return (error);
+ }
+
+ ctx->drq = 0xdeadbeef;
+
+ *dmamp = (bus_dmamap_t) ctx;
+
+ return (0);
+}
+
+/*
+ * Destroy a MCA DMA map.
+ */
+static void
+_mca_bus_dmamap_destroy(t, map)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+{
+ struct i386_mca_dmactx *ctx = (struct i386_mca_dmactx *)map;
+
+ _isa_bus_dmamap_destroy(t, ctx->dm);
+
+ FREE(ctx, M_DEVBUF);
+}
+
+/*
+ * Load a MCA DMA map with a linear buffer.
+ */
+static int
+_mca_bus_dmamap_load(t, map, buf, buflen, p, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ void *buf;
+ bus_size_t buflen;
+ struct proc *p;
+ int flags;
+{
+ struct i386_mca_dmactx *ctx = (struct i386_mca_dmactx *)map;
+
+ return _isa_bus_dmamap_load(t, ctx->dm, buf, buflen, p, flags);
+}
+
+/*
+ * Like _mca_bus_dmamap_load(), but for mbufs.
+ */
+static int
+_mca_bus_dmamap_load_mbuf(t, map, m0, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ struct mbuf *m0;
+ int flags;
+{
+
+ panic("_mca_bus_dmamap_load_mbuf: not implemented");
+}
+
+/*
+ * Like _mca_bus_dmamap_load(), but for uios.
+ */
+static int
+_mca_bus_dmamap_load_uio(t, map, uio, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ struct uio *uio;
+ int flags;
+{
+
+ panic("_mca_bus_dmamap_load_uio: not implemented");
+}
+
+/*
+ * Like _mca_bus_dmamap_load(), but for raw memory allocated with
+ * bus_dmamem_alloc().
+ */
+static int
+_mca_bus_dmamap_load_raw(t, map, segs, nsegs, size, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ bus_dma_segment_t *segs;
+ int nsegs;
+ bus_size_t size;
+ int flags;
+{
+
+ panic("_mca_bus_dmamap_load_raw: not implemented");
+}
+
+/*
+ * Unload a MCA DMA map.
+ */
+static void
+_mca_bus_dmamap_unload(t, map)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+{
+ struct i386_mca_dmactx *ctx = (struct i386_mca_dmactx *)map;
+
+ return _isa_bus_dmamap_unload(t, ctx->dm);
+}
+
+/*
+ * Synchronize a MCA DMA map.
+ */
+static void
+_mca_bus_dmamap_sync(t, _map, offset, len, ops)
+ bus_dma_tag_t t;
+ bus_dmamap_t _map;
+ bus_addr_t offset;
+ bus_size_t len;
+ int ops;
+{
+ struct i386_mca_dmactx *ctx = (struct i386_mca_dmactx *) _map;
+ bus_addr_t phys;
+ bus_size_t cnt;
+ int isread;
+
+ _isa_bus_dmamap_sync(t, ctx->dm, offset, len, ops);
+
+ /*
+ * Don't do anything if not PRE* operation, allow only
+ * one of PREREAD and PREWRITE.
+ */
+ if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE)
+ return;
+
+#ifdef DEBUG
+ if (ctx->drq == 0xdeadbeef) {
+ panic("_mca_bus_dmamap_sync: DRQ not set");
+ /* NOTREACHED */
+ }
+#endif
+
+ isread = (ops & BUS_DMASYNC_PREREAD);
+ phys = ctx->dm->dm_segs[0].ds_addr;
+ cnt = ctx->dm->dm_segs[0].ds_len;
+
+ /*
+ * Initialize the MCA DMA controller appropriately. The exact
+ * sequence to setup the controller is taken from Minix.
+ */
+
+ /* Disable access to dma channel */
+ bus_space_write_1(dmaiot, dmaextcmdh, 0, 0x90 + ctx->drq);
+
+ /* Clear the address byte pointer */
+ bus_space_write_1(dmaiot, dmaextcmdh, 0, 0x20 + ctx->drq);
+ /* address bits 0..7 */
+ bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff);
+ /* address bits 8..15 */
+ bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff);
+ /* address bits 16..23 */
+ bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff);
+
+ /* Clear the count byte pointer */
+ bus_space_write_1(dmaiot, dmaextcmdh, 0, 0x40 + ctx->drq);
+ /* count bits 0..7 */
+ bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff);
+ /* count bits 8..15 */
+ bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff);
+
+ /* Set the transfer mode */
+ bus_space_write_1(dmaiot, dmaextcmdh, 0, 0x70 + ctx->drq);
+ bus_space_write_1(dmaiot, dmaexech, 0, (isread) ? 0x4C : 0x44);
+
+ /* Enable access to dma channel */
+ bus_space_write_1(dmaiot, dmaextcmdh, 0, 0xA0 + ctx->drq);
+}
+
+/*
+ * Allocate a dma map, and set up drq.
+ */
+int
+mca_dmamap_create(t, size, nsegments, maxsegsz, boundary, flags, dmamp, drq)
+ bus_dma_tag_t t;
+ bus_size_t size;
+ int nsegments;
+ bus_size_t maxsegsz;
+ bus_size_t boundary;
+ int flags;
+ bus_dmamap_t *dmamp;
+ int drq;
+{
+ int error;
+ struct i386_mca_dmactx *ctx;
+
+#ifdef DEBUG
+ /* Sanity check */
+ if (drq < 0 || drq >= 16) {
+ printf("mcadma_create: invalid DMA Arbitration Level %d\n",
+ drq);
+ return (EINVAL);
+ }
+#endif
+
+ if ((error = bus_dmamap_create(t, size, nsegments,
+ maxsegsz, boundary, flags, dmamp)) != 0)
+ return (error);
+
+ /* Set DRQ appropriately */
+ ctx = (struct i386_mca_dmactx *) *dmamp;
+ ctx->drq = drq;
+
+ return (0);
}
--ELM713407792-10865-0_--