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