xref: /netbsd-src/sys/arch/macppc/dev/obio.c (revision ca453df649ce9db45b64d73678ba06cbccf9aa11)
1 /*	$NetBSD: obio.c,v 1.33 2011/06/18 08:08:28 matt 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.33 2011/06/18 08:08:28 matt 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 	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(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 #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