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