xref: /netbsd/sys/arch/vax/vax/sgmap.c (revision c4a72b64)
1 /* $NetBSD: sgmap.c,v 1.10 2002/09/27 15:37:00 provos Exp $ */
2 
3 /*-
4  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/malloc.h>
45 
46 #include <uvm/uvm_extern.h>
47 
48 #include <machine/bus.h>
49 #include <machine/sgmap.h>
50 
51 void
52 vax_sgmap_init(t, sgmap, name, sgvabase, sgvasize, ptva, minptalign)
53 	bus_dma_tag_t t;
54 	struct vax_sgmap *sgmap;
55 	const char *name;
56 	bus_addr_t sgvabase;
57 	bus_size_t sgvasize;
58 	struct pte *ptva;
59 	bus_size_t minptalign;
60 {
61 	bus_dma_segment_t seg;
62 	size_t ptsize;
63 	int rseg;
64 
65 	if (sgvasize & PGOFSET) {
66 		printf("size botch for sgmap `%s'\n", name);
67 		goto die;
68 	}
69 
70 	sgmap->aps_sgvabase = sgvabase;
71 	sgmap->aps_sgvasize = sgvasize;
72 
73 	if (ptva != NULL) {
74 		/*
75 		 * We already have a page table; this may be a system
76 		 * where the page table resides in bridge-resident SRAM.
77 		 */
78 		sgmap->aps_pt = ptva;
79 	} else {
80 		/*
81 		 * Compute the page table size and allocate it.  At minimum,
82 		 * this must be aligned to the page table size.  However,
83 		 * some platforms have more strict alignment reqirements.
84 		 */
85 		ptsize = (sgvasize / VAX_NBPG) * sizeof(struct pte);
86 		if (minptalign != 0) {
87 			if (minptalign < ptsize)
88 				minptalign = ptsize;
89 		} else
90 			minptalign = ptsize;
91 		if (bus_dmamem_alloc(t, ptsize, minptalign, 0, &seg, 1, &rseg,
92 		    BUS_DMA_NOWAIT)) {
93 			panic("unable to allocate page table for sgmap `%s'",
94 			    name);
95 			goto die;
96 		}
97 		sgmap->aps_pt = (struct pte *)(seg.ds_addr | KERNBASE);
98 	}
99 
100 	/*
101 	 * Create the extent map used to manage the virtual address
102 	 * space.
103 	 */
104 	sgmap->aps_ex = extent_create((char *)name, sgvabase, sgvasize - 1,
105 	    M_DMAMAP, NULL, 0, EX_NOWAIT|EX_NOCOALESCE);
106 	if (sgmap->aps_ex == NULL) {
107 		printf("unable to create extent map for sgmap `%s'\n", name);
108 		goto die;
109 	}
110 
111 	return;
112  die:
113 	panic("vax_sgmap_init");
114 }
115 
116 int
117 vax_sgmap_alloc(map, origlen, sgmap, flags)
118 	bus_dmamap_t map;
119 	bus_size_t origlen;
120 	struct vax_sgmap *sgmap;
121 	int flags;
122 {
123 	int error;
124 	bus_size_t len = origlen;
125 
126 #ifdef DIAGNOSTIC
127 	if (map->_dm_flags & DMAMAP_HAS_SGMAP)
128 		panic("vax_sgmap_alloc: already have sgva space");
129 #endif
130 
131 	/* If we need a spill page (for the VS4000 SCSI), make sure we
132 	 * allocate enough space for an extra page.
133 	 */
134 	if (flags & VAX_BUS_DMA_SPILLPAGE) {
135 		len += VAX_NBPG;
136 	}
137 
138 	map->_dm_sgvalen = vax_round_page(len);
139 #if 0
140 	printf("len %x -> %x, _dm_sgvalen %x _dm_boundary %x boundary %x -> ",
141 	    origlen, len, map->_dm_sgvalen, map->_dm_boundary, boundary);
142 #endif
143 
144 	error = extent_alloc(sgmap->aps_ex, map->_dm_sgvalen, VAX_NBPG,
145 	    0, (flags & BUS_DMA_NOWAIT) ? EX_NOWAIT : EX_WAITOK,
146 	    &map->_dm_sgva);
147 #if 0
148 	printf("error %d _dm_sgva %x\n", error, map->_dm_sgva);
149 #endif
150 
151 	if (error == 0)
152 		map->_dm_flags |= DMAMAP_HAS_SGMAP;
153 	else
154 		map->_dm_flags &= ~DMAMAP_HAS_SGMAP;
155 
156 	return (error);
157 }
158 
159 void
160 vax_sgmap_free(map, sgmap)
161 	bus_dmamap_t map;
162 	struct vax_sgmap *sgmap;
163 {
164 
165 #ifdef DIAGNOSTIC
166 	if ((map->_dm_flags & DMAMAP_HAS_SGMAP) == 0)
167 		panic("vax_sgmap_free: no sgva space to free");
168 #endif
169 
170 	if (extent_free(sgmap->aps_ex, map->_dm_sgva, map->_dm_sgvalen,
171 	    EX_NOWAIT))
172 		panic("vax_sgmap_free");
173 
174 	map->_dm_flags &= ~DMAMAP_HAS_SGMAP;
175 }
176 
177 int
178 vax_sgmap_load(t, map, buf, buflen, p, flags, sgmap)
179 	bus_dma_tag_t t;
180 	bus_dmamap_t map;
181 	void *buf;
182 	bus_size_t buflen;
183 	struct proc *p;
184 	int flags;
185 	struct vax_sgmap *sgmap;
186 {
187 	vaddr_t endva, va = (vaddr_t)buf;
188 	paddr_t pa;
189 	bus_addr_t dmaoffset;
190 	bus_size_t dmalen;
191 	long *pte, *page_table = (long *)sgmap->aps_pt;
192 	int pteidx, error;
193 
194 	/*
195 	 * Make sure that on error condition we return "no valid mappings".
196 	 */
197 	map->dm_mapsize = 0;
198 	map->dm_nsegs = 0;
199 
200 	if (buflen > map->_dm_size)
201 		return (EINVAL);
202 
203 	/*
204 	 * Remember the offset into the first page and the total
205 	 * transfer length.
206 	 */
207 	dmaoffset = ((u_long)buf) & VAX_PGOFSET;
208 	dmalen = buflen;
209 
210 
211 	/*
212 	 * Allocate the necessary virtual address space for the
213 	 * mapping.  Round the size, since we deal with whole pages.
214 	 */
215 	endva = vax_round_page(va + buflen);
216 	va = vax_trunc_page(va);
217 	if ((map->_dm_flags & DMAMAP_HAS_SGMAP) == 0) {
218 		error = vax_sgmap_alloc(map, (endva - va), sgmap, flags);
219 		if (error)
220 			return (error);
221 	}
222 
223 	pteidx = map->_dm_sgva >> VAX_PGSHIFT;
224 	pte = &page_table[pteidx];
225 
226 	/*
227 	 * Generate the DMA address.
228 	 */
229 	map->dm_segs[0].ds_addr = map->_dm_sgva + dmaoffset;
230 	map->dm_segs[0].ds_len = dmalen;
231 
232 
233 	map->_dm_pteidx = pteidx;
234 	map->_dm_ptecnt = 0;
235 
236 	/*
237 	 * Create the bus-specific page tables.
238 	 * Can be done much more efficient than this.
239 	 */
240 	for (; va < endva; va += VAX_NBPG, pte++, map->_dm_ptecnt++) {
241 		/*
242 		 * Get the physical address for this segment.
243 		 */
244 		if (p != NULL)
245 			(void) pmap_extract(p->p_vmspace->vm_map.pmap, va, &pa);
246 		else
247 			pa = kvtophys(va);
248 
249 		/*
250 		 * Load the current PTE with this page.
251 		 */
252 		*pte = (pa >> VAX_PGSHIFT) | PG_V;
253 	}
254 	/* The VS4000 SCSI prefetcher doesn't like to end on a page boundary
255 	 * so add an extra page to quiet it down.
256 	 */
257 	if (flags & VAX_BUS_DMA_SPILLPAGE) {
258 		*pte = pte[-1];
259 		map->_dm_ptecnt++;
260 	}
261 
262 	map->dm_mapsize = buflen;
263 	map->dm_nsegs = 1;
264 	return (0);
265 }
266 
267 int
268 vax_sgmap_load_mbuf(t, map, m, flags, sgmap)
269 	bus_dma_tag_t t;
270 	bus_dmamap_t map;
271 	struct mbuf *m;
272 	int flags;
273 	struct vax_sgmap *sgmap;
274 {
275 
276 	panic("vax_sgmap_load_mbuf : not implemented");
277 }
278 
279 int
280 vax_sgmap_load_uio(t, map, uio, flags, sgmap)
281 	bus_dma_tag_t t;
282 	bus_dmamap_t map;
283 	struct uio *uio;
284 	int flags;
285 	struct vax_sgmap *sgmap;
286 {
287 
288 	panic("vax_sgmap_load_uio : not implemented");
289 }
290 
291 int
292 vax_sgmap_load_raw(t, map, segs, nsegs, size, flags, sgmap)
293 	bus_dma_tag_t t;
294 	bus_dmamap_t map;
295 	bus_dma_segment_t *segs;
296 	int nsegs;
297 	bus_size_t size;
298 	int flags;
299 	struct vax_sgmap *sgmap;
300 {
301 
302 	panic("vax_sgmap_load_raw : not implemented");
303 }
304 
305 void
306 vax_sgmap_unload(t, map, sgmap)
307 	bus_dma_tag_t t;
308 	bus_dmamap_t map;
309 	struct vax_sgmap *sgmap;
310 {
311 	long *pte, *page_table = (long *)sgmap->aps_pt;
312 	int ptecnt;
313 
314 	/*
315 	 * Invalidate the PTEs for the mapping.
316 	 */
317 	for (ptecnt = map->_dm_ptecnt, pte = &page_table[map->_dm_pteidx];
318 		ptecnt-- != 0; ) {
319 		*pte++ = 0;
320 	}
321 
322 	/*
323 	 * Free the virtual address space used by the mapping
324 	 * if necessary.
325 	 */
326 	if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
327 		vax_sgmap_free(map, sgmap);
328 	/*
329 	 * Mark the mapping invalid.
330 	 */
331 	map->dm_mapsize = 0;
332 	map->dm_nsegs = 0;
333 }
334