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