NetBSD/sys/arch/i386/isa/isa_machdep.c
fvdl ae4b76c8d3 New interrupt code. The basic idea behind it is to hide the differences
in interrupt controllers in struct pic, and try to keep as much
common code as possible. At the lowest (asm) level, this is done
with CPP macros.

The main structure is now struct intrsource, describing an established
interrupt line, of any kind (soft/hard local apic/legacy apic/IO apic).
For quick masking, there may be a maximum of 32 sources per CPU.
Sources can be assigned to any CPU in the MP case, though currently they
all go to the boot CPU.
2002-11-22 15:23:35 +00:00

957 lines
24 KiB
C

/* $NetBSD: isa_machdep.c,v 1.58 2002/11/22 15:23:49 fvdl Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
* Simulation Facility, NASA Ames Research Center.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)isa.c 7.2 (Berkeley) 5/13/91
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: isa_machdep.c,v 1.58 2002/11/22 15:23:49 fvdl Exp $");
#define ISA_DMA_STATS
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/mbuf.h>
#define _I386_BUS_DMA_PRIVATE
#include <machine/bus.h>
#include <machine/pio.h>
#include <machine/cpufunc.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
#include <uvm/uvm_extern.h>
#include "ioapic.h"
#if NIOAPIC > 0
#include <machine/i82093var.h>
#include <machine/mpbiosvar.h>
#endif
#include "mca.h"
#if NMCA > 0
#include <machine/mca_machdep.h> /* for MCA_system */
#endif
#include "eisa.h" /* XXX */
/*
* ISA can only DMA to 0-16M.
*/
#define ISA_DMA_BOUNCE_THRESHOLD (16 * 1024 * 1024)
extern paddr_t avail_end;
#define IDTVEC(name) __CONCAT(X,name)
typedef void (vector) __P((void));
extern vector *IDTVEC(intr)[];
/*
* Cookie used by ISA dma. A pointer to one of these it stashed in
* the DMA map.
*/
struct i386_isa_dma_cookie {
int id_flags; /* flags; see below */
/*
* Information about the original buffer used during
* DMA map syncs. Note that origibuflen is only used
* for ID_BUFTYPE_LINEAR.
*/
void *id_origbuf; /* pointer to orig buffer if
bouncing */
bus_size_t id_origbuflen; /* ...and size */
int id_buftype; /* type of buffer */
void *id_bouncebuf; /* pointer to the bounce buffer */
bus_size_t id_bouncebuflen; /* ...and size */
int id_nbouncesegs; /* number of valid bounce segs */
bus_dma_segment_t id_bouncesegs[0]; /* array of bounce buffer
physical memory segments */
};
/* id_flags */
#define ID_MIGHT_NEED_BOUNCE 0x01 /* map could need bounce buffers */
#define ID_HAS_BOUNCE 0x02 /* map currently has bounce buffers */
#define ID_IS_BOUNCING 0x04 /* map is bouncing current xfer */
/* id_buftype */
#define ID_BUFTYPE_INVALID 0
#define ID_BUFTYPE_LINEAR 1
#define ID_BUFTYPE_MBUF 2
#define ID_BUFTYPE_UIO 3
#define ID_BUFTYPE_RAW 4
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));
int _isa_bus_dmamap_load_mbuf __P((bus_dma_tag_t, bus_dmamap_t,
struct mbuf *, int));
int _isa_bus_dmamap_load_uio __P((bus_dma_tag_t, bus_dmamap_t,
struct uio *, int));
int _isa_bus_dmamap_load_raw __P((bus_dma_tag_t, bus_dmamap_t,
bus_dma_segment_t *, int, bus_size_t, 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));
int _isa_dma_alloc_bouncebuf __P((bus_dma_tag_t, bus_dmamap_t,
bus_size_t, int));
void _isa_dma_free_bouncebuf __P((bus_dma_tag_t, bus_dmamap_t));
/*
* Entry points for ISA DMA. These are mostly wrappers around
* the generic functions that understand how to deal with bounce
* buffers, if necessary.
*/
struct i386_bus_dma_tag isa_bus_dma_tag = {
ISA_DMA_BOUNCE_THRESHOLD,
_isa_bus_dmamap_create,
_isa_bus_dmamap_destroy,
_isa_bus_dmamap_load,
_isa_bus_dmamap_load_mbuf,
_isa_bus_dmamap_load_uio,
_isa_bus_dmamap_load_raw,
_isa_bus_dmamap_unload,
_isa_bus_dmamap_sync,
_isa_bus_dmamem_alloc,
_bus_dmamem_free,
_bus_dmamem_map,
_bus_dmamem_unmap,
_bus_dmamem_mmap,
};
#define LEGAL_IRQ(x) ((x) >= 0 && (x) < NUM_LEGACY_IRQS && (x) != 2)
int
isa_intr_alloc(isa_chipset_tag_t ic, int mask, int type, int *irq)
{
int i, tmp, bestirq, count;
struct intrhand **p, *q;
struct intrsource *isp;
struct cpu_info *ci;
if (type == IST_NONE)
panic("intr_alloc: bogus type");
ci = &cpu_info_primary;
bestirq = -1;
count = -1;
/* some interrupts should never be dynamically allocated */
mask &= 0xdef8;
/*
* XXX some interrupts will be used later (6 for fdc, 12 for pms).
* the right answer is to do "breadth-first" searching of devices.
*/
mask &= 0xefbf;
simple_lock(&ci->ci_slock);
for (i = 0; i < NUM_LEGACY_IRQS; i++) {
if (LEGAL_IRQ(i) == 0 || (mask & (1<<i)) == 0)
continue;
isp = ci->ci_isources[i];
if (isp == NULL) {
/*
* if nothing's using the irq, just return it
*/
*irq = i;
simple_unlock(&ci->ci_slock);
return (0);
}
switch(isp->is_type) {
case IST_EDGE:
case IST_LEVEL:
if (type != isp->is_type)
continue;
/*
* if the irq is shareable, count the number of other
* handlers, and if it's smaller than the last irq like
* this, remember it
*
* XXX We should probably also consider the
* interrupt level and stick IPL_TTY with other
* IPL_TTY, etc.
*/
for (p = &isp->is_handlers, tmp = 0; (q = *p) != NULL;
p = &q->ih_next, tmp++)
;
if ((bestirq == -1) || (count > tmp)) {
bestirq = i;
count = tmp;
}
break;
case IST_PULSE:
/* this just isn't shareable */
continue;
}
}
simple_unlock(&ci->ci_slock);
if (bestirq == -1)
return (1);
*irq = bestirq;
return (0);
}
const struct evcnt *
isa_intr_evcnt(isa_chipset_tag_t ic, int irq)
{
/* XXX for now, no evcnt parent reported */
return NULL;
}
void *
isa_intr_establish(ic, irq, type, level, ih_fun, ih_arg)
isa_chipset_tag_t ic;
int irq;
int type;
int level;
int (*ih_fun) __P((void *));
void *ih_arg;
{
struct pic *pic;
int pin;
#if NIOAPIC > 0
int mpih;
#endif
pin = irq;
pic = &i8259_pic;
#if NIOAPIC > 0
if (mp_busses != NULL) {
if (intr_find_mpmapping(mp_isa_bus, irq, &mpih) == 0 ||
intr_find_mpmapping(mp_eisa_bus, irq, &mpih) == 0) {
if (!APIC_IRQ_ISLEGACY(mpih)) {
pin = APIC_IRQ_PIN(mpih);
pic = (struct pic *)
ioapic_find(APIC_IRQ_APIC(mpih));
if (pic == NULL) {
printf("isa_intr_establish: "
"unknown apic %d\n",
APIC_IRQ_APIC(mpih));
return NULL;
}
}
} else
printf("isa_intr_establish: no MP mapping found\n");
}
#endif
return intr_establish(irq, pic, pin, type, level, ih_fun, ih_arg);
}
/*
* Deregister an interrupt handler.
*/
void
isa_intr_disestablish(ic, arg)
isa_chipset_tag_t ic;
void *arg;
{
struct intrhand *ih = arg;
if (!LEGAL_IRQ(ih->ih_pin))
panic("intr_disestablish: bogus irq");
intr_disestablish(ih);
}
void
isa_attach_hook(parent, self, iba)
struct device *parent, *self;
struct isabus_attach_args *iba;
{
extern struct i386_isa_chipset i386_isa_chipset;
extern int isa_has_been_seen;
/*
* Notify others that might need to know that the ISA bus
* has now been attached.
*/
if (isa_has_been_seen)
panic("isaattach: ISA bus already seen!");
isa_has_been_seen = 1;
/*
* Since we can only have one ISA bus, we just use a single
* statically allocated ISA chipset structure. Pass it up
* now.
*/
iba->iba_ic = &i386_isa_chipset;
}
int
isa_mem_alloc(t, size, align, boundary, flags, addrp, bshp)
bus_space_tag_t t;
bus_size_t size, align;
bus_addr_t boundary;
int flags;
bus_addr_t *addrp;
bus_space_handle_t *bshp;
{
/*
* Allocate physical address space in the ISA hole.
*/
return (bus_space_alloc(t, IOM_BEGIN, IOM_END - 1, size, align,
boundary, flags, addrp, bshp));
}
void
isa_mem_free(t, bsh, size)
bus_space_tag_t t;
bus_space_handle_t bsh;
bus_size_t size;
{
bus_space_free(t, bsh, size);
}
/**********************************************************************
* bus.h dma interface entry points
**********************************************************************/
#ifdef ISA_DMA_STATS
#define STAT_INCR(v) (v)++
#define STAT_DECR(v) do { \
if ((v) == 0) \
printf("%s:%d -- Already 0!\n", __FILE__, __LINE__); \
else \
(v)--; \
} while (0)
u_long isa_dma_stats_loads;
u_long isa_dma_stats_bounces;
u_long isa_dma_stats_nbouncebufs;
#else
#define STAT_INCR(v)
#define STAT_DECR(v)
#endif
/*
* Create an ISA DMA map.
*/
int
_isa_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_isa_dma_cookie *cookie;
bus_dmamap_t map;
int error, cookieflags;
void *cookiestore;
size_t cookiesize;
/* Call common function to create the basic map. */
error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary,
flags, dmamp);
if (error)
return (error);
map = *dmamp;
map->_dm_cookie = NULL;
cookiesize = sizeof(struct i386_isa_dma_cookie);
/*
* ISA only has 24-bits of address space. This means
* we can't DMA to pages over 16M. In order to DMA to
* arbitrary buffers, we use "bounce buffers" - pages
* in memory below the 16M boundary. On DMA reads,
* DMA happens to the bounce buffers, and is copied into
* the caller's buffer. On writes, data is copied into
* but bounce buffer, and the DMA happens from those
* pages. To software using the DMA mapping interface,
* this looks simply like a data cache.
*
* If we have more than 16M of RAM in the system, we may
* need bounce buffers. We check and remember that here.
*
* There are exceptions, however. VLB devices can do
* 32-bit DMA, and indicate that here.
*
* ...or, there is an opposite case. The most segments
* a transfer will require is (maxxfer / PAGE_SIZE) + 1. If
* the caller can't handle that many segments (e.g. the
* ISA DMA controller), we may have to bounce it as well.
*/
if (avail_end <= t->_bounce_thresh ||
(flags & ISABUS_DMA_32BIT) != 0) {
/* Bouncing not necessary due to memory size. */
map->_dm_bounce_thresh = 0;
}
cookieflags = 0;
if (map->_dm_bounce_thresh != 0 ||
((map->_dm_size / PAGE_SIZE) + 1) > map->_dm_segcnt) {
cookieflags |= ID_MIGHT_NEED_BOUNCE;
cookiesize += (sizeof(bus_dma_segment_t) * map->_dm_segcnt);
}
/*
* Allocate our cookie.
*/
if ((cookiestore = malloc(cookiesize, M_DMAMAP,
(flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) {
error = ENOMEM;
goto out;
}
memset(cookiestore, 0, cookiesize);
cookie = (struct i386_isa_dma_cookie *)cookiestore;
cookie->id_flags = cookieflags;
map->_dm_cookie = cookie;
if (cookieflags & ID_MIGHT_NEED_BOUNCE) {
/*
* Allocate the bounce pages now if the caller
* wishes us to do so.
*/
if ((flags & BUS_DMA_ALLOCNOW) == 0)
goto out;
error = _isa_dma_alloc_bouncebuf(t, map, size, flags);
}
out:
if (error) {
if (map->_dm_cookie != NULL)
free(map->_dm_cookie, M_DMAMAP);
_bus_dmamap_destroy(t, map);
}
return (error);
}
/*
* Destroy an ISA DMA map.
*/
void
_isa_bus_dmamap_destroy(t, map)
bus_dma_tag_t t;
bus_dmamap_t map;
{
struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
/*
* Free any bounce pages this map might hold.
*/
if (cookie->id_flags & ID_HAS_BOUNCE)
_isa_dma_free_bouncebuf(t, map);
free(cookie, M_DMAMAP);
_bus_dmamap_destroy(t, map);
}
/*
* Load an ISA DMA map with a linear buffer.
*/
int
_isa_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_isa_dma_cookie *cookie = map->_dm_cookie;
int error;
STAT_INCR(isa_dma_stats_loads);
/*
* Make sure that on error condition we return "no valid mappings."
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
/*
* Try to load the map the normal way. If this errors out,
* and we can bounce, we will.
*/
error = _bus_dmamap_load(t, map, buf, buflen, p, flags);
if (error == 0 ||
(error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0))
return (error);
/*
* First attempt failed; bounce it.
*/
STAT_INCR(isa_dma_stats_bounces);
/*
* Allocate bounce pages, if necessary.
*/
if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) {
error = _isa_dma_alloc_bouncebuf(t, map, buflen, flags);
if (error)
return (error);
}
/*
* Cache a pointer to the caller's buffer and load the DMA map
* with the bounce buffer.
*/
cookie->id_origbuf = buf;
cookie->id_origbuflen = buflen;
cookie->id_buftype = ID_BUFTYPE_LINEAR;
error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, buflen,
p, flags);
if (error) {
/*
* Free the bounce pages, unless our resources
* are reserved for our exclusive use.
*/
if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
_isa_dma_free_bouncebuf(t, map);
return (error);
}
/* ...so _isa_bus_dmamap_sync() knows we're bouncing */
cookie->id_flags |= ID_IS_BOUNCING;
return (0);
}
/*
* Like _isa_bus_dmamap_load(), but for mbufs.
*/
int
_isa_bus_dmamap_load_mbuf(t, map, m0, flags)
bus_dma_tag_t t;
bus_dmamap_t map;
struct mbuf *m0;
int flags;
{
struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
int error;
/*
* Make sure on error condition we return "no valid mappings."
*/
map->dm_mapsize = 0;
map->dm_nsegs = 0;
#ifdef DIAGNOSTIC
if ((m0->m_flags & M_PKTHDR) == 0)
panic("_isa_bus_dmamap_load_mbuf: no packet header");
#endif
if (m0->m_pkthdr.len > map->_dm_size)
return (EINVAL);
/*
* Try to load the map the normal way. If this errors out,
* and we can bounce, we will.
*/
error = _bus_dmamap_load_mbuf(t, map, m0, flags);
if (error == 0 ||
(error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0))
return (error);
/*
* First attempt failed; bounce it.
*/
STAT_INCR(isa_dma_stats_bounces);
/*
* Allocate bounce pages, if necessary.
*/
if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) {
error = _isa_dma_alloc_bouncebuf(t, map, m0->m_pkthdr.len,
flags);
if (error)
return (error);
}
/*
* Cache a pointer to the caller's buffer and load the DMA map
* with the bounce buffer.
*/
cookie->id_origbuf = m0;
cookie->id_origbuflen = m0->m_pkthdr.len; /* not really used */
cookie->id_buftype = ID_BUFTYPE_MBUF;
error = _bus_dmamap_load(t, map, cookie->id_bouncebuf,
m0->m_pkthdr.len, NULL, flags);
if (error) {
/*
* Free the bounce pages, unless our resources
* are reserved for our exclusive use.
*/
if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
_isa_dma_free_bouncebuf(t, map);
return (error);
}
/* ...so _isa_bus_dmamap_sync() knows we're bouncing */
cookie->id_flags |= ID_IS_BOUNCING;
return (0);
}
/*
* Like _isa_bus_dmamap_load(), but for uios.
*/
int
_isa_bus_dmamap_load_uio(t, map, uio, flags)
bus_dma_tag_t t;
bus_dmamap_t map;
struct uio *uio;
int flags;
{
panic("_isa_bus_dmamap_load_uio: not implemented");
}
/*
* Like _isa_bus_dmamap_load(), but for raw memory allocated with
* bus_dmamem_alloc().
*/
int
_isa_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("_isa_bus_dmamap_load_raw: not implemented");
}
/*
* Unload an ISA DMA map.
*/
void
_isa_bus_dmamap_unload(t, map)
bus_dma_tag_t t;
bus_dmamap_t map;
{
struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
/*
* If we have bounce pages, free them, unless they're
* reserved for our exclusive use.
*/
if ((cookie->id_flags & ID_HAS_BOUNCE) &&
(map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
_isa_dma_free_bouncebuf(t, map);
cookie->id_flags &= ~ID_IS_BOUNCING;
cookie->id_buftype = ID_BUFTYPE_INVALID;
/*
* Do the generic bits of the unload.
*/
_bus_dmamap_unload(t, map);
}
/*
* Synchronize an ISA DMA map.
*/
void
_isa_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_isa_dma_cookie *cookie = map->_dm_cookie;
/*
* Mixing PRE and POST operations is not allowed.
*/
if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 &&
(ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0)
panic("_isa_bus_dmamap_sync: mix PRE and POST");
#ifdef DIAGNOSTIC
if ((ops & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) {
if (offset >= map->dm_mapsize)
panic("_isa_bus_dmamap_sync: bad offset");
if (len == 0 || (offset + len) > map->dm_mapsize)
panic("_isa_bus_dmamap_sync: bad length");
}
#endif
/*
* If we're not bouncing, just return; nothing to do.
*/
if ((cookie->id_flags & ID_IS_BOUNCING) == 0)
return;
switch (cookie->id_buftype) {
case ID_BUFTYPE_LINEAR:
/*
* Nothing to do for pre-read.
*/
if (ops & BUS_DMASYNC_PREWRITE) {
/*
* Copy the caller's buffer to the bounce buffer.
*/
memcpy((char *)cookie->id_bouncebuf + offset,
(char *)cookie->id_origbuf + offset, len);
}
if (ops & BUS_DMASYNC_POSTREAD) {
/*
* Copy the bounce buffer to the caller's buffer.
*/
memcpy((char *)cookie->id_origbuf + offset,
(char *)cookie->id_bouncebuf + offset, len);
}
/*
* Nothing to do for post-write.
*/
break;
case ID_BUFTYPE_MBUF:
{
struct mbuf *m, *m0 = cookie->id_origbuf;
bus_size_t minlen, moff;
/*
* Nothing to do for pre-read.
*/
if (ops & BUS_DMASYNC_PREWRITE) {
/*
* Copy the caller's buffer to the bounce buffer.
*/
m_copydata(m0, offset, len,
(char *)cookie->id_bouncebuf + offset);
}
if (ops & BUS_DMASYNC_POSTREAD) {
/*
* Copy the bounce buffer to the caller's buffer.
*/
for (moff = offset, m = m0; m != NULL && len != 0;
m = m->m_next) {
/* Find the beginning mbuf. */
if (moff >= m->m_len) {
moff -= m->m_len;
continue;
}
/*
* Now at the first mbuf to sync; nail
* each one until we have exhausted the
* length.
*/
minlen = len < m->m_len - moff ?
len : m->m_len - moff;
memcpy(mtod(m, caddr_t) + moff,
(char *)cookie->id_bouncebuf + offset,
minlen);
moff = 0;
len -= minlen;
offset += minlen;
}
}
/*
* Nothing to do for post-write.
*/
break;
}
case ID_BUFTYPE_UIO:
panic("_isa_bus_dmamap_sync: ID_BUFTYPE_UIO");
break;
case ID_BUFTYPE_RAW:
panic("_isa_bus_dmamap_sync: ID_BUFTYPE_RAW");
break;
case ID_BUFTYPE_INVALID:
panic("_isa_bus_dmamap_sync: ID_BUFTYPE_INVALID");
break;
default:
printf("unknown buffer type %d\n", cookie->id_buftype);
panic("_isa_bus_dmamap_sync");
}
}
/*
* Allocate memory safe for ISA DMA.
*/
int
_isa_bus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags)
bus_dma_tag_t t;
bus_size_t size, alignment, boundary;
bus_dma_segment_t *segs;
int nsegs;
int *rsegs;
int flags;
{
paddr_t high;
if (avail_end > ISA_DMA_BOUNCE_THRESHOLD)
high = trunc_page(ISA_DMA_BOUNCE_THRESHOLD);
else
high = trunc_page(avail_end);
return (_bus_dmamem_alloc_range(t, size, alignment, boundary,
segs, nsegs, rsegs, flags, 0, high));
}
/**********************************************************************
* ISA DMA utility functions
**********************************************************************/
int
_isa_dma_alloc_bouncebuf(t, map, size, flags)
bus_dma_tag_t t;
bus_dmamap_t map;
bus_size_t size;
int flags;
{
struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
int error = 0;
cookie->id_bouncebuflen = round_page(size);
error = _isa_bus_dmamem_alloc(t, cookie->id_bouncebuflen,
PAGE_SIZE, map->_dm_boundary, cookie->id_bouncesegs,
map->_dm_segcnt, &cookie->id_nbouncesegs, flags);
if (error)
goto out;
error = _bus_dmamem_map(t, cookie->id_bouncesegs,
cookie->id_nbouncesegs, cookie->id_bouncebuflen,
(caddr_t *)&cookie->id_bouncebuf, flags);
out:
if (error) {
_bus_dmamem_free(t, cookie->id_bouncesegs,
cookie->id_nbouncesegs);
cookie->id_bouncebuflen = 0;
cookie->id_nbouncesegs = 0;
} else {
cookie->id_flags |= ID_HAS_BOUNCE;
STAT_INCR(isa_dma_stats_nbouncebufs);
}
return (error);
}
void
_isa_dma_free_bouncebuf(t, map)
bus_dma_tag_t t;
bus_dmamap_t map;
{
struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
STAT_DECR(isa_dma_stats_nbouncebufs);
_bus_dmamem_unmap(t, cookie->id_bouncebuf,
cookie->id_bouncebuflen);
_bus_dmamem_free(t, cookie->id_bouncesegs,
cookie->id_nbouncesegs);
cookie->id_bouncebuflen = 0;
cookie->id_nbouncesegs = 0;
cookie->id_flags &= ~ID_HAS_BOUNCE;
}