1 /* $NetBSD: obio.c,v 1.40 2013/04/25 11:22:22 macallan Exp $ */ 2 3 /*- 4 * Copyright (C) 1998 Internet Research Institute, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by 18 * Internet Research Institute, Inc. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: obio.c,v 1.40 2013/04/25 11:22:22 macallan Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/device.h> 41 #include <sys/sysctl.h> 42 43 #include <dev/pci/pcivar.h> 44 #include <dev/pci/pcidevs.h> 45 46 #include <dev/ofw/openfirm.h> 47 48 #include <machine/autoconf.h> 49 50 #include <macppc/dev/obiovar.h> 51 52 #include <powerpc/cpu.h> 53 #include <sys/cpufreq.h> 54 55 #include "opt_obio.h" 56 57 #ifdef OBIO_DEBUG 58 # define DPRINTF printf 59 #else 60 # define DPRINTF while (0) printf 61 #endif 62 63 static void obio_attach(device_t, device_t, void *); 64 static int obio_match(device_t, cfdata_t, void *); 65 static int obio_print(void *, const char *); 66 67 struct obio_softc { 68 device_t sc_dev; 69 bus_space_tag_t sc_tag; 70 bus_space_handle_t sc_bh; 71 int sc_node; 72 #ifdef OBIO_SPEED_CONTROL 73 int sc_voltage; 74 int sc_busspeed; 75 int sc_spd_hi, sc_spd_lo; 76 struct cpufreq sc_cf; 77 #endif 78 }; 79 80 static struct obio_softc *obio0 = NULL; 81 82 #ifdef OBIO_SPEED_CONTROL 83 static void obio_setup_gpios(struct obio_softc *, int); 84 static void obio_set_cpu_speed(struct obio_softc *, int); 85 static int obio_get_cpu_speed(struct obio_softc *); 86 static int sysctl_cpuspeed_temp(SYSCTLFN_ARGS); 87 static int sysctl_cpuspeed_cur(SYSCTLFN_ARGS); 88 static int sysctl_cpuspeed_available(SYSCTLFN_ARGS); 89 static void obio_get_freq(void *, void *); 90 static void obio_set_freq(void *, void *); 91 static const char *keylargo[] = {"Keylargo", 92 "AAPL,Keylargo", 93 NULL}; 94 95 #endif 96 97 CFATTACH_DECL_NEW(obio, sizeof(struct obio_softc), 98 obio_match, obio_attach, NULL, NULL); 99 100 int 101 obio_match(device_t parent, cfdata_t cf, void *aux) 102 { 103 struct pci_attach_args *pa = aux; 104 105 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE) 106 switch (PCI_PRODUCT(pa->pa_id)) { 107 case PCI_PRODUCT_APPLE_GC: 108 case PCI_PRODUCT_APPLE_OHARE: 109 case PCI_PRODUCT_APPLE_HEATHROW: 110 case PCI_PRODUCT_APPLE_PADDINGTON: 111 case PCI_PRODUCT_APPLE_KEYLARGO: 112 case PCI_PRODUCT_APPLE_PANGEA_MACIO: 113 case PCI_PRODUCT_APPLE_INTREPID: 114 case PCI_PRODUCT_APPLE_K2: 115 case PCI_PRODUCT_APPLE_SHASTA: 116 return 1; 117 } 118 119 return 0; 120 } 121 122 /* 123 * Attach all the sub-devices we can find 124 */ 125 void 126 obio_attach(device_t parent, device_t self, void *aux) 127 { 128 struct obio_softc *sc = device_private(self); 129 struct pci_attach_args *pa = aux; 130 struct confargs ca; 131 bus_space_handle_t bsh; 132 int node, child, namelen, error; 133 u_int reg[20]; 134 int intr[6], parent_intr = 0, parent_nintr = 0; 135 int map_size = 0x1000; 136 char name[32]; 137 char compat[32]; 138 139 sc->sc_dev = self; 140 #ifdef OBIO_SPEED_CONTROL 141 sc->sc_voltage = -1; 142 sc->sc_busspeed = -1; 143 sc->sc_spd_lo = 600; 144 sc->sc_spd_hi = 800; 145 #endif 146 147 switch (PCI_PRODUCT(pa->pa_id)) { 148 149 case PCI_PRODUCT_APPLE_GC: 150 case PCI_PRODUCT_APPLE_OHARE: 151 case PCI_PRODUCT_APPLE_HEATHROW: 152 case PCI_PRODUCT_APPLE_PADDINGTON: 153 case PCI_PRODUCT_APPLE_KEYLARGO: 154 case PCI_PRODUCT_APPLE_PANGEA_MACIO: 155 case PCI_PRODUCT_APPLE_INTREPID: 156 node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag); 157 if (node == -1) 158 node = OF_finddevice("mac-io"); 159 if (node == -1) 160 node = OF_finddevice("/pci/mac-io"); 161 break; 162 case PCI_PRODUCT_APPLE_K2: 163 case PCI_PRODUCT_APPLE_SHASTA: 164 node = OF_finddevice("mac-io"); 165 map_size = 0x10000; 166 break; 167 168 default: 169 node = -1; 170 break; 171 } 172 if (node == -1) 173 panic("macio not found or unknown"); 174 175 sc->sc_node = node; 176 177 #if defined (PMAC_G5) 178 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 20) 179 { 180 return; 181 } 182 #else 183 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 12) 184 return; 185 #endif /* PMAC_G5 */ 186 187 /* 188 * XXX 189 * This relies on the primary obio always attaching first which is 190 * true on the PowerBook 3400c and similar machines but may or may 191 * not work on others. We can't rely on the node name since Apple 192 * didn't follow anything remotely resembling a consistent naming 193 * scheme. 194 */ 195 if (obio0 == NULL) 196 obio0 = sc; 197 198 ca.ca_baseaddr = reg[2]; 199 ca.ca_tag = pa->pa_memt; 200 sc->sc_tag = pa->pa_memt; 201 error = bus_space_map (pa->pa_memt, ca.ca_baseaddr, map_size, 0, &bsh); 202 if (error) 203 panic(": failed to map mac-io %#x", ca.ca_baseaddr); 204 sc->sc_bh = bsh; 205 206 printf(": addr 0x%x\n", ca.ca_baseaddr); 207 208 /* Enable internal modem (KeyLargo) */ 209 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_KEYLARGO) { 210 aprint_normal("%s: enabling KeyLargo internal modem\n", 211 device_xname(self)); 212 bus_space_write_4(ca.ca_tag, bsh, 0x40, 213 bus_space_read_4(ca.ca_tag, bsh, 0x40) & ~(1<<25)); 214 } 215 216 /* Enable internal modem (Pangea) */ 217 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PANGEA_MACIO) { 218 /* set reset */ 219 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x04); 220 /* power modem on */ 221 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x02, 0x04); 222 /* unset reset */ 223 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x05); 224 } 225 226 /* Gatwick and Paddington use same product ID */ 227 namelen = OF_getprop(node, "compatible", compat, sizeof(compat)); 228 229 if (strcmp(compat, "gatwick") == 0) { 230 parent_nintr = OF_getprop(node, "AAPL,interrupts", intr, 231 sizeof(intr)); 232 parent_intr = intr[0]; 233 } else { 234 /* Enable CD and microphone sound input. */ 235 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PADDINGTON) 236 bus_space_write_1(ca.ca_tag, bsh, 0x37, 0x03); 237 } 238 239 for (child = OF_child(node); child; child = OF_peer(child)) { 240 namelen = OF_getprop(child, "name", name, sizeof(name)); 241 if (namelen < 0) 242 continue; 243 if (namelen >= sizeof(name)) 244 continue; 245 246 #ifdef OBIO_SPEED_CONTROL 247 if (strcmp(name, "gpio") == 0) { 248 249 obio_setup_gpios(sc, child); 250 continue; 251 } 252 #endif 253 254 name[namelen] = 0; 255 ca.ca_name = name; 256 ca.ca_node = child; 257 ca.ca_tag = pa->pa_memt; 258 259 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg)); 260 261 if (strcmp(compat, "gatwick") != 0) { 262 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr, 263 sizeof(intr)); 264 if (ca.ca_nintr == -1) 265 ca.ca_nintr = OF_getprop(child, "interrupts", intr, 266 sizeof(intr)); 267 } else { 268 intr[0] = parent_intr; 269 ca.ca_nintr = parent_nintr; 270 } 271 ca.ca_reg = reg; 272 ca.ca_intr = intr; 273 274 config_found(self, &ca, obio_print); 275 } 276 } 277 278 static const char * const skiplist[] = { 279 "interrupt-controller", 280 "gpio", 281 "escc-legacy", 282 "timer", 283 "i2c", 284 "power-mgt", 285 "escc", 286 "battery", 287 "backlight" 288 289 }; 290 291 #define N_LIST (sizeof(skiplist) / sizeof(skiplist[0])) 292 293 int 294 obio_print(void *aux, const char *obio) 295 { 296 struct confargs *ca = aux; 297 int i; 298 299 for (i = 0; i < N_LIST; i++) 300 if (strcmp(ca->ca_name, skiplist[i]) == 0) 301 return QUIET; 302 303 if (obio) 304 aprint_normal("%s at %s", ca->ca_name, obio); 305 306 if (ca->ca_nreg > 0) 307 aprint_normal(" offset 0x%x", ca->ca_reg[0]); 308 309 return UNCONF; 310 } 311 312 void obio_write_4(int offset, uint32_t value) 313 { 314 if (obio0 == NULL) 315 return; 316 bus_space_write_4(obio0->sc_tag, obio0->sc_bh, offset, value); 317 } 318 319 void obio_write_1(int offset, uint8_t value) 320 { 321 if (obio0 == NULL) 322 return; 323 bus_space_write_1(obio0->sc_tag, obio0->sc_bh, offset, value); 324 } 325 326 uint32_t obio_read_4(int offset) 327 { 328 if (obio0 == NULL) 329 return 0xffffffff; 330 return bus_space_read_4(obio0->sc_tag, obio0->sc_bh, offset); 331 } 332 333 uint8_t obio_read_1(int offset) 334 { 335 if (obio0 == NULL) 336 return 0xff; 337 return bus_space_read_1(obio0->sc_tag, obio0->sc_bh, offset); 338 } 339 340 #ifdef OBIO_SPEED_CONTROL 341 342 static void 343 obio_setup_cpufreq(device_t dev) 344 { 345 struct obio_softc *sc = device_private(dev); 346 int ret; 347 348 ret = cpufreq_register(&sc->sc_cf); 349 if (ret != 0) 350 aprint_error_dev(sc->sc_dev, "cpufreq_register() failed, error %d\n", ret); 351 } 352 353 static void 354 obio_setup_gpios(struct obio_softc *sc, int node) 355 { 356 uint32_t gpio_base, reg[6]; 357 const struct sysctlnode *sysctl_node, *me, *freq; 358 struct cpufreq *cf = &sc->sc_cf; 359 char name[32]; 360 int child, use_dfs, cpunode, hiclock; 361 362 if (of_compatible(sc->sc_node, keylargo) == -1) 363 return; 364 365 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 4) 366 return; 367 368 gpio_base = reg[0]; 369 DPRINTF("gpio_base: %02x\n", gpio_base); 370 371 /* now look for voltage and bus speed gpios */ 372 use_dfs = 0; 373 for (child = OF_child(node); child; child = OF_peer(child)) { 374 375 if (OF_getprop(child, "name", name, sizeof(name)) < 1) 376 continue; 377 378 if (OF_getprop(child, "reg", reg, sizeof(reg)) < 4) 379 continue; 380 381 /* 382 * These register offsets either have to be added to the obio 383 * base address or to the gpio base address. This differs 384 * even in the same OF-tree! So we guess the offset is 385 * based on obio when it is larger than the gpio_base. 386 */ 387 if (reg[0] >= gpio_base) 388 reg[0] -= gpio_base; 389 390 if (strcmp(name, "frequency-gpio") == 0) { 391 DPRINTF("found frequency_gpio at %02x\n", reg[0]); 392 sc->sc_busspeed = gpio_base + reg[0]; 393 } 394 if (strcmp(name, "voltage-gpio") == 0) { 395 DPRINTF("found voltage_gpio at %02x\n", reg[0]); 396 sc->sc_voltage = gpio_base + reg[0]; 397 } 398 if (strcmp(name, "cpu-vcore-select") == 0) { 399 DPRINTF("found cpu-vcore-select at %02x\n", reg[0]); 400 sc->sc_voltage = gpio_base + reg[0]; 401 /* frequency gpio is not needed, we use cpu's DFS */ 402 use_dfs = 1; 403 } 404 } 405 406 if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0 && !use_dfs)) 407 return; 408 409 printf("%s: enabling Intrepid CPU speed control\n", 410 device_xname(sc->sc_dev)); 411 412 sc->sc_spd_lo = curcpu()->ci_khz / 1000; 413 hiclock = 0; 414 cpunode = OF_finddevice("/cpus/@0"); 415 OF_getprop(cpunode, "clock-frequency", &hiclock, 4); 416 if (hiclock != 0) 417 sc->sc_spd_hi = (hiclock + 500000) / 1000000; 418 printf("hiclock: %d\n", sc->sc_spd_hi); 419 420 sysctl_node = NULL; 421 422 if (sysctl_createv(NULL, 0, NULL, 423 &me, 424 CTLFLAG_READWRITE, CTLTYPE_NODE, "intrepid", NULL, NULL, 425 0, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL) != 0) 426 printf("couldn't create 'intrepid' node\n"); 427 428 if (sysctl_createv(NULL, 0, NULL, 429 &freq, 430 CTLFLAG_READWRITE, CTLTYPE_NODE, "frequency", NULL, NULL, 431 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL) != 0) 432 printf("couldn't create 'frequency' node\n"); 433 434 if (sysctl_createv(NULL, 0, NULL, 435 &sysctl_node, 436 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 437 CTLTYPE_INT, "target", "CPU speed", sysctl_cpuspeed_temp, 438 0, (void *)sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num, 439 CTL_CREATE, CTL_EOL) == 0) { 440 } else 441 printf("couldn't create 'target' node\n"); 442 443 if (sysctl_createv(NULL, 0, NULL, 444 &sysctl_node, 445 CTLFLAG_READWRITE, 446 CTLTYPE_INT, "current", NULL, sysctl_cpuspeed_cur, 447 1, (void *)sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num, 448 CTL_CREATE, CTL_EOL) == 0) { 449 } else 450 printf("couldn't create 'current' node\n"); 451 452 if (sysctl_createv(NULL, 0, NULL, 453 &sysctl_node, 454 CTLFLAG_READWRITE, 455 CTLTYPE_STRING, "available", NULL, sysctl_cpuspeed_available, 456 2, (void *)sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num, 457 CTL_CREATE, CTL_EOL) == 0) { 458 } else 459 printf("couldn't create 'available' node\n"); 460 printf("speed: %d\n", curcpu()->ci_khz); 461 462 /* support cpufreq */ 463 snprintf(cf->cf_name, CPUFREQ_NAME_MAX, "Intrepid"); 464 cf->cf_state[0].cfs_freq = sc->sc_spd_hi; 465 cf->cf_state[1].cfs_freq = sc->sc_spd_lo; 466 cf->cf_state_count = 2; 467 cf->cf_mp = FALSE; 468 cf->cf_cookie = sc; 469 cf->cf_get_freq = obio_get_freq; 470 cf->cf_set_freq = obio_set_freq; 471 /* 472 * XXX 473 * cpufreq_register() calls xc_broadcast() which relies on kthreads 474 * running so we need to postpone it 475 */ 476 config_interrupts(sc->sc_dev, obio_setup_cpufreq); 477 } 478 479 static void 480 obio_set_cpu_speed(struct obio_softc *sc, int fast) 481 { 482 483 if (sc->sc_voltage < 0) 484 return; 485 486 if (sc->sc_busspeed >= 0) { 487 /* set voltage and speed via gpio */ 488 if (fast) { 489 bus_space_write_1(sc->sc_tag, sc->sc_bh, 490 sc->sc_voltage, 5); 491 bus_space_write_1(sc->sc_tag, sc->sc_bh, 492 sc->sc_busspeed, 5); 493 } else { 494 bus_space_write_1(sc->sc_tag, sc->sc_bh, 495 sc->sc_busspeed, 4); 496 bus_space_write_1(sc->sc_tag, sc->sc_bh, 497 sc->sc_voltage, 4); 498 } 499 } 500 else { 501 /* set voltage via gpio and speed via the 7447A's DFS bit */ 502 if (fast) { 503 bus_space_write_1(sc->sc_tag, sc->sc_bh, 504 sc->sc_voltage, 5); 505 DELAY(1000); 506 } 507 508 /* set DFS for all cpus */ 509 cpu_set_dfs(fast ? 1 : 2); 510 DELAY(100); 511 512 if (!fast) { 513 bus_space_write_1(sc->sc_tag, sc->sc_bh, 514 sc->sc_voltage, 4); 515 DELAY(1000); 516 } 517 } 518 } 519 520 static int 521 obio_get_cpu_speed(struct obio_softc *sc) 522 { 523 524 if (sc->sc_voltage < 0) 525 return 0; 526 527 if (sc->sc_busspeed >= 0) { 528 if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed) 529 & 1) 530 return 1; 531 } 532 else 533 return cpu_get_dfs() == 1; 534 535 return 0; 536 } 537 538 static void 539 obio_get_freq(void *cookie, void *spd) 540 { 541 struct obio_softc *sc = cookie; 542 uint32_t *freq; 543 544 freq = spd; 545 if (obio_get_cpu_speed(sc) == 0) { 546 *freq = sc->sc_spd_lo; 547 } else 548 *freq = sc->sc_spd_hi; 549 } 550 551 static void 552 obio_set_freq(void *cookie, void *spd) 553 { 554 struct obio_softc *sc = cookie; 555 uint32_t *freq; 556 557 freq = spd; 558 if (*freq == sc->sc_spd_lo) { 559 obio_set_cpu_speed(sc, 0); 560 } else if (*freq == sc->sc_spd_hi) { 561 obio_set_cpu_speed(sc, 1); 562 } else 563 aprint_error_dev(sc->sc_dev, "%s(%d) bogus CPU speed\n", __func__, *freq); 564 } 565 566 static int 567 sysctl_cpuspeed_temp(SYSCTLFN_ARGS) 568 { 569 struct sysctlnode node = *rnode; 570 struct obio_softc *sc = node.sysctl_data; 571 int speed, mhz; 572 573 speed = obio_get_cpu_speed(sc); 574 switch (speed) { 575 case 0: 576 mhz = sc->sc_spd_lo; 577 break; 578 case 1: 579 mhz = sc->sc_spd_hi; 580 break; 581 default: 582 speed = -1; 583 } 584 node.sysctl_data = &mhz; 585 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 586 int new_reg; 587 588 new_reg = *(int *)node.sysctl_data; 589 if (new_reg == sc->sc_spd_lo) { 590 obio_set_cpu_speed(sc, 0); 591 } else if (new_reg == sc->sc_spd_hi) { 592 obio_set_cpu_speed(sc, 1); 593 } else { 594 printf("%s: new_reg %d\n", __func__, new_reg); 595 return EINVAL; 596 } 597 return 0; 598 } 599 return EINVAL; 600 } 601 602 static int 603 sysctl_cpuspeed_cur(SYSCTLFN_ARGS) 604 { 605 struct sysctlnode node = *rnode; 606 struct obio_softc *sc = node.sysctl_data; 607 int speed, mhz; 608 609 speed = obio_get_cpu_speed(sc); 610 switch (speed) { 611 case 0: 612 mhz = sc->sc_spd_lo; 613 break; 614 case 1: 615 mhz = sc->sc_spd_hi; 616 break; 617 default: 618 speed = -1; 619 } 620 node.sysctl_data = &mhz; 621 return sysctl_lookup(SYSCTLFN_CALL(&node)); 622 } 623 624 static int 625 sysctl_cpuspeed_available(SYSCTLFN_ARGS) 626 { 627 struct sysctlnode node = *rnode; 628 struct obio_softc *sc = node.sysctl_data; 629 char buf[128]; 630 int speed; 631 632 speed = obio_get_cpu_speed(sc); 633 snprintf(buf, 128, "%d %d", sc->sc_spd_lo, sc->sc_spd_hi); 634 node.sysctl_data = buf; 635 return(sysctl_lookup(SYSCTLFN_CALL(&node))); 636 } 637 638 SYSCTL_SETUP(sysctl_ams_setup, "sysctl obio subtree setup") 639 { 640 641 sysctl_createv(NULL, 0, NULL, NULL, 642 CTLFLAG_PERMANENT, 643 CTLTYPE_NODE, "machdep", NULL, 644 NULL, 0, NULL, 0, 645 CTL_MACHDEP, CTL_EOL); 646 } 647 648 #endif /* OBIO_SPEEDCONTROL */ 649