bus_machdep.c revision 110030
132516Sgibbs/*-
232516Sgibbs * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
332516Sgibbs * All rights reserved.
432516Sgibbs *
532516Sgibbs * This code is derived from software contributed to The NetBSD Foundation
632516Sgibbs * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
732516Sgibbs * NASA Ames Research Center.
832516Sgibbs *
932516Sgibbs * Redistribution and use in source and binary forms, with or without
1032516Sgibbs * modification, are permitted provided that the following conditions
1132516Sgibbs * are met:
1232516Sgibbs * 1. Redistributions of source code must retain the above copyright
1332516Sgibbs *    notice, this list of conditions and the following disclaimer.
1432516Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1532516Sgibbs *    notice, this list of conditions and the following disclaimer in the
1632516Sgibbs *    documentation and/or other materials provided with the distribution.
1732516Sgibbs * 3. All advertising materials mentioning features or use of this software
1832516Sgibbs *    must display the following acknowledgement:
1932516Sgibbs *	This product includes software developed by the NetBSD
2032516Sgibbs *	Foundation, Inc. and its contributors.
2132516Sgibbs * 4. Neither the name of The NetBSD Foundation nor the names of its
2232516Sgibbs *    contributors may be used to endorse or promote products derived
2332516Sgibbs *    from this software without specific prior written permission.
2432516Sgibbs *
2532516Sgibbs * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2632516Sgibbs * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2732516Sgibbs * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2832516Sgibbs * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2932516Sgibbs * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3032516Sgibbs * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3132516Sgibbs * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3232516Sgibbs * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3332516Sgibbs * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3432516Sgibbs * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3532516Sgibbs * POSSIBILITY OF SUCH DAMAGE.
3632516Sgibbs */
3732516Sgibbs/*
3832516Sgibbs * Copyright (c) 1992, 1993
3932516Sgibbs *	The Regents of the University of California.  All rights reserved.
4032516Sgibbs *
4132516Sgibbs * This software was developed by the Computer Systems Engineering group
4232516Sgibbs * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
4332516Sgibbs * contributed to Berkeley.
4432516Sgibbs *
4532516Sgibbs * All advertising materials mentioning features or use of this software
4632516Sgibbs * must display the following acknowledgement:
4732516Sgibbs *	This product includes software developed by the University of
4832516Sgibbs *	California, Lawrence Berkeley Laboratory.
4932516Sgibbs *
5032516Sgibbs * Redistribution and use in source and binary forms, with or without
5132516Sgibbs * modification, are permitted provided that the following conditions
5232516Sgibbs * are met:
5332516Sgibbs * 1. Redistributions of source code must retain the above copyright
5432516Sgibbs *    notice, this list of conditions and the following disclaimer.
5532516Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
5632516Sgibbs *    notice, this list of conditions and the following disclaimer in the
5732516Sgibbs *    documentation and/or other materials provided with the distribution.
5832516Sgibbs * 3. All advertising materials mentioning features or use of this software
5932516Sgibbs *    must display the following acknowledgement:
6032516Sgibbs *	This product includes software developed by the University of
6132516Sgibbs *	California, Berkeley and its contributors.
6232516Sgibbs * 4. Neither the name of the University nor the names of its contributors
6332516Sgibbs *    may be used to endorse or promote products derived from this software
6432516Sgibbs *    without specific prior written permission.
6532516Sgibbs *
6632516Sgibbs * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6732516Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6832516Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6932516Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
7032516Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
7132516Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
7232516Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
7332516Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
7432516Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
7532516Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
7632516Sgibbs * SUCH DAMAGE.
7732516Sgibbs */
7832516Sgibbs/*
7932516Sgibbs * Copyright (c) 1997, 1998 Justin T. Gibbs.
8032516Sgibbs * All rights reserved.
8132516Sgibbs * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
8232516Sgibbs *
8332516Sgibbs * Redistribution and use in source and binary forms, with or without
8432516Sgibbs * modification, are permitted provided that the following conditions
8532516Sgibbs * are met:
8632516Sgibbs * 1. Redistributions of source code must retain the above copyright
8732516Sgibbs *    notice, this list of conditions, and the following disclaimer,
8832516Sgibbs *    without modification, immediately at the beginning of the file.
8932516Sgibbs * 2. The name of the author may not be used to endorse or promote products
9032516Sgibbs *    derived from this software without specific prior written permission.
9132516Sgibbs *
9232516Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
9332516Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9432516Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
9532516Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
9632516Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
9732516Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
9832516Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
9932516Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
10032516Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
10132516Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
10232516Sgibbs * SUCH DAMAGE.
10332516Sgibbs *
10432516Sgibbs *	from: @(#)machdep.c	8.6 (Berkeley) 1/14/94
10532516Sgibbs *	from: NetBSD: machdep.c,v 1.111 2001/09/15 07:13:40 eeh Exp
10632516Sgibbs *	and
10732516Sgibbs * 	from: FreeBSD: src/sys/i386/i386/busdma_machdep.c,v 1.24 2001/08/15
10832516Sgibbs *
10932516Sgibbs * $FreeBSD: head/sys/sparc64/sparc64/bus_machdep.c 110030 2003-01-29 07:25:27Z scottl $
11032516Sgibbs */
11132516Sgibbs
11232516Sgibbs#include <sys/param.h>
11332516Sgibbs#include <sys/bus.h>
11432516Sgibbs#include <sys/malloc.h>
11532516Sgibbs#include <sys/mbuf.h>
11632516Sgibbs#include <sys/proc.h>
11732516Sgibbs#include <sys/smp.h>
11832516Sgibbs#include <sys/systm.h>
11932516Sgibbs#include <sys/uio.h>
12032516Sgibbs
12132516Sgibbs#include <vm/vm.h>
12232516Sgibbs#include <vm/vm_extern.h>
12332516Sgibbs#include <vm/vm_kern.h>
12432516Sgibbs#include <vm/vm_page.h>
12532516Sgibbs#include <vm/vm_param.h>
12632516Sgibbs#include <vm/vm_map.h>
12732516Sgibbs
12832516Sgibbs#include <machine/asi.h>
12932516Sgibbs#include <machine/bus.h>
13032516Sgibbs#include <machine/bus_private.h>
13132516Sgibbs#include <machine/cache.h>
13232516Sgibbs#include <machine/smp.h>
13332516Sgibbs#include <machine/tlb.h>
13432516Sgibbs
13532516Sgibbs/* ASI's for bus access. */
13632516Sgibbsint bus_type_asi[] = {
13732516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* UPA */
13832516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* SBUS */
13932516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT_L,		/* PCI configuration space */
14032516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT_L,		/* PCI memory space */
14132516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT_L,		/* PCI I/O space */
14232516Sgibbs	0
14332516Sgibbs};
14432516Sgibbs
14532516Sgibbsint bus_stream_asi[] = {
14632516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* UPA */
14732516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* SBUS */
14832516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* PCI configuration space */
14932516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* PCI memory space */
15032516Sgibbs	ASI_PHYS_BYPASS_EC_WITH_EBIT,		/* PCI I/O space */
15132516Sgibbs	0
15232516Sgibbs};
15332516Sgibbs
15432516Sgibbs/*
15532516Sgibbs * busdma support code.
15632516Sgibbs * Note: there is no support for bounce buffers yet.
15732516Sgibbs */
15832516Sgibbs
15932516Sgibbsstatic int nexus_dmamap_create(bus_dma_tag_t, bus_dma_tag_t, int,
16032516Sgibbs    bus_dmamap_t *);
16132516Sgibbsstatic int nexus_dmamap_destroy(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t);
16232516Sgibbsstatic int nexus_dmamap_load(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
16332516Sgibbs    void *, bus_size_t, bus_dmamap_callback_t *, void *, int);
16432516Sgibbsstatic int nexus_dmamap_load_mbuf(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
16532516Sgibbs    struct mbuf *, bus_dmamap_callback2_t *, void *, int);
16632516Sgibbsstatic int nexus_dmamap_load_uio(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
16732516Sgibbs    struct uio *, bus_dmamap_callback2_t *, void *, int);
16832516Sgibbsstatic void nexus_dmamap_unload(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t);
16932516Sgibbsstatic void nexus_dmamap_sync(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
17032516Sgibbs    bus_dmasync_op_t);
17132516Sgibbsstatic int nexus_dmamem_alloc_size(bus_dma_tag_t, bus_dma_tag_t, void **, int,
17232516Sgibbs    bus_dmamap_t *, u_long size);
17332516Sgibbsstatic int nexus_dmamem_alloc(bus_dma_tag_t, bus_dma_tag_t, void **, int,
17432516Sgibbs    bus_dmamap_t *);
17532516Sgibbsstatic void nexus_dmamem_free_size(bus_dma_tag_t, bus_dma_tag_t, void *,
17632516Sgibbs    bus_dmamap_t, u_long size);
17732516Sgibbsstatic void nexus_dmamem_free(bus_dma_tag_t, bus_dma_tag_t, void *,
17832516Sgibbs    bus_dmamap_t);
17932516Sgibbs
18032516Sgibbs/*
18132516Sgibbs * Since there is now way for a device to obtain a dma tag from its parent
18232516Sgibbs * we use this kluge to handle different the different supported bus systems.
18332516Sgibbs * The sparc64_root_dma_tag is used as parent for tags that have none, so that
18432516Sgibbs * the correct methods will be used.
18532516Sgibbs */
18632516Sgibbsbus_dma_tag_t sparc64_root_dma_tag;
18732516Sgibbs
18832516Sgibbs/*
18932516Sgibbs * Allocate a device specific dma_tag.
19032516Sgibbs */
19132516Sgibbsint
19232516Sgibbsbus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
19332516Sgibbs    bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
19432516Sgibbs    bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
19532516Sgibbs    int nsegments, bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat)
19632516Sgibbs{
19732516Sgibbs
19832516Sgibbs	bus_dma_tag_t newtag;
19932516Sgibbs
20032516Sgibbs	/* Return a NULL tag on failure */
20132516Sgibbs	*dmat = NULL;
20232516Sgibbs
20332516Sgibbs	newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT);
20432516Sgibbs	if (newtag == NULL)
20532516Sgibbs		return (ENOMEM);
20632516Sgibbs
20732516Sgibbs	newtag->dt_parent = parent != NULL ? parent : sparc64_root_dma_tag;
20832516Sgibbs	newtag->dt_alignment = alignment;
20932516Sgibbs	newtag->dt_boundary = boundary;
21032516Sgibbs	newtag->dt_lowaddr = trunc_page((vm_offset_t)lowaddr) + (PAGE_SIZE - 1);
21132516Sgibbs	newtag->dt_highaddr = trunc_page((vm_offset_t)highaddr) +
21232516Sgibbs	    (PAGE_SIZE - 1);
21332516Sgibbs	newtag->dt_filter = filter;
21432516Sgibbs	newtag->dt_filterarg = filterarg;
21532516Sgibbs	newtag->dt_maxsize = maxsize;
21632516Sgibbs	newtag->dt_nsegments = nsegments;
21732516Sgibbs	newtag->dt_maxsegsz = maxsegsz;
21832516Sgibbs	newtag->dt_flags = flags;
21932516Sgibbs	newtag->dt_ref_count = 1; /* Count ourselves */
22032516Sgibbs	newtag->dt_map_count = 0;
22132516Sgibbs
22232516Sgibbs	newtag->dt_dmamap_create = NULL;
22332516Sgibbs	newtag->dt_dmamap_destroy = NULL;
22432516Sgibbs	newtag->dt_dmamap_load = NULL;
22532516Sgibbs	newtag->dt_dmamap_load_mbuf = NULL;
22632516Sgibbs	newtag->dt_dmamap_load_uio = NULL;
22732516Sgibbs	newtag->dt_dmamap_unload = NULL;
22832516Sgibbs	newtag->dt_dmamap_sync = NULL;
22932516Sgibbs	newtag->dt_dmamem_alloc_size = NULL;
23032516Sgibbs	newtag->dt_dmamem_alloc = NULL;
23132516Sgibbs	newtag->dt_dmamem_free_size = NULL;
23232516Sgibbs	newtag->dt_dmamem_free = NULL;
23332516Sgibbs
23432516Sgibbs	/* Take into account any restrictions imposed by our parent tag */
23532516Sgibbs	if (parent != NULL) {
23632516Sgibbs		newtag->dt_lowaddr = ulmin(parent->dt_lowaddr,
23732516Sgibbs		    newtag->dt_lowaddr);
23832516Sgibbs		newtag->dt_highaddr = ulmax(parent->dt_highaddr,
23932516Sgibbs		    newtag->dt_highaddr);
24032516Sgibbs		/*
24132516Sgibbs		 * XXX Not really correct??? Probably need to honor boundary
24232516Sgibbs		 *     all the way up the inheritence chain.
24332516Sgibbs		 */
24432516Sgibbs		newtag->dt_boundary = ulmin(parent->dt_boundary,
24532516Sgibbs		    newtag->dt_boundary);
24632516Sgibbs	}
24732516Sgibbs	newtag->dt_parent->dt_ref_count++;
24832516Sgibbs
24932516Sgibbs	*dmat = newtag;
25032516Sgibbs	return (0);
25132516Sgibbs}
25232516Sgibbs
25332516Sgibbsint
25432516Sgibbsbus_dma_tag_destroy(bus_dma_tag_t dmat)
25532516Sgibbs{
25632516Sgibbs	bus_dma_tag_t parent;
25732516Sgibbs
25832516Sgibbs	if (dmat != NULL) {
25932516Sgibbs		if (dmat->dt_map_count != 0)
26032516Sgibbs			return (EBUSY);
26132516Sgibbs		while (dmat != NULL) {
26232516Sgibbs			parent = dmat->dt_parent;
26332516Sgibbs			dmat->dt_ref_count--;
26432516Sgibbs			if (dmat->dt_ref_count == 0) {
26532516Sgibbs				free(dmat, M_DEVBUF);
26632516Sgibbs				/*
26732516Sgibbs				 * Last reference count, so
26832516Sgibbs				 * release our reference
26932516Sgibbs				 * count on our parent.
27032516Sgibbs				 */
27132516Sgibbs				dmat = parent;
27232516Sgibbs			} else
27332516Sgibbs				dmat = NULL;
27432516Sgibbs		}
27532516Sgibbs	}
27632516Sgibbs	return (0);
27732516Sgibbs}
27832516Sgibbs
27932516Sgibbs/*
28032516Sgibbs * Common function for DMA map creation.  May be called by bus-specific
28132516Sgibbs * DMA map creation functions.
28232516Sgibbs */
28332516Sgibbsstatic int
28432516Sgibbsnexus_dmamap_create(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, int flags,
28532516Sgibbs    bus_dmamap_t *mapp)
28632516Sgibbs{
28732516Sgibbs
28832516Sgibbs	*mapp = malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO);
28932516Sgibbs	if (*mapp != NULL) {
29032516Sgibbs		ddmat->dt_map_count++;
29132516Sgibbs		sparc64_dmamap_init(*mapp);
29232516Sgibbs		return (0);
29332516Sgibbs	} else
29432516Sgibbs		return (ENOMEM);
29532516Sgibbs}
29632516Sgibbs
29732516Sgibbs/*
29832516Sgibbs * Common function for DMA map destruction.  May be called by bus-specific
29932516Sgibbs * DMA map destruction functions.
30032516Sgibbs */
30132516Sgibbsstatic int
30232516Sgibbsnexus_dmamap_destroy(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, bus_dmamap_t map)
30332516Sgibbs{
30432516Sgibbs
30532516Sgibbs	free(map, M_DEVBUF);
30632516Sgibbs	ddmat->dt_map_count--;
30732516Sgibbs	return (0);
30832516Sgibbs}
30932516Sgibbs
31032516Sgibbs/*
31132516Sgibbs * Utility function to load a linear buffer.  lastaddrp holds state
31232516Sgibbs * between invocations (for multiple-buffer loads).  segp contains
31332516Sgibbs * the starting segment on entrace, and the ending segment on exit.
31432516Sgibbs * first indicates if this is the first invocation of this function.
31532516Sgibbs */
31632516Sgibbsstatic int
31732516Sgibbs_nexus_dmamap_load_buffer(bus_dma_tag_t ddmat, bus_dma_segment_t segs[],
31832516Sgibbs    void *buf, bus_size_t buflen, struct thread *td, int flags,
31932516Sgibbs    vm_offset_t *lastaddrp, int *segp, int first)
32032516Sgibbs{
32132516Sgibbs	bus_size_t sgsize;
32232516Sgibbs	bus_addr_t curaddr, lastaddr, baddr, bmask;
32332516Sgibbs	vm_offset_t vaddr = (vm_offset_t)buf;
32432516Sgibbs	int seg;
32532516Sgibbs	pmap_t pmap;
32632516Sgibbs
32732516Sgibbs	if (td != NULL)
32832516Sgibbs		pmap = vmspace_pmap(td->td_proc->p_vmspace);
32932516Sgibbs	else
33032516Sgibbs		pmap = NULL;
33132516Sgibbs
33232516Sgibbs	lastaddr = *lastaddrp;
33332516Sgibbs	bmask  = ~(ddmat->dt_boundary - 1);
33432516Sgibbs
33532516Sgibbs	for (seg = *segp; buflen > 0 ; ) {
33632516Sgibbs		/*
33732516Sgibbs		 * Get the physical address for this segment.
33832516Sgibbs		 */
33932516Sgibbs		if (pmap)
34032516Sgibbs			curaddr = pmap_extract(pmap, vaddr);
34132516Sgibbs		else
34232516Sgibbs			curaddr = pmap_kextract(vaddr);
34332516Sgibbs
34432516Sgibbs		/*
34532516Sgibbs		 * Compute the segment size, and adjust counts.
34632516Sgibbs		 */
34732516Sgibbs		sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
34832516Sgibbs		if (buflen < sgsize)
34932516Sgibbs			sgsize = buflen;
35032516Sgibbs
35132516Sgibbs		/*
35232516Sgibbs		 * Make sure we don't cross any boundaries.
35332516Sgibbs		 */
35432516Sgibbs		if (ddmat->dt_boundary > 0) {
35532516Sgibbs			baddr = (curaddr + ddmat->dt_boundary) & bmask;
35632516Sgibbs			if (sgsize > (baddr - curaddr))
35732516Sgibbs				sgsize = (baddr - curaddr);
35832516Sgibbs		}
35932516Sgibbs
36032516Sgibbs		/*
36132516Sgibbs		 * Insert chunk into a segment, coalescing with
36232516Sgibbs		 * previous segment if possible.
36332516Sgibbs		 */
36432516Sgibbs		if (first) {
36532516Sgibbs			segs[seg].ds_addr = curaddr;
36632516Sgibbs			segs[seg].ds_len = sgsize;
36732516Sgibbs			first = 0;
36832516Sgibbs		} else {
36932516Sgibbs			if (curaddr == lastaddr &&
37032516Sgibbs			    (segs[seg].ds_len + sgsize) <= ddmat->dt_maxsegsz &&
37132516Sgibbs			    (ddmat->dt_boundary == 0 ||
37232516Sgibbs			     (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
37332516Sgibbs				segs[seg].ds_len += sgsize;
37432516Sgibbs			else {
37532516Sgibbs				if (++seg >= ddmat->dt_nsegments)
37632516Sgibbs					break;
37732516Sgibbs				segs[seg].ds_addr = curaddr;
37832516Sgibbs				segs[seg].ds_len = sgsize;
37932516Sgibbs			}
38032516Sgibbs		}
38132516Sgibbs
38232516Sgibbs		lastaddr = curaddr + sgsize;
38332516Sgibbs		vaddr += sgsize;
38432516Sgibbs		buflen -= sgsize;
38532516Sgibbs	}
38632516Sgibbs
38732516Sgibbs	*segp = seg;
38832516Sgibbs	*lastaddrp = lastaddr;
38932516Sgibbs
39032516Sgibbs	/*
39132516Sgibbs	 * Did we fit?
39232516Sgibbs	 */
39332516Sgibbs	return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
39432516Sgibbs}
39532516Sgibbs
39632516Sgibbs/*
39732516Sgibbs * Common function for loading a DMA map with a linear buffer.  May
39832516Sgibbs * be called by bus-specific DMA map load functions.
39932516Sgibbs *
40032516Sgibbs * Most SPARCs have IOMMUs in the bus controllers.  In those cases
40132516Sgibbs * they only need one segment and will use virtual addresses for DVMA.
40232516Sgibbs * Those bus controllers should intercept these vectors and should
40332516Sgibbs * *NEVER* call nexus_dmamap_load() which is used only by devices that
40432516Sgibbs * bypass DVMA.
40532516Sgibbs */
40632516Sgibbsstatic int
40732516Sgibbsnexus_dmamap_load(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, bus_dmamap_t map,
40832516Sgibbs    void *buf, bus_size_t buflen, bus_dmamap_callback_t *callback,
40932516Sgibbs    void *callback_arg, int flags)
41032516Sgibbs{
41132516Sgibbs#ifdef __GNUC__
41232516Sgibbs	bus_dma_segment_t dm_segments[ddmat->dt_nsegments];
41332516Sgibbs#else
41432516Sgibbs	bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
41532516Sgibbs#endif
41632516Sgibbs	vm_offset_t lastaddr;
41732516Sgibbs	int error, nsegs;
41832516Sgibbs
41932516Sgibbs	error = _nexus_dmamap_load_buffer(ddmat, dm_segments, buf, buflen,
42032516Sgibbs	    NULL, flags, &lastaddr, &nsegs, 1);
42132516Sgibbs
42232516Sgibbs	if (error == 0) {
42332516Sgibbs		(*callback)(callback_arg, dm_segments, nsegs + 1, 0);
42432516Sgibbs		map->dm_loaded = 1;
42532516Sgibbs	} else
42632516Sgibbs		(*callback)(callback_arg, NULL, 0, error);
42732516Sgibbs
42832516Sgibbs	return (0);
42932516Sgibbs}
43032516Sgibbs
43132516Sgibbs/*
43232516Sgibbs * Like nexus_dmamap_load(), but for mbufs.
43332516Sgibbs */
43432516Sgibbsstatic int
43532516Sgibbsnexus_dmamap_load_mbuf(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat,
43632516Sgibbs    bus_dmamap_t map, struct mbuf *m0, bus_dmamap_callback2_t *callback,
43732516Sgibbs    void *callback_arg, int flags)
43832516Sgibbs{
43932516Sgibbs#ifdef __GNUC__
44032516Sgibbs	bus_dma_segment_t dm_segments[ddmat->dt_nsegments];
44132516Sgibbs#else
44232516Sgibbs	bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
44332516Sgibbs#endif
44432516Sgibbs	int nsegs, error;
44532516Sgibbs
44632516Sgibbs	KASSERT(m0->m_flags & M_PKTHDR,
44732516Sgibbs		("nexus_dmamap_load_mbuf: no packet header"));
44832516Sgibbs
44932516Sgibbs	nsegs = 0;
45032516Sgibbs	error = 0;
45132516Sgibbs	if (m0->m_pkthdr.len <= ddmat->dt_maxsize) {
45232516Sgibbs		int first = 1;
45332516Sgibbs		vm_offset_t lastaddr = 0;
45432516Sgibbs		struct mbuf *m;
45532516Sgibbs
45632516Sgibbs		for (m = m0; m != NULL && error == 0; m = m->m_next) {
45732516Sgibbs			error = _nexus_dmamap_load_buffer(ddmat,
45832516Sgibbs			    dm_segments, m->m_data, m->m_len, NULL, flags,
45932516Sgibbs			    &lastaddr, &nsegs, first);
46032516Sgibbs			first = 0;
46132516Sgibbs		}
46232516Sgibbs	} else {
46332516Sgibbs		error = EINVAL;
46432516Sgibbs	}
46532516Sgibbs
46632516Sgibbs	if (error) {
46732516Sgibbs		/* force "no valid mappings" in callback */
46832516Sgibbs		(*callback)(callback_arg, dm_segments, 0, 0, error);
46932516Sgibbs	} else {
47032516Sgibbs		map->dm_loaded = 1;
47132516Sgibbs		(*callback)(callback_arg, dm_segments, nsegs + 1,
47232516Sgibbs		    m0->m_pkthdr.len, error);
47332516Sgibbs	}
47432516Sgibbs	return (error);
47532516Sgibbs}
47632516Sgibbs
47732516Sgibbs/*
47832516Sgibbs * Like nexus_dmamap_load(), but for uios.
47932516Sgibbs */
48032516Sgibbsstatic int
48132516Sgibbsnexus_dmamap_load_uio(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat,
48232516Sgibbs    bus_dmamap_t map, struct uio *uio, bus_dmamap_callback2_t *callback,
48332516Sgibbs    void *callback_arg, int flags)
48432516Sgibbs{
48532516Sgibbs	vm_offset_t lastaddr;
48632516Sgibbs#ifdef __GNUC__
48732516Sgibbs	bus_dma_segment_t dm_segments[ddmat->dt_nsegments];
48832516Sgibbs#else
48932516Sgibbs	bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
49032516Sgibbs#endif
49132516Sgibbs	int nsegs, error, first, i;
49232516Sgibbs	bus_size_t resid;
49332516Sgibbs	struct iovec *iov;
49432516Sgibbs	struct thread *td = NULL;
49532516Sgibbs
49632516Sgibbs	resid = uio->uio_resid;
49732516Sgibbs	iov = uio->uio_iov;
49832516Sgibbs
49932516Sgibbs	if (uio->uio_segflg == UIO_USERSPACE) {
50032516Sgibbs		td = uio->uio_td;
50132516Sgibbs		KASSERT(td != NULL,
50232516Sgibbs			("nexus_dmamap_load_uio: USERSPACE but no proc"));
50332516Sgibbs	}
50432516Sgibbs
50532516Sgibbs	nsegs = 0;
50632516Sgibbs	error = 0;
50732516Sgibbs	first = 1;
50832516Sgibbs	for (i = 0; i < uio->uio_iovcnt && resid != 0 && !error; i++) {
50932516Sgibbs		/*
51032516Sgibbs		 * Now at the first iovec to load.  Load each iovec
51132516Sgibbs		 * until we have exhausted the residual count.
51232516Sgibbs		 */
51332516Sgibbs		bus_size_t minlen =
51432516Sgibbs			resid < iov[i].iov_len ? resid : iov[i].iov_len;
51532516Sgibbs		caddr_t addr = (caddr_t) iov[i].iov_base;
51632516Sgibbs
51732516Sgibbs		error = _nexus_dmamap_load_buffer(ddmat, dm_segments, addr,
51832516Sgibbs		    minlen, td, flags, &lastaddr, &nsegs, first);
51932516Sgibbs		first = 0;
52032516Sgibbs
52132516Sgibbs		resid -= minlen;
52232516Sgibbs	}
52332516Sgibbs
52432516Sgibbs	if (error) {
52532516Sgibbs		/* force "no valid mappings" in callback */
52632516Sgibbs		(*callback)(callback_arg, dm_segments, 0, 0, error);
52732516Sgibbs	} else {
52832516Sgibbs		map->dm_loaded = 1;
52932516Sgibbs		(*callback)(callback_arg, dm_segments, nsegs + 1,
53032516Sgibbs		    uio->uio_resid, error);
53132516Sgibbs	}
53232516Sgibbs	return (error);
53332516Sgibbs}
53432516Sgibbs
53532516Sgibbs/*
53632516Sgibbs * Common function for unloading a DMA map.  May be called by
53732516Sgibbs * bus-specific DMA map unload functions.
53832516Sgibbs */
53932516Sgibbsstatic void
54032516Sgibbsnexus_dmamap_unload(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, bus_dmamap_t map)
54132516Sgibbs{
54232516Sgibbs
54332516Sgibbs	map->dm_loaded = 0;
54432516Sgibbs}
54532516Sgibbs
54632516Sgibbs/*
54732516Sgibbs * Common function for DMA map synchronization.  May be called
54832516Sgibbs * by bus-specific DMA map synchronization functions.
54932516Sgibbs */
55032516Sgibbsstatic void
55132516Sgibbsnexus_dmamap_sync(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, bus_dmamap_t map,
55232516Sgibbs    bus_dmasync_op_t op)
55332516Sgibbs{
55432516Sgibbs
55532516Sgibbs	/*
55632516Sgibbs	 * We sync out our caches, but the bus must do the same.
55732516Sgibbs	 *
55832516Sgibbs	 * Actually a #Sync is expensive.  We should optimize.
55932516Sgibbs	 */
56032516Sgibbs	if ((op == BUS_DMASYNC_PREREAD) || (op == BUS_DMASYNC_PREWRITE)) {
56132516Sgibbs		/*
56232516Sgibbs		 * Don't really need to do anything, but flush any pending
56332516Sgibbs		 * writes anyway.
56432516Sgibbs		 */
56532516Sgibbs		membar(Sync);
56632516Sgibbs	}
56732516Sgibbs#if 0
56832516Sgibbs	/* Should not be needed. */
56932516Sgibbs	if (op == BUS_DMASYNC_POSTREAD) {
57032516Sgibbs		ecache_flush((vm_offset_t)map->buf,
57132516Sgibbs		    (vm_offset_t)map->buf + map->buflen - 1);
57232516Sgibbs	}
57332516Sgibbs#endif
57432516Sgibbs	if (op == BUS_DMASYNC_POSTWRITE) {
57532516Sgibbs		/* Nothing to do.  Handled by the bus controller. */
57632516Sgibbs	}
57732516Sgibbs}
57832516Sgibbs
57932516Sgibbs/*
58032516Sgibbs * Helper functions for buses that use their private dmamem_alloc/dmamem_free
58132516Sgibbs * versions.
58232516Sgibbs * These differ from the dmamap_alloc() functions in that they create a tag
58332516Sgibbs * that is specifically for use with dmamem_alloc'ed memory.
58432516Sgibbs * These are primitive now, but I expect that some fields of the map will need
58532516Sgibbs * to be filled soon.
586 */
587int
588sparc64_dmamem_alloc_map(bus_dma_tag_t dmat, bus_dmamap_t *mapp)
589{
590
591	*mapp = malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO);
592	if (*mapp == NULL)
593		return (ENOMEM);
594
595	dmat->dt_map_count++;
596	sparc64_dmamap_init(*mapp);
597	return (0);
598}
599
600void
601sparc64_dmamem_free_map(bus_dma_tag_t dmat, bus_dmamap_t map)
602{
603
604	free(map, M_DEVBUF);
605	dmat->dt_map_count--;
606}
607
608/*
609 * Common function for DMA-safe memory allocation.  May be called
610 * by bus-specific DMA memory allocation functions.
611 */
612static int
613nexus_dmamem_alloc_size(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, void **vaddr,
614    int flags, bus_dmamap_t *mapp, bus_size_t size)
615{
616
617	if (size > ddmat->dt_maxsize)
618		return (ENOMEM);
619
620	if ((size <= PAGE_SIZE)) {
621		*vaddr = malloc(size, M_DEVBUF,
622		    (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : 0);
623	} else {
624		/*
625		 * XXX: Use contigmalloc until it is merged into this facility
626		 * and handles multi-seg allocations.  Nobody is doing multi-seg
627		 * allocations yet though.
628		 */
629		*vaddr = contigmalloc(size, M_DEVBUF,
630		    (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : 0,
631		    0ul, ddmat->dt_lowaddr,
632		    ddmat->dt_alignment ? ddmat->dt_alignment : 1UL,
633		    ddmat->dt_boundary);
634	}
635	if (*vaddr == NULL) {
636		free(*mapp, M_DEVBUF);
637		return (ENOMEM);
638	}
639	return (0);
640}
641
642static int
643nexus_dmamem_alloc(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, void **vaddr,
644    int flags, bus_dmamap_t *mapp)
645{
646	return (sparc64_dmamem_alloc_size(pdmat, ddmat, vaddr, flags, mapp,
647		ddmat->maxsize));
648}
649
650/*
651 * Common function for freeing DMA-safe memory.  May be called by
652 * bus-specific DMA memory free functions.
653 */
654static void
655nexus_dmamem_free_size(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, void *vaddr,
656    bus_dmamap_t map, bus_size_t size)
657{
658
659	sparc64_dmamem_free_map(ddmat, map);
660	if ((size <= PAGE_SIZE))
661		free(vaddr, M_DEVBUF);
662	else
663		contigfree(vaddr, size, M_DEVBUF);
664}
665
666static void
667nexus_dmamem_free(bus_dma_tag_t pdmat, bus_dma_tag_t ddmat, void *vaddr,
668    bus_dmamap_t map)
669{
670	sparc64_dmamem_free_size(pdmat, ddmat, vaddr, map, ddmat->dt_maxsize);
671}
672
673struct bus_dma_tag nexus_dmatag = {
674	NULL,
675	NULL,
676	8,
677	0,
678	0,
679	0x3ffffffff,
680	NULL,		/* XXX */
681	NULL,
682	0x3ffffffff,	/* XXX */
683	0xff,		/* XXX */
684	0xffffffff,	/* XXX */
685	0,
686	0,
687	0,
688	nexus_dmamap_create,
689	nexus_dmamap_destroy,
690	nexus_dmamap_load,
691	nexus_dmamap_load_mbuf,
692	nexus_dmamap_load_uio,
693	nexus_dmamap_unload,
694	nexus_dmamap_sync,
695
696	nexus_dmamem_alloc_size,
697	nexus_dmamem_alloc,
698	nexus_dmamem_free_size,
699	nexus_dmamem_free,
700};
701
702/*
703 * Helpers to map/unmap bus memory
704 */
705int
706sparc64_bus_mem_map(bus_space_tag_t tag, bus_space_handle_t handle,
707    bus_size_t size, int flags, vm_offset_t vaddr, void **hp)
708{
709	vm_offset_t addr;
710	vm_offset_t sva;
711	vm_offset_t va;
712	vm_offset_t pa;
713	vm_size_t vsz;
714	u_long pm_flags;
715
716	addr = (vm_offset_t)handle;
717	size = round_page(size);
718	if (size == 0) {
719		printf("sparc64_bus_map: zero size\n");
720		return (EINVAL);
721	}
722	switch (tag->bst_type) {
723	case PCI_CONFIG_BUS_SPACE:
724	case PCI_IO_BUS_SPACE:
725	case PCI_MEMORY_BUS_SPACE:
726		pm_flags = TD_IE;
727		break;
728	default:
729		pm_flags = 0;
730		break;
731	}
732
733	if (!(flags & BUS_SPACE_MAP_CACHEABLE))
734		pm_flags |= TD_E;
735
736	if (vaddr != NULL)
737		sva = trunc_page(vaddr);
738	else {
739		if ((sva = kmem_alloc_nofault(kernel_map, size)) == NULL)
740			panic("sparc64_bus_map: cannot allocate virtual "
741			    "memory");
742	}
743
744	/* Preserve page offset. */
745	*hp = (void *)(sva | ((u_long)addr & PAGE_MASK));
746
747	pa = trunc_page(addr);
748	if ((flags & BUS_SPACE_MAP_READONLY) == 0)
749		pm_flags |= TD_W;
750
751	va = sva;
752	vsz = size;
753	do {
754		pmap_kenter_flags(va, pa, pm_flags);
755		va += PAGE_SIZE;
756		pa += PAGE_SIZE;
757	} while ((vsz -= PAGE_SIZE) > 0);
758	tlb_range_demap(kernel_pmap, sva, sva + size - 1);
759	return (0);
760}
761
762int
763sparc64_bus_mem_unmap(void *bh, bus_size_t size)
764{
765	vm_offset_t sva;
766	vm_offset_t va;
767	vm_offset_t endva;
768
769	sva = trunc_page((vm_offset_t)bh);
770	endva = sva + round_page(size);
771	for (va = sva; va < endva; va += PAGE_SIZE)
772		pmap_kremove_flags(va);
773	tlb_range_demap(kernel_pmap, sva, sva + size - 1);
774	kmem_free(kernel_map, sva, size);
775	return (0);
776}
777
778/*
779 * Fake up a bus tag, for use by console drivers in early boot when the regular
780 * means to allocate resources are not yet available.
781 * Note that these tags are not eligible for bus_space_barrier operations.
782 * Addr is the physical address of the desired start of the handle.
783 */
784bus_space_handle_t
785sparc64_fake_bustag(int space, bus_addr_t addr, struct bus_space_tag *ptag)
786{
787
788	ptag->bst_cookie = NULL;
789	ptag->bst_parent = NULL;
790	ptag->bst_type = space;
791	ptag->bst_bus_barrier = NULL;
792	return (addr);
793}
794
795/*
796 * Base bus space handlers.
797 */
798static void nexus_bus_barrier(bus_space_tag_t, bus_space_handle_t,
799    bus_size_t, bus_size_t, int);
800
801static void
802nexus_bus_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset,
803    bus_size_t size, int flags)
804{
805
806	/*
807	 * We have lots of alternatives depending on whether we're
808	 * synchronizing loads with loads, loads with stores, stores
809	 * with loads, or stores with stores.  The only ones that seem
810	 * generic are #Sync and #MemIssue.  I'll use #Sync for safety.
811	 */
812	switch(flags) {
813	case BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE:
814	case BUS_SPACE_BARRIER_READ:
815	case BUS_SPACE_BARRIER_WRITE:
816		membar(Sync);
817		break;
818	default:
819		panic("sparc64_bus_barrier: unknown flags");
820	}
821	return;
822}
823
824struct bus_space_tag nexus_bustag = {
825	NULL,				/* cookie */
826	NULL,				/* parent bus tag */
827	UPA_BUS_SPACE,			/* type */
828	nexus_bus_barrier,		/* bus_space_barrier */
829};
830