obio.c revision 1.32 1 /* $NetBSD: obio.c,v 1.32 2011/03/16 05:08:29 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.32 2011/03/16 05:08:29 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
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 int sc_spd_hi, sc_spd_lo;
75 #endif
76 };
77
78 static struct obio_softc *obio0 = NULL;
79
80 #ifdef OBIO_SPEED_CONTROL
81 static void obio_setup_gpios(struct obio_softc *, int);
82 static void obio_set_cpu_speed(struct obio_softc *, int);
83 static int obio_get_cpu_speed(struct obio_softc *);
84 static int sysctl_cpuspeed_temp(SYSCTLFN_ARGS);
85 static int sysctl_cpuspeed_cur(SYSCTLFN_ARGS);
86 static int sysctl_cpuspeed_available(SYSCTLFN_ARGS);
87
88 static const char *keylargo[] = {"Keylargo",
89 "AAPL,Keylargo",
90 NULL};
91
92 #endif
93
94 CFATTACH_DECL(obio, sizeof(struct obio_softc),
95 obio_match, obio_attach, NULL, NULL);
96
97 int
98 obio_match(struct device *parent, struct cfdata *cf, void *aux)
99 {
100 struct pci_attach_args *pa = aux;
101
102 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE)
103 switch (PCI_PRODUCT(pa->pa_id)) {
104 case PCI_PRODUCT_APPLE_GC:
105 case PCI_PRODUCT_APPLE_OHARE:
106 case PCI_PRODUCT_APPLE_HEATHROW:
107 case PCI_PRODUCT_APPLE_PADDINGTON:
108 case PCI_PRODUCT_APPLE_KEYLARGO:
109 case PCI_PRODUCT_APPLE_PANGEA_MACIO:
110 case PCI_PRODUCT_APPLE_INTREPID:
111 case PCI_PRODUCT_APPLE_K2:
112 return 1;
113 }
114
115 return 0;
116 }
117
118 /*
119 * Attach all the sub-devices we can find
120 */
121 void
122 obio_attach(struct device *parent, struct device *self, void *aux)
123 {
124 struct obio_softc *sc = (struct obio_softc *)self;
125 struct pci_attach_args *pa = aux;
126 struct confargs ca;
127 bus_space_handle_t bsh;
128 int node, child, namelen, error;
129 u_int reg[20];
130 int intr[6], parent_intr = 0, parent_nintr = 0;
131 char name[32];
132 char compat[32];
133
134 #ifdef OBIO_SPEED_CONTROL
135 sc->sc_voltage = -1;
136 sc->sc_busspeed = -1;
137 sc->sc_spd_lo = 600;
138 sc->sc_spd_hi = 800;
139 #endif
140
141 switch (PCI_PRODUCT(pa->pa_id)) {
142
143 case PCI_PRODUCT_APPLE_GC:
144 case PCI_PRODUCT_APPLE_OHARE:
145 case PCI_PRODUCT_APPLE_HEATHROW:
146 case PCI_PRODUCT_APPLE_PADDINGTON:
147 case PCI_PRODUCT_APPLE_KEYLARGO:
148 case PCI_PRODUCT_APPLE_PANGEA_MACIO:
149 case PCI_PRODUCT_APPLE_INTREPID:
150 node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag);
151 if (node == -1)
152 node = OF_finddevice("mac-io");
153 if (node == -1)
154 node = OF_finddevice("/pci/mac-io");
155 break;
156 case PCI_PRODUCT_APPLE_K2:
157 node = OF_finddevice("mac-io");
158 break;
159
160 default:
161 node = -1;
162 break;
163 }
164 if (node == -1)
165 panic("macio not found or unknown");
166
167 sc->sc_node = node;
168
169 #if defined (PMAC_G5)
170 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 20)
171 {
172 return;
173 }
174 #else
175 if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 12)
176 return;
177 #endif /* PMAC_G5 */
178
179 /*
180 * XXX
181 * This relies on the primary obio always attaching first which is
182 * true on the PowerBook 3400c and similar machines but may or may
183 * not work on others. We can't rely on the node name since Apple
184 * didn't follow anything remotely resembling a consistent naming
185 * scheme.
186 */
187 if (obio0 == NULL)
188 obio0 = sc;
189
190 ca.ca_baseaddr = reg[2];
191 ca.ca_tag = pa->pa_memt;
192 sc->sc_tag = pa->pa_memt;
193 error = bus_space_map (pa->pa_memt, ca.ca_baseaddr, 0x80, 0, &bsh);
194 if (error)
195 panic(": failed to map mac-io %#x", ca.ca_baseaddr);
196 sc->sc_bh = bsh;
197
198 printf(": addr 0x%x\n", ca.ca_baseaddr);
199
200 /* Enable internal modem (KeyLargo) */
201 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_KEYLARGO) {
202 aprint_normal("%s: enabling KeyLargo internal modem\n",
203 self->dv_xname);
204 bus_space_write_4(ca.ca_tag, bsh, 0x40,
205 bus_space_read_4(ca.ca_tag, bsh, 0x40) & ~(1<<25));
206 }
207
208 /* Enable internal modem (Pangea) */
209 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PANGEA_MACIO) {
210 /* set reset */
211 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x04);
212 /* power modem on */
213 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x02, 0x04);
214 /* unset reset */
215 bus_space_write_1(ca.ca_tag, bsh, 0x006a + 0x03, 0x05);
216 }
217
218 /* Gatwick and Paddington use same product ID */
219 namelen = OF_getprop(node, "compatible", compat, sizeof(compat));
220
221 if (strcmp(compat, "gatwick") == 0) {
222 parent_nintr = OF_getprop(node, "AAPL,interrupts", intr,
223 sizeof(intr));
224 parent_intr = intr[0];
225 } else {
226 /* Enable CD and microphone sound input. */
227 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_PADDINGTON)
228 bus_space_write_1(ca.ca_tag, bsh, 0x37, 0x03);
229 }
230
231 for (child = OF_child(node); child; child = OF_peer(child)) {
232 namelen = OF_getprop(child, "name", name, sizeof(name));
233 if (namelen < 0)
234 continue;
235 if (namelen >= sizeof(name))
236 continue;
237
238 #ifdef OBIO_SPEED_CONTROL
239 if (strcmp(name, "gpio") == 0) {
240
241 obio_setup_gpios(sc, child);
242 continue;
243 }
244 #endif
245
246 name[namelen] = 0;
247 ca.ca_name = name;
248 ca.ca_node = child;
249 ca.ca_tag = pa->pa_memt;
250
251 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
252
253 if (strcmp(compat, "gatwick") != 0) {
254 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
255 sizeof(intr));
256 if (ca.ca_nintr == -1)
257 ca.ca_nintr = OF_getprop(child, "interrupts", intr,
258 sizeof(intr));
259 } else {
260 intr[0] = parent_intr;
261 ca.ca_nintr = parent_nintr;
262 }
263 ca.ca_reg = reg;
264 ca.ca_intr = intr;
265
266 config_found(self, &ca, obio_print);
267 }
268 }
269
270 static const char * const skiplist[] = {
271 "interrupt-controller",
272 "gpio",
273 "escc-legacy",
274 "timer",
275 "i2c",
276 "power-mgt",
277 "escc",
278 "battery",
279 "backlight"
280
281 };
282
283 #define N_LIST (sizeof(skiplist) / sizeof(skiplist[0]))
284
285 int
286 obio_print(void *aux, const char *obio)
287 {
288 struct confargs *ca = aux;
289 int i;
290
291 for (i = 0; i < N_LIST; i++)
292 if (strcmp(ca->ca_name, skiplist[i]) == 0)
293 return QUIET;
294
295 if (obio)
296 aprint_normal("%s at %s", ca->ca_name, obio);
297
298 if (ca->ca_nreg > 0)
299 aprint_normal(" offset 0x%x", ca->ca_reg[0]);
300
301 return UNCONF;
302 }
303
304 void obio_write_4(int offset, uint32_t value)
305 {
306 if (obio0 == NULL)
307 return;
308 bus_space_write_4(obio0->sc_tag, obio0->sc_bh, offset, value);
309 }
310
311 void obio_write_1(int offset, uint8_t value)
312 {
313 if (obio0 == NULL)
314 return;
315 bus_space_write_1(obio0->sc_tag, obio0->sc_bh, offset, value);
316 }
317
318 uint32_t obio_read_4(int offset)
319 {
320 if (obio0 == NULL)
321 return 0xffffffff;
322 return bus_space_read_4(obio0->sc_tag, obio0->sc_bh, offset);
323 }
324
325 uint8_t obio_read_1(int offset)
326 {
327 if (obio0 == NULL)
328 return 0xff;
329 return bus_space_read_1(obio0->sc_tag, obio0->sc_bh, offset);
330 }
331
332 #ifdef OBIO_SPEED_CONTROL
333
334 static void
335 obio_setup_gpios(struct obio_softc *sc, int node)
336 {
337 uint32_t gpio_base, reg[6];
338 struct sysctlnode *sysctl_node, *me, *freq;
339 char name[32];
340 int child, use_dfs, cpunode, hiclock;
341
342 if (of_compatible(sc->sc_node, keylargo) == -1)
343 return;
344
345 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 4)
346 return;
347
348 gpio_base = reg[0];
349 DPRINTF("gpio_base: %02x\n", gpio_base);
350
351 /* now look for voltage and bus speed gpios */
352 use_dfs = 0;
353 for (child = OF_child(node); child; child = OF_peer(child)) {
354
355 if (OF_getprop(child, "name", name, sizeof(name)) < 1)
356 continue;
357
358 if (OF_getprop(child, "reg", reg, sizeof(reg)) < 4)
359 continue;
360
361 /*
362 * These register offsets either have to be added to the obio
363 * base address or to the gpio base address. This differs
364 * even in the same OF-tree! So we guess the offset is
365 * based on obio when it is larger than the gpio_base.
366 */
367 if (reg[0] >= gpio_base)
368 reg[0] -= gpio_base;
369
370 if (strcmp(name, "frequency-gpio") == 0) {
371 DPRINTF("found frequency_gpio at %02x\n", reg[0]);
372 sc->sc_busspeed = gpio_base + reg[0];
373 }
374 if (strcmp(name, "voltage-gpio") == 0) {
375 DPRINTF("found voltage_gpio at %02x\n", reg[0]);
376 sc->sc_voltage = gpio_base + reg[0];
377 }
378 if (strcmp(name, "cpu-vcore-select") == 0) {
379 DPRINTF("found cpu-vcore-select at %02x\n", reg[0]);
380 sc->sc_voltage = gpio_base + reg[0];
381 /* frequency gpio is not needed, we use cpu's DFS */
382 use_dfs = 1;
383 }
384 }
385
386 if ((sc->sc_voltage < 0) || (sc->sc_busspeed < 0 && !use_dfs))
387 return;
388
389 printf("%s: enabling Intrepid CPU speed control\n",
390 sc->sc_dev.dv_xname);
391
392 sc->sc_spd_lo = curcpu()->ci_khz / 1000;
393 hiclock = 0;
394 cpunode = OF_finddevice("/cpus/@0");
395 OF_getprop(cpunode, "clock-frequency", &hiclock, 4);
396 printf("hiclock: %d\n", (hiclock + 500000) / 1000000);
397 sysctl_node = NULL;
398
399 if (sysctl_createv(NULL, 0, NULL,
400 (const struct sysctlnode **)&me,
401 CTLFLAG_READWRITE, CTLTYPE_NODE, "intrepid", NULL, NULL,
402 0, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL) != 0)
403 printf("couldn't create 'interpid' node\n");
404
405 if (sysctl_createv(NULL, 0, NULL,
406 (const struct sysctlnode **)&freq,
407 CTLFLAG_READWRITE, CTLTYPE_NODE, "frequency", NULL, NULL,
408 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL) != 0)
409 printf("couldn't create 'frequency' node\n");
410
411 if (sysctl_createv(NULL, 0, NULL,
412 (const struct sysctlnode **)&sysctl_node,
413 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
414 CTLTYPE_INT, "target", "CPU speed", sysctl_cpuspeed_temp,
415 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
416 CTL_CREATE, CTL_EOL) == 0) {
417 sysctl_node->sysctl_data = (void *)sc;
418 } else
419 printf("couldn't create 'target' node\n");
420
421 if (sysctl_createv(NULL, 0, NULL,
422 (const struct sysctlnode **)&sysctl_node,
423 CTLFLAG_READWRITE | CTLFLAG_IMMEDIATE,
424 CTLTYPE_INT, "current", NULL, sysctl_cpuspeed_cur,
425 1, NULL, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
426 CTL_CREATE, CTL_EOL) == 0) {
427 sysctl_node->sysctl_data = (void *)sc;
428 } else
429 printf("couldn't create 'current' node\n");
430
431 if (sysctl_createv(NULL, 0, NULL,
432 (const struct sysctlnode **)&sysctl_node,
433 CTLFLAG_READWRITE,
434 CTLTYPE_STRING, "available", NULL, sysctl_cpuspeed_available,
435 2, NULL, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
436 CTL_CREATE, CTL_EOL) == 0) {
437 sysctl_node->sysctl_data = (void *)sc;
438 } else
439 printf("couldn't create 'available' node\n");
440 printf("speed: %d\n", curcpu()->ci_khz);
441 }
442
443 static void
444 obio_set_cpu_speed(struct obio_softc *sc, int fast)
445 {
446
447 if (sc->sc_voltage < 0)
448 return;
449
450 if (sc->sc_busspeed >= 0) {
451 /* set voltage and speed via gpio */
452 if (fast) {
453 bus_space_write_1(sc->sc_tag, sc->sc_bh,
454 sc->sc_voltage, 5);
455 bus_space_write_1(sc->sc_tag, sc->sc_bh,
456 sc->sc_busspeed, 5);
457 } else {
458 bus_space_write_1(sc->sc_tag, sc->sc_bh,
459 sc->sc_busspeed, 4);
460 bus_space_write_1(sc->sc_tag, sc->sc_bh,
461 sc->sc_voltage, 4);
462 }
463 }
464 else {
465 /* set voltage via gpio and speed via the 7447A's DFS bit */
466 if (fast) {
467 bus_space_write_1(sc->sc_tag, sc->sc_bh,
468 sc->sc_voltage, 5);
469 DELAY(1000);
470 }
471
472 /* set DFS for all cpus */
473 cpu_set_dfs(fast ? 1 : 2);
474 DELAY(100);
475
476 if (!fast) {
477 bus_space_write_1(sc->sc_tag, sc->sc_bh,
478 sc->sc_voltage, 4);
479 DELAY(1000);
480 }
481 }
482 }
483
484 static int
485 obio_get_cpu_speed(struct obio_softc *sc)
486 {
487
488 if (sc->sc_voltage < 0)
489 return 0;
490
491 if (sc->sc_busspeed >= 0) {
492 if (bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_busspeed)
493 & 1)
494 return 1;
495 }
496 else
497 return cpu_get_dfs() == 1;
498
499 return 0;
500 }
501
502 static int
503 sysctl_cpuspeed_temp(SYSCTLFN_ARGS)
504 {
505 struct sysctlnode node = *rnode;
506 struct obio_softc *sc = node.sysctl_data;
507 int speed, mhz;
508
509 speed = obio_get_cpu_speed(sc);
510 switch (speed) {
511 case 0:
512 mhz = sc->sc_spd_lo;
513 break;
514 case 1:
515 mhz = sc->sc_spd_hi;
516 break;
517 default:
518 speed = -1;
519 }
520 node.sysctl_idata = mhz;
521 node.sysctl_data = &mhz;
522 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
523 int new_reg;
524
525 new_reg = node.sysctl_idata;
526 if (new_reg == sc->sc_spd_lo) {
527 obio_set_cpu_speed(sc, 0);
528 } else if (new_reg == sc->sc_spd_hi) {
529 obio_set_cpu_speed(sc, 1);
530 } else {
531 printf("%s: new_reg %d\n", __func__, new_reg);
532 return EINVAL;
533 }
534 return 0;
535 }
536 return EINVAL;
537 }
538
539 static int
540 sysctl_cpuspeed_cur(SYSCTLFN_ARGS)
541 {
542 struct sysctlnode node = *rnode;
543 struct obio_softc *sc = node.sysctl_data;
544 int speed, mhz;
545
546 speed = obio_get_cpu_speed(sc);
547 switch (speed) {
548 case 0:
549 mhz = sc->sc_spd_lo;
550 break;
551 case 1:
552 mhz = sc->sc_spd_hi;
553 break;
554 default:
555 speed = -1;
556 }
557 node.sysctl_idata = mhz;
558 node.sysctl_data = &mhz;
559 return sysctl_lookup(SYSCTLFN_CALL(&node));
560 }
561
562 static int
563 sysctl_cpuspeed_available(SYSCTLFN_ARGS)
564 {
565 struct sysctlnode node = *rnode;
566 struct obio_softc *sc = node.sysctl_data;
567 char buf[128];
568 int speed;
569
570 speed = obio_get_cpu_speed(sc);
571 snprintf(buf, 128, "%d %d", sc->sc_spd_lo, sc->sc_spd_hi);
572 node.sysctl_data = buf;
573 return(sysctl_lookup(SYSCTLFN_CALL(&node)));
574 }
575
576 SYSCTL_SETUP(sysctl_ams_setup, "sysctl obio subtree setup")
577 {
578
579 sysctl_createv(NULL, 0, NULL, NULL,
580 CTLFLAG_PERMANENT,
581 CTLTYPE_NODE, "machdep", NULL,
582 NULL, 0, NULL, 0,
583 CTL_MACHDEP, CTL_EOL);
584 }
585
586 #endif /* OBIO_SPEEDCONTROL */
587