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