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