1 /* $NetBSD: dio.c,v 1.26 2003/11/17 14:37:59 tsutsui Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Autoconfiguration and mapping support for the DIO bus. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: dio.c,v 1.26 2003/11/17 14:37:59 tsutsui Exp $"); 45 46 #define _HP300_INTR_H_PRIVATE 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/kernel.h> 51 #include <sys/device.h> 52 53 #include <machine/bus.h> 54 55 #include <uvm/uvm_extern.h> 56 57 #include <machine/autoconf.h> 58 #include <machine/cpu.h> 59 #include <machine/hp300spu.h> 60 61 #include <hp300/dev/dmavar.h> 62 63 #include <hp300/dev/dioreg.h> 64 #include <hp300/dev/diovar.h> 65 66 #include <hp300/dev/diodevs.h> 67 #include <hp300/dev/diodevs_data.h> 68 69 #include "locators.h" 70 #define diocf_scode cf_loc[DIOCF_SCODE] 71 72 struct dio_softc { 73 struct device sc_dev; 74 struct bus_space_tag sc_tag; 75 }; 76 77 static int dio_scodesize __P((struct dio_attach_args *)); 78 char *dio_devinfo __P((struct dio_attach_args *, char *, size_t)); 79 80 int diomatch __P((struct device *, struct cfdata *, void *)); 81 void dioattach __P((struct device *, struct device *, void *)); 82 int dioprint __P((void *, const char *)); 83 int diosubmatch __P((struct device *, struct cfdata *, void *)); 84 85 CFATTACH_DECL(dio, sizeof(struct dio_softc), 86 diomatch, dioattach, NULL, NULL); 87 88 int 89 diomatch(parent, match, aux) 90 struct device *parent; 91 struct cfdata *match; 92 void *aux; 93 { 94 static int dio_matched = 0; 95 96 /* Allow only one instance. */ 97 if (dio_matched) 98 return (0); 99 100 dio_matched = 1; 101 return (1); 102 } 103 104 void 105 dioattach(parent, self, aux) 106 struct device *parent, *self; 107 void *aux; 108 { 109 struct dio_softc *sc = (struct dio_softc *)self; 110 struct dio_attach_args da; 111 caddr_t pa, va; 112 bus_space_tag_t bst = &sc->sc_tag; 113 int scode, scmax, scodesize; 114 115 printf("\n"); 116 117 memset(bst, 0, sizeof(struct bus_space_tag)); 118 bst->bustype = HP300_BUS_SPACE_DIO; 119 120 scmax = DIO_SCMAX(machineid); 121 122 for (scode = 0; scode < scmax; ) { 123 if (DIO_INHOLE(scode)) { 124 scode++; 125 continue; 126 } 127 128 /* 129 * Temporarily map the space corresponding to 130 * the current select code unless: 131 */ 132 pa = dio_scodetopa(scode); 133 va = iomap(pa, PAGE_SIZE); 134 if (va == NULL) { 135 printf("%s: can't map scode %d\n", 136 self->dv_xname, scode); 137 scode++; 138 continue; 139 } 140 141 /* Check for hardware. */ 142 if (badaddr(va)) { 143 iounmap(va, PAGE_SIZE); 144 scode++; 145 continue; 146 } 147 148 /* Fill out attach args. */ 149 memset(&da, 0, sizeof(da)); 150 da.da_bst = bst; 151 da.da_scode = scode; 152 153 da.da_id = DIO_ID(va); 154 if (DIO_ISFRAMEBUFFER(da.da_id)) 155 da.da_secid = DIO_SECID(va); 156 da.da_addr = (bus_addr_t)dio_scodetopa(scode); 157 da.da_size = DIO_SIZE(scode, va); 158 scodesize = dio_scodesize(&da); 159 if (DIO_ISDIO(scode)) 160 da.da_size *= scodesize; 161 da.da_ipl = DIO_IPL(va); 162 163 /* No longer need the device to be mapped. */ 164 iounmap(va, PAGE_SIZE); 165 166 /* Attach matching device. */ 167 config_found_sm(self, &da, dioprint, diosubmatch); 168 scode += scodesize; 169 } 170 } 171 172 int 173 diosubmatch(parent, cf, aux) 174 struct device *parent; 175 struct cfdata *cf; 176 void *aux; 177 { 178 struct dio_attach_args *da = aux; 179 180 if (cf->diocf_scode != DIOCF_SCODE_DEFAULT && 181 cf->diocf_scode != da->da_scode) 182 return (0); 183 184 return (config_match(parent, cf, aux)); 185 } 186 187 int 188 dioprint(aux, pnp) 189 void *aux; 190 const char *pnp; 191 { 192 struct dio_attach_args *da = aux; 193 char buf[128]; 194 195 if (pnp) 196 aprint_normal("%s at %s", 197 dio_devinfo(da, buf, sizeof(buf)), pnp); 198 aprint_normal(" scode %d ipl %d", da->da_scode, da->da_ipl); 199 return (UNCONF); 200 } 201 202 /* 203 * Convert a select code to a system physical address. 204 */ 205 void * 206 dio_scodetopa(scode) 207 int scode; 208 { 209 u_long rval; 210 211 if (DIO_ISDIO(scode)) 212 rval = DIO_BASE + (scode * DIO_DEVSIZE); 213 else if (DIO_ISDIOII(scode)) 214 rval = DIOII_BASE + ((scode - DIOII_SCBASE) * DIOII_DEVSIZE); 215 else 216 rval = 0; 217 218 return ((void *)rval); 219 } 220 221 /* 222 * Return the select code size for this device, defaulting to 1 223 * if we don't know what kind of device we have. 224 */ 225 static int 226 dio_scodesize(da) 227 struct dio_attach_args *da; 228 { 229 int i; 230 231 /* 232 * Find the dio_devdata matchind the primary id. 233 * If we're a framebuffer, we also check the secondary id. 234 */ 235 for (i = 0; i < DIO_NDEVICES; i++) { 236 if (da->da_id == dio_devdatas[i].dd_id) { 237 if (DIO_ISFRAMEBUFFER(da->da_id)) { 238 if (da->da_secid == dio_devdatas[i].dd_secid) { 239 goto foundit; 240 } 241 } else { 242 foundit: 243 return (dio_devdatas[i].dd_nscode); 244 } 245 } 246 } 247 248 /* 249 * Device is unknown. Print a warning and assume a default. 250 */ 251 printf("WARNING: select code size unknown for id = 0x%x secid = 0x%x\n", 252 da->da_id, da->da_secid); 253 return (1); 254 } 255 256 /* 257 * Return a reasonable description of a DIO device. 258 */ 259 char * 260 dio_devinfo(da, buf, buflen) 261 struct dio_attach_args *da; 262 char *buf; 263 size_t buflen; 264 { 265 #ifdef DIOVERBOSE 266 int i; 267 #endif 268 269 memset(buf, 0, buflen); 270 271 #ifdef DIOVERBOSE 272 /* 273 * Find the description matching our primary id. 274 * If we're a framebuffer, we also check the secondary id. 275 */ 276 for (i = 0; i < DIO_NDEVICES; i++) { 277 if (da->da_id == dio_devdescs[i].dd_id) { 278 if (DIO_ISFRAMEBUFFER(da->da_id)) { 279 if (da->da_secid == dio_devdescs[i].dd_secid) { 280 goto foundit; 281 } 282 } else { 283 foundit: 284 sprintf(buf, "%s", dio_devdescs[i].dd_desc); 285 return (buf); 286 } 287 } 288 } 289 #endif /* DIOVERBOSE */ 290 291 /* 292 * Device is unknown. Construct something reasonable. 293 */ 294 sprintf(buf, "device id = 0x%x secid = 0x%x", 295 da->da_id, da->da_secid); 296 return (buf); 297 } 298 299 /* 300 * Establish an interrupt handler for a DIO device. 301 */ 302 void * 303 dio_intr_establish(func, arg, ipl, priority) 304 int (*func) __P((void *)); 305 void *arg; 306 int ipl; 307 int priority; 308 { 309 void *ih; 310 311 ih = intr_establish(func, arg, ipl, priority); 312 313 if (priority == IPL_BIO) 314 dmacomputeipl(); 315 316 return (ih); 317 } 318 319 /* 320 * Remove an interrupt handler for a DIO device. 321 */ 322 void 323 dio_intr_disestablish(arg) 324 void *arg; 325 { 326 struct hp300_intrhand *ih = arg; 327 int priority = ih->ih_priority; 328 329 intr_disestablish(arg); 330 331 if (priority == IPL_BIO) 332 dmacomputeipl(); 333 } 334 335 /* 336 * DIO specific bus_space(9) support functions. 337 */ 338 static u_int8_t dio_bus_space_read_oddbyte_1 __P((bus_space_tag_t, 339 bus_space_handle_t, bus_size_t)); 340 static void dio_bus_space_write_oddbyte_1 __P((bus_space_tag_t, 341 bus_space_handle_t, bus_size_t, u_int8_t)); 342 343 static void dio_bus_space_read_multi_oddbyte_1 __P((bus_space_tag_t, 344 bus_space_handle_t, bus_size_t, u_int8_t *, bus_size_t)); 345 static void dio_bus_space_write_multi_oddbyte_1 __P((bus_space_tag_t, 346 bus_space_handle_t, bus_size_t, const u_int8_t *, bus_size_t)); 347 348 static void dio_bus_space_read_region_oddbyte_1 __P((bus_space_tag_t, 349 bus_space_handle_t, bus_size_t, u_int8_t *, bus_size_t)); 350 static void dio_bus_space_write_region_oddbyte_1 __P((bus_space_tag_t, 351 bus_space_handle_t, bus_size_t, const u_int8_t *, bus_size_t)); 352 353 static void dio_bus_space_set_multi_oddbyte_1 __P((bus_space_tag_t, 354 bus_space_handle_t, bus_size_t, u_int8_t, bus_size_t)); 355 356 static void dio_bus_space_set_region_oddbyte_1 __P((bus_space_tag_t, 357 bus_space_handle_t, bus_size_t, u_int8_t, bus_size_t)); 358 359 /* 360 * dio_set_bus_space_oddbyte(): 361 * Override bus_space functions in bus_space_tag_t 362 * for devices which have odd byte address space. 363 */ 364 void 365 dio_set_bus_space_oddbyte(bst) 366 bus_space_tag_t bst; 367 { 368 369 /* XXX only 1-byte functions for now */ 370 bst->bsr1 = dio_bus_space_read_oddbyte_1; 371 bst->bsw1 = dio_bus_space_write_oddbyte_1; 372 373 bst->bsrm1 = dio_bus_space_read_multi_oddbyte_1; 374 bst->bswm1 = dio_bus_space_write_multi_oddbyte_1; 375 376 bst->bsrr1 = dio_bus_space_read_region_oddbyte_1; 377 bst->bswr1 = dio_bus_space_write_region_oddbyte_1; 378 379 bst->bssm1 = dio_bus_space_set_multi_oddbyte_1; 380 381 bst->bssr1 = dio_bus_space_set_region_oddbyte_1; 382 } 383 384 static u_int8_t 385 dio_bus_space_read_oddbyte_1(bst, bsh, offset) 386 bus_space_tag_t bst; 387 bus_space_handle_t bsh; 388 bus_size_t offset; 389 { 390 391 return *(volatile u_int8_t *)(bsh + (offset << 1) + 1); 392 } 393 394 static void dio_bus_space_write_oddbyte_1(bst, bsh, offset, val) 395 bus_space_tag_t bst; 396 bus_space_handle_t bsh; 397 bus_size_t offset; 398 u_int8_t val; 399 { 400 401 *(volatile u_int8_t *)(bsh + (offset << 1) + 1) = val; 402 } 403 404 static void 405 dio_bus_space_read_multi_oddbyte_1(bst, bsh, offset, addr, len) 406 bus_space_tag_t bst; 407 bus_space_handle_t bsh; 408 bus_size_t offset; 409 u_int8_t *addr; 410 bus_size_t len; 411 { 412 413 __asm __volatile ( 414 " movl %0,%%a0 ;\n" 415 " movl %1,%%a1 ;\n" 416 " movl %2,%%d0 ;\n" 417 "1: movb %%a0@,%%a1@+ ;\n" 418 " subql #1,%%d0 ;\n" 419 " jne 1b" 420 : 421 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 422 : "%a0","%a1","%d0"); 423 } 424 425 static void 426 dio_bus_space_write_multi_oddbyte_1(bst, bsh, offset, addr, len) 427 bus_space_tag_t bst; 428 bus_space_handle_t bsh; 429 bus_size_t offset; 430 const u_int8_t *addr; 431 bus_size_t len; 432 { 433 434 __asm __volatile ( 435 " movl %0,%%a0 ;\n" 436 " movl %1,%%a1 ;\n" 437 " movl %2,%%d0 ;\n" 438 "1: movb %%a1@+,%%a0@ ;\n" 439 " subql #1,%%d0 ;\n" 440 " jne 1b" 441 : 442 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 443 : "%a0","%a1","%d0"); 444 } 445 446 static void 447 dio_bus_space_read_region_oddbyte_1(bst, bsh, offset, addr, len) 448 bus_space_tag_t bst; 449 bus_space_handle_t bsh; 450 bus_size_t offset; 451 u_int8_t *addr; 452 bus_size_t len; 453 { 454 __asm __volatile ( 455 " movl %0,%%a0 ;\n" 456 " movl %1,%%a1 ;\n" 457 " movl %2,%%d0 ;\n" 458 "1: movb %%a0@,%%a1@+ ;\n" 459 " addql #2,%%a0 ;\n" 460 " subql #1,%%d0 ;\n" 461 " jne 1b" 462 : 463 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 464 : "%a0","%a1","%d0"); 465 } 466 467 static void 468 dio_bus_space_write_region_oddbyte_1(bst, bsh, offset, addr, len) 469 bus_space_tag_t bst; 470 bus_space_handle_t bsh; 471 bus_size_t offset; 472 const u_int8_t *addr; 473 bus_size_t len; 474 { 475 476 __asm __volatile ( 477 " movl %0,%%a0 ;\n" 478 " movl %1,%%a1 ;\n" 479 " movl %2,%%d0 ;\n" 480 "1: movb %%a1@+,%%a0@ ;\n" 481 " addql #2,%%a0 ;\n" 482 " subql #1,%%d0 ;\n" 483 " jne 1b" 484 : 485 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 486 : "%a0","%a1","%d0"); 487 } 488 489 static void 490 dio_bus_space_set_multi_oddbyte_1(bst, bsh, offset, val, count) 491 bus_space_tag_t bst; 492 bus_space_handle_t bsh; 493 bus_size_t offset; 494 u_int8_t val; 495 bus_size_t count; 496 { 497 __asm __volatile ( 498 " movl %0,%%a0 ;\n" 499 " movl %1,%%d1 ;\n" 500 " movl %2,%%d0 ;\n" 501 "1: movb %%d1,%%a0@ ;\n" 502 " subql #1,%%d0 ;\n" 503 " jne 1b" 504 : 505 : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count) 506 : "%a0","%d0","%d1"); 507 } 508 509 static void 510 dio_bus_space_set_region_oddbyte_1(bst, bsh, offset, val, count) 511 bus_space_tag_t bst; 512 bus_space_handle_t bsh; 513 bus_size_t offset; 514 u_int8_t val; 515 bus_size_t count; 516 { 517 518 __asm __volatile ( 519 " movl %0,%%a0 ;\n" 520 " movl %1,%%d1 ;\n" 521 " movl %2,%%d0 ;\n" 522 "1: movb %%d1,%%a0@ ;\n" 523 " addql #2,%%a0 ;\n" 524 " subql #1,%%d0 ;\n" 525 " jne 1b" 526 : 527 : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count) 528 : "%a0","%d0","%d1"); 529 } 530