Home | History | Annotate | Line # | Download | only in apbus
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