busdma_machdep.c revision 99657
178342Sbenno/*
299657Sbenno * Copyright (c) 2002 Peter Grehan
399657Sbenno * Copyright (c) 1997, 1998 Justin T. Gibbs.
478342Sbenno * All rights reserved.
578342Sbenno *
678342Sbenno * Redistribution and use in source and binary forms, with or without
778342Sbenno * modification, are permitted provided that the following conditions
878342Sbenno * are met:
978342Sbenno * 1. Redistributions of source code must retain the above copyright
1099657Sbenno *    notice, this list of conditions, and the following disclaimer,
1199657Sbenno *    without modification, immediately at the beginning of the file.
1299657Sbenno * 2. The name of the author may not be used to endorse or promote products
1399657Sbenno *    derived from this software without specific prior written permission.
1478342Sbenno *
1599657Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1699657Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1799657Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1899657Sbenno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1999657Sbenno * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2099657Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2199657Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2299657Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2399657Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2499657Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2599657Sbenno * SUCH DAMAGE.
2699657Sbenno *
2799657Sbenno *   From i386/busdma_machdep.c,v 1.26 2002/04/19 22:58:09 alfred
2878342Sbenno */
2978342Sbenno
3078342Sbenno#ifndef lint
3178342Sbennostatic const char rcsid[] =
3278342Sbenno  "$FreeBSD: head/sys/powerpc/powerpc/busdma_machdep.c 99657 2002-07-09 12:47:14Z benno $";
3378342Sbenno#endif /* not lint */
3478342Sbenno
3599657Sbenno/*
3699657Sbenno * MacPPC bus dma support routines
3799657Sbenno */
3878342Sbenno
3999657Sbenno#include <sys/param.h>
4099657Sbenno#include <sys/systm.h>
4199657Sbenno#include <sys/malloc.h>
4299657Sbenno#include <sys/bus.h>
4399657Sbenno#include <sys/interrupt.h>
4499657Sbenno#include <sys/lock.h>
4599657Sbenno#include <sys/proc.h>
4699657Sbenno#include <sys/mutex.h>
4799657Sbenno
4899657Sbenno#include <vm/vm.h>
4999657Sbenno#include <vm/vm_page.h>
5099657Sbenno
5199657Sbenno#include <machine/bus.h>
5299657Sbenno
5399657Sbennostruct bus_dma_tag {
5499657Sbenno	bus_dma_tag_t     parent;
5599657Sbenno	bus_size_t        alignment;
5699657Sbenno	bus_size_t        boundary;
5799657Sbenno	bus_addr_t        lowaddr;
5899657Sbenno	bus_addr_t        highaddr;
5999657Sbenno	bus_dma_filter_t *filter;
6099657Sbenno	void             *filterarg;
6199657Sbenno	bus_size_t        maxsize;
6299657Sbenno	u_int             nsegments;
6399657Sbenno	bus_size_t        maxsegsz;
6499657Sbenno	int               flags;
6599657Sbenno	int               ref_count;
6699657Sbenno	int               map_count;
6799657Sbenno};
6899657Sbenno
6999657Sbennostruct bus_dmamap {
7099657Sbenno        bus_dma_tag_t          dmat;
7199657Sbenno        void                  *buf;             /* unmapped buffer pointer */
7299657Sbenno        bus_size_t             buflen;          /* unmapped buffer length */
7399657Sbenno        bus_dmamap_callback_t *callback;
7499657Sbenno        void                  *callback_arg;
7599657Sbenno};
7699657Sbenno
7799657Sbenno/*
7899657Sbenno * Allocate a device specific dma_tag.
7999657Sbenno */
8099657Sbennoint
8199657Sbennobus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
8299657Sbenno		   bus_size_t boundary, bus_addr_t lowaddr,
8399657Sbenno		   bus_addr_t highaddr, bus_dma_filter_t *filter,
8499657Sbenno		   void *filterarg, bus_size_t maxsize, int nsegments,
8599657Sbenno		   bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat)
8699657Sbenno{
8799657Sbenno	bus_dma_tag_t newtag;
8899657Sbenno	int error = 0;
8999657Sbenno
9099657Sbenno	/* Return a NULL tag on failure */
9199657Sbenno	*dmat = NULL;
9299657Sbenno
9399657Sbenno	newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT);
9499657Sbenno	if (newtag == NULL)
9599657Sbenno		return (ENOMEM);
9699657Sbenno
9799657Sbenno	newtag->parent = parent;
9899657Sbenno	newtag->alignment = alignment;
9999657Sbenno	newtag->boundary = boundary;
10099657Sbenno	newtag->lowaddr = trunc_page((vm_offset_t)lowaddr) + (PAGE_SIZE - 1);
10199657Sbenno	newtag->highaddr = trunc_page((vm_offset_t)highaddr) + (PAGE_SIZE - 1);
10299657Sbenno	newtag->filter = filter;
10399657Sbenno	newtag->filterarg = filterarg;
10499657Sbenno        newtag->maxsize = maxsize;
10599657Sbenno        newtag->nsegments = nsegments;
10699657Sbenno	newtag->maxsegsz = maxsegsz;
10799657Sbenno	newtag->flags = flags;
10899657Sbenno	newtag->ref_count = 1; /* Count ourself */
10999657Sbenno	newtag->map_count = 0;
11099657Sbenno
11199657Sbenno        /*
11299657Sbenno	 * Take into account any restrictions imposed by our parent tag
11399657Sbenno	 */
11499657Sbenno        if (parent != NULL) {
11599657Sbenno                newtag->lowaddr = min(parent->lowaddr, newtag->lowaddr);
11699657Sbenno                newtag->highaddr = max(parent->highaddr, newtag->highaddr);
11799657Sbenno
11899657Sbenno                /*
11999657Sbenno                 * XXX Not really correct??? Probably need to honor boundary
12099657Sbenno                 *     all the way up the inheritence chain.
12199657Sbenno                 */
12299657Sbenno                newtag->boundary = max(parent->boundary, newtag->boundary);
12399657Sbenno                if (newtag->filter == NULL) {
12499657Sbenno                        /*
12599657Sbenno                         * Short circuit looking at our parent directly
12699657Sbenno                         * since we have encapsulated all of its information
12799657Sbenno                         */
12899657Sbenno                        newtag->filter = parent->filter;
12999657Sbenno                        newtag->filterarg = parent->filterarg;
13099657Sbenno                        newtag->parent = parent->parent;
13199657Sbenno		}
13299657Sbenno                if (newtag->parent != NULL) {
13399657Sbenno                        parent->ref_count++;
13499657Sbenno		}
13599657Sbenno	}
13699657Sbenno
13799657Sbenno	*dmat = newtag;
13899657Sbenno	return (error);
13999657Sbenno}
14099657Sbenno
14199657Sbennoint
14299657Sbennobus_dma_tag_destroy(bus_dma_tag_t dmat)
14399657Sbenno{
14499657Sbenno	if (dmat != NULL) {
14599657Sbenno
14699657Sbenno                if (dmat->map_count != 0)
14799657Sbenno                        return (EBUSY);
14899657Sbenno
14999657Sbenno                while (dmat != NULL) {
15099657Sbenno                        bus_dma_tag_t parent;
15199657Sbenno
15299657Sbenno                        parent = dmat->parent;
15399657Sbenno                        dmat->ref_count--;
15499657Sbenno                        if (dmat->ref_count == 0) {
15599657Sbenno                                free(dmat, M_DEVBUF);
15699657Sbenno                                /*
15799657Sbenno                                 * Last reference count, so
15899657Sbenno                                 * release our reference
15999657Sbenno                                 * count on our parent.
16099657Sbenno                                 */
16199657Sbenno                                dmat = parent;
16299657Sbenno                        } else
16399657Sbenno                                dmat = NULL;
16499657Sbenno                }
16599657Sbenno        }
16699657Sbenno        return (0);
16799657Sbenno}
16899657Sbenno
16999657Sbenno/*
17099657Sbenno * Allocate a handle for mapping from kva/uva/physical
17199657Sbenno * address space into bus device space.
17299657Sbenno */
17399657Sbennoint
17499657Sbennobus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
17599657Sbenno{
17699657Sbenno	*mapp = NULL;
17799657Sbenno	dmat->map_count++;
17899657Sbenno
17999657Sbenno	return (0);
18099657Sbenno}
18199657Sbenno
18299657Sbenno/*
18399657Sbenno * Destroy a handle for mapping from kva/uva/physical
18499657Sbenno * address space into bus device space.
18599657Sbenno */
18699657Sbennoint
18799657Sbennobus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
18899657Sbenno{
18999657Sbenno        if (map != NULL) {
19099657Sbenno		panic("dmamap_destroy: NULL?\n");
19199657Sbenno        }
19299657Sbenno        dmat->map_count--;
19399657Sbenno        return (0);
19499657Sbenno}
19599657Sbenno
19699657Sbenno/*
19799657Sbenno * Allocate a piece of memory that can be efficiently mapped into
19899657Sbenno * bus device space based on the constraints lited in the dma tag.
19999657Sbenno * A dmamap to for use with dmamap_load is also allocated.
20099657Sbenno */
20199657Sbennoint
20299657Sbennobus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
20399657Sbenno                 bus_dmamap_t *mapp)
20499657Sbenno{
20599657Sbenno        *mapp = NULL;
20699657Sbenno
20799657Sbenno        if (dmat->maxsize <= PAGE_SIZE) {
20899657Sbenno                *vaddr = malloc(dmat->maxsize, M_DEVBUF,
20999657Sbenno                             (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK);
21099657Sbenno        } else {
21199657Sbenno                /*
21299657Sbenno                 * XXX Use Contigmalloc until it is merged into this facility
21399657Sbenno                 *     and handles multi-seg allocations.  Nobody is doing
21499657Sbenno                 *     multi-seg allocations yet though.
21599657Sbenno                 */
21699657Sbenno                *vaddr = contigmalloc(dmat->maxsize, M_DEVBUF,
21799657Sbenno                    (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK,
21899657Sbenno                    0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul,
21999657Sbenno                    dmat->boundary);
22099657Sbenno        }
22199657Sbenno
22299657Sbenno        if (*vaddr == NULL)
22399657Sbenno                return (ENOMEM);
22499657Sbenno
22599657Sbenno        return (0);
22699657Sbenno}
22799657Sbenno
22899657Sbenno/*
22999657Sbenno * Free a piece of memory and it's allociated dmamap, that was allocated
23099657Sbenno * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
23199657Sbenno */
23278342Sbennovoid
23399657Sbennobus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
23478342Sbenno{
23599657Sbenno        if (map != NULL)
23699657Sbenno                panic("bus_dmamem_free: Invalid map freed\n");
23799657Sbenno        if (dmat->maxsize <= PAGE_SIZE)
23899657Sbenno		free(vaddr, M_DEVBUF);
23999657Sbenno        else
24099657Sbenno		contigfree(vaddr, dmat->maxsize, M_DEVBUF);
24199657Sbenno}
24278342Sbenno
24399657Sbenno/*
24499657Sbenno * Map the buffer buf into bus space using the dmamap map.
24599657Sbenno */
24699657Sbennoint
24799657Sbennobus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
24899657Sbenno                bus_size_t buflen, bus_dmamap_callback_t *callback,
24999657Sbenno                void *callback_arg, int flags)
25099657Sbenno{
25199657Sbenno        vm_offset_t             vaddr;
25299657Sbenno        vm_offset_t             paddr;
25399657Sbenno#ifdef __GNUC__
25499657Sbenno        bus_dma_segment_t       dm_segments[dmat->nsegments];
25599657Sbenno#else
25699657Sbenno        bus_dma_segment_t       dm_segments[BUS_DMAMAP_NSEGS];
25799657Sbenno#endif
25899657Sbenno        bus_dma_segment_t      *sg;
25999657Sbenno        int                     seg;
26099657Sbenno        int                     error = 0;
26199657Sbenno        vm_offset_t             nextpaddr;
26299657Sbenno
26399657Sbenno        if (map != NULL)
26499657Sbenno		panic("bus_dmamap_load: Invalid map\n");
26599657Sbenno
26699657Sbenno        vaddr = (vm_offset_t)buf;
26799657Sbenno        sg = &dm_segments[0];
26899657Sbenno        seg = 1;
26999657Sbenno        sg->ds_len = 0;
27099657Sbenno        nextpaddr = 0;
27199657Sbenno
27299657Sbenno        do {
27399657Sbenno		bus_size_t      size;
27499657Sbenno
27599657Sbenno                paddr = pmap_kextract(vaddr);
27699657Sbenno                size = PAGE_SIZE - (paddr & PAGE_MASK);
27799657Sbenno                if (size > buflen)
27899657Sbenno                        size = buflen;
27999657Sbenno
28099657Sbenno                if (sg->ds_len == 0) {
28199657Sbenno                        sg->ds_addr = paddr;
28299657Sbenno                        sg->ds_len = size;
28399657Sbenno                } else if (paddr == nextpaddr) {
28499657Sbenno                        sg->ds_len += size;
28599657Sbenno                } else {
28699657Sbenno                        /* Go to the next segment */
28799657Sbenno                        sg++;
28899657Sbenno                        seg++;
28999657Sbenno                        if (seg > dmat->nsegments)
29099657Sbenno				break;
29199657Sbenno                        sg->ds_addr = paddr;
29299657Sbenno                        sg->ds_len = size;
29399657Sbenno                }
29499657Sbenno                vaddr += size;
29599657Sbenno                nextpaddr = paddr + size;
29699657Sbenno                buflen -= size;
29799657Sbenno
29899657Sbenno        } while (buflen > 0);
29999657Sbenno
30099657Sbenno        if (buflen != 0) {
30199657Sbenno                printf("bus_dmamap_load: Too many segs! buf_len = 0x%lx\n",
30299657Sbenno                       (u_long)buflen);
30399657Sbenno                error = EFBIG;
30499657Sbenno        }
30599657Sbenno
30699657Sbenno        (*callback)(callback_arg, dm_segments, seg, error);
30799657Sbenno
30899657Sbenno        return (0);
30978342Sbenno}
31099657Sbenno
31199657Sbenno/*
31299657Sbenno * Release the mapping held by map.
31399657Sbenno */
31499657Sbennovoid
31599657Sbennobus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
31699657Sbenno{}
31799657Sbenno
31899657Sbennovoid
31999657Sbennobus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
32099657Sbenno{}
32199657Sbenno
32299657Sbenno
32399657Sbenno
32499657Sbenno
32599657Sbenno
326