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