apbus.c revision 1.15 1 /* $NetBSD: apbus.c,v 1.15 2003/05/10 09:46:24 tsutsui Exp $ */
2
3 /*-
4 * Copyright (C) 1999 SHIMIZU Ryo. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/malloc.h>
32 #include <sys/device.h>
33 #include <sys/proc.h>
34
35 #include <uvm/uvm_extern.h>
36
37 #include <machine/adrsmap.h>
38 #include <machine/autoconf.h>
39 #define _NEWSMIPS_BUS_DMA_PRIVATE
40 #include <machine/bus.h>
41 #include <machine/intr.h>
42 #include <newsmips/apbus/apbusvar.h>
43
44 static int apbusmatch (struct device *, struct cfdata *, void *);
45 static void apbusattach (struct device *, struct device *, void *);
46 static int apbusprint (void *, const char *);
47 /* static void *aptokseg0 (void *); */
48 static void apbus_dma_unmapped (bus_dma_tag_t, bus_dmamap_t);
49 static int apbus_dma_mapalloc (bus_dma_tag_t, bus_dmamap_t, int);
50 static void apbus_dma_mapfree (bus_dma_tag_t, bus_dmamap_t);
51 static void apbus_dma_mapset (bus_dma_tag_t, bus_dmamap_t);
52 static int apbus_dmamap_create (bus_dma_tag_t, bus_size_t, int, bus_size_t,
53 bus_size_t, int, bus_dmamap_t *);
54 static void apbus_dmamap_destroy (bus_dma_tag_t, bus_dmamap_t);
55 static int apbus_dmamap_load (bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t,
56 struct proc *, int);
57 static int apbus_dmamap_load_mbuf (bus_dma_tag_t, bus_dmamap_t, struct mbuf *,
58 int);
59 static int apbus_dmamap_load_uio (bus_dma_tag_t, bus_dmamap_t, struct uio *,
60 int);
61 static int apbus_dmamap_load_raw (bus_dma_tag_t, bus_dmamap_t,
62 bus_dma_segment_t *, int, bus_size_t, int);
63 static void apbus_dmamap_sync (bus_dma_tag_t, bus_dmamap_t, bus_addr_t,
64 bus_size_t, int);
65
66 #define MAXAPDEVNUM 32
67
68 struct apbus_softc {
69 struct device apbs_dev;
70 };
71
72 CFATTACH_DECL(ap, sizeof(struct apbus_softc),
73 apbusmatch, apbusattach, NULL, NULL);
74
75 #define NLEVEL 2
76 static struct newsmips_intr apintr_tab[NLEVEL];
77
78 static int
79 apbusmatch(parent, cfdata, aux)
80 struct device *parent;
81 struct cfdata *cfdata;
82 void *aux;
83 {
84 struct confargs *ca = aux;
85
86 if (strcmp(ca->ca_name, "ap") != 0)
87 return 0;
88
89 return 1;
90 }
91
92
93 static void
94 apbusattach(parent, self, aux)
95 struct device *parent;
96 struct device *self;
97 void *aux;
98 {
99 struct apbus_attach_args child;
100 struct apbus_dev *apdev;
101 struct apbus_ctl *apctl;
102 struct newsmips_intr *ip;
103 int i;
104
105 *(volatile u_int *)(NEWS5000_APBUS_INTST) = 0xffffffff;
106 *(volatile u_int *)(NEWS5000_APBUS_INTMSK) = 0xffffffff;
107 *(volatile u_int *)(NEWS5000_APBUS_CTRL) = 0x00000004;
108 *(volatile u_int *)(NEWS5000_APBUS_DMA) = 0xffffffff;
109
110 printf("\n");
111
112 for (i = 0; i < NLEVEL; i++) {
113 ip = &apintr_tab[i];
114 LIST_INIT(&ip->intr_q);
115 }
116
117 /*
118 * get first ap-device
119 */
120 apdev = apbus_lookupdev(NULL);
121
122 /*
123 * trace device chain
124 */
125 while (apdev) {
126 apctl = apdev->apbd_ctl;
127
128 /*
129 * probe physical device only
130 * (no pseudo device)
131 */
132 if (apctl && apctl->apbc_hwbase) {
133 /*
134 * ... and, all units
135 */
136 while (apctl) {
137 /* make apbus_attach_args for devices */
138 child.apa_name = apdev->apbd_name;
139 child.apa_ctlnum = apctl->apbc_ctlno;
140 child.apa_slotno = apctl->apbc_sl;
141 child.apa_hwbase = apctl->apbc_hwbase;
142
143 config_found(self, &child, apbusprint);
144
145 apctl = apctl->apbc_link;
146 }
147 }
148
149 apdev = apdev->apbd_link;
150 }
151 }
152
153 int
154 apbusprint(aux, pnp)
155 void *aux;
156 const char *pnp;
157 {
158 struct apbus_attach_args *a = aux;
159
160 if (pnp)
161 aprint_normal("%s at %s slot%d addr 0x%lx",
162 a->apa_name, pnp, a->apa_slotno, a->apa_hwbase);
163
164 return UNCONF;
165 }
166
167 #if 0
168 void *
169 aptokseg0(va)
170 void *va;
171 {
172 vaddr_t addr = (vaddr_t)va;
173
174 if (addr >= 0xfff00000) {
175 addr -= 0xfff00000;
176 addr += physmem << PGSHIFT;
177 addr += 0x80000000;
178 va = (void *)addr;
179 }
180 return va;
181 }
182 #endif
183
184 void
185 apbus_wbflush()
186 {
187 volatile int *wbflush = (int *)NEWS5000_WBFLUSH;
188
189 (void)*wbflush;
190 }
191
192 /*
193 * called by hardware interrupt routine
194 */
195 int
196 apbus_intr_call(level, stat)
197 int level;
198 int stat;
199 {
200 struct newsmips_intr *ip;
201 struct newsmips_intrhand *ih;
202 int nintr;
203
204 ip = &apintr_tab[level];
205
206 nintr = 0;
207 LIST_FOREACH(ih, &ip->intr_q, ih_q) {
208 if (ih->ih_mask & stat)
209 nintr += (*ih->ih_func)(ih->ih_arg);
210 }
211 return nintr;
212 }
213
214 /*
215 * register device interrupt routine
216 */
217 void *
218 apbus_intr_establish(level, mask, priority, func, arg, name, ctlno)
219 int level;
220 int mask;
221 int priority;
222 int (*func) (void *);
223 void *arg;
224 char *name;
225 int ctlno;
226 {
227 struct newsmips_intr *ip;
228 struct newsmips_intrhand *ih, *curih;
229 volatile u_int32_t *inten0, *inten1;
230
231 ip = &apintr_tab[level];
232
233 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
234 if (ih == NULL)
235 panic("apbus_intr_establish: can't malloc handler info");
236 ih->ih_mask = mask;
237 ih->ih_priority = priority;
238 ih->ih_func = func;
239 ih->ih_arg = arg;
240
241 if (LIST_EMPTY(&ip->intr_q)) {
242 LIST_INSERT_HEAD(&ip->intr_q, ih, ih_q);
243 goto done;
244 }
245
246 for (curih = LIST_FIRST(&ip->intr_q);
247 LIST_NEXT(curih, ih_q) != NULL;
248 curih = LIST_NEXT(curih, ih_q)) {
249 if (ih->ih_priority > curih->ih_priority) {
250 LIST_INSERT_BEFORE(curih, ih, ih_q);
251 goto done;
252 }
253 }
254
255 LIST_INSERT_AFTER(curih, ih, ih_q);
256
257 done:
258 switch (level) {
259 case 0:
260 inten0 = (volatile u_int32_t *)NEWS5000_INTEN0;
261 *inten0 |= mask;
262 break;
263 case 1:
264 inten1 = (volatile u_int32_t *)NEWS5000_INTEN1;
265 *inten1 |= mask;
266 break;
267 }
268
269 return (void *)ih;
270 }
271
272 static void
273 apbus_dma_unmapped(t, map)
274 bus_dma_tag_t t;
275 bus_dmamap_t map;
276 {
277 int seg;
278
279 for (seg = 0; seg < map->dm_nsegs; seg++) {
280 /*
281 * set MSB to indicate unmapped DMA.
282 * also need bit 30 for memory over 256MB.
283 */
284 if ((map->dm_segs[seg].ds_addr & 0x30000000) == 0)
285 map->dm_segs[seg].ds_addr |= 0x80000000;
286 else
287 map->dm_segs[seg].ds_addr |= 0xc0000000;
288 }
289 }
290
291 #define APBUS_NDMAMAP (NEWS5000_APBUS_MAPSIZE / NEWS5000_APBUS_MAPENT)
292 #define APBUS_MAPTBL(n, v) (*(volatile u_int *)(NEWS5000_APBUS_DMAMAP + \
293 NEWS5000_APBUS_MAPENT * (n) + 1) = (v))
294 static u_char apbus_dma_maptbl[APBUS_NDMAMAP];
295
296 static int
297 apbus_dma_mapalloc(t, map, flags)
298 bus_dma_tag_t t;
299 bus_dmamap_t map;
300 int flags;
301 {
302 int i, j, cnt;
303
304 cnt = round_page(map->_dm_size) / PAGE_SIZE;
305
306 again:
307 for (i = 0; i < APBUS_NDMAMAP; i += j + 1) {
308 for (j = 0; j < cnt; j++) {
309 if (apbus_dma_maptbl[i + j])
310 break;
311 }
312 if (j == cnt) {
313 for (j = 0; j < cnt; j++)
314 apbus_dma_maptbl[i + j] = 1;
315 map->_dm_maptbl = i;
316 map->_dm_maptblcnt = cnt;
317 return 0;
318 }
319 }
320 if ((flags & BUS_DMA_NOWAIT) == 0) {
321 tsleep(&apbus_dma_maptbl, PRIBIO, "apdmat", 0);
322 goto again;
323 }
324 return ENOMEM;
325 }
326
327 static void
328 apbus_dma_mapfree(t, map)
329 bus_dma_tag_t t;
330 bus_dmamap_t map;
331 {
332 int i, n;
333
334 if (map->_dm_maptblcnt > 0) {
335 n = map->_dm_maptbl;
336 for (i = 0; i < map->_dm_maptblcnt; i++, n++) {
337 #ifdef DIAGNOSTIC
338 if (apbus_dma_maptbl[n] == 0)
339 panic("freeing free DMA map");
340 APBUS_MAPTBL(n, 0xffffffff); /* causes DMA error */
341 #endif
342 apbus_dma_maptbl[n] = 0;
343 }
344 wakeup(&apbus_dma_maptbl);
345 map->_dm_maptblcnt = 0;
346 }
347 }
348
349 static void
350 apbus_dma_mapset(t, map)
351 bus_dma_tag_t t;
352 bus_dmamap_t map;
353 {
354 int i;
355 bus_addr_t addr, eaddr;
356 int seg;
357 bus_dma_segment_t *segs;
358
359 i = 0;
360 for (seg = 0; seg < map->dm_nsegs; seg++) {
361 segs = &map->dm_segs[seg];
362 for (addr = segs->ds_addr, eaddr = addr + segs->ds_len;
363 addr < eaddr; addr += PAGE_SIZE, i++) {
364 #ifdef DIAGNOSTIC
365 if (i >= map->_dm_maptblcnt)
366 panic("DMA map table overflow");
367 #endif
368 APBUS_MAPTBL(map->_dm_maptbl + i,
369 NEWS5000_APBUS_MAP_VALID |
370 NEWS5000_APBUS_MAP_COHERENT |
371 (addr >> PGSHIFT));
372 }
373 }
374 map->dm_segs[0].ds_addr = map->_dm_maptbl << PGSHIFT;
375 map->dm_segs[0].ds_len = map->dm_mapsize;
376 map->dm_nsegs = 1;
377 }
378
379 static int
380 apbus_dmamap_create(t, size, nsegments, maxsegsz, boundary, flags, dmamp)
381 bus_dma_tag_t t;
382 bus_size_t size;
383 int nsegments;
384 bus_size_t maxsegsz;
385 bus_size_t boundary;
386 int flags;
387 bus_dmamap_t *dmamp;
388 {
389 int error;
390
391 if (flags & NEWSMIPS_DMAMAP_MAPTBL)
392 nsegments = round_page(size) / PAGE_SIZE;
393 error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary,
394 flags, dmamp);
395 if (error == 0 && (flags & NEWSMIPS_DMAMAP_MAPTBL)) {
396 error = apbus_dma_mapalloc(t, *dmamp, flags);
397 if (error) {
398 _bus_dmamap_destroy(t, *dmamp);
399 *dmamp = NULL;
400 }
401 }
402 return error;
403 }
404
405 static void
406 apbus_dmamap_destroy(t, map)
407 bus_dma_tag_t t;
408 bus_dmamap_t map;
409 {
410 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
411 apbus_dma_mapfree(t, map);
412 _bus_dmamap_destroy(t, map);
413 }
414
415 static int
416 apbus_dmamap_load(t, map, buf, buflen, p, flags)
417 bus_dma_tag_t t;
418 bus_dmamap_t map;
419 void *buf;
420 bus_size_t buflen;
421 struct proc *p;
422 int flags;
423 {
424 int error;
425
426 error = _bus_dmamap_load(t, map, buf, buflen, p, flags);
427 if (error == 0) {
428 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
429 apbus_dma_mapset(t, map);
430 else
431 apbus_dma_unmapped(t, map);
432 }
433 return error;
434 }
435
436 static int
437 apbus_dmamap_load_mbuf(t, map, m0, flags)
438 bus_dma_tag_t t;
439 bus_dmamap_t map;
440 struct mbuf *m0;
441 int flags;
442 {
443 int error;
444
445 error = _bus_dmamap_load_mbuf(t, map, m0, flags);
446 if (error == 0) {
447 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
448 apbus_dma_mapset(t, map);
449 else
450 apbus_dma_unmapped(t, map);
451 }
452 return error;
453 }
454
455 static int
456 apbus_dmamap_load_uio(t, map, uio, flags)
457 bus_dma_tag_t t;
458 bus_dmamap_t map;
459 struct uio *uio;
460 int flags;
461 {
462 int error;
463
464 error = _bus_dmamap_load_uio(t, map, uio, flags);
465 if (error == 0) {
466 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
467 apbus_dma_mapset(t, map);
468 else
469 apbus_dma_unmapped(t, map);
470 }
471 return error;
472 }
473
474 static int
475 apbus_dmamap_load_raw(t, map, segs, nsegs, size, flags)
476 bus_dma_tag_t t;
477 bus_dmamap_t map;
478 bus_dma_segment_t *segs;
479 int nsegs;
480 bus_size_t size;
481 int flags;
482 {
483 int error;
484
485 error = _bus_dmamap_load_raw(t, map, segs, nsegs, size, flags);
486 if (error == 0) {
487 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
488 apbus_dma_mapset(t, map);
489 else
490 apbus_dma_unmapped(t, map);
491 }
492 return error;
493 }
494
495 static void
496 apbus_dmamap_sync(t, map, offset, len, ops)
497 bus_dma_tag_t t;
498 bus_dmamap_t map;
499 bus_addr_t offset;
500 bus_size_t len;
501 int ops;
502 {
503
504 /*
505 * Flush DMA cache by issuing IO read for the AProm of specified slot.
506 */
507 bus_space_read_4(t->_slotbaset, t->_slotbaseh, 0);
508
509 bus_dmamap_sync(&newsmips_default_bus_dma_tag, map, offset, len, ops);
510 }
511
512 struct newsmips_bus_dma_tag apbus_dma_tag = {
513 apbus_dmamap_create,
514 apbus_dmamap_destroy,
515 apbus_dmamap_load,
516 apbus_dmamap_load_mbuf,
517 apbus_dmamap_load_uio,
518 apbus_dmamap_load_raw,
519 _bus_dmamap_unload,
520 apbus_dmamap_sync,
521 _bus_dmamem_alloc,
522 _bus_dmamem_free,
523 _bus_dmamem_map,
524 _bus_dmamem_unmap,
525 _bus_dmamem_mmap,
526 };
527
528 struct newsmips_bus_dma_tag *
529 apbus_dmatag_init(apa)
530 struct apbus_attach_args *apa;
531 {
532 struct newsmips_bus_dma_tag *dmat;
533
534 dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
535 if (dmat != NULL) {
536 memcpy(dmat, &apbus_dma_tag, sizeof(*dmat));
537 dmat->_slotno = apa->apa_slotno;
538 dmat->_slotbaset = 0;
539 dmat->_slotbaseh = apa->apa_hwbase;
540 }
541 return dmat;
542 }
543