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