File: [local] / sys / dev / pci / agp.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:13:56 2008 UTC (16 years, 5 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: agp.c,v 1.6 2007/08/04 19:40:25 reyk Exp $ */
/*-
* Copyright (c) 2000 Doug Rabson
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD: src/sys/pci/agp.c,v 1.12 2001/05/19 01:28:07 alfred Exp $
*/
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/agpio.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <uvm/uvm.h>
#include <dev/pci/pcivar.h>
#include <dev/ic/mc6845reg.h>
#include <dev/ic/pcdisplayvar.h>
#include <dev/ic/vgareg.h>
#include <dev/ic/vgavar.h>
#include <dev/pci/agpvar.h>
#include <dev/pci/agpreg.h>
struct agp_memory *agp_find_memory(struct vga_pci_softc *sc, int id);
const struct agp_product *agp_lookup(struct pci_attach_args *pa);
struct pci_attach_args agp_pchb_pa;
int agp_pchb_pa_set = 0;
void
agp_attach(struct device *parent, struct device *self, void *aux)
{
struct pci_attach_args *pa = aux;
struct vga_pci_softc *sc = (struct vga_pci_softc *)self;
const struct agp_product *ap;
u_int memsize;
int i, ret;
ap = agp_lookup(pa);
if (ap) {
static const int agp_max[][2] = {
{0, 0},
{32, 4},
{64, 28},
{128, 96},
{256, 204},
{512, 440},
{1024, 942},
{2048, 1920},
{4096, 3932}
};
#define agp_max_size (sizeof(agp_max)/sizeof(agp_max[0]))
/*
* Work out an upper bound for agp memory allocation. This
* uses a heuristic table from the Linux driver.
*/
memsize = ptoa(physmem) >> 20;
for (i = 0; i < agp_max_size && memsize > agp_max[i][0]; i++)
;
if (i == agp_max_size)
i = agp_max_size - 1;
sc->sc_maxmem = agp_max[i][1] << 20;
/*
* The lock is used to prevent re-entry to
* agp_generic_bind_memory() since that function can sleep.
*/
lockinit(&sc->sc_lock, PZERO|PCATCH, "agplk", 0, 0);
TAILQ_INIT(&sc->sc_memory);
sc->sc_pcitag = pa->pa_tag;
sc->sc_pc = pa->pa_pc;
sc->sc_id = pa->pa_id;
sc->sc_dmat = pa->pa_dmat;
pci_get_capability(sc->sc_pc, sc->sc_pcitag, PCI_CAP_AGP,
&sc->sc_capoff, NULL);
ret = (*ap->ap_attach)(sc, pa, &agp_pchb_pa);
if (ret == 0)
printf(": aperture at 0x%lx, size 0x%lx",
(u_long)sc->sc_apaddr,
(u_long)AGP_GET_APERTURE(sc));
else {
sc->sc_chipc = NULL;
printf(": AGP GART");
}
}
}
paddr_t
agp_mmap(void *v, off_t off, int prot)
{
struct vga_config* vs = (struct vga_config*) v;
struct vga_pci_softc* sc = (struct vga_pci_softc *)vs->vc_softc;
if (sc->sc_apaddr) {
if (off > AGP_GET_APERTURE(sc))
return (-1);
return atop(sc->sc_apaddr + off);
}
return -1;
}
int
agp_ioctl(void *v, u_long cmd, caddr_t addr, int flag, struct proc *pb)
{
struct vga_config *vc = v;
struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc;
struct agp_memory *mem;
agp_info *info;
agp_setup *setup;
agp_allocate *alloc;
agp_bind *bind;
agp_unbind *unbind;
vsize_t size;
int error = 0;
if (sc->sc_methods == NULL || sc->sc_chipc == NULL)
return (ENXIO);
switch (cmd) {
case AGPIOC_INFO:
if (!sc->sc_chipc)
return (ENXIO);
case AGPIOC_ACQUIRE:
case AGPIOC_RELEASE:
case AGPIOC_SETUP:
case AGPIOC_ALLOCATE:
case AGPIOC_DEALLOCATE:
case AGPIOC_BIND:
case AGPIOC_UNBIND:
if (cmd != AGPIOC_INFO && !(flag & FWRITE))
return (EPERM);
break;
}
switch(cmd) {
case AGPIOC_INFO:
info = (agp_info *)addr;
bzero(info, sizeof *info);
info->bridge_id = sc->sc_id;
if (sc->sc_capoff != 0)
info->agp_mode = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
AGP_STATUS + sc->sc_capoff);
else
info->agp_mode = 0; /* i810 doesn't have real AGP */
info->aper_base = sc->sc_apaddr;
info->aper_size = AGP_GET_APERTURE(sc) >> 20;
info->pg_total =
info->pg_system = sc->sc_maxmem >> AGP_PAGE_SHIFT;
info->pg_used = sc->sc_allocated >> AGP_PAGE_SHIFT;
break;
case AGPIOC_ACQUIRE:
if (sc->sc_state != AGP_ACQUIRE_FREE)
error = EBUSY;
else
sc->sc_state = AGP_ACQUIRE_USER;
break;
case AGPIOC_RELEASE:
if (sc->sc_state == AGP_ACQUIRE_FREE)
break;
if (sc->sc_state != AGP_ACQUIRE_USER) {
error = EBUSY;
break;
}
/*
* Clear out the aperture and free any
* outstanding memory blocks.
*/
TAILQ_FOREACH(mem, &sc->sc_memory, am_link) {
if (mem->am_is_bound) {
printf("agp_release_helper: mem %d is bound\n",
mem->am_id);
AGP_UNBIND_MEMORY(sc, mem);
}
}
sc->sc_state = AGP_ACQUIRE_FREE;
break;
case AGPIOC_SETUP:
setup = (agp_setup *)addr;
error = AGP_ENABLE(sc, setup->agp_mode);
break;
case AGPIOC_ALLOCATE:
alloc = (agp_allocate *)addr;
size = alloc->pg_count << AGP_PAGE_SHIFT;
if (sc->sc_allocated + size > sc->sc_maxmem)
error = EINVAL;
else {
mem = AGP_ALLOC_MEMORY(sc, alloc->type, size);
if (mem) {
alloc->key = mem->am_id;
alloc->physical = mem->am_physical;
} else
error = ENOMEM;
}
break;
case AGPIOC_DEALLOCATE:
mem = agp_find_memory(sc, *(int *)addr);
if (mem)
AGP_FREE_MEMORY(sc, mem);
else
error = ENOENT;
break;
case AGPIOC_BIND:
bind = (agp_bind *)addr;
mem = agp_find_memory(sc, bind->key);
if (!mem)
error = ENOENT;
else
error = AGP_BIND_MEMORY(sc, mem,
bind->pg_start << AGP_PAGE_SHIFT);
break;
case AGPIOC_UNBIND:
unbind = (agp_unbind *)addr;
mem = agp_find_memory(sc, unbind->key);
if (!mem)
error = ENOENT;
else
error = AGP_UNBIND_MEMORY(sc, mem);
break;
default:
error = ENOTTY;
}
return (error);
}
#ifdef notyet
void
agp_close(void *v)
{
struct vga_config *vc = v;
struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc;
struct agp_memory *mem;
/*
* Clear out the aperture and free any
* outstanding memory blocks.
*/
TAILQ_FOREACH(mem, &sc->sc_memory, am_link) {
if (mem->am_is_bound) {
AGP_UNBIND_MEMORY(sc, mem);
}
}
while (!TAILQ_EMPTY(&sc->sc_memory)) {
mem = TAILQ_FIRST(&sc->sc_memory);
AGP_FREE_MEMORY(sc, mem);
}
sc->sc_state = AGP_ACQUIRE_FREE;
}
#endif
struct agp_memory *
agp_find_memory(struct vga_pci_softc *sc, int id)
{
struct agp_memory *mem;
AGP_DPF("searching for memory block %d\n", id);
TAILQ_FOREACH(mem, &sc->sc_memory, am_link) {
AGP_DPF("considering memory block %d\n", mem->am_id);
if (mem->am_id == id)
return (mem);
}
return 0;
}
const struct agp_product *
agp_lookup(struct pci_attach_args *pa)
{
const struct agp_product *ap;
if (!agp_pchb_pa_set)
return (NULL);
agp_pchb_pa_set = 0;
/* First find the vendor. */
for (ap = agp_products; ap->ap_attach != NULL; ap++)
if (ap->ap_vendor == PCI_VENDOR(pa->pa_id))
break;
if (ap->ap_attach == NULL)
return (NULL);
/* Now find the product within the vendor's domain. */
for (; ap->ap_attach != NULL; ap++) {
/* Ran out of this vendor's section of the table. */
if (ap->ap_vendor != PCI_VENDOR(pa->pa_id))
return (NULL);
if (ap->ap_product == PCI_PRODUCT(pa->pa_id))
break; /* Exact match. */
if (ap->ap_product == (u_int32_t) -1)
break; /* Wildcard match. */
}
if (ap->ap_attach == NULL)
ap = NULL;
return (ap);
}
void
pciagp_set_pchb(struct pci_attach_args *pa)
{
if (!agp_pchb_pa_set) {
memcpy(&agp_pchb_pa, pa, sizeof *pa);
agp_pchb_pa_set++;
}
}
int
agp_map_aperture(struct vga_pci_softc *sc, u_int32_t bar, u_int32_t memtype)
{
/*
* Find and the aperture. Don't map it (yet), this would
* eat KVA.
*/
if (pci_mapreg_info(sc->sc_pc, sc->sc_pcitag, bar,
memtype, &sc->sc_apaddr, &sc->sc_apsize,
&sc->sc_apflags) != 0)
return ENXIO;
return 0;
}
struct agp_gatt *
agp_alloc_gatt(struct vga_pci_softc *sc)
{
u_int32_t apsize = AGP_GET_APERTURE(sc);
u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
struct agp_gatt *gatt;
int nseg;
gatt = malloc(sizeof(*gatt), M_DEVBUF, M_NOWAIT);
if (!gatt)
return (NULL);
bzero(gatt, sizeof(*gatt));
gatt->ag_entries = entries;
if (agp_alloc_dmamem(sc->sc_dmat, entries * sizeof(u_int32_t),
0, &gatt->ag_dmamap, (caddr_t *)&gatt->ag_virtual,
&gatt->ag_physical, &gatt->ag_dmaseg, 1, &nseg) != 0)
return NULL;
gatt->ag_size = entries * sizeof(u_int32_t);
memset(gatt->ag_virtual, 0, gatt->ag_size);
agp_flush_cache();
return gatt;
}
void
agp_free_gatt(struct vga_pci_softc *sc, struct agp_gatt *gatt)
{
agp_free_dmamem(sc->sc_dmat, gatt->ag_size, gatt->ag_dmamap,
(caddr_t)gatt->ag_virtual, &gatt->ag_dmaseg, 1);
free(gatt, M_DEVBUF);
}
int
agp_generic_detach(struct vga_pci_softc *sc)
{
lockmgr(&sc->sc_lock, LK_DRAIN, NULL);
agp_flush_cache();
return 0;
}
int
agp_generic_enable(struct vga_pci_softc *sc, u_int32_t mode)
{
pcireg_t tstatus, mstatus;
pcireg_t command;
int rq, sba, fw, rate, capoff;
if (pci_get_capability(sc->sc_pc, sc->sc_pcitag, PCI_CAP_AGP,
&capoff, NULL) == 0) {
printf("agp_generic_enable: not an AGP capable device\n");
return -1;
}
tstatus = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
sc->sc_capoff + AGP_STATUS);
mstatus = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
capoff + AGP_STATUS);
/* Set RQ to the min of mode, tstatus and mstatus */
rq = AGP_MODE_GET_RQ(mode);
if (AGP_MODE_GET_RQ(tstatus) < rq)
rq = AGP_MODE_GET_RQ(tstatus);
if (AGP_MODE_GET_RQ(mstatus) < rq)
rq = AGP_MODE_GET_RQ(mstatus);
/* Set SBA if all three can deal with SBA */
sba = (AGP_MODE_GET_SBA(tstatus)
& AGP_MODE_GET_SBA(mstatus)
& AGP_MODE_GET_SBA(mode));
/* Similar for FW */
fw = (AGP_MODE_GET_FW(tstatus)
& AGP_MODE_GET_FW(mstatus)
& AGP_MODE_GET_FW(mode));
/* Figure out the max rate */
rate = (AGP_MODE_GET_RATE(tstatus)
& AGP_MODE_GET_RATE(mstatus)
& AGP_MODE_GET_RATE(mode));
if (rate & AGP_MODE_RATE_4x)
rate = AGP_MODE_RATE_4x;
else if (rate & AGP_MODE_RATE_2x)
rate = AGP_MODE_RATE_2x;
else
rate = AGP_MODE_RATE_1x;
/* Construct the new mode word and tell the hardware */
command = AGP_MODE_SET_RQ(0, rq);
command = AGP_MODE_SET_SBA(command, sba);
command = AGP_MODE_SET_FW(command, fw);
command = AGP_MODE_SET_RATE(command, rate);
command = AGP_MODE_SET_AGP(command, 1);
pci_conf_write(sc->sc_pc, sc->sc_pcitag,
sc->sc_capoff + AGP_COMMAND, command);
pci_conf_write(sc->sc_pc, sc->sc_pcitag, capoff + AGP_COMMAND, command);
return 0;
}
struct agp_memory *
agp_generic_alloc_memory(struct vga_pci_softc *sc, int type, vsize_t size)
{
struct agp_memory *mem;
if (type != 0) {
printf("agp_generic_alloc_memory: unsupported type %d\n", type);
return 0;
}
mem = malloc(sizeof *mem, M_DEVBUF, M_WAITOK);
if (mem == NULL)
return NULL;
bzero(mem, sizeof *mem);
if (bus_dmamap_create(sc->sc_dmat, size, size / PAGE_SIZE + 1,
size, 0, BUS_DMA_NOWAIT, &mem->am_dmamap) != 0) {
free(mem, M_DEVBUF);
return NULL;
}
mem->am_id = sc->sc_nextid++;
mem->am_size = size;
TAILQ_INSERT_TAIL(&sc->sc_memory, mem, am_link);
sc->sc_allocated += size;
return mem;
}
int
agp_generic_free_memory(struct vga_pci_softc *sc, struct agp_memory *mem)
{
if (mem->am_is_bound)
return EBUSY;
sc->sc_allocated -= mem->am_size;
TAILQ_REMOVE(&sc->sc_memory, mem, am_link);
bus_dmamap_destroy(sc->sc_dmat, mem->am_dmamap);
free(mem, M_DEVBUF);
return 0;
}
int
agp_generic_bind_memory(struct vga_pci_softc *sc, struct agp_memory *mem,
off_t offset)
{
bus_dma_segment_t *segs, *seg;
bus_size_t done, j;
bus_addr_t pa;
off_t i, k;
int nseg, error;
lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
if (mem->am_is_bound) {
printf("AGP: memory already bound\n");
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
return EINVAL;
}
if (offset < 0
|| (offset & (AGP_PAGE_SIZE - 1)) != 0
|| offset + mem->am_size > AGP_GET_APERTURE(sc)) {
printf("AGP: binding memory at bad offset %#lx\n",
(unsigned long) offset);
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
return EINVAL;
}
/*
* The memory here needs to be directly accessable from the
* AGP video card, so it should be allocated using bus_dma.
* However, it need not be contiguous, since individual pages
* are translated using the GATT.
*/
nseg = (mem->am_size + PAGE_SIZE - 1) / PAGE_SIZE;
segs = malloc(nseg * sizeof *segs, M_DEVBUF, M_WAITOK);
if (segs == NULL) {
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
AGP_DPF("malloc segs (%u) failed\n",
nseg * sizeof *segs);
return ENOMEM;
}
if ((error = bus_dmamem_alloc(sc->sc_dmat, mem->am_size, PAGE_SIZE, 0,
segs, nseg, &mem->am_nseg, BUS_DMA_WAITOK)) != 0) {
free(segs, M_DEVBUF);
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
AGP_DPF("bus_dmamem_alloc failed %d\n", error);
return error;
}
if ((error = bus_dmamem_map(sc->sc_dmat, segs, mem->am_nseg,
mem->am_size, &mem->am_virtual, BUS_DMA_WAITOK)) != 0) {
bus_dmamem_free(sc->sc_dmat, segs, mem->am_nseg);
free(segs, M_DEVBUF);
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
AGP_DPF("bus_dmamem_map failed %d\n", error);
return error;
}
if ((error = bus_dmamap_load(sc->sc_dmat, mem->am_dmamap,
mem->am_virtual, mem->am_size, NULL,
BUS_DMA_WAITOK)) != 0) {
bus_dmamem_unmap(sc->sc_dmat, mem->am_virtual,
mem->am_size);
bus_dmamem_free(sc->sc_dmat, segs, mem->am_nseg);
free(segs, M_DEVBUF);
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
AGP_DPF("bus_dmamap_load failed %d\n", error);
return error;
}
mem->am_dmaseg = segs;
/*
* Bind the individual pages and flush the chipset's
* TLB.
*/
done = 0;
for (i = 0; i < mem->am_dmamap->dm_nsegs; i++) {
seg = &mem->am_dmamap->dm_segs[i];
/*
* Install entries in the GATT, making sure that if
* AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not
* aligned to PAGE_SIZE, we don't modify too many GATT
* entries.
*/
for (j = 0; j < seg->ds_len && (done + j) < mem->am_size;
j += AGP_PAGE_SIZE) {
pa = seg->ds_addr + j;
AGP_DPF("binding offset %#lx to pa %#lx\n",
(unsigned long)(offset + done + j),
(unsigned long)pa);
error = AGP_BIND_PAGE(sc, offset + done + j, pa);
if (error) {
/*
* Bail out. Reverse all the mappings
* and unwire the pages.
*/
for (k = 0; k < done + j; k += AGP_PAGE_SIZE)
AGP_UNBIND_PAGE(sc, offset + k);
bus_dmamap_unload(sc->sc_dmat, mem->am_dmamap);
bus_dmamem_unmap(sc->sc_dmat, mem->am_virtual,
mem->am_size);
bus_dmamem_free(sc->sc_dmat, mem->am_dmaseg,
mem->am_nseg);
free(mem->am_dmaseg, M_DEVBUF);
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
AGP_DPF("AGP_BIND_PAGE failed %d\n", error);
return error;
}
}
done += seg->ds_len;
}
/*
* Flush the cpu cache since we are providing a new mapping
* for these pages.
*/
agp_flush_cache();
/*
* Make sure the chipset gets the new mappings.
*/
AGP_FLUSH_TLB(sc);
mem->am_offset = offset;
mem->am_is_bound = 1;
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
return 0;
}
int
agp_generic_unbind_memory(struct vga_pci_softc *sc, struct agp_memory *mem)
{
int i;
lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
if (!mem->am_is_bound) {
printf("AGP: memory is not bound\n");
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
return EINVAL;
}
/*
* Unbind the individual pages and flush the chipset's
* TLB. Unwire the pages so they can be swapped.
*/
for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
AGP_UNBIND_PAGE(sc, mem->am_offset + i);
agp_flush_cache();
AGP_FLUSH_TLB(sc);
bus_dmamap_unload(sc->sc_dmat, mem->am_dmamap);
bus_dmamem_unmap(sc->sc_dmat, mem->am_virtual, mem->am_size);
bus_dmamem_free(sc->sc_dmat, mem->am_dmaseg, mem->am_nseg);
free(mem->am_dmaseg, M_DEVBUF);
mem->am_offset = 0;
mem->am_is_bound = 0;
lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
return 0;
}
int
agp_alloc_dmamem(bus_dma_tag_t tag, size_t size, int flags,
bus_dmamap_t *mapp, caddr_t *vaddr, bus_addr_t *baddr,
bus_dma_segment_t *seg, int nseg, int *rseg)
{
int error, level = 0;
if ((error = bus_dmamem_alloc(tag, size, PAGE_SIZE, 0,
seg, nseg, rseg, BUS_DMA_NOWAIT)) != 0)
goto out;
level++;
if ((error = bus_dmamem_map(tag, seg, *rseg, size, vaddr,
BUS_DMA_NOWAIT | flags)) != 0)
goto out;
level++;
if ((error = bus_dmamap_create(tag, size, *rseg, size, 0,
BUS_DMA_NOWAIT, mapp)) != 0)
goto out;
level++;
if ((error = bus_dmamap_load(tag, *mapp, *vaddr, size, NULL,
BUS_DMA_NOWAIT)) != 0)
goto out;
*baddr = (*mapp)->dm_segs[0].ds_addr;
return 0;
out:
switch (level) {
case 3:
bus_dmamap_destroy(tag, *mapp);
/* FALLTHROUGH */
case 2:
bus_dmamem_unmap(tag, *vaddr, size);
/* FALLTHROUGH */
case 1:
bus_dmamem_free(tag, seg, *rseg);
break;
default:
break;
}
return error;
}
void
agp_free_dmamem(bus_dma_tag_t tag, size_t size, bus_dmamap_t map,
caddr_t vaddr, bus_dma_segment_t *seg, int nseg)
{
bus_dmamap_unload(tag, map);
bus_dmamap_destroy(tag, map);
bus_dmamem_unmap(tag, vaddr, size);
bus_dmamem_free(tag, seg, nseg);
}