1 /* $NetBSD: obio.c,v 1.31 2010/12/05 13:33:50 phx 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.31 2010/12/05 13:33:50 phx 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 54 #include "opt_obio.h" 55 56 #ifdef OBIO_DEBUG 57 # define DPRINTF printf 58 #else 59 # define DPRINTF while (0) printf 60 #endif 61 62 static void obio_attach(struct device *, struct device *, void *); 63 static int obio_match(struct device *, struct cfdata *, void *); 64 static int obio_print(void *, const char *); 65 66 struct obio_softc { 67 struct device sc_dev; 68 bus_space_tag_t sc_tag; 69 bus_space_handle_t sc_bh; 70 int sc_node; 71 #ifdef OBIO_SPEED_CONTROL 72 int sc_voltage; 73 int sc_busspeed; 74 #endif 75 }; 76 77 static struct obio_softc *obio0 = NULL; 78 79 #ifdef OBIO_SPEED_CONTROL 80 static void obio_setup_gpios(struct obio_softc *, int); 81 static void obio_set_cpu_speed(struct obio_softc *, int); 82 static int obio_get_cpu_speed(struct obio_softc *); 83 static int sysctl_cpuspeed_temp(SYSCTLFN_ARGS); 84 85 static const char *keylargo[] = {"Keylargo", 86 "AAPL,Keylargo", 87 NULL}; 88 89 #endif 90 91 CFATTACH_DECL(obio, sizeof(struct obio_softc), 92 obio_match, obio_attach, NULL, NULL); 93 94 int 95 obio_match(struct device *parent, struct cfdata *cf, void *aux) 96 { 97 struct pci_attach_args *pa = aux; 98 99 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE) 100 switch (PCI_PRODUCT(pa->pa_id)) { 101 case PCI_PRODUCT_APPLE_GC: 102 case PCI_PRODUCT_APPLE_OHARE: 103 case PCI_PRODUCT_APPLE_HEATHROW: 104 case PCI_PRODUCT_APPLE_PADDINGTON: 105 case PCI_PRODUCT_APPLE_KEYLARGO: 106 case PCI_PRODUCT_APPLE_PANGEA_MACIO: 107 case PCI_PRODUCT_APPLE_INTREPID: 108 case PCI_PRODUCT_APPLE_K2: 109 return 1; 110 } 111 112 return 0; 113 } 114 115 /* 116 * Attach all the sub-devices we can find 117 */ 118 void 119 obio_attach(struct device *parent, struct device *self, void *aux) 120 { 121 struct obio_softc *sc = (struct obio_softc *)self; 122 struct pci_attach_args *pa = aux; 123 struct confargs ca; 124 bus_space_handle_t bsh; 125 int node, child, namelen, error; 126 u_int reg[20]; 127 int intr[6], parent_intr = 0, parent_nintr = 0; 128 char name[32]; 129 char compat[32]; 130 131 #ifdef OBIO_SPEED_CONTROL 132 sc->sc_voltage = -1; 133 sc->sc_busspeed = -1; 134 #endif 135 136 switch (PCI_PRODUCT(pa->pa_id)) { 137 138 case PCI_PRODUCT_APPLE_GC: 139 case PCI_PRODUCT_APPLE_OHARE: 140 case PCI_PRODUCT_APPLE_HEATHROW: 141 case PCI_PRODUCT_APPLE_PADDINGTON: 142 case PCI_PRODUCT_APPLE_KEYLARGO: 143 case PCI_PRODUCT_APPLE_PANGEA_MACIO: 144 case PCI_PRODUCT_APPLE_INTREPID: 145 node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag); 146 if (node == -1) 147 node = OF_finddevice("mac-io"); 148 if (node == -1) 149 node = OF_finddevice("/pci/mac-io"); 150 break; 151 case PCI_PRODUCT_APPLE_K2: 152 node = OF_finddevice("mac-io"); 153 break; 154 155 default: 156 node = -1; 157 break; 158 } 159 if (node == -1) 160 panic("macio not found or unknown"); 161 162 sc->sc_node = node; 163 164 #if defined (PMAC_G5) 165 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 20) 166 { 167 return; 168 } 169 #else 170 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 12) 171 return; 172 #endif /* PMAC_G5 */ 173 174 /* 175 * XXX 176 * This relies on the primary obio always attaching first which is 177 * true on the PowerBook 3400c and similar machines but may or may 178 * not work on others. We can't rely on the node name since Apple 179 * didn't follow anything remotely resembling a consistent naming 180 * scheme. 181 */ 182 if (obio0 == NULL) 183 obio0 = sc; 184 185 ca.ca_baseaddr = reg[2]; 186 ca.ca_tag = pa->pa_memt; 187 sc->sc_tag = pa->pa_memt; 188 error = bus_space_map (pa->pa_memt, ca.ca_baseaddr, 0x80, 0, &bsh); 189 if (error) 190 panic(": failed to map mac-io %#x", ca.ca_baseaddr); 191 sc->sc_bh = bsh; 192 193 printf(": addr 0x%x\n", ca.ca_baseaddr); 194 195 /* Enable internal modem (KeyLargo) */ 196 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_KEYLARGO) { 197 aprint_normal("%s: enabling KeyLargo internal modem\n", 198 self->dv_xname); 199 bus_space_write_4(ca.ca_tag, bsh, 0x40, 200 bus_space_read_4(ca.ca_tag, bsh, 0x40) & ~(1<<25)); 201 } 202 203 /* Enable internal modem (Pangea) */ 204 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PANGEA_MACIO) { 205 /* set reset */ 206 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x04); 207 /* power modem on */ 208 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x02, 0x04); 209 /* unset reset */ 210 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x05); 211 } 212 213 /* Gatwick and Paddington use same product ID */ 214 namelen = OF_getprop(node, "compatible", compat, sizeof(compat)); 215 216 if (strcmp(compat, "gatwick") == 0) { 217 parent_nintr = OF_getprop(node, "AAPL,interrupts", intr, 218 sizeof(intr)); 219 parent_intr = intr[0]; 220 } else { 221 /* Enable CD and microphone sound input. */ 222 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PADDINGTON) 223 bus_space_write_1(ca.ca_tag, bsh, 0x37, 0x03); 224 } 225 226 for (child = OF_child(node); child; child = OF_peer(child)) { 227 namelen = OF_getprop(child, "name", name, sizeof(name)); 228 if (namelen < 0) 229 continue; 230 if (namelen >= sizeof(name)) 231 continue; 232 233 #ifdef OBIO_SPEED_CONTROL 234 if (strcmp(name, "gpio") == 0) { 235 236 obio_setup_gpios(sc, child); 237 continue; 238 } 239 #endif 240 241 name[namelen] = 0; 242 ca.ca_name = name; 243 ca.ca_node = child; 244 ca.ca_tag = pa->pa_memt; 245 246 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg)); 247 248 if (strcmp(compat, "gatwick") != 0) { 249 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr, 250 sizeof(intr)); 251 if (ca.ca_nintr == -1) 252 ca.ca_nintr = OF_getprop(child, "interrupts", intr, 253 sizeof(intr)); 254 } else { 255 intr[0] = parent_intr; 256 ca.ca_nintr = parent_nintr; 257 } 258 ca.ca_reg = reg; 259 ca.ca_intr = intr; 260 261 config_found(self, &ca, obio_print); 262 } 263 } 264 265 static const char * const skiplist[] = { 266 "interrupt-controller", 267 "gpio", 268 "escc-legacy", 269 "timer", 270 "i2c", 271 "power-mgt", 272 "escc", 273 "battery", 274 "backlight" 275 276 }; 277 278 #define N_LIST (sizeof(skiplist) / sizeof(skiplist[0])) 279 280 int 281 obio_print(void *aux, const char *obio) 282 { 283 struct confargs *ca = aux; 284 int i; 285 286 for (i = 0; i < N_LIST; i++) 287 if (strcmp(ca->ca_name, skiplist[i]) == 0) 288 return QUIET; 289 290 if (obio) 291 aprint_normal("%s at %s", ca->ca_name, obio); 292 293 if (ca->ca_nreg > 0) 294 aprint_normal(" offset 0x%x", ca->ca_reg[0]); 295 296 return UNCONF; 297 } 298 299 void obio_write_4(int offset, uint32_t value) 300 { 301 if (obio0 == NULL) 302 return; 303 bus_space_write_4(obio0->sc_tag, obio0->sc_bh, offset, value); 304 } 305 306 void obio_write_1(int offset, uint8_t value) 307 { 308 if (obio0 == NULL) 309 return; 310 bus_space_write_1(obio0->sc_tag, obio0->sc_bh, offset, value); 311 } 312 313 uint32_t obio_read_4(int offset) 314 { 315 if (obio0 == NULL) 316 return 0xffffffff; 317 return bus_space_read_4(obio0->sc_tag, obio0->sc_bh, offset); 318 } 319 320 uint8_t obio_read_1(int offset) 321 { 322 if (obio0 == NULL) 323 return 0xff; 324 return bus_space_read_1(obio0->sc_tag, obio0->sc_bh, offset); 325 } 326 327 #ifdef OBIO_SPEED_CONTROL 328 329 static void 330 obio_setup_gpios(struct obio_softc *sc, int node) 331 { 332 uint32_t gpio_base, reg[6]; 333 struct sysctlnode *sysctl_node; 334 char name[32]; 335 int child, use_dfs; 336 337 if (of_compatible(sc->sc_node, keylargo) == -1) 338 return; 339 340 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 4) 341 return; 342 343 gpio_base = reg[0]; 344 DPRINTF("gpio_base: %02x\n", gpio_base); 345 346 /* now look for voltage and bus speed gpios */ 347 use_dfs = 0; 348 for (child = OF_child(node); child; child = OF_peer(child)) { 349 350 if (OF_getprop(child, "name", name, sizeof(name)) < 1) 351 continue; 352 353 if (OF_getprop(child, "reg", reg, sizeof(reg)) < 4) 354 continue; 355 356 /* 357 * These register offsets either have to be added to the obio 358 * base address or to the gpio base address. This differs 359 * even in the same OF-tree! So we guess the offset is 360 * based on obio when it is larger than the gpio_base. 361 */ 362 if (reg[0] >= gpio_base) 363 reg[0] -= gpio_base; 364 365 if (strcmp(name, "frequency-gpio") == 0) { 366 DPRINTF("found frequency_gpio at %02x\n", reg[0]); 367 sc->sc_busspeed = gpio_base + reg[0]; 368 } 369 if (strcmp(name, "voltage-gpio") == 0) { 370 DPRINTF("found voltage_gpio at %02x\n", reg[0]); 371 sc->sc_voltage = gpio_base + reg[0]; 372 } 373 if (strcmp(name, "cpu-vcore-select") == 0) { 374 DPRINTF("found cpu-vcore-select at %02x\n", reg[0]); 375 sc->sc_voltage = gpio_base + reg[0]; 376 /* frequency gpio is not needed, we use cpu's DFS */ 377 use_dfs = 1; 378 } 379 } 380 381 if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0 && !use_dfs)) 382 return; 383 384 printf("%s: enabling Intrepid CPU speed control\n", 385 sc->sc_dev.dv_xname); 386 387 sysctl_node = NULL; 388 sysctl_createv(NULL, 0, NULL, 389 (const struct sysctlnode **)&sysctl_node, 390 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE, 391 CTLTYPE_INT, "cpu_speed", "CPU speed", sysctl_cpuspeed_temp, 392 (unsigned long)sc, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL); 393 if (sysctl_node != NULL) 394 sysctl_node->sysctl_data = (void *)sc; 395 } 396 397 static void 398 obio_set_cpu_speed(struct obio_softc *sc, int fast) 399 { 400 401 if (sc->sc_voltage < 0) 402 return; 403 404 if (sc->sc_busspeed >= 0) { 405 /* set voltage and speed via gpio */ 406 if (fast) { 407 bus_space_write_1(sc->sc_tag, sc->sc_bh, 408 sc->sc_voltage, 5); 409 bus_space_write_1(sc->sc_tag, sc->sc_bh, 410 sc->sc_busspeed, 5); 411 } else { 412 bus_space_write_1(sc->sc_tag, sc->sc_bh, 413 sc->sc_busspeed, 4); 414 bus_space_write_1(sc->sc_tag, sc->sc_bh, 415 sc->sc_voltage, 4); 416 } 417 } 418 else { 419 /* set voltage via gpio and speed via the 7447A's DFS bit */ 420 if (fast) { 421 bus_space_write_1(sc->sc_tag, sc->sc_bh, 422 sc->sc_voltage, 5); 423 DELAY(1000); 424 } 425 426 /* set DFS for all cpus */ 427 cpu_set_dfs(fast ? 1 : 2); 428 DELAY(100); 429 430 if (!fast) { 431 bus_space_write_1(sc->sc_tag, sc->sc_bh, 432 sc->sc_voltage, 4); 433 DELAY(1000); 434 } 435 } 436 } 437 438 static int 439 obio_get_cpu_speed(struct obio_softc *sc) 440 { 441 442 if (sc->sc_voltage < 0) 443 return 0; 444 445 if (sc->sc_busspeed >= 0) { 446 if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed) 447 & 1) 448 return 1; 449 } 450 else 451 return cpu_get_dfs() == 1; 452 453 return 0; 454 } 455 456 static int 457 sysctl_cpuspeed_temp(SYSCTLFN_ARGS) 458 { 459 struct sysctlnode node = *rnode; 460 struct obio_softc *sc = node.sysctl_data; 461 const int *np = newp; 462 int speed, nd = 0; 463 464 speed = obio_get_cpu_speed(sc); 465 node.sysctl_idata = speed; 466 if (np) { 467 /* we're asked to write */ 468 nd = *np; 469 node.sysctl_data = &speed; 470 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 471 int new_reg; 472 473 new_reg = (max(0, min(1, node.sysctl_idata))); 474 obio_set_cpu_speed(sc, new_reg); 475 return 0; 476 } 477 return EINVAL; 478 } else { 479 node.sysctl_size = 4; 480 return(sysctl_lookup(SYSCTLFN_CALL(&node))); 481 } 482 } 483 484 #endif /* OBIO_SPEEDCONTROL */ 485