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