1 /* $NetBSD: dio.c,v 1.35 2007/03/04 05:59:47 christos 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.35 2007/03/04 05:59:47 christos 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(struct dio_attach_args *); 78 static const char *dio_devinfo(struct dio_attach_args *, char *, size_t); 79 80 static int diomatch(struct device *, struct cfdata *, void *); 81 static void dioattach(struct device *, struct device *, void *); 82 static int dioprint(void *, const char *); 83 static int diosubmatch(struct device *, struct cfdata *, const int *, void *); 84 85 CFATTACH_DECL(dio, sizeof(struct dio_softc), 86 diomatch, dioattach, NULL, NULL); 87 88 static int 89 diomatch(struct device *parent, struct cfdata *match, void *aux) 90 { 91 static int dio_matched = 0; 92 93 /* Allow only one instance. */ 94 if (dio_matched) 95 return 0; 96 97 dio_matched = 1; 98 return 1; 99 } 100 101 static void 102 dioattach(struct device *parent, struct device *self, void *aux) 103 { 104 struct dio_softc *sc = (struct dio_softc *)self; 105 struct dio_attach_args da; 106 bus_addr_t pa; 107 void *va; 108 bus_space_tag_t bst = &sc->sc_tag; 109 bus_space_handle_t bsh; 110 int scode, scmax, scodesize; 111 112 printf("\n"); 113 114 memset(bst, 0, sizeof(struct bus_space_tag)); 115 bst->bustype = HP300_BUS_SPACE_DIO; 116 117 scmax = DIO_SCMAX(machineid); 118 119 for (scode = 0; scode < scmax; ) { 120 if (DIO_INHOLE(scode)) { 121 scode++; 122 continue; 123 } 124 125 /* 126 * Temporarily map the space corresponding to 127 * the current select code unless: 128 */ 129 pa = (bus_addr_t)dio_scodetopa(scode); 130 if (bus_space_map(bst, pa, PAGE_SIZE, 0, &bsh)) { 131 printf("%s: can't map scode %d\n", 132 self->dv_xname, scode); 133 scode++; 134 continue; 135 } 136 va = bus_space_vaddr(bst, bsh); 137 138 /* Check for hardware. */ 139 if (badaddr(va)) { 140 bus_space_unmap(bst, bsh, PAGE_SIZE); 141 scode++; 142 continue; 143 } 144 145 /* Fill out attach args. */ 146 memset(&da, 0, sizeof(da)); 147 da.da_bst = bst; 148 da.da_scode = scode; 149 150 da.da_id = DIO_ID(va); 151 if (DIO_ISFRAMEBUFFER(da.da_id)) 152 da.da_secid = DIO_SECID(va); 153 da.da_addr = pa; 154 da.da_size = DIO_SIZE(scode, va); 155 scodesize = dio_scodesize(&da); 156 if (DIO_ISDIO(scode)) 157 da.da_size *= scodesize; 158 da.da_ipl = DIO_IPL(va); 159 160 /* No longer need the device to be mapped. */ 161 bus_space_unmap(bst, bsh, PAGE_SIZE); 162 163 /* Attach matching device. */ 164 config_found_sm_loc(self, "dio", NULL, &da, dioprint, 165 diosubmatch); 166 scode += scodesize; 167 } 168 } 169 170 static int 171 diosubmatch(struct device *parent, struct cfdata *cf, 172 const int *ldesc, void *aux) 173 { 174 struct dio_attach_args *da = aux; 175 176 if (cf->diocf_scode != DIOCF_SCODE_DEFAULT && 177 cf->diocf_scode != da->da_scode) 178 return 0; 179 180 return config_match(parent, cf, aux); 181 } 182 183 static int 184 dioprint(void *aux, const char *pnp) 185 { 186 struct dio_attach_args *da = aux; 187 char buf[128]; 188 189 if (pnp) 190 aprint_normal("%s at %s", 191 dio_devinfo(da, buf, sizeof(buf)), pnp); 192 aprint_normal(" scode %d ipl %d", da->da_scode, da->da_ipl); 193 return UNCONF; 194 } 195 196 /* 197 * Convert a select code to a system physical address. 198 */ 199 void * 200 dio_scodetopa(int scode) 201 { 202 u_long rval; 203 204 if (DIO_ISDIO(scode)) 205 rval = DIO_BASE + (scode * DIO_DEVSIZE); 206 else if (DIO_ISDIOII(scode)) 207 rval = DIOII_BASE + ((scode - DIOII_SCBASE) * DIOII_DEVSIZE); 208 else 209 rval = 0; 210 211 return (void *)rval; 212 } 213 214 /* 215 * Return the select code size for this device, defaulting to 1 216 * if we don't know what kind of device we have. 217 */ 218 static int 219 dio_scodesize(struct dio_attach_args *da) 220 { 221 int i; 222 223 /* 224 * Find the dio_devdata matchind the primary id. 225 * If we're a framebuffer, we also check the secondary id. 226 */ 227 for (i = 0; i < DIO_NDEVICES; i++) { 228 if (da->da_id == dio_devdatas[i].dd_id) { 229 if (DIO_ISFRAMEBUFFER(da->da_id)) { 230 if (da->da_secid == dio_devdatas[i].dd_secid) { 231 goto foundit; 232 } 233 } else { 234 foundit: 235 return dio_devdatas[i].dd_nscode; 236 } 237 } 238 } 239 240 /* 241 * Device is unknown. Print a warning and assume a default. 242 */ 243 printf("WARNING: select code size unknown for id = 0x%x secid = 0x%x\n", 244 da->da_id, da->da_secid); 245 return 1; 246 } 247 248 /* 249 * Return a reasonable description of a DIO device. 250 */ 251 static const char * 252 dio_devinfo(struct dio_attach_args *da, char *buf, size_t buflen) 253 { 254 #ifdef DIOVERBOSE 255 int i; 256 #endif 257 258 memset(buf, 0, buflen); 259 260 #ifdef DIOVERBOSE 261 /* 262 * Find the description matching our primary id. 263 * If we're a framebuffer, we also check the secondary id. 264 */ 265 for (i = 0; i < DIO_NDEVICES; i++) { 266 if (da->da_id == dio_devdescs[i].dd_id) { 267 if (DIO_ISFRAMEBUFFER(da->da_id)) { 268 if (da->da_secid == dio_devdescs[i].dd_secid) { 269 goto foundit; 270 } 271 } else { 272 foundit: 273 sprintf(buf, "%s", dio_devdescs[i].dd_desc); 274 return buf; 275 } 276 } 277 } 278 #endif /* DIOVERBOSE */ 279 280 /* 281 * Device is unknown. Construct something reasonable. 282 */ 283 sprintf(buf, "device id = 0x%x secid = 0x%x", 284 da->da_id, da->da_secid); 285 return buf; 286 } 287 288 /* 289 * Establish an interrupt handler for a DIO device. 290 */ 291 void * 292 dio_intr_establish(int (*func)(void *), void *arg, int ipl, int priority) 293 { 294 void *ih; 295 296 ih = intr_establish(func, arg, ipl, priority); 297 298 if (priority == IPL_BIO) 299 dmacomputeipl(); 300 301 return ih; 302 } 303 304 /* 305 * Remove an interrupt handler for a DIO device. 306 */ 307 void 308 dio_intr_disestablish(void *arg) 309 { 310 struct hp300_intrhand *ih = arg; 311 int priority = ih->ih_priority; 312 313 intr_disestablish(arg); 314 315 if (priority == IPL_BIO) 316 dmacomputeipl(); 317 } 318 319 /* 320 * DIO specific bus_space(9) support functions. 321 */ 322 static uint8_t dio_bus_space_read_oddbyte_1(bus_space_tag_t, 323 bus_space_handle_t, bus_size_t); 324 static void dio_bus_space_write_oddbyte_1(bus_space_tag_t, 325 bus_space_handle_t, bus_size_t, uint8_t); 326 327 static void dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t, 328 bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); 329 static void dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t, 330 bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); 331 332 static void dio_bus_space_read_region_oddbyte_1(bus_space_tag_t, 333 bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); 334 static void dio_bus_space_write_region_oddbyte_1(bus_space_tag_t, 335 bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); 336 337 static void dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t, 338 bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); 339 340 static void dio_bus_space_set_region_oddbyte_1(bus_space_tag_t, 341 bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); 342 343 /* 344 * dio_set_bus_space_oddbyte(): 345 * Override bus_space functions in bus_space_tag_t 346 * for devices which have odd byte address space. 347 */ 348 void 349 dio_set_bus_space_oddbyte(bus_space_tag_t bst) 350 { 351 352 /* XXX only 1-byte functions for now */ 353 bst->bsr1 = dio_bus_space_read_oddbyte_1; 354 bst->bsw1 = dio_bus_space_write_oddbyte_1; 355 356 bst->bsrm1 = dio_bus_space_read_multi_oddbyte_1; 357 bst->bswm1 = dio_bus_space_write_multi_oddbyte_1; 358 359 bst->bsrr1 = dio_bus_space_read_region_oddbyte_1; 360 bst->bswr1 = dio_bus_space_write_region_oddbyte_1; 361 362 bst->bssm1 = dio_bus_space_set_multi_oddbyte_1; 363 364 bst->bssr1 = dio_bus_space_set_region_oddbyte_1; 365 } 366 367 static uint8_t 368 dio_bus_space_read_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 369 bus_size_t offset) 370 { 371 372 return *(volatile uint8_t *)(bsh + (offset << 1) + 1); 373 } 374 375 static void 376 dio_bus_space_write_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 377 bus_size_t offset, uint8_t val) 378 { 379 380 *(volatile uint8_t *)(bsh + (offset << 1) + 1) = val; 381 } 382 383 static void 384 dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 385 bus_size_t offset, uint8_t *addr, bus_size_t len) 386 { 387 388 __asm volatile ( 389 " movl %0,%%a0 ;\n" 390 " movl %1,%%a1 ;\n" 391 " movl %2,%%d0 ;\n" 392 "1: movb %%a0@,%%a1@+ ;\n" 393 " subql #1,%%d0 ;\n" 394 " jne 1b" 395 : 396 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 397 : "%a0","%a1","%d0"); 398 } 399 400 static void 401 dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 402 bus_size_t offset, const uint8_t *addr, bus_size_t len) 403 { 404 405 __asm volatile ( 406 " movl %0,%%a0 ;\n" 407 " movl %1,%%a1 ;\n" 408 " movl %2,%%d0 ;\n" 409 "1: movb %%a1@+,%%a0@ ;\n" 410 " subql #1,%%d0 ;\n" 411 " jne 1b" 412 : 413 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 414 : "%a0","%a1","%d0"); 415 } 416 417 static void 418 dio_bus_space_read_region_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 419 bus_size_t offset, uint8_t *addr, bus_size_t len) 420 { 421 __asm volatile ( 422 " movl %0,%%a0 ;\n" 423 " movl %1,%%a1 ;\n" 424 " movl %2,%%d0 ;\n" 425 "1: movb %%a0@,%%a1@+ ;\n" 426 " addql #2,%%a0 ;\n" 427 " subql #1,%%d0 ;\n" 428 " jne 1b" 429 : 430 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 431 : "%a0","%a1","%d0"); 432 } 433 434 static void 435 dio_bus_space_write_region_oddbyte_1(bus_space_tag_t bst, 436 bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr, 437 bus_size_t len) 438 { 439 440 __asm volatile ( 441 " movl %0,%%a0 ;\n" 442 " movl %1,%%a1 ;\n" 443 " movl %2,%%d0 ;\n" 444 "1: movb %%a1@+,%%a0@ ;\n" 445 " addql #2,%%a0 ;\n" 446 " subql #1,%%d0 ;\n" 447 " jne 1b" 448 : 449 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 450 : "%a0","%a1","%d0"); 451 } 452 453 static void 454 dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 455 bus_size_t offset, uint8_t val, bus_size_t count) 456 { 457 __asm volatile ( 458 " movl %0,%%a0 ;\n" 459 " movl %1,%%d1 ;\n" 460 " movl %2,%%d0 ;\n" 461 "1: movb %%d1,%%a0@ ;\n" 462 " subql #1,%%d0 ;\n" 463 " jne 1b" 464 : 465 : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count) 466 : "%a0","%d0","%d1"); 467 } 468 469 static void 470 dio_bus_space_set_region_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 471 bus_size_t offset, uint8_t val, bus_size_t count) 472 { 473 474 __asm volatile ( 475 " movl %0,%%a0 ;\n" 476 " movl %1,%%d1 ;\n" 477 " movl %2,%%d0 ;\n" 478 "1: movb %%d1,%%a0@ ;\n" 479 " addql #2,%%a0 ;\n" 480 " subql #1,%%d0 ;\n" 481 " jne 1b" 482 : 483 : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count) 484 : "%a0","%d0","%d1"); 485 } 486