1 /* $NetBSD: dio.c,v 1.39 2014/03/26 17:55:26 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 * 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.39 2014/03/26 17:55:26 christos 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 snprintf(buf, buflen, "%s", 267 dio_devdescs[i].dd_desc); 268 return buf; 269 } 270 } 271 } 272 #endif /* DIOVERBOSE */ 273 274 /* 275 * Device is unknown. Construct something reasonable. 276 */ 277 snprintf(buf, buflen, "device id = 0x%x secid = 0x%x", 278 da->da_id, da->da_secid); 279 return buf; 280 } 281 282 /* 283 * Establish an interrupt handler for a DIO device. 284 */ 285 void * 286 dio_intr_establish(int (*func)(void *), void *arg, int ipl, int priority) 287 { 288 void *ih; 289 290 ih = intr_establish(func, arg, ipl, priority); 291 292 if (priority == IPL_BIO) 293 dmacomputeipl(); 294 295 return ih; 296 } 297 298 /* 299 * Remove an interrupt handler for a DIO device. 300 */ 301 void 302 dio_intr_disestablish(void *arg) 303 { 304 struct hp300_intrhand *ih = arg; 305 int priority = ih->ih_priority; 306 307 intr_disestablish(arg); 308 309 if (priority == IPL_BIO) 310 dmacomputeipl(); 311 } 312 313 /* 314 * DIO specific bus_space(9) support functions. 315 */ 316 static uint8_t dio_bus_space_read_oddbyte_1(bus_space_tag_t, 317 bus_space_handle_t, bus_size_t); 318 static void dio_bus_space_write_oddbyte_1(bus_space_tag_t, 319 bus_space_handle_t, bus_size_t, uint8_t); 320 321 static void dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t, 322 bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); 323 static void dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t, 324 bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); 325 326 static void dio_bus_space_read_region_oddbyte_1(bus_space_tag_t, 327 bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); 328 static void dio_bus_space_write_region_oddbyte_1(bus_space_tag_t, 329 bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); 330 331 static void dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t, 332 bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); 333 334 static void dio_bus_space_set_region_oddbyte_1(bus_space_tag_t, 335 bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); 336 337 /* 338 * dio_set_bus_space_oddbyte(): 339 * Override bus_space functions in bus_space_tag_t 340 * for devices which have odd byte address space. 341 */ 342 void 343 dio_set_bus_space_oddbyte(bus_space_tag_t bst) 344 { 345 346 /* XXX only 1-byte functions for now */ 347 bst->bsr1 = dio_bus_space_read_oddbyte_1; 348 bst->bsw1 = dio_bus_space_write_oddbyte_1; 349 350 bst->bsrm1 = dio_bus_space_read_multi_oddbyte_1; 351 bst->bswm1 = dio_bus_space_write_multi_oddbyte_1; 352 353 bst->bsrr1 = dio_bus_space_read_region_oddbyte_1; 354 bst->bswr1 = dio_bus_space_write_region_oddbyte_1; 355 356 bst->bssm1 = dio_bus_space_set_multi_oddbyte_1; 357 358 bst->bssr1 = dio_bus_space_set_region_oddbyte_1; 359 } 360 361 static uint8_t 362 dio_bus_space_read_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 363 bus_size_t offset) 364 { 365 366 return *(volatile uint8_t *)(bsh + (offset << 1) + 1); 367 } 368 369 static void 370 dio_bus_space_write_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 371 bus_size_t offset, uint8_t val) 372 { 373 374 *(volatile uint8_t *)(bsh + (offset << 1) + 1) = val; 375 } 376 377 static void 378 dio_bus_space_read_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 379 bus_size_t offset, uint8_t *addr, bus_size_t len) 380 { 381 382 __asm volatile ( 383 " movl %0,%%a0 ;\n" 384 " movl %1,%%a1 ;\n" 385 " movl %2,%%d0 ;\n" 386 "1: movb %%a0@,%%a1@+ ;\n" 387 " subql #1,%%d0 ;\n" 388 " jne 1b" 389 : 390 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 391 : "%a0","%a1","%d0"); 392 } 393 394 static void 395 dio_bus_space_write_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 396 bus_size_t offset, const uint8_t *addr, bus_size_t len) 397 { 398 399 __asm volatile ( 400 " movl %0,%%a0 ;\n" 401 " movl %1,%%a1 ;\n" 402 " movl %2,%%d0 ;\n" 403 "1: movb %%a1@+,%%a0@ ;\n" 404 " subql #1,%%d0 ;\n" 405 " jne 1b" 406 : 407 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 408 : "%a0","%a1","%d0"); 409 } 410 411 static void 412 dio_bus_space_read_region_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 413 bus_size_t offset, uint8_t *addr, bus_size_t len) 414 { 415 __asm volatile ( 416 " movl %0,%%a0 ;\n" 417 " movl %1,%%a1 ;\n" 418 " movl %2,%%d0 ;\n" 419 "1: movb %%a0@,%%a1@+ ;\n" 420 " addql #2,%%a0 ;\n" 421 " subql #1,%%d0 ;\n" 422 " jne 1b" 423 : 424 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 425 : "%a0","%a1","%d0"); 426 } 427 428 static void 429 dio_bus_space_write_region_oddbyte_1(bus_space_tag_t bst, 430 bus_space_handle_t bsh, bus_size_t offset, const uint8_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 " addql #2,%%a0 ;\n" 440 " subql #1,%%d0 ;\n" 441 " jne 1b" 442 : 443 : "r" (bsh + (offset << 1) + 1), "g" (addr), "g" (len) 444 : "%a0","%a1","%d0"); 445 } 446 447 static void 448 dio_bus_space_set_multi_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 449 bus_size_t offset, uint8_t val, bus_size_t count) 450 { 451 __asm volatile ( 452 " movl %0,%%a0 ;\n" 453 " movl %1,%%d1 ;\n" 454 " movl %2,%%d0 ;\n" 455 "1: movb %%d1,%%a0@ ;\n" 456 " subql #1,%%d0 ;\n" 457 " jne 1b" 458 : 459 : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count) 460 : "%a0","%d0","%d1"); 461 } 462 463 static void 464 dio_bus_space_set_region_oddbyte_1(bus_space_tag_t bst, bus_space_handle_t bsh, 465 bus_size_t offset, uint8_t val, bus_size_t count) 466 { 467 468 __asm volatile ( 469 " movl %0,%%a0 ;\n" 470 " movl %1,%%d1 ;\n" 471 " movl %2,%%d0 ;\n" 472 "1: movb %%d1,%%a0@ ;\n" 473 " addql #2,%%a0 ;\n" 474 " subql #1,%%d0 ;\n" 475 " jne 1b" 476 : 477 : "r" (bsh + (offset << 1) + 1), "g" (val), "g" (count) 478 : "%a0","%d0","%d1"); 479 } 480