sgmap.c revision 1.8 1 /* $NetBSD: sgmap.c,v 1.8 2000/06/29 07:14:34 mrg 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'\n",
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 if (sgmap->aps_flags & SGMAP_KA49) {
253 unsigned long tmp = pa >> VAX_PGSHIFT;
254 int cnt;
255
256 for (cnt = 0; tmp != 0; tmp >>= 1) {
257 cnt += (tmp & 1);
258 }
259 *pte = pa | PG_V | ((cnt & 1) ? 0 : 0x10000000);
260 #if 0
261 printf("[%d]: va=0x%08lx map=0x%08lx\n",
262 pteidx + map->_dm_ptecnt, va + dmaoffset, *pte);
263 #endif
264 } else {
265 *pte = (pa >> VAX_PGSHIFT) | PG_V;
266 }
267 }
268 /* The VS4000 SCSI prefetcher doesn't like to end on a page boundary
269 * so add an extra page to quiet it down.
270 */
271 if (flags & VAX_BUS_DMA_SPILLPAGE) {
272 *pte = pte[-1];
273 map->_dm_ptecnt++;
274 }
275
276 map->dm_mapsize = buflen;
277 map->dm_nsegs = 1;
278 return (0);
279 }
280
281 int
282 vax_sgmap_load_mbuf(t, map, m, flags, sgmap)
283 bus_dma_tag_t t;
284 bus_dmamap_t map;
285 struct mbuf *m;
286 int flags;
287 struct vax_sgmap *sgmap;
288 {
289
290 panic("vax_sgmap_load_mbuf : not implemented");
291 }
292
293 int
294 vax_sgmap_load_uio(t, map, uio, flags, sgmap)
295 bus_dma_tag_t t;
296 bus_dmamap_t map;
297 struct uio *uio;
298 int flags;
299 struct vax_sgmap *sgmap;
300 {
301
302 panic("vax_sgmap_load_uio : not implemented");
303 }
304
305 int
306 vax_sgmap_load_raw(t, map, segs, nsegs, size, flags, sgmap)
307 bus_dma_tag_t t;
308 bus_dmamap_t map;
309 bus_dma_segment_t *segs;
310 int nsegs;
311 bus_size_t size;
312 int flags;
313 struct vax_sgmap *sgmap;
314 {
315
316 panic("vax_sgmap_load_raw : not implemented");
317 }
318
319 void
320 vax_sgmap_unload(t, map, sgmap)
321 bus_dma_tag_t t;
322 bus_dmamap_t map;
323 struct vax_sgmap *sgmap;
324 {
325 long *pte, *page_table = (long *)sgmap->aps_pt;
326 int ptecnt;
327
328 /*
329 * Invalidate the PTEs for the mapping.
330 */
331 for (ptecnt = map->_dm_ptecnt, pte = &page_table[map->_dm_pteidx];
332 ptecnt-- != 0; ) {
333 *pte++ = 0;
334 }
335
336 /*
337 * Free the virtual address space used by the mapping
338 * if necessary.
339 */
340 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
341 vax_sgmap_free(map, sgmap);
342 /*
343 * Mark the mapping invalid.
344 */
345 map->dm_mapsize = 0;
346 map->dm_nsegs = 0;
347 }
348