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