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