apbus.c revision 1.4 1 /* $NetBSD: apbus.c,v 1.4 2000/10/18 12:47:37 onoe 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
34 #include <uvm/uvm_extern.h>
35
36 #include <machine/adrsmap.h>
37 #include <machine/autoconf.h>
38 #define _NEWSMIPS_BUS_DMA_PRIVATE
39 #include <machine/bus.h>
40 #include <newsmips/apbus/apbusvar.h>
41
42 static int apbusmatch __P((struct device *, struct cfdata *, void *));
43 static void apbusattach __P((struct device *, struct device *, void *));
44 static int apbusprint __P((void *, const char *));
45 /* static void *aptokseg0 __P((void *)); */
46
47 #define MAXAPDEVNUM 32
48
49 struct apbus_softc {
50 struct device apbs_dev;
51 };
52
53 struct cfattach ap_ca = {
54 sizeof(struct apbus_softc), apbusmatch, apbusattach
55 };
56
57 #define APBUS_DEVNAMELEN 16
58
59 struct ap_intrhand {
60 struct ap_intrhand *ai_next;
61 int ai_mask;
62 int ai_priority;
63 int (*ai_func) __P((void*)); /* function */
64 void *ai_aux; /* softc */
65 char ai_name[APBUS_DEVNAMELEN];
66 int ai_ctlno;
67 };
68
69 #define NLEVEL 2
70
71 static struct ap_intrhand *apintr[NLEVEL];
72
73 static int
74 apbusmatch(parent, cfdata, aux)
75 struct device *parent;
76 struct cfdata *cfdata;
77 void *aux;
78 {
79 struct confargs *ca = aux;
80
81 if (strcmp(ca->ca_name, "ap") != 0)
82 return 0;
83
84 return 1;
85 }
86
87
88 static void
89 apbusattach(parent, self, aux)
90 struct device *parent;
91 struct device *self;
92 void *aux;
93 {
94 struct apbus_attach_args child;
95 struct apbus_dev *apdev;
96 struct apbus_ctl *apctl;
97
98 *(volatile u_int *)(NEWS5000_APBUS_INTST) = 0xffffffff;
99 *(volatile u_int *)(NEWS5000_APBUS_INTMSK) = 0xffffffff;
100 *(volatile u_int *)(NEWS5000_APBUS_CTRL) = 0x00000004;
101 *(volatile u_int *)(NEWS5000_APBUS_DMA) = 0xffffffff;
102
103 printf("\n");
104
105 /*
106 * get first ap-device
107 */
108 apdev = apbus_lookupdev(NULL);
109
110 /*
111 * trace device chain
112 */
113 while (apdev) {
114 apctl = apdev->apbd_ctl;
115
116 /*
117 * probe physical device only
118 * (no pseudo device)
119 */
120 if (apctl && apctl->apbc_hwbase) {
121 /*
122 * ... and, all units
123 */
124 while (apctl) {
125 /* make apbus_attach_args for devices */
126 child.apa_name = apdev->apbd_name;
127 child.apa_ctlnum = apctl->apbc_ctlno;
128 child.apa_slotno = apctl->apbc_sl;
129 child.apa_hwbase = apctl->apbc_hwbase;
130
131 config_found(self, &child, apbusprint);
132
133 apctl = apctl->apbc_link;
134 }
135 }
136
137 apdev = apdev->apbd_link;
138 }
139 }
140
141 int
142 apbusprint(aux, pnp)
143 void *aux;
144 const char *pnp;
145 {
146 struct apbus_attach_args *a = aux;
147
148 if (pnp)
149 printf("%s at %s slot%d addr 0x%lx",
150 a->apa_name, pnp, a->apa_slotno, a->apa_hwbase);
151
152 return UNCONF;
153 }
154
155 #if 0
156 void *
157 aptokseg0(va)
158 void *va;
159 {
160 vaddr_t addr = (vaddr_t)va;
161
162 if (addr >= 0xfff00000) {
163 addr -= 0xfff00000;
164 addr += physmem << PGSHIFT;
165 addr += 0x80000000;
166 va = (void *)addr;
167 }
168 return va;
169 }
170 #endif
171
172 void
173 apbus_wbflush()
174 {
175 volatile int *wbflush = (int *)NEWS5000_WBFLUSH;
176
177 (void)*wbflush;
178 }
179
180 /*
181 * called by hardware interrupt routine
182 */
183 int
184 apbus_intr_call(level, stat)
185 int level;
186 int stat;
187 {
188 int nintr = 0;
189 struct ap_intrhand *ai;
190
191 for (ai = apintr[level]; ai != NULL; ai = ai->ai_next) {
192 if (ai->ai_mask & stat) {
193 nintr += (*ai->ai_func)(ai->ai_aux);
194 }
195 }
196 return nintr;
197 }
198
199 /*
200 * register device interrupt routine
201 */
202 void *
203 apbus_intr_establish(level, mask, priority, func, aux, name, ctlno)
204 int level;
205 int mask;
206 int priority;
207 int (*func) __P((void *));
208 void *aux;
209 char *name;
210 int ctlno;
211 {
212 struct ap_intrhand *ai, **aip;
213 volatile unsigned int *inten0 = (volatile unsigned int *)NEWS5000_INTEN0;
214 volatile unsigned int *inten1 = (volatile unsigned int *)NEWS5000_INTEN1;
215
216 ai = malloc(sizeof(*ai), M_DEVBUF, M_NOWAIT);
217 if (ai == NULL)
218 panic("apbus_intr_establish: can't malloc handler info");
219 ai->ai_mask = mask;
220 ai->ai_priority = priority;
221 ai->ai_func = func;
222 ai->ai_aux = aux;
223 strncpy(ai->ai_name, name, APBUS_DEVNAMELEN-1);
224 ai->ai_ctlno = ctlno;
225
226 for (aip = &apintr[level]; *aip != NULL; aip = &(*aip)->ai_next) {
227 if ((*aip)->ai_priority < priority)
228 break;
229 }
230 ai->ai_next = *aip;
231 *aip = ai;
232 switch (level) {
233 case 0:
234 *inten0 |= mask;
235 break;
236 case 1:
237 *inten1 |= mask;
238 break;
239 }
240
241 return (void *)ai;
242 }
243
244 static void
245 apbus_dma_unmapped(t, map)
246 bus_dma_tag_t t;
247 bus_dmamap_t map;
248 {
249 int seg;
250
251 for (seg = 0; seg < map->dm_nsegs; seg++) {
252 /*
253 * set MSB to indicate unmapped DMA.
254 * also need bit 30 for memory over 256MB.
255 */
256 if ((map->dm_segs[seg].ds_addr & 0x30000000) == 0)
257 map->dm_segs[seg].ds_addr |= 0x80000000;
258 else
259 map->dm_segs[seg].ds_addr |= 0xc0000000;
260 }
261 }
262
263 #define APBUS_NDMAMAP (NEWS5000_APBUS_MAPSIZE / NEWS5000_APBUS_MAPENT)
264 #define APBUS_MAPTBL(n, v) (*(volatile u_int *)(NEWS5000_APBUS_DMAMAP + \
265 NEWS5000_APBUS_MAPENT * (n) + 1) = (v))
266 static u_char apbus_dma_maptbl[APBUS_NDMAMAP];
267
268 static int
269 apbus_dma_mapalloc(t, map, flags)
270 bus_dma_tag_t t;
271 bus_dmamap_t map;
272 int flags;
273 {
274 int i, j, cnt;
275
276 cnt = round_page(map->_dm_size) / NBPG;
277
278 again:
279 for (i = 0; i < APBUS_NDMAMAP; i += j + 1) {
280 for (j = 0; j < cnt; j++) {
281 if (apbus_dma_maptbl[i + j])
282 break;
283 }
284 if (j == cnt) {
285 for (j = 0; j < cnt; j++)
286 apbus_dma_maptbl[i + j] = 1;
287 map->_dm_maptbl = i;
288 map->_dm_maptblcnt = cnt;
289 return 0;
290 }
291 }
292 if ((flags & BUS_DMA_NOWAIT) == 0) {
293 tsleep(&apbus_dma_maptbl, PRIBIO, "apdmat", 0);
294 goto again;
295 }
296 return ENOMEM;
297 }
298
299 static void
300 apbus_dma_mapfree(t, map)
301 bus_dma_tag_t t;
302 bus_dmamap_t map;
303 {
304 int i, n;
305
306 if (map->_dm_maptblcnt > 0) {
307 n = map->_dm_maptbl;
308 for (i = 0; i < map->_dm_maptblcnt; i++, n++) {
309 #ifdef DIAGNOSTIC
310 if (apbus_dma_maptbl[n] == 0)
311 panic("freeing free dma map");
312 APBUS_MAPTBL(n, 0xffffffff); /* causes DMA error */
313 #endif
314 apbus_dma_maptbl[n] = 0;
315 }
316 wakeup(&apbus_dma_maptbl);
317 map->_dm_maptblcnt = 0;
318 }
319 }
320
321 static void
322 apbus_dma_mapset(t, map)
323 bus_dma_tag_t t;
324 bus_dmamap_t map;
325 {
326 int i;
327 bus_addr_t addr, eaddr;
328 int seg;
329 bus_dma_segment_t *segs;
330
331 i = 0;
332 for (seg = 0; seg < map->dm_nsegs; seg++) {
333 segs = &map->dm_segs[seg];
334 for (addr = segs->ds_addr, eaddr = addr + segs->ds_len;
335 addr < eaddr; addr += NBPG, i++) {
336 #ifdef DIAGNOSTIC
337 if (i >= map->_dm_maptblcnt)
338 panic("dma map table overflow");
339 #endif
340 APBUS_MAPTBL(map->_dm_maptbl + i,
341 NEWS5000_APBUS_MAP_VALID |
342 NEWS5000_APBUS_MAP_COHERENT |
343 (addr >> PGSHIFT));
344 }
345 }
346 map->dm_segs[0].ds_addr = map->_dm_maptbl << PGSHIFT;
347 map->dm_segs[0].ds_len = map->dm_mapsize;
348 map->dm_nsegs = 1;
349 }
350
351 int
352 apbus_dmamap_create(t, size, nsegments, maxsegsz, boundary, flags, dmamp)
353 bus_dma_tag_t t;
354 bus_size_t size;
355 int nsegments;
356 bus_size_t maxsegsz;
357 bus_size_t boundary;
358 int flags;
359 bus_dmamap_t *dmamp;
360 {
361 int error;
362
363 if (flags & NEWSMIPS_DMAMAP_MAPTBL)
364 nsegments = round_page(size) / NBPG;
365 error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary,
366 flags, dmamp);
367 if (error == 0 && (flags & NEWSMIPS_DMAMAP_MAPTBL)) {
368 error = apbus_dma_mapalloc(t, *dmamp, flags);
369 if (error) {
370 _bus_dmamap_destroy(t, *dmamp);
371 *dmamp = NULL;
372 }
373 }
374 return error;
375 }
376
377 void
378 apbus_dmamap_destroy(t, map)
379 bus_dma_tag_t t;
380 bus_dmamap_t map;
381 {
382 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
383 apbus_dma_mapfree(t, map);
384 _bus_dmamap_destroy(t, map);
385 }
386
387 int
388 apbus_dmamap_load(t, map, buf, buflen, p, flags)
389 bus_dma_tag_t t;
390 bus_dmamap_t map;
391 void *buf;
392 bus_size_t buflen;
393 struct proc *p;
394 int flags;
395 {
396 int error;
397
398 error = _bus_dmamap_load(t, map, buf, buflen, p, flags);
399 if (error == 0) {
400 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
401 apbus_dma_mapset(t, map);
402 else
403 apbus_dma_unmapped(t, map);
404 }
405 return error;
406 }
407
408 int
409 apbus_dmamap_load_mbuf(t, map, m0, flags)
410 bus_dma_tag_t t;
411 bus_dmamap_t map;
412 struct mbuf *m0;
413 int flags;
414 {
415 int error;
416
417 error = _bus_dmamap_load_mbuf(t, map, m0, flags);
418 if (error == 0) {
419 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
420 apbus_dma_mapset(t, map);
421 else
422 apbus_dma_unmapped(t, map);
423 }
424 return error;
425 }
426
427 int
428 apbus_dmamap_load_uio(t, map, uio, flags)
429 bus_dma_tag_t t;
430 bus_dmamap_t map;
431 struct uio *uio;
432 int flags;
433 {
434 int error;
435
436 error = _bus_dmamap_load_uio(t, map, uio, flags);
437 if (error == 0) {
438 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
439 apbus_dma_mapset(t, map);
440 else
441 apbus_dma_unmapped(t, map);
442 }
443 return error;
444 }
445
446 int
447 apbus_dmamap_load_raw(t, map, segs, nsegs, size, flags)
448 bus_dma_tag_t t;
449 bus_dmamap_t map;
450 bus_dma_segment_t *segs;
451 int nsegs;
452 bus_size_t size;
453 int flags;
454 {
455 int error;
456
457 error = _bus_dmamap_load_raw(t, map, segs, nsegs, size, flags);
458 if (error == 0) {
459 if (map->_dm_flags & NEWSMIPS_DMAMAP_MAPTBL)
460 apbus_dma_mapset(t, map);
461 else
462 apbus_dma_unmapped(t, map);
463 }
464 return error;
465 }
466
467 void
468 apbus_dmamap_sync(t, map, offset, len, ops)
469 bus_dma_tag_t t;
470 bus_dmamap_t map;
471 bus_addr_t offset;
472 bus_size_t len;
473 int ops;
474 {
475
476 /*
477 * Flush DMA cache by issueing IO read for the AProm of specified slot.
478 */
479 bus_space_read_4(t->_slotbaset, t->_slotbaseh, 0);
480
481 _bus_dmamap_sync(t, map, offset, len, ops);
482 }
483
484 struct newsmips_bus_dma_tag apbus_dma_tag = {
485 apbus_dmamap_create,
486 apbus_dmamap_destroy,
487 apbus_dmamap_load,
488 apbus_dmamap_load_mbuf,
489 apbus_dmamap_load_uio,
490 apbus_dmamap_load_raw,
491 _bus_dmamap_unload,
492 apbus_dmamap_sync,
493 _bus_dmamem_alloc,
494 _bus_dmamem_free,
495 _bus_dmamem_map,
496 _bus_dmamem_unmap,
497 _bus_dmamem_mmap,
498 };
499
500 struct newsmips_bus_dma_tag *
501 apbus_dmatag_init(apa)
502 struct apbus_attach_args *apa;
503 {
504 struct newsmips_bus_dma_tag *dmat;
505
506 dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
507 if (dmat != NULL) {
508 memcpy(dmat, &apbus_dma_tag, sizeof(*dmat));
509 dmat->_slotno = apa->apa_slotno;
510 dmat->_slotbaset = 0;
511 dmat->_slotbaseh = apa->apa_hwbase;
512 }
513 return dmat;
514 }
515