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