bus_machdep.c revision 127977
1139749Simp/*- 2113584Ssimokawa * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc. 3103285Sikob * All rights reserved. 4103285Sikob * 5103285Sikob * This code is derived from software contributed to The NetBSD Foundation 6103285Sikob * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 7103285Sikob * NASA Ames Research Center. 8103285Sikob * 9103285Sikob * Redistribution and use in source and binary forms, with or without 10103285Sikob * modification, are permitted provided that the following conditions 11103285Sikob * are met: 12103285Sikob * 1. Redistributions of source code must retain the above copyright 13103285Sikob * notice, this list of conditions and the following disclaimer. 14103285Sikob * 2. Redistributions in binary form must reproduce the above copyright 15103285Sikob * notice, this list of conditions and the following disclaimer in the 16103285Sikob * documentation and/or other materials provided with the distribution. 17103285Sikob * 3. All advertising materials mentioning features or use of this software 18103285Sikob * must display the following acknowledgement: 19103285Sikob * This product includes software developed by the NetBSD 20103285Sikob * Foundation, Inc. and its contributors. 21103285Sikob * 4. Neither the name of The NetBSD Foundation nor the names of its 22103285Sikob * contributors may be used to endorse or promote products derived 23103285Sikob * from this software without specific prior written permission. 24103285Sikob * 25103285Sikob * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26103285Sikob * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27103285Sikob * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28103285Sikob * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29103285Sikob * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30103285Sikob * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31103285Sikob * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32103285Sikob * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33103285Sikob * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34103285Sikob * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35227843Smarius * POSSIBILITY OF SUCH DAMAGE. 36227843Smarius */ 37227843Smarius/* 38103285Sikob * Copyright (c) 1992, 1993 39103285Sikob * The Regents of the University of California. All rights reserved. 40103285Sikob * 41103285Sikob * This software was developed by the Computer Systems Engineering group 42193066Sjamie * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 43103285Sikob * contributed to Berkeley. 44129879Sphk * 45103285Sikob * Redistribution and use in source and binary forms, with or without 46103285Sikob * modification, are permitted provided that the following conditions 47103285Sikob * are met: 48169806Ssimokawa * 1. Redistributions of source code must retain the above copyright 49103285Sikob * notice, this list of conditions and the following disclaimer. 50170374Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright 51170374Ssimokawa * notice, this list of conditions and the following disclaimer in the 52127468Ssimokawa * documentation and/or other materials provided with the distribution. 53117067Ssimokawa * 4. Neither the name of the University nor the names of its contributors 54117067Ssimokawa * may be used to endorse or promote products derived from this software 55103285Sikob * without specific prior written permission. 56103285Sikob * 57113584Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 58103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59127468Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60127468Ssimokawa * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61127468Ssimokawa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62127468Ssimokawa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63127468Ssimokawa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64127468Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65127468Ssimokawa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67103285Sikob * SUCH DAMAGE. 68110072Ssimokawa */ 69103285Sikob/* 70103285Sikob * Copyright (c) 1997, 1998 Justin T. Gibbs. 71127468Ssimokawa * All rights reserved. 72103285Sikob * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved. 73116376Ssimokawa * 74116376Ssimokawa * Redistribution and use in source and binary forms, with or without 75116376Ssimokawa * modification, are permitted provided that the following conditions 76116376Ssimokawa * are met: 77116376Ssimokawa * 1. Redistributions of source code must retain the above copyright 78116376Ssimokawa * notice, this list of conditions, and the following disclaimer, 79116376Ssimokawa * without modification, immediately at the beginning of the file. 80188704Ssbruno * 2. The name of the author may not be used to endorse or promote products 81103285Sikob * derived from this software without specific prior written permission. 82108281Ssimokawa * 83109736Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 84109736Ssimokawa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 85109736Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 86120850Ssimokawa * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 87120850Ssimokawa * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 88103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 89110195Ssimokawa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 90110269Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 91110195Ssimokawa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 92103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 93103285Sikob * SUCH DAMAGE. 94103285Sikob * 95103285Sikob * from: @(#)machdep.c 8.6 (Berkeley) 1/14/94 96125238Ssimokawa * from: NetBSD: machdep.c,v 1.111 2001/09/15 07:13:40 eeh Exp 97125238Ssimokawa * and 98124169Ssimokawa * from: FreeBSD: src/sys/i386/i386/busdma_machdep.c,v 1.24 2001/08/15 99124169Ssimokawa * 100124169Ssimokawa * $FreeBSD: head/sys/sparc64/sparc64/bus_machdep.c 127977 2004-04-07 05:00:01Z imp $ 101170374Ssimokawa */ 102103285Sikob 103124169Ssimokawa#include <sys/param.h> 104103285Sikob#include <sys/bus.h> 105212413Savg#include <sys/lock.h> 106124169Ssimokawa#include <sys/malloc.h> 107124169Ssimokawa#include <sys/mbuf.h> 108124169Ssimokawa#include <sys/mutex.h> 109124169Ssimokawa#include <sys/proc.h> 110124169Ssimokawa#include <sys/smp.h> 111124169Ssimokawa#include <sys/systm.h> 112169806Ssimokawa#include <sys/uio.h> 113106543Ssimokawa 114124169Ssimokawa#include <vm/vm.h> 115106543Ssimokawa#include <vm/vm_extern.h> 116124169Ssimokawa#include <vm/vm_kern.h> 117170374Ssimokawa#include <vm/vm_page.h> 118103285Sikob#include <vm/vm_param.h> 119103285Sikob#include <vm/vm_map.h> 120103285Sikob 121125238Ssimokawa#include <machine/asi.h> 122125238Ssimokawa#include <machine/atomic.h> 123103285Sikob#include <machine/bus.h> 124103285Sikob#include <machine/bus_private.h> 125108642Ssimokawa#include <machine/cache.h> 126116978Ssimokawa#include <machine/smp.h> 127103285Sikob#include <machine/tlb.h> 128103285Sikob 129103285Sikobstatic void nexus_bus_barrier(bus_space_tag_t, bus_space_handle_t, 130103285Sikob bus_size_t, bus_size_t, int); 131103285Sikob 132227843Smarius/* ASI's for bus access. */ 133103285Sikobint bus_type_asi[] = { 134124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* UPA */ 135124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* SBUS */ 136124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT_L, /* PCI configuration space */ 137124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT_L, /* PCI memory space */ 138103285Sikob ASI_PHYS_BYPASS_EC_WITH_EBIT_L, /* PCI I/O space */ 139124251Ssimokawa 0 140124251Ssimokawa}; 141124251Ssimokawa 142124251Ssimokawaint bus_stream_asi[] = { 143124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* UPA */ 144124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* SBUS */ 145124251Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* PCI configuration space */ 146114909Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* PCI memory space */ 147114909Ssimokawa ASI_PHYS_BYPASS_EC_WITH_EBIT, /* PCI I/O space */ 148114909Ssimokawa 0 149114909Ssimokawa}; 150106813Ssimokawa 151103285Sikob/* 152103285Sikob * Convenience function for manipulating driver locks from busdma (during 153103285Sikob * busdma_swi, for example). Drivers that don't provide their own locks 154103285Sikob * should specify &Giant to dmat->lockfuncarg. Drivers that use their own 155103285Sikob * non-mutex locking scheme don't have to use this at all. 156103285Sikob */ 157103285Sikobvoid 158110072Ssimokawabusdma_lock_mutex(void *arg, bus_dma_lock_op_t op) 159103285Sikob{ 160106810Ssimokawa struct mtx *dmtx; 161110072Ssimokawa 162103285Sikob dmtx = (struct mtx *)arg; 163103285Sikob switch (op) { 164110072Ssimokawa case BUS_DMA_LOCK: 165110072Ssimokawa mtx_lock(dmtx); 166110072Ssimokawa break; 167110193Ssimokawa case BUS_DMA_UNLOCK: 168120660Ssimokawa mtx_unlock(dmtx); 169103285Sikob break; 170110072Ssimokawa default: 171110072Ssimokawa panic("Unknown operation 0x%x for busdma_lock_mutex!", op); 172106810Ssimokawa } 173103285Sikob} 174106813Ssimokawa 175103285Sikob/* 176110072Ssimokawa * dflt_lock should never get called. It gets put into the dma tag when 177110072Ssimokawa * lockfunc == NULL, which is only valid if the maps that are associated 178110072Ssimokawa * with the tag are meant to never be defered. 179110582Ssimokawa * XXX Should have a way to identify which driver is responsible here. 180110072Ssimokawa */ 181110072Ssimokawastatic void 182110072Ssimokawadflt_lock(void *arg, bus_dma_lock_op_t op) 183110072Ssimokawa{ 184110072Ssimokawa#ifdef INVARIANTS 185170374Ssimokawa panic("driver error: busdma dflt_lock called"); 186110193Ssimokawa#else 187110582Ssimokawa printf("DRIVER_ERROR: busdma dflt_lock called\n"); 188110072Ssimokawa#endif 189170374Ssimokawa} 190110072Ssimokawa 191110072Ssimokawa/* 192110072Ssimokawa * Since there is no way for a device to obtain a dma tag from its parent 193110072Ssimokawa * we use this kluge to handle different the different supported bus systems. 194110072Ssimokawa * The sparc64_root_dma_tag is used as parent for tags that have none, so that 195110072Ssimokawa * the correct methods will be used. 196110072Ssimokawa */ 197110072Ssimokawabus_dma_tag_t sparc64_root_dma_tag; 198103285Sikob 199103285Sikob/* 200103285Sikob * Allocate a device specific dma_tag. 201103285Sikob */ 202103285Sikobint 203103285Sikobbus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, 204103285Sikob bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, 205170374Ssimokawa bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, 206103285Sikob int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc, 207103285Sikob void *lockfuncarg, bus_dma_tag_t *dmat) 208103285Sikob{ 209103285Sikob bus_dma_tag_t impptag; 210103285Sikob bus_dma_tag_t newtag; 211167632Ssimokawa 212167632Ssimokawa /* Return a NULL tag on failure */ 213103285Sikob *dmat = NULL; 214103285Sikob 215120660Ssimokawa newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT); 216103285Sikob if (newtag == NULL) 217103285Sikob return (ENOMEM); 218103285Sikob 219103285Sikob impptag = parent != NULL ? parent : sparc64_root_dma_tag; 220124251Ssimokawa /* 221103285Sikob * The method table pointer and the cookie need to be taken over from 222103285Sikob * the parent or the root tag. 223170425Ssimokawa */ 224170425Ssimokawa newtag->dt_cookie = impptag->dt_cookie; 225170425Ssimokawa newtag->dt_mt = impptag->dt_mt; 226170425Ssimokawa 227170425Ssimokawa newtag->dt_parent = parent; 228170425Ssimokawa newtag->dt_alignment = alignment; 229170425Ssimokawa newtag->dt_boundary = boundary; 230170425Ssimokawa newtag->dt_lowaddr = trunc_page((vm_offset_t)lowaddr) + (PAGE_SIZE - 1); 231170425Ssimokawa newtag->dt_highaddr = trunc_page((vm_offset_t)highaddr) + 232170425Ssimokawa (PAGE_SIZE - 1); 233170425Ssimokawa newtag->dt_filter = filter; 234103285Sikob newtag->dt_filterarg = filterarg; 235103285Sikob newtag->dt_maxsize = maxsize; 236103285Sikob newtag->dt_nsegments = nsegments; 237103285Sikob newtag->dt_maxsegsz = maxsegsz; 238103285Sikob newtag->dt_flags = flags; 239120660Ssimokawa newtag->dt_ref_count = 1; /* Count ourselves */ 240120660Ssimokawa newtag->dt_map_count = 0; 241120660Ssimokawa 242120660Ssimokawa if (lockfunc != NULL) { 243103285Sikob newtag->dt_lockfunc = lockfunc; 244120660Ssimokawa newtag->dt_lockfuncarg = lockfuncarg; 245103285Sikob } else { 246120660Ssimokawa newtag->dt_lockfunc = dflt_lock; 247120660Ssimokawa newtag->dt_lockfuncarg = NULL; 248120660Ssimokawa } 249120660Ssimokawa 250124251Ssimokawa /* Take into account any restrictions imposed by our parent tag */ 251124251Ssimokawa if (parent != NULL) { 252103285Sikob newtag->dt_lowaddr = ulmin(parent->dt_lowaddr, 253103285Sikob newtag->dt_lowaddr); 254106790Ssimokawa newtag->dt_highaddr = ulmax(parent->dt_highaddr, 255103285Sikob newtag->dt_highaddr); 256103285Sikob /* 257103285Sikob * XXX Not really correct??? Probably need to honor boundary 258103285Sikob * all the way up the inheritence chain. 259103285Sikob */ 260108655Ssimokawa newtag->dt_boundary = ulmin(parent->dt_boundary, 261108655Ssimokawa newtag->dt_boundary); 262170374Ssimokawa atomic_add_int(&parent->dt_ref_count, 1); 263103285Sikob } 264103285Sikob 265170374Ssimokawa *dmat = newtag; 266103285Sikob return (0); 267170374Ssimokawa} 268130460Sdfr 269103285Sikobint 270103285Sikobbus_dma_tag_destroy(bus_dma_tag_t dmat) 271103285Sikob{ 272103285Sikob bus_dma_tag_t parent; 273103285Sikob 274103285Sikob if (dmat != NULL) { 275103285Sikob if (dmat->dt_map_count != 0) 276103285Sikob return (EBUSY); 277103285Sikob while (dmat != NULL) { 278103285Sikob parent = dmat->dt_parent; 279103285Sikob atomic_subtract_int(&dmat->dt_ref_count, 1); 280103285Sikob if (dmat->dt_ref_count == 0) { 281103285Sikob free(dmat, M_DEVBUF); 282170374Ssimokawa /* 283170374Ssimokawa * Last reference count, so 284170374Ssimokawa * release our reference 285170374Ssimokawa * count on our parent. 286170374Ssimokawa */ 287170374Ssimokawa dmat = parent; 288170374Ssimokawa } else 289170374Ssimokawa dmat = NULL; 290103285Sikob } 291103285Sikob } 292103285Sikob return (0); 293103285Sikob} 294170374Ssimokawa 295170374Ssimokawa/* Allocate/free a tag, and do the necessary management work. */ 296170374Ssimokawaint 297170374Ssimokawasparc64_dma_alloc_map(bus_dma_tag_t dmat, bus_dmamap_t *mapp) 298170374Ssimokawa{ 299170374Ssimokawa 300170374Ssimokawa *mapp = malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO); 301170374Ssimokawa if (*mapp == NULL) 302170374Ssimokawa return (ENOMEM); 303170374Ssimokawa 304170374Ssimokawa SLIST_INIT(&(*mapp)->dm_reslist); 305170374Ssimokawa dmat->dt_map_count++; 306170374Ssimokawa return (0); 307170374Ssimokawa} 308103285Sikob 309103285Sikobvoid 310103285Sikobsparc64_dma_free_map(bus_dma_tag_t dmat, bus_dmamap_t map) 311106790Ssimokawa{ 312106790Ssimokawa 313106790Ssimokawa free(map, M_DEVBUF); 314103285Sikob dmat->dt_map_count--; 315103285Sikob} 316170374Ssimokawa 317170374Ssimokawastatic int 318170374Ssimokawanexus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) 319170374Ssimokawa{ 320103285Sikob 321170374Ssimokawa return (sparc64_dma_alloc_map(dmat, mapp)); 322103285Sikob} 323170374Ssimokawa 324170374Ssimokawastatic int 325103285Sikobnexus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) 326103285Sikob{ 327103285Sikob 328103285Sikob sparc64_dma_free_map(dmat, map); 329103285Sikob return (0); 330103285Sikob} 331106790Ssimokawa 332125238Ssimokawa/* 333125238Ssimokawa * Utility function to load a linear buffer. lastaddrp holds state 334125238Ssimokawa * between invocations (for multiple-buffer loads). segp contains 335125238Ssimokawa * the starting segment on entrace, and the ending segment on exit. 336125238Ssimokawa * first indicates if this is the first invocation of this function. 337125238Ssimokawa */ 338103285Sikobstatic int 339125238Ssimokawa_nexus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t segs[], 340103285Sikob void *buf, bus_size_t buflen, struct thread *td, int flags, 341108281Ssimokawa bus_addr_t *lastaddrp, int *segp, int first) 342125238Ssimokawa{ 343103285Sikob bus_size_t sgsize; 344106790Ssimokawa bus_addr_t curaddr, lastaddr, baddr, bmask; 345110577Ssimokawa vm_offset_t vaddr = (vm_offset_t)buf; 346170374Ssimokawa int seg; 347110577Ssimokawa pmap_t pmap; 348170374Ssimokawa 349170374Ssimokawa if (td != NULL) 350110577Ssimokawa pmap = vmspace_pmap(td->td_proc->p_vmspace); 351110577Ssimokawa else 352170374Ssimokawa pmap = NULL; 353111040Ssimokawa 354110577Ssimokawa lastaddr = *lastaddrp; 355120660Ssimokawa bmask = ~(dmat->dt_boundary - 1); 356120660Ssimokawa 357110577Ssimokawa for (seg = *segp; buflen > 0 ; ) { 358110577Ssimokawa /* 359110577Ssimokawa * Get the physical address for this segment. 360170374Ssimokawa */ 361110577Ssimokawa if (pmap) 362111040Ssimokawa curaddr = pmap_extract(pmap, vaddr); 363171513Ssimokawa else 364110577Ssimokawa curaddr = pmap_kextract(vaddr); 365169119Ssimokawa 366170427Ssimokawa /* 367170427Ssimokawa * Compute the segment size, and adjust counts. 368170427Ssimokawa */ 369110577Ssimokawa sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK); 370110577Ssimokawa if (buflen < sgsize) 371110577Ssimokawa sgsize = buflen; 372110577Ssimokawa 373170374Ssimokawa /* 374170374Ssimokawa * Make sure we don't cross any boundaries. 375170374Ssimokawa */ 376110577Ssimokawa if (dmat->dt_boundary > 0) { 377249291Swill baddr = (curaddr + dmat->dt_boundary) & bmask; 378170374Ssimokawa if (sgsize > (baddr - curaddr)) 379170374Ssimokawa sgsize = (baddr - curaddr); 380110577Ssimokawa } 381110577Ssimokawa 382171513Ssimokawa /* 383111040Ssimokawa * Insert chunk into a segment, coalescing with 384170374Ssimokawa * previous segment if possible. 385170374Ssimokawa */ 386170374Ssimokawa if (first) { 387170374Ssimokawa segs[seg].ds_addr = curaddr; 388110577Ssimokawa segs[seg].ds_len = sgsize; 389110577Ssimokawa first = 0; 390170374Ssimokawa } else { 391110577Ssimokawa if (curaddr == lastaddr && 392110577Ssimokawa (segs[seg].ds_len + sgsize) <= dmat->dt_maxsegsz && 393110577Ssimokawa (dmat->dt_boundary == 0 || 394110577Ssimokawa (segs[seg].ds_addr & bmask) == (curaddr & bmask))) 395170374Ssimokawa segs[seg].ds_len += sgsize; 396110577Ssimokawa else { 397110577Ssimokawa if (++seg >= dmat->dt_nsegments) 398121463Ssimokawa break; 399121463Ssimokawa segs[seg].ds_addr = curaddr; 400121463Ssimokawa segs[seg].ds_len = sgsize; 401121463Ssimokawa } 402121463Ssimokawa } 403121463Ssimokawa 404170374Ssimokawa lastaddr = curaddr + sgsize; 405170374Ssimokawa vaddr += sgsize; 406170374Ssimokawa buflen -= sgsize; 407170374Ssimokawa } 408121463Ssimokawa 409170374Ssimokawa *segp = seg; 410110577Ssimokawa *lastaddrp = lastaddr; 411110577Ssimokawa 412110577Ssimokawa /* 413103285Sikob * Did we fit? 414103285Sikob */ 415103285Sikob return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ 416103285Sikob} 417118455Ssimokawa 418103285Sikob/* 419118455Ssimokawa * Common function for loading a DMA map with a linear buffer. May 420103285Sikob * be called by bus-specific DMA map load functions. 421103285Sikob * 422103285Sikob * Most SPARCs have IOMMUs in the bus controllers. In those cases 423103285Sikob * they only need one segment and will use virtual addresses for DVMA. 424103285Sikob * Those bus controllers should intercept these vectors and should 425103285Sikob * *NEVER* call nexus_dmamap_load() which is used only by devices that 426116978Ssimokawa * bypass DVMA. 427103285Sikob */ 428118455Ssimokawastatic int 429118455Ssimokawanexus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, 430103285Sikob bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg, 431118455Ssimokawa int flags) 432118455Ssimokawa{ 433187993Ssbruno#ifdef __GNUC__ 434187993Ssbruno bus_dma_segment_t dm_segments[dmat->dt_nsegments]; 435187993Ssbruno#else 436187993Ssbruno bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 437187993Ssbruno#endif 438187993Ssbruno bus_addr_t lastaddr; 439187993Ssbruno int error, nsegs; 440187993Ssbruno 441187993Ssbruno error = _nexus_dmamap_load_buffer(dmat, dm_segments, buf, buflen, 442187993Ssbruno NULL, flags, &lastaddr, &nsegs, 1); 443187993Ssbruno 444187993Ssbruno if (error == 0) { 445187993Ssbruno (*callback)(callback_arg, dm_segments, nsegs + 1, 0); 446187993Ssbruno map->dm_flags |= DMF_LOADED; 447187993Ssbruno } else 448187993Ssbruno (*callback)(callback_arg, NULL, 0, error); 449187993Ssbruno 450187993Ssbruno return (0); 451187993Ssbruno} 452187993Ssbruno 453187993Ssbruno/* 454187993Ssbruno * Like nexus_dmamap_load(), but for mbufs. 455187993Ssbruno */ 456187993Ssbrunostatic int 457187993Ssbrunonexus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0, 458170374Ssimokawa bus_dmamap_callback2_t *callback, void *callback_arg, int flags) 459171513Ssimokawa{ 460170374Ssimokawa#ifdef __GNUC__ 461170374Ssimokawa bus_dma_segment_t dm_segments[dmat->dt_nsegments]; 462170374Ssimokawa#else 463170374Ssimokawa bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 464108853Ssimokawa#endif 465110577Ssimokawa int nsegs, error; 466110577Ssimokawa 467110193Ssimokawa M_ASSERTPKTHDR(m0); 468169806Ssimokawa 469172836Sjulian nsegs = 0; 470169806Ssimokawa error = 0; 471169806Ssimokawa if (m0->m_pkthdr.len <= dmat->dt_maxsize) { 472103285Sikob int first = 1; 473103285Sikob bus_addr_t lastaddr = 0; 474103285Sikob struct mbuf *m; 475103285Sikob 476103285Sikob for (m = m0; m != NULL && error == 0; m = m->m_next) { 477103285Sikob if (m->m_len > 0) { 478103285Sikob error = _nexus_dmamap_load_buffer(dmat, 479187993Ssbruno dm_segments, m->m_data, m->m_len, NULL, 480169117Ssimokawa flags, &lastaddr, &nsegs, first); 481187993Ssbruno first = 0; 482103285Sikob } 483103285Sikob } 484103285Sikob } else { 485103285Sikob error = EINVAL; 486103285Sikob } 487103285Sikob 488103285Sikob if (error) { 489103285Sikob /* force "no valid mappings" in callback */ 490103285Sikob (*callback)(callback_arg, dm_segments, 0, 0, error); 491212413Savg } else { 492103285Sikob map->dm_flags |= DMF_LOADED; 493103285Sikob (*callback)(callback_arg, dm_segments, nsegs + 1, 494103285Sikob m0->m_pkthdr.len, error); 495103285Sikob } 496103285Sikob return (error); 497103285Sikob} 498103285Sikob 499103285Sikob/* 500103285Sikob * Like nexus_dmamap_load(), but for uios. 501103285Sikob */ 502103285Sikobstatic int 503103285Sikobnexus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, struct uio *uio, 504103285Sikob bus_dmamap_callback2_t *callback, void *callback_arg, int flags) 505106790Ssimokawa{ 506116978Ssimokawa bus_addr_t lastaddr; 507116978Ssimokawa#ifdef __GNUC__ 508116978Ssimokawa bus_dma_segment_t dm_segments[dmat->dt_nsegments]; 509116978Ssimokawa#else 510116978Ssimokawa bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 511116978Ssimokawa#endif 512116978Ssimokawa int nsegs, error, first, i; 513116978Ssimokawa bus_size_t resid; 514116978Ssimokawa struct iovec *iov; 515116978Ssimokawa struct thread *td = NULL; 516116978Ssimokawa 517116978Ssimokawa resid = uio->uio_resid; 518116978Ssimokawa iov = uio->uio_iov; 519103285Sikob 520103285Sikob if (uio->uio_segflg == UIO_USERSPACE) { 521103285Sikob td = uio->uio_td; 522103285Sikob KASSERT(td != NULL, 523118455Ssimokawa ("nexus_dmamap_load_uio: USERSPACE but no proc")); 524103285Sikob } 525103285Sikob 526169806Ssimokawa nsegs = 0; 527111078Ssimokawa error = 0; 528118455Ssimokawa first = 1; 529103285Sikob for (i = 0; i < uio->uio_iovcnt && resid != 0 && !error; i++) { 530103285Sikob /* 531169806Ssimokawa * Now at the first iovec to load. Load each iovec 532170374Ssimokawa * until we have exhausted the residual count. 533169806Ssimokawa */ 534170374Ssimokawa bus_size_t minlen = 535170374Ssimokawa resid < iov[i].iov_len ? resid : iov[i].iov_len; 536170374Ssimokawa caddr_t addr = (caddr_t) iov[i].iov_base; 537170374Ssimokawa 538169806Ssimokawa if (minlen > 0) { 539178915Ssimokawa error = _nexus_dmamap_load_buffer(dmat, dm_segments, 540178915Ssimokawa addr, minlen, td, flags, &lastaddr, &nsegs, first); 541178915Ssimokawa first = 0; 542118455Ssimokawa 543118455Ssimokawa resid -= minlen; 544106790Ssimokawa } 545118455Ssimokawa } 546118455Ssimokawa 547111078Ssimokawa if (error) { 548169806Ssimokawa /* force "no valid mappings" in callback */ 549169806Ssimokawa (*callback)(callback_arg, dm_segments, 0, 0, error); 550169806Ssimokawa } else { 551111078Ssimokawa map->dm_flags |= DMF_LOADED; 552178915Ssimokawa (*callback)(callback_arg, dm_segments, nsegs + 1, 553169806Ssimokawa uio->uio_resid, error); 554111078Ssimokawa } 555111078Ssimokawa return (error); 556111078Ssimokawa} 557111078Ssimokawa 558169806Ssimokawa/* 559169806Ssimokawa * Common function for unloading a DMA map. May be called by 560169806Ssimokawa * bus-specific DMA map unload functions. 561169806Ssimokawa */ 562171513Ssimokawastatic void 563170374Ssimokawanexus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) 564103285Sikob{ 565103285Sikob 566103285Sikob map->dm_flags &= ~DMF_LOADED; 567103285Sikob} 568103285Sikob 569103285Sikob/* 570103285Sikob * Common function for DMA map synchronization. May be called 571103285Sikob * by bus-specific DMA map synchronization functions. 572103285Sikob */ 573106790Ssimokawastatic void 574110577Ssimokawanexus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op) 575110577Ssimokawa{ 576110798Ssimokawa 577110577Ssimokawa /* 578110577Ssimokawa * We sync out our caches, but the bus must do the same. 579110577Ssimokawa * 580110577Ssimokawa * Actually a #Sync is expensive. We should optimize. 581110577Ssimokawa */ 582170374Ssimokawa if ((op & BUS_DMASYNC_PREREAD) || (op & BUS_DMASYNC_PREWRITE)) { 583111942Ssimokawa /* 584170374Ssimokawa * Don't really need to do anything, but flush any pending 585110577Ssimokawa * writes anyway. 586170374Ssimokawa */ 587113584Ssimokawa membar(Sync); 588110577Ssimokawa } 589110577Ssimokawa#if 0 590110577Ssimokawa /* Should not be needed. */ 591110798Ssimokawa if (op & BUS_DMASYNC_POSTREAD) { 592110798Ssimokawa ecache_flush((vm_offset_t)map->buf, 593110798Ssimokawa (vm_offset_t)map->buf + map->buflen - 1); 594170374Ssimokawa } 595170374Ssimokawa#endif 596110798Ssimokawa if (op & BUS_DMASYNC_POSTWRITE) { 597110798Ssimokawa /* Nothing to do. Handled by the bus controller. */ 598170374Ssimokawa } 599170374Ssimokawa} 600170374Ssimokawa 601110798Ssimokawa/* 602110798Ssimokawa * Common function for DMA-safe memory allocation. May be called 603110798Ssimokawa * by bus-specific DMA memory allocation functions. 604110798Ssimokawa */ 605171513Ssimokawastatic int 606170374Ssimokawanexus_dmamem_alloc(bus_dma_tag_t dmat, void **vaddr, int flags, 607171513Ssimokawa bus_dmamap_t *mapp) 608170374Ssimokawa{ 609170374Ssimokawa int mflags; 610170374Ssimokawa 611170374Ssimokawa if (flags & BUS_DMA_NOWAIT) 612249291Swill mflags = M_NOWAIT; 613170374Ssimokawa else 614170374Ssimokawa mflags = M_WAITOK; 615170374Ssimokawa if (flags & BUS_DMA_ZERO) 616170374Ssimokawa mflags |= M_ZERO; 617171513Ssimokawa 618170374Ssimokawa if ((dmat->dt_maxsize <= PAGE_SIZE)) { 619170374Ssimokawa *vaddr = malloc(dmat->dt_maxsize, M_DEVBUF, mflags); 620170374Ssimokawa } else { 621110798Ssimokawa /* 622110798Ssimokawa * XXX: Use contigmalloc until it is merged into this facility 623116376Ssimokawa * and handles multi-seg allocations. Nobody is doing multi-seg 624116376Ssimokawa * allocations yet though. 625103285Sikob */ 626103285Sikob *vaddr = contigmalloc(dmat->dt_maxsize, M_DEVBUF, mflags, 627103285Sikob 0ul, dmat->dt_lowaddr, 628103285Sikob dmat->dt_alignment ? dmat->dt_alignment : 1UL, 629103285Sikob dmat->dt_boundary); 630103285Sikob } 631103285Sikob if (*vaddr == NULL) 632103285Sikob return (ENOMEM); 633103285Sikob return (0); 634103285Sikob} 635103285Sikob 636103285Sikob/* 637103285Sikob * Common function for freeing DMA-safe memory. May be called by 638103285Sikob * bus-specific DMA memory free functions. 639103285Sikob */ 640103285Sikobstatic void 641103285Sikobnexus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) 642103285Sikob{ 643103285Sikob 644103285Sikob if ((dmat->dt_maxsize <= PAGE_SIZE)) 645103285Sikob free(vaddr, M_DEVBUF); 646103285Sikob else { 647103285Sikob contigfree(vaddr, dmat->dt_maxsize, M_DEVBUF); 648103285Sikob } 649103285Sikob} 650103285Sikob 651103285Sikobstruct bus_dma_methods nexus_dma_methods = { 652103285Sikob nexus_dmamap_create, 653103285Sikob nexus_dmamap_destroy, 654103285Sikob nexus_dmamap_load, 655103285Sikob nexus_dmamap_load_mbuf, 656103285Sikob nexus_dmamap_load_uio, 657103285Sikob nexus_dmamap_unload, 658103285Sikob nexus_dmamap_sync, 659103285Sikob nexus_dmamem_alloc, 660103285Sikob nexus_dmamem_free, 661103285Sikob}; 662103285Sikob 663103285Sikobstruct bus_dma_tag nexus_dmatag = { 664103285Sikob NULL, 665103285Sikob NULL, 666116376Ssimokawa 8, 667113584Ssimokawa 0, 668116376Ssimokawa 0, 669116376Ssimokawa 0x3ffffffff, 670116376Ssimokawa NULL, /* XXX */ 671116376Ssimokawa NULL, 672116376Ssimokawa 0x3ffffffff, /* XXX */ 673116376Ssimokawa 0xff, /* XXX */ 674116376Ssimokawa 0xffffffff, /* XXX */ 675116376Ssimokawa 0, 676116376Ssimokawa 0, 677116376Ssimokawa 0, 678116376Ssimokawa NULL, 679116376Ssimokawa NULL, 680116376Ssimokawa &nexus_dma_methods, 681116376Ssimokawa}; 682116376Ssimokawa 683116376Ssimokawa/* 684116376Ssimokawa * Helpers to map/unmap bus memory 685116376Ssimokawa */ 686116376Ssimokawaint 687116376Ssimokawasparc64_bus_mem_map(bus_space_tag_t tag, bus_space_handle_t handle, 688116376Ssimokawa bus_size_t size, int flags, vm_offset_t vaddr, void **hp) 689189928Ssbruno{ 690189928Ssbruno vm_offset_t addr; 691116376Ssimokawa vm_offset_t sva; 692116376Ssimokawa vm_offset_t va; 693116376Ssimokawa vm_paddr_t pa; 694116376Ssimokawa vm_size_t vsz; 695116376Ssimokawa u_long pm_flags; 696116376Ssimokawa 697116376Ssimokawa addr = (vm_offset_t)handle; 698116376Ssimokawa size = round_page(size); 699116376Ssimokawa if (size == 0) { 700116376Ssimokawa printf("sparc64_bus_map: zero size\n"); 701116376Ssimokawa return (EINVAL); 702116376Ssimokawa } 703116376Ssimokawa switch (tag->bst_type) { 704116376Ssimokawa case PCI_CONFIG_BUS_SPACE: 705116376Ssimokawa case PCI_IO_BUS_SPACE: 706116376Ssimokawa case PCI_MEMORY_BUS_SPACE: 707116376Ssimokawa pm_flags = TD_IE; 708116376Ssimokawa break; 709116376Ssimokawa default: 710116376Ssimokawa pm_flags = 0; 711116376Ssimokawa break; 712116376Ssimokawa } 713116376Ssimokawa 714116376Ssimokawa if (!(flags & BUS_SPACE_MAP_CACHEABLE)) 715116376Ssimokawa pm_flags |= TD_E; 716116376Ssimokawa 717116376Ssimokawa if (vaddr != 0L) 718116376Ssimokawa sva = trunc_page(vaddr); 719116376Ssimokawa else { 720127468Ssimokawa if ((sva = kmem_alloc_nofault(kernel_map, size)) == 0) 721127468Ssimokawa panic("sparc64_bus_map: cannot allocate virtual " 722127468Ssimokawa "memory"); 723127468Ssimokawa } 724116376Ssimokawa 725116376Ssimokawa /* Preserve page offset. */ 726127468Ssimokawa *hp = (void *)(sva | ((u_long)addr & PAGE_MASK)); 727193066Sjamie 728194118Sjamie pa = trunc_page(addr); 729193066Sjamie if ((flags & BUS_SPACE_MAP_READONLY) == 0) 730116376Ssimokawa pm_flags |= TD_W; 731116376Ssimokawa 732116376Ssimokawa va = sva; 733116376Ssimokawa vsz = size; 734116376Ssimokawa do { 735116376Ssimokawa pmap_kenter_flags(va, pa, pm_flags); 736169117Ssimokawa va += PAGE_SIZE; 737116376Ssimokawa pa += PAGE_SIZE; 738116376Ssimokawa } while ((vsz -= PAGE_SIZE) > 0); 739117350Ssimokawa tlb_range_demap(kernel_pmap, sva, sva + size - 1); 740116376Ssimokawa return (0); 741189928Ssbruno} 742116376Ssimokawa 743116376Ssimokawaint 744187993Ssbrunosparc64_bus_mem_unmap(void *bh, bus_size_t size) 745187993Ssbruno{ 746116376Ssimokawa vm_offset_t sva; 747187993Ssbruno vm_offset_t va; 748169117Ssimokawa vm_offset_t endva; 749116376Ssimokawa 750187993Ssbruno sva = trunc_page((vm_offset_t)bh); 751187993Ssbruno endva = sva + round_page(size); 752187993Ssbruno for (va = sva; va < endva; va += PAGE_SIZE) 753187993Ssbruno pmap_kremove_flags(va); 754116376Ssimokawa tlb_range_demap(kernel_pmap, sva, sva + size - 1); 755116376Ssimokawa kmem_free(kernel_map, sva, size); 756113584Ssimokawa return (0); 757113584Ssimokawa} 758113584Ssimokawa 759113584Ssimokawa/* 760113584Ssimokawa * Fake up a bus tag, for use by console drivers in early boot when the regular 761113584Ssimokawa * means to allocate resources are not yet available. 762113584Ssimokawa * Addr is the physical address of the desired start of the handle. 763113584Ssimokawa */ 764113584Ssimokawabus_space_handle_t 765116376Ssimokawasparc64_fake_bustag(int space, bus_addr_t addr, struct bus_space_tag *ptag) 766117350Ssimokawa{ 767189928Ssbruno 768189928Ssbruno ptag->bst_cookie = NULL; 769189928Ssbruno ptag->bst_parent = NULL; 770189928Ssbruno ptag->bst_type = space; 771189928Ssbruno ptag->bst_bus_barrier = nexus_bus_barrier; 772189928Ssbruno return (addr); 773189928Ssbruno} 774189928Ssbruno 775189928Ssbruno/* 776189928Ssbruno * Base bus space handlers. 777189928Ssbruno */ 778189928Ssbruno 779189928Ssbrunostatic void 780189928Ssbrunonexus_bus_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, 781189928Ssbruno bus_size_t size, int flags) 782189928Ssbruno{ 783189928Ssbruno 784189928Ssbruno /* 785189928Ssbruno * We have lots of alternatives depending on whether we're 786189928Ssbruno * synchronizing loads with loads, loads with stores, stores 787189928Ssbruno * with loads, or stores with stores. The only ones that seem 788189928Ssbruno * generic are #Sync and #MemIssue. I'll use #Sync for safety. 789189928Ssbruno */ 790189928Ssbruno switch(flags) { 791189928Ssbruno case BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE: 792103285Sikob case BUS_SPACE_BARRIER_READ: 793106790Ssimokawa case BUS_SPACE_BARRIER_WRITE: 794103285Sikob membar(Sync); 795106790Ssimokawa break; 796103285Sikob default: 797103285Sikob panic("sparc64_bus_barrier: unknown flags"); 798106543Ssimokawa } 799103285Sikob return; 800103285Sikob} 801106543Ssimokawa 802103285Sikobstruct bus_space_tag nexus_bustag = { 803103285Sikob NULL, /* cookie */ 804103285Sikob NULL, /* parent bus tag */ 805103285Sikob UPA_BUS_SPACE, /* type */ 806103285Sikob nexus_bus_barrier, /* bus_space_barrier */ 807103285Sikob}; 808103285Sikob