Home | History | Annotate | Line # | Download | only in dev
tctrl.c revision 1.4
      1 /*	$NetBSD: tctrl.c,v 1.4 1999/12/15 08:12:30 garbled Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Matt Thomas.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/param.h>
     40 #include <sys/systm.h>
     41 #include <sys/ioctl.h>
     42 #include <sys/select.h>
     43 #include <sys/tty.h>
     44 #include <sys/proc.h>
     45 #include <sys/user.h>
     46 #include <sys/conf.h>
     47 #include <sys/file.h>
     48 #include <sys/uio.h>
     49 #include <sys/kernel.h>
     50 #include <sys/syslog.h>
     51 #include <sys/types.h>
     52 #include <sys/device.h>
     53 #include <sys/envsys.h>
     54 #include <sys/poll.h>
     55 
     56 #include <machine/apmvar.h>
     57 #include <machine/autoconf.h>
     58 #include <machine/cpu.h>
     59 #include <machine/bus.h>
     60 #include <machine/tctrl.h>
     61 
     62 #include <sparc/dev/ts102reg.h>
     63 #include <sparc/dev/tctrlvar.h>
     64 
     65 cdev_decl(tctrl);
     66 
     67 extern struct cfdriver tctrl_cd;
     68 
     69 static const char *tctrl_ext_statuses[16] = {
     70 	"main power available",
     71 	"internal battery attached",
     72 	"external battery attached",
     73 	"external VGA attached",
     74 	"external keyboard attached",
     75 	"external mouse attached",
     76 	"lid down",
     77 	"internal battery charging",
     78 	"external battery charging",
     79 	"internal battery discharging",
     80 	"external battery discharging",
     81 };
     82 
     83 struct tctrl_softc {
     84 	struct	device sc_dev;
     85 	bus_space_tag_t	sc_memt;
     86 	bus_space_handle_t	sc_memh;
     87 	unsigned int	sc_junk;
     88 	unsigned int	sc_ext_status;
     89 	unsigned int	sc_flags;
     90 #define TCTRL_SEND_REQUEST		0x0001
     91 #define TCTRL_APM_CTLOPEN		0x0002
     92 	unsigned int	sc_wantdata;
     93 	enum { TCTRL_IDLE, TCTRL_ARGS,
     94 		TCTRL_ACK, TCTRL_DATA } sc_state;
     95 	u_int8_t	sc_cmdbuf[16];
     96 	u_int8_t	sc_rspbuf[16];
     97 	u_int8_t	sc_bitport;
     98 	u_int8_t	sc_tft_on;
     99 	u_int8_t	sc_op;
    100 	u_int8_t	sc_cmdoff;
    101 	u_int8_t	sc_cmdlen;
    102 	u_int8_t	sc_rspoff;
    103 	u_int8_t	sc_rsplen;
    104 	/* APM stuff */
    105 #define APM_NEVENTS 16
    106 	struct	apm_event_info sc_event_list[APM_NEVENTS];
    107 	int	sc_event_count;
    108 	int	sc_event_ptr;
    109 	struct	selinfo sc_rsel;
    110 	/* ENVSYS stuff */
    111 #define ENVSYS_NUMSENSORS 3
    112 	struct	envsys_sensor sc_esensors[ENVSYS_NUMSENSORS];
    113 
    114 	struct	evcnt sc_intrcnt;	/* interrupt counting */
    115 };
    116 
    117 #define TCTRL_STD_DEV		0
    118 #define TCTRL_APMCTL_DEV	8
    119 
    120 static int tctrl_match __P((struct device *parent, struct cfdata *cf,
    121 	void *aux));
    122 static void tctrl_attach __P((struct device *parent, struct device *self,
    123 	void *aux));
    124 static void tctrl_write __P((struct tctrl_softc *sc, bus_size_t off,
    125 	u_int8_t v));
    126 static u_int8_t tctrl_read __P((struct tctrl_softc *sc, bus_size_t off));
    127 static void tctrl_write_data __P((struct tctrl_softc *sc, u_int8_t v));
    128 static u_int8_t tctrl_read_data __P((struct tctrl_softc *sc));
    129 static int tctrl_intr __P((void *arg));
    130 static void tctrl_setup_bitport __P((void));
    131 static void tctrl_setup_bitport_nop __P((void));
    132 static void tctrl_read_ext_status __P((void));
    133 static void tctrl_read_event_status __P((void *arg));
    134 static int tctrl_apm_record_event __P((struct tctrl_softc *sc,
    135 	u_int event_type));
    136 
    137 struct cfattach tctrl_ca = {
    138 	sizeof(struct tctrl_softc), tctrl_match, tctrl_attach
    139 };
    140 
    141 extern struct cfdriver tctrl_cd;
    142 /* XXX wtf is this? see i386/apm.c */
    143 int tctrl_apm_evindex;
    144 
    145 static int
    146 tctrl_match(parent, cf, aux)
    147 	struct device *parent;
    148 	struct cfdata *cf;
    149 	void *aux;
    150 {
    151 	union obio_attach_args *uoba = aux;
    152 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
    153 
    154 	if (uoba->uoba_isobio4 != 0) {
    155 		return (0);
    156 	}
    157 
    158 	/* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
    159 	 * (who's interface is off the TS102 PCMCIA controller but there
    160 	 * exists a OpenProm for microcontroller interface).
    161 	 */
    162 	return strcmp("uctrl", sa->sa_name) == 0;
    163 }
    164 
    165 static void
    166 tctrl_attach(parent, self, aux)
    167 	struct device *parent;
    168 	struct device *self;
    169 	void *aux;
    170 {
    171 	struct tctrl_softc *sc = (void *)self;
    172 	union obio_attach_args *uoba = aux;
    173 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
    174 	unsigned int i, v;
    175 #if 0
    176 	unsigned int ack, msb, lsb;
    177 #endif
    178 
    179 	/* We're living on a sbus slot that looks like an obio that
    180 	 * looks like an sbus slot.
    181 	 */
    182 	sc->sc_memt = sa->sa_bustag;
    183 	if (sbus_bus_map(sc->sc_memt, sa->sa_slot,
    184 			 sa->sa_offset - TS102_REG_UCTRL_INT, sa->sa_size,
    185 			 BUS_SPACE_MAP_LINEAR, 0,
    186 			 &sc->sc_memh) != 0) {
    187 		printf(": can't map registers\n");
    188 		return;
    189 	}
    190 
    191 	printf("\n");
    192 
    193 	sc->sc_tft_on = 1;
    194 
    195 	/* clear any pending data.
    196 	 */
    197 	for (i = 0; i < 10000; i++) {
    198 		if ((TS102_UCTRL_STS_RXNE_STA &
    199 		    tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) {
    200 			break;
    201 		}
    202 		v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
    203 		tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
    204 	}
    205 
    206 	if (sa->sa_nintr != 0) {
    207 		(void)bus_intr_establish(sc->sc_memt, sa->sa_pri,
    208 		    0, tctrl_intr, sc);
    209 		evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
    210 	}
    211 
    212 	/* See what the external status is
    213 	 */
    214 
    215 	tctrl_read_ext_status();
    216 	if (sc->sc_ext_status != 0) {
    217 		const char *sep;
    218 
    219 		printf("%s: ", sc->sc_dev.dv_xname);
    220 		v = sc->sc_ext_status;
    221 		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
    222 			if (v & 1) {
    223 				printf("%s%s", sep, tctrl_ext_statuses[i]);
    224 				sep = ", ";
    225 			}
    226 		}
    227 		printf("\n");
    228 	}
    229 
    230 	/* Get a current of the control bitport;
    231 	 */
    232 	tctrl_setup_bitport_nop();
    233 	tctrl_write(sc, TS102_REG_UCTRL_INT,
    234 		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK);
    235 
    236 	sc->sc_wantdata = 0;
    237 	sc->sc_event_count = 0;
    238 
    239 	/* prime the sensor data */
    240 	sprintf(sc->sc_esensors[0].desc, "%s", "Internal Unit Temperature");
    241 	sc->sc_esensors[0].units = ENVSYS_STEMP;
    242 	sprintf(sc->sc_esensors[1].desc, "%s", "Internal Battery Voltage");
    243 	sc->sc_esensors[1].units = ENVSYS_SVOLTS_DC;
    244 	sprintf(sc->sc_esensors[2].desc, "%s", "DC-In Voltage");
    245 	sc->sc_esensors[2].units = ENVSYS_SVOLTS_DC;
    246 }
    247 
    248 static int
    249 tctrl_intr(arg)
    250 	void *arg;
    251 {
    252 	struct tctrl_softc *sc = arg;
    253 	unsigned int v, d;
    254 	int progress = 0;
    255 
    256     again:
    257 	/* find out the cause(s) of the interrupt */
    258 	v = tctrl_read(sc, TS102_REG_UCTRL_STS);
    259 
    260 	/* clear the cause(s) of the interrupt */
    261 	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
    262 
    263 	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
    264 	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
    265 		v &= ~TS102_UCTRL_STS_TXNF_STA;
    266 		if (tctrl_read(sc, TS102_REG_UCTRL_INT) & TS102_UCTRL_INT_TXNF_REQ) {
    267 			tctrl_write(sc, TS102_REG_UCTRL_INT, 0);
    268 			progress = 1;
    269 		}
    270 	}
    271 	if ((v == 0) && ((sc->sc_flags & TCTRL_SEND_REQUEST) == 0 ||
    272 	    sc->sc_state != TCTRL_IDLE)) {
    273 		wakeup(sc);
    274 		return progress;
    275 	}
    276 
    277 	progress = 1;
    278 	if (v & TS102_UCTRL_STS_RXNE_STA) {
    279 		d = tctrl_read_data(sc);
    280 		switch (sc->sc_state) {
    281 		case TCTRL_IDLE:
    282 			if (d == 0xfa) {
    283 				/* external event */
    284 				timeout(tctrl_read_event_status, (void *)0, 1);
    285 			} else {
    286 				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
    287 					sc->sc_dev.dv_xname, sc->sc_op, d);
    288 			}
    289 			goto again;
    290 		case TCTRL_ACK:
    291 			if (d != 0xfe) {
    292 				printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n",
    293 					sc->sc_dev.dv_xname, sc->sc_op, d);
    294 			}
    295 #ifdef TCTRLDEBUG
    296 			printf(" ack=0x%02x", d);
    297 #endif
    298 			sc->sc_rsplen--;
    299 			sc->sc_rspoff = 0;
    300 			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
    301 			sc->sc_wantdata = sc->sc_rsplen ? 1 : 0;
    302 #ifdef TCTRLDEBUG
    303 			if (sc->sc_rsplen > 0) {
    304 				printf(" [data(%u)]", sc->sc_rsplen);
    305 			} else {
    306 				printf(" [idle]\n");
    307 			}
    308 #endif
    309 			goto again;
    310 		case TCTRL_DATA:
    311 			sc->sc_rspbuf[sc->sc_rspoff++] = d;
    312 #ifdef TCTRLDEBUG
    313 			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
    314 #endif
    315 			if (sc->sc_rspoff == sc->sc_rsplen) {
    316 #ifdef TCTRLDEBUG
    317 				printf(" [idle]\n");
    318 #endif
    319 				sc->sc_state = TCTRL_IDLE;
    320 				sc->sc_wantdata = 0;
    321 			}
    322 			goto again;
    323 		default:
    324 			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
    325 			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
    326 			goto again;
    327 		}
    328 	}
    329 	if ((sc->sc_state == TCTRL_IDLE && sc->sc_wantdata == 0) ||
    330 	    sc->sc_flags & TCTRL_SEND_REQUEST) {
    331 		if (sc->sc_flags & TCTRL_SEND_REQUEST) {
    332 			sc->sc_flags &= ~TCTRL_SEND_REQUEST;
    333 			sc->sc_wantdata = 1;
    334 		}
    335 		if (sc->sc_cmdlen > 0) {
    336 			tctrl_write(sc, TS102_REG_UCTRL_INT,
    337 				tctrl_read(sc, TS102_REG_UCTRL_INT)
    338 				|TS102_UCTRL_INT_TXNF_MSK
    339 				|TS102_UCTRL_INT_TXNF_REQ);
    340 			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
    341 		}
    342 	}
    343 	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
    344 		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
    345 #ifdef TCTRLDEBUG
    346 		if (sc->sc_cmdoff == 1) {
    347 			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
    348 				sc->sc_cmdbuf[0], sc->sc_rsplen);
    349 		} else {
    350 			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
    351 				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
    352 		}
    353 #endif
    354 		if (sc->sc_cmdoff == sc->sc_cmdlen) {
    355 			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
    356 #ifdef TCTRLDEBUG
    357 			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
    358 #endif
    359 			if (sc->sc_cmdoff == 1) {
    360 				sc->sc_op = sc->sc_cmdbuf[0];
    361 			}
    362 			tctrl_write(sc, TS102_REG_UCTRL_INT,
    363 				tctrl_read(sc, TS102_REG_UCTRL_INT)
    364 				& (~TS102_UCTRL_INT_TXNF_MSK
    365 				   |TS102_UCTRL_INT_TXNF_REQ));
    366 		} else if (sc->sc_state == TCTRL_IDLE) {
    367 			sc->sc_op = sc->sc_cmdbuf[0];
    368 			sc->sc_state = TCTRL_ARGS;
    369 #ifdef TCTRLDEBUG
    370 			printf(" [args]");
    371 #endif
    372 		}
    373 	}
    374 	goto again;
    375 }
    376 
    377 static void
    378 tctrl_setup_bitport_nop(void)
    379 {
    380 	struct tctrl_softc *sc;
    381 	struct tctrl_req req;
    382 	int s;
    383 
    384 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    385 	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
    386 	req.cmdbuf[1] = 0xff;
    387 	req.cmdbuf[2] = 0;
    388 	req.cmdlen = 3;
    389 	req.rsplen = 2;
    390 	req.p = NULL;
    391 	tadpole_request(&req, 1);
    392 	s = splts102();
    393 	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
    394 	splx(s);
    395 }
    396 
    397 static void
    398 tctrl_setup_bitport(void)
    399 {
    400 	struct tctrl_softc *sc;
    401 	struct tctrl_req req;
    402 	int s;
    403 
    404 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    405 	s = splts102();
    406 	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
    407 	    || (!sc->sc_tft_on)) {
    408 		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
    409 	} else {
    410 		req.cmdbuf[2] = 0;
    411 	}
    412 	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
    413 	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
    414 	req.cmdlen = 3;
    415 	req.rsplen = 2;
    416 	req.p = NULL;
    417 	tadpole_request(&req, 1);
    418 	s = splts102();
    419 	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
    420 	splx(s);
    421 }
    422 
    423 static void
    424 tctrl_read_ext_status(void)
    425 {
    426 	struct tctrl_softc *sc;
    427 	struct tctrl_req req;
    428 	int s;
    429 
    430 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    431 	req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
    432 	req.cmdlen = 1;
    433 	req.rsplen = 3;
    434 	req.p = NULL;
    435 #ifdef TCTRLDEBUG
    436 	printf("pre read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
    437 #endif
    438 	tadpole_request(&req, 1);
    439 	s = splts102();
    440 	sc->sc_ext_status = req.rspbuf[0] * 256 + req.rspbuf[1];
    441 	splx(s);
    442 #ifdef TCTRLDEBUG
    443 	printf("post read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
    444 #endif
    445 }
    446 
    447 /*
    448  * return 0 if the user will notice and handle the event,
    449  * return 1 if the kernel driver should do so.
    450  */
    451 static int
    452 tctrl_apm_record_event(sc, event_type)
    453 	struct tctrl_softc *sc;
    454 	u_int event_type;
    455 {
    456 	struct apm_event_info *evp;
    457 
    458 	if ((sc->sc_flags & TCTRL_APM_CTLOPEN) &&
    459 	    (sc->sc_event_count < APM_NEVENTS)) {
    460 		evp = &sc->sc_event_list[sc->sc_event_ptr];
    461 		sc->sc_event_count++;
    462 		sc->sc_event_ptr++;
    463 		sc->sc_event_ptr %= APM_NEVENTS;
    464 		evp->type = event_type;
    465 		evp->index = ++tctrl_apm_evindex;
    466 		selwakeup(&sc->sc_rsel);
    467 		return(sc->sc_flags & TCTRL_APM_CTLOPEN) ? 0 : 1;
    468 	}
    469 	return(1);
    470 }
    471 
    472 static void
    473 tctrl_read_event_status(arg)
    474 	void *arg;
    475 {
    476 	struct tctrl_softc *sc;
    477 	struct tctrl_req req;
    478 	int s;
    479 	unsigned int v;
    480 
    481 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    482 	req.cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
    483 	req.cmdlen = 1;
    484 	req.rsplen = 3;
    485 	req.p = NULL;
    486 	tadpole_request(&req, 1);
    487 	s = splts102();
    488 	v = req.rspbuf[0] * 256 + req.rspbuf[1];
    489 	if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
    490 		printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
    491 	}
    492 	if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
    493 /*printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);*/
    494 /* according to a tadpole header, and observation */
    495 #ifdef TCTRLDEBUG
    496 		printf("%s: Battery charge level change\n", sc->sc_dev.dv_xname);
    497 #endif
    498 	}
    499 	if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
    500 		if (tctrl_apm_record_event(sc, APM_BATTERY_LOW))
    501 			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
    502 	}
    503 	if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
    504 		splx(s);
    505 		tctrl_read_ext_status();
    506 		s = splts102();
    507 		if (tctrl_apm_record_event(sc, APM_POWER_CHANGE))
    508 			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
    509 			    (sc->sc_ext_status &
    510 			    TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ?
    511 			    "restored" : "removed");
    512 	}
    513 	if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
    514 		splx(s);
    515 		tctrl_read_ext_status();
    516 		tctrl_setup_bitport();
    517 #ifdef TCTRLDEBUG
    518 		printf("%s: lid %s\n", sc->sc_dev.dv_xname,
    519 		    (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
    520 		    ? "closed" : "opened");
    521 #endif
    522 	}
    523 	splx(s);
    524 }
    525 
    526 void
    527 tadpole_request(req, spin)
    528 	struct tctrl_req *req;
    529 	int spin;
    530 {
    531 	struct tctrl_softc *sc;
    532 	int i, s;
    533 
    534 	if (tctrl_cd.cd_devs == NULL
    535 	    || tctrl_cd.cd_ndevs == 0
    536 	    || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) {
    537 		return;
    538 	}
    539 
    540 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    541 	while (sc->sc_wantdata != 0) {
    542 		if (req->p != NULL)
    543 			tsleep(&sc->sc_wantdata, PLOCK, "tctrl_lock", 10);
    544 		else
    545 			DELAY(1);
    546 	}
    547 	if (spin)
    548 		s = splhigh();
    549 	else
    550 		s = splts102();
    551 	sc->sc_flags |= TCTRL_SEND_REQUEST;
    552 	memcpy(sc->sc_cmdbuf, req->cmdbuf, req->cmdlen);
    553 	sc->sc_wantdata = 1;
    554 	sc->sc_rsplen = req->rsplen;
    555 	sc->sc_cmdlen = req->cmdlen;
    556 	sc->sc_cmdoff = sc->sc_rspoff = 0;
    557 
    558 	/* we spin for certain commands, like poweroffs */
    559 	if (spin) {
    560 		for (i = 0; i < 30000; i++) {
    561 			tctrl_intr(sc);
    562 			DELAY(1);
    563 		}
    564 	} else {
    565 		tctrl_intr(sc);
    566 		while ((sc->sc_rspoff != sc->sc_rsplen) ||
    567 		    (sc->sc_cmdoff != sc->sc_cmdlen))
    568 			if (req->p != NULL)
    569 				tsleep(sc, PWAIT, "tctrl_data", 0);
    570 			else
    571 				DELAY(1);
    572 	}
    573 	memcpy(req->rspbuf, sc->sc_rspbuf, req->rsplen);
    574 	splx(s);
    575 }
    576 
    577 void
    578 tadpole_powerdown(void)
    579 {
    580 	struct tctrl_req req;
    581 
    582 	req.cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
    583 	req.cmdlen = 1;
    584 	req.rsplen = 1;
    585 	req.p = NULL;
    586 	tadpole_request(&req, 1);
    587 }
    588 
    589 void
    590 tadpole_set_video(enabled)
    591 	int enabled;
    592 {
    593 	struct tctrl_softc *sc;
    594 	struct tctrl_req req;
    595 	int s;
    596 
    597 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    598 	while (sc->sc_wantdata != 0)
    599 		DELAY(1);
    600 	s = splts102();
    601 	req.p = NULL;
    602 	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN && !enabled)
    603 	    || (sc->sc_tft_on)) {
    604 		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
    605 	} else {
    606 		req.cmdbuf[2] = 0;
    607 	}
    608 	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
    609 	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
    610 	req.cmdlen = 3;
    611 	req.rsplen = 2;
    612 
    613 	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
    614 		sc->sc_tft_on = enabled;
    615 		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
    616 			splx(s);
    617 			return;
    618 		}
    619 		tadpole_request(&req, 1);
    620 		sc->sc_bitport =
    621 		    (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
    622 	}
    623 	splx(s);
    624 }
    625 
    626 static void
    627 tctrl_write_data(sc, v)
    628 	struct tctrl_softc *sc;
    629 	u_int8_t v;
    630 {
    631 	unsigned int i;
    632 
    633 	for (i = 0; i < 100; i++)  {
    634 		if (TS102_UCTRL_STS_TXNF_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
    635 			break;
    636 	}
    637 	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
    638 }
    639 
    640 static u_int8_t
    641 tctrl_read_data(sc)
    642 	struct tctrl_softc *sc;
    643 {
    644 	unsigned int i, v;
    645 
    646 	for (i = 0; i < 100000; i++) {
    647 		if (TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
    648 			break;
    649 		DELAY(1);
    650 	}
    651 
    652 	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
    653 	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
    654 	return v;
    655 }
    656 
    657 static u_int8_t
    658 tctrl_read(sc, off)
    659 	struct tctrl_softc *sc;
    660 	bus_size_t off;
    661 {
    662 
    663 	sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off);
    664 	return sc->sc_junk;
    665 }
    666 
    667 static void
    668 tctrl_write(sc, off, v)
    669 	struct tctrl_softc *sc;
    670 	bus_size_t off;
    671 	u_int8_t v;
    672 {
    673 
    674 	sc->sc_junk = v;
    675 	bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v);
    676 }
    677 
    678 int
    679 tctrlopen(dev, flags, mode, p)
    680 	dev_t dev;
    681 	int flags, mode;
    682 	struct proc *p;
    683 {
    684 	int unit = (minor(dev)&0xf0);
    685 	int ctl = (minor(dev)&0x0f);
    686 	struct tctrl_softc *sc;
    687 
    688 	if (unit >= tctrl_cd.cd_ndevs)
    689 		return(ENXIO);
    690 	sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
    691 	if (!sc)
    692 		return(ENXIO);
    693 
    694 	switch (ctl) {
    695 	case TCTRL_STD_DEV:
    696 		break;
    697 	case TCTRL_APMCTL_DEV:
    698 		if (!(flags & FWRITE))
    699 			return(EINVAL);
    700 		if (sc->sc_flags & TCTRL_APM_CTLOPEN)
    701 			return(EBUSY);
    702 		sc->sc_flags |= TCTRL_APM_CTLOPEN;
    703 		break;
    704 	default:
    705 		return(ENXIO);
    706 		break;
    707 	}
    708 
    709 	return(0);
    710 }
    711 
    712 int
    713 tctrlclose(dev, flags, mode, p)
    714 	dev_t dev;
    715 	int flags, mode;
    716 	struct proc *p;
    717 {
    718 	int ctl = (minor(dev)&0x0f);
    719 	struct tctrl_softc *sc;
    720 
    721 	sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
    722 	if (!sc)
    723 		return(ENXIO);
    724 
    725 	switch (ctl) {
    726 	case TCTRL_STD_DEV:
    727 		break;
    728 	case TCTRL_APMCTL_DEV:
    729 		sc->sc_flags &= ~TCTRL_APM_CTLOPEN;
    730 		break;
    731 	}
    732 	return(0);
    733 }
    734 
    735 int
    736 tctrlioctl(dev, cmd, data, flags, p)
    737         dev_t dev;
    738         u_long cmd;
    739         caddr_t data;
    740         int flags;
    741         struct proc *p;
    742 {
    743 	struct tctrl_req req, *reqn;
    744 	envsys_range_t *envrange;
    745 	envsys_temp_data_t *envdata;
    746 	envsys_temp_info_t *envinfo;
    747 	struct apm_power_info *powerp;
    748 	struct apm_event_info *evp;
    749 	struct tctrl_softc *sc;
    750 	int i;
    751 	u_int j;
    752 	u_int16_t a;
    753 	u_int8_t c;
    754 
    755 	if (tctrl_cd.cd_devs == NULL
    756 	    || tctrl_cd.cd_ndevs == 0
    757 	    || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) {
    758 		return ENXIO;
    759 	}
    760 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
    761         switch (cmd) {
    762 
    763 	case APM_IOC_STANDBY:
    764 		return(EOPNOTSUPP); /* for now */
    765 
    766 	case APM_IOC_SUSPEND:
    767 		return(EOPNOTSUPP); /* for now */
    768 
    769 	case APM_IOC_GETPOWER:
    770 		powerp = (struct apm_power_info *)data;
    771 		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_RATE;
    772 		req.cmdlen = 1;
    773 		req.rsplen = 2;
    774 		req.p = p;
    775 		tadpole_request(&req, 0);
    776 		if (req.rspbuf[0] > 0x00)
    777 			powerp->battery_state = APM_BATT_CHARGING;
    778 		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_LEVEL;
    779 		req.cmdlen = 1;
    780 		req.rsplen = 3;
    781 		req.p = p;
    782 		tadpole_request(&req, 0);
    783 		c = req.rspbuf[0];
    784 		powerp->battery_life = c;
    785 		powerp->minutes_left = (45 * c) / 100; /* XXX based on 45 min */
    786 		if (powerp->battery_state != APM_BATT_CHARGING) {
    787 			if (c < 0x20)
    788 				powerp->battery_state = APM_BATT_CRITICAL;
    789 			else if (c < 0x40)
    790 				powerp->battery_state = APM_BATT_LOW;
    791 			else if (c < 0x66)
    792 				powerp->battery_state = APM_BATT_HIGH;
    793 			else
    794 				powerp->battery_state = APM_BATT_UNKNOWN;
    795 		}
    796 		req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
    797 		req.cmdlen = 1;
    798 		req.rsplen = 3;
    799 		req.p = p;
    800 		tadpole_request(&req, 0);
    801 		a = req.rspbuf[0] * 256 + req.rspbuf[1];
    802 		if (a & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE)
    803 			powerp->ac_state = APM_AC_ON;
    804 		else
    805 			powerp->ac_state = APM_AC_OFF;
    806 		break;
    807 
    808 	case APM_IOC_NEXTEVENT:
    809 		if (!sc->sc_event_count)
    810 			return EAGAIN;
    811 
    812 		evp = (struct apm_event_info *)data;
    813 		i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
    814 		i %= APM_NEVENTS;
    815 		*evp = sc->sc_event_list[i];
    816 		sc->sc_event_count--;
    817 		return(0);
    818 
    819 	/* this ioctl assumes the caller knows exactly what he is doing */
    820 	case TCTRL_CMD_REQ:
    821 		reqn = (struct tctrl_req *)data;
    822 		if ((i = suser(p->p_ucred, &p->p_acflag)) != 0 &&
    823 		    (reqn->cmdbuf[0] == TS102_OP_CTL_BITPORT ||
    824 		    (reqn->cmdbuf[0] >= TS102_OP_CTL_WATCHDOG &&
    825 		    reqn->cmdbuf[0] <= TS102_OP_CTL_SECURITY_KEY) ||
    826 		    reqn->cmdbuf[0] == TS102_OP_CTL_TIMEZONE ||
    827 		    reqn->cmdbuf[0] == TS102_OP_CTL_DIAGNOSTIC_MODE ||
    828 		    reqn->cmdbuf[0] == TS102_OP_CMD_SOFTWARE_RESET ||
    829 		    (reqn->cmdbuf[0] >= TS102_OP_CMD_SET_RTC &&
    830 		    reqn->cmdbuf[0] < TS102_OP_RD_INT_CHARGE_LEVEL) ||
    831 		    reqn->cmdbuf[0] > TS102_OP_RD_EXT_CHARGE_LEVEL))
    832 			return(i);
    833 		reqn->p = p;
    834 		tadpole_request(reqn, 0);
    835 		break;
    836 
    837 	case ENVSYS_VERSION:
    838 		*(int32_t *)data = 1000;
    839 		break;
    840 
    841 	case ENVSYS_GRANGE:
    842 		envrange = (envsys_range_t *)data;
    843 		i = 0;
    844 		envrange->high = envrange->low = 0;
    845 		for (j=0; j < ENVSYS_NUMSENSORS; j++) {
    846 			if (!i && envrange->units == sc->sc_esensors[j].units) {
    847 				envrange->low = j;
    848 				i++;
    849 			}
    850 			if (i && envrange->units == sc->sc_esensors[j].units)
    851 				envrange->high = j;
    852 		}
    853 		if (!i) {
    854 			envrange->high = 0;
    855 			envrange->low = 1;
    856 		}
    857 		break;
    858 
    859 	case ENVSYS_GTREDATA:
    860 		envdata = (envsys_temp_data_t *)data;
    861 		if (envdata->sensor >= ENVSYS_NUMSENSORS) {
    862 			envdata->validflags = 0;
    863 			break;
    864 		}
    865 		envdata->warnflags = ENVSYS_WARN_OK;
    866 		if (envdata->sensor == 0) {
    867 			envdata->validflags |= ENVSYS_FVALID;
    868 			req.cmdbuf[0] = TS102_OP_RD_CURRENT_TEMP;
    869 			req.cmdlen = 1;
    870 			req.rsplen = 2;
    871 			req.p = p;
    872 			tadpole_request(&req, 0);
    873 			envdata->cur.data_us =             /* 273160? */
    874 			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000/9+273000);
    875 			envdata->validflags |= ENVSYS_FCURVALID;
    876 			req.cmdbuf[0] = TS102_OP_RD_MAX_TEMP;
    877 			req.cmdlen = 1;
    878 			req.rsplen = 2;
    879 			req.p = p;
    880 			tadpole_request(&req, 0);
    881 			envdata->max.data_us =
    882 			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000/9+273000);
    883 			envdata->validflags |= ENVSYS_FMAXVALID;
    884 			req.cmdbuf[0] = TS102_OP_RD_MIN_TEMP;
    885 			req.cmdlen = 1;
    886 			req.rsplen = 2;
    887 			req.p = p;
    888 			tadpole_request(&req, 0);
    889 			envdata->min.data_us =
    890 			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000/9+273000);
    891 			envdata->validflags |= ENVSYS_FMINVALID;
    892 			envdata->units = sc->sc_esensors[envdata->sensor].units;
    893 			break;
    894 		} else if (envdata->sensor == 1 || envdata->sensor == 2) {
    895 			envdata->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
    896 			envdata->units = sc->sc_esensors[envdata->sensor].units;
    897 			if (envdata->sensor == 1)
    898 				req.cmdbuf[0] = TS102_OP_RD_INT_BATT_VLT;
    899 			else
    900 				req.cmdbuf[0] = TS102_OP_RD_DC_IN_VLT;
    901 			req.cmdlen = 1;
    902 			req.rsplen = 2;
    903 			req.p = p;
    904 			tadpole_request(&req, 0);
    905 			envdata->cur.data_s = (int32_t)req.rspbuf[0]*1000/11;
    906 			break;
    907 		}
    908 		break;
    909 
    910         case ENVSYS_GTREINFO:
    911 		envinfo = (envsys_temp_info_t *)data;
    912 		if (envinfo->sensor >= ENVSYS_NUMSENSORS) {
    913 			envinfo->validflags = 0;
    914 			break;
    915 		}
    916 		envinfo->units = sc->sc_esensors[envinfo->sensor].units;
    917 		memcpy(envinfo->desc, sc->sc_esensors[envinfo->sensor].desc,
    918 		    sizeof(sc->sc_esensors[envinfo->sensor].desc) >
    919 		    sizeof(envinfo->desc) ? sizeof(envinfo->desc) :
    920 		    sizeof(sc->sc_esensors[envinfo->sensor].desc));
    921 		if (envinfo->units == ENVSYS_STEMP) {
    922 			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID|
    923 			    ENVSYS_FMINVALID|ENVSYS_FMAXVALID;
    924 		} else if (envinfo->units == ENVSYS_SVOLTS_DC) {
    925 			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
    926 		} else
    927 			envinfo->validflags = 0;
    928                 break;
    929 
    930         case ENVSYS_STREINFO:
    931 		envinfo = (envsys_temp_info_t *)data;
    932 		if (envinfo->sensor >= ENVSYS_NUMSENSORS) {
    933 			envinfo->validflags = 0;
    934 			break;
    935 		}
    936 		if (envinfo->units == sc->sc_esensors[envinfo->sensor].units)
    937 			memcpy(sc->sc_esensors[envinfo->sensor].desc,
    938 			    envinfo->desc,
    939 			    sizeof(envinfo->desc) > sizeof(char)*32 ?
    940 			    sizeof(char)*32 : sizeof(envinfo->desc) );
    941 		if (envinfo->units == ENVSYS_STEMP) {
    942 			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID|
    943 			    ENVSYS_FMINVALID|ENVSYS_FMAXVALID;
    944 		} else if (envinfo->units == ENVSYS_SVOLTS_DC) {
    945 			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
    946 		} else
    947 			envinfo->validflags = 0;
    948                 break;
    949 
    950 
    951         default:
    952                 return (ENOTTY);
    953         }
    954         return (0);
    955 }
    956 
    957 int
    958 tctrlpoll(dev, events, p)
    959 	dev_t dev;
    960 	int events;
    961 	struct proc *p;
    962 {
    963 	struct tctrl_softc *sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
    964 	int revents = 0;
    965 
    966 	if (events & (POLLIN | POLLRDNORM)) {
    967 		if (sc->sc_event_count)
    968 			revents |= events & (POLLIN | POLLRDNORM);
    969 		else
    970 			selrecord(p, &sc->sc_rsel);
    971 	}
    972 
    973 	return (revents);
    974 }
    975