tctrl.c revision 1.25
119304Speter/*	$NetBSD: tctrl.c,v 1.25 2003/07/15 00:04:57 lukem Exp $	*/
219304Speter
319304Speter/*-
419304Speter * Copyright (c) 1998 The NetBSD Foundation, Inc.
519304Speter * All rights reserved.
619304Speter *
719304Speter * This code is derived from software contributed to The NetBSD Foundation
819304Speter * by Matt Thomas.
919304Speter *
1019304Speter * Redistribution and use in source and binary forms, with or without
1119304Speter * modification, are permitted provided that the following conditions
1219304Speter * are met:
1319304Speter * 1. Redistributions of source code must retain the above copyright
1419304Speter *    notice, this list of conditions and the following disclaimer.
1519304Speter * 2. Redistributions in binary form must reproduce the above copyright
1619304Speter *    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/cdefs.h>
40__KERNEL_RCSID(0, "$NetBSD: tctrl.c,v 1.25 2003/07/15 00:04:57 lukem Exp $");
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/callout.h>
45#include <sys/ioctl.h>
46#include <sys/select.h>
47#include <sys/tty.h>
48#include <sys/proc.h>
49#include <sys/user.h>
50#include <sys/conf.h>
51#include <sys/file.h>
52#include <sys/uio.h>
53#include <sys/kernel.h>
54#include <sys/syslog.h>
55#include <sys/types.h>
56#include <sys/device.h>
57#include <sys/envsys.h>
58#include <sys/poll.h>
59
60#include <machine/apmvar.h>
61#include <machine/autoconf.h>
62#include <machine/bus.h>
63#include <machine/intr.h>
64#include <machine/tctrl.h>
65
66#include <sparc/dev/ts102reg.h>
67#include <sparc/dev/tctrlvar.h>
68#include <sparc/sparc/auxiotwo.h>
69
70extern struct cfdriver tctrl_cd;
71
72dev_type_open(tctrlopen);
73dev_type_close(tctrlclose);
74dev_type_ioctl(tctrlioctl);
75dev_type_poll(tctrlpoll);
76dev_type_kqfilter(tctrlkqfilter);
77
78const struct cdevsw tctrl_cdevsw = {
79	tctrlopen, tctrlclose, noread, nowrite, tctrlioctl,
80	nostop, notty, tctrlpoll, nommap, tctrlkqfilter,
81};
82
83static const char *tctrl_ext_statuses[16] = {
84	"main power available",
85	"internal battery attached",
86	"external battery attached",
87	"external VGA attached",
88	"external keyboard attached",
89	"external mouse attached",
90	"lid down",
91	"internal battery charging",
92	"external battery charging",
93	"internal battery discharging",
94	"external battery discharging",
95};
96
97struct tctrl_softc {
98	struct	device sc_dev;
99	bus_space_tag_t	sc_memt;
100	bus_space_handle_t	sc_memh;
101	unsigned int	sc_junk;
102	unsigned int	sc_ext_status;
103	unsigned int	sc_flags;
104#define TCTRL_SEND_REQUEST		0x0001
105#define TCTRL_APM_CTLOPEN		0x0002
106	unsigned int	sc_wantdata;
107	volatile unsigned short	sc_lcdstate;
108	enum { TCTRL_IDLE, TCTRL_ARGS,
109		TCTRL_ACK, TCTRL_DATA } sc_state;
110	u_int8_t	sc_cmdbuf[16];
111	u_int8_t	sc_rspbuf[16];
112	u_int8_t	sc_bitport;
113	u_int8_t	sc_tft_on;
114	u_int8_t	sc_op;
115	u_int8_t	sc_cmdoff;
116	u_int8_t	sc_cmdlen;
117	u_int8_t	sc_rspoff;
118	u_int8_t	sc_rsplen;
119	/* APM stuff */
120#define APM_NEVENTS 16
121	struct	apm_event_info sc_event_list[APM_NEVENTS];
122	int	sc_event_count;
123	int	sc_event_ptr;
124	struct	selinfo sc_rsel;
125	/* ENVSYS stuff */
126#define ENVSYS_NUMSENSORS 3
127	struct	envsys_sensor sc_esensors[ENVSYS_NUMSENSORS];
128
129	struct	evcnt sc_intrcnt;	/* interrupt counting */
130};
131
132#define TCTRL_STD_DEV		0
133#define TCTRL_APMCTL_DEV	8
134
135static struct callout tctrl_event_ch = CALLOUT_INITIALIZER;
136
137static int tctrl_match __P((struct device *parent, struct cfdata *cf,
138	void *aux));
139static void tctrl_attach __P((struct device *parent, struct device *self,
140	void *aux));
141static void tctrl_write __P((struct tctrl_softc *sc, bus_size_t off,
142	u_int8_t v));
143static u_int8_t tctrl_read __P((struct tctrl_softc *sc, bus_size_t off));
144static void tctrl_write_data __P((struct tctrl_softc *sc, u_int8_t v));
145static u_int8_t tctrl_read_data __P((struct tctrl_softc *sc));
146static int tctrl_intr __P((void *arg));
147static void tctrl_setup_bitport __P((void));
148static void tctrl_setup_bitport_nop __P((void));
149static void tctrl_read_ext_status __P((void));
150static void tctrl_read_event_status __P((void *arg));
151static int tctrl_apm_record_event __P((struct tctrl_softc *sc,
152	u_int event_type));
153static void tctrl_init_lcd __P((void));
154
155CFATTACH_DECL(tctrl, sizeof(struct tctrl_softc),
156    tctrl_match, tctrl_attach, NULL, NULL);
157
158extern struct cfdriver tctrl_cd;
159/* XXX wtf is this? see i386/apm.c */
160int tctrl_apm_evindex;
161
162static int
163tctrl_match(parent, cf, aux)
164	struct device *parent;
165	struct cfdata *cf;
166	void *aux;
167{
168	union obio_attach_args *uoba = aux;
169	struct sbus_attach_args *sa = &uoba->uoba_sbus;
170
171	if (uoba->uoba_isobio4 != 0) {
172		return (0);
173	}
174
175	/* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
176	 * (who's interface is off the TS102 PCMCIA controller but there
177	 * exists a OpenProm for microcontroller interface).
178	 */
179	return strcmp("uctrl", sa->sa_name) == 0;
180}
181
182static void
183tctrl_attach(parent, self, aux)
184	struct device *parent;
185	struct device *self;
186	void *aux;
187{
188	struct tctrl_softc *sc = (void *)self;
189	union obio_attach_args *uoba = aux;
190	struct sbus_attach_args *sa = &uoba->uoba_sbus;
191	unsigned int i, v;
192#if 0
193	unsigned int ack, msb, lsb;
194#endif
195
196	/* We're living on a sbus slot that looks like an obio that
197	 * looks like an sbus slot.
198	 */
199	sc->sc_memt = sa->sa_bustag;
200	if (sbus_bus_map(sc->sc_memt,
201			 sa->sa_slot,
202			 sa->sa_offset - TS102_REG_UCTRL_INT,
203			 sa->sa_size,
204			 BUS_SPACE_MAP_LINEAR, &sc->sc_memh) != 0) {
205		printf(": can't map registers\n");
206		return;
207	}
208
209	printf("\n");
210
211	sc->sc_tft_on = 1;
212
213	/* clear any pending data.
214	 */
215	for (i = 0; i < 10000; i++) {
216		if ((TS102_UCTRL_STS_RXNE_STA &
217		    tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) {
218			break;
219		}
220		v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
221		tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
222	}
223
224	if (sa->sa_nintr != 0) {
225		(void)bus_intr_establish(sc->sc_memt, sa->sa_pri, IPL_NONE,
226					 tctrl_intr, sc);
227		evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
228				     sc->sc_dev.dv_xname, "intr");
229	}
230
231	/* See what the external status is
232	 */
233
234	tctrl_read_ext_status();
235	if (sc->sc_ext_status != 0) {
236		const char *sep;
237
238		printf("%s: ", sc->sc_dev.dv_xname);
239		v = sc->sc_ext_status;
240		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
241			if (v & 1) {
242				printf("%s%s", sep, tctrl_ext_statuses[i]);
243				sep = ", ";
244			}
245		}
246		printf("\n");
247	}
248
249	/* Get a current of the control bitport;
250	 */
251	tctrl_setup_bitport_nop();
252	tctrl_write(sc, TS102_REG_UCTRL_INT,
253		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK);
254
255	sc->sc_wantdata = 0;
256	sc->sc_event_count = 0;
257
258	/* prime the sensor data */
259	sprintf(sc->sc_esensors[0].desc, "%s", "Internal Unit Temperature");
260	sc->sc_esensors[0].units = ENVSYS_STEMP;
261	sprintf(sc->sc_esensors[1].desc, "%s", "Internal Battery Voltage");
262	sc->sc_esensors[1].units = ENVSYS_SVOLTS_DC;
263	sprintf(sc->sc_esensors[2].desc, "%s", "DC-In Voltage");
264	sc->sc_esensors[2].units = ENVSYS_SVOLTS_DC;
265
266	/* initialize the LCD */
267	tctrl_init_lcd();
268
269	/* initialize sc_lcdstate */
270	sc->sc_lcdstate = 0;
271	tctrl_set_lcd(2, 0);
272}
273
274static int
275tctrl_intr(arg)
276	void *arg;
277{
278	struct tctrl_softc *sc = arg;
279	unsigned int v, d;
280	int progress = 0;
281
282    again:
283	/* find out the cause(s) of the interrupt */
284	v = tctrl_read(sc, TS102_REG_UCTRL_STS) & TS102_UCTRL_STS_MASK;
285
286	/* clear the cause(s) of the interrupt */
287	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
288
289	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
290	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
291		v &= ~TS102_UCTRL_STS_TXNF_STA;
292		if (tctrl_read(sc, TS102_REG_UCTRL_INT) & TS102_UCTRL_INT_TXNF_REQ) {
293			tctrl_write(sc, TS102_REG_UCTRL_INT, 0);
294			progress = 1;
295		}
296	}
297	if ((v == 0) && ((sc->sc_flags & TCTRL_SEND_REQUEST) == 0 ||
298	    sc->sc_state != TCTRL_IDLE)) {
299		wakeup(sc);
300		return progress;
301	}
302
303	progress = 1;
304	if (v & TS102_UCTRL_STS_RXNE_STA) {
305		d = tctrl_read_data(sc);
306		switch (sc->sc_state) {
307		case TCTRL_IDLE:
308			if (d == 0xfa) {
309				/* external event */
310				callout_reset(&tctrl_event_ch, 1,
311				    tctrl_read_event_status, NULL);
312			} else {
313				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
314					sc->sc_dev.dv_xname, sc->sc_op, d);
315			}
316			goto again;
317		case TCTRL_ACK:
318			if (d != 0xfe) {
319				printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n",
320					sc->sc_dev.dv_xname, sc->sc_op, d);
321			}
322#ifdef TCTRLDEBUG
323			printf(" ack=0x%02x", d);
324#endif
325			sc->sc_rsplen--;
326			sc->sc_rspoff = 0;
327			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
328			sc->sc_wantdata = sc->sc_rsplen ? 1 : 0;
329#ifdef TCTRLDEBUG
330			if (sc->sc_rsplen > 0) {
331				printf(" [data(%u)]", sc->sc_rsplen);
332			} else {
333				printf(" [idle]\n");
334			}
335#endif
336			goto again;
337		case TCTRL_DATA:
338			sc->sc_rspbuf[sc->sc_rspoff++] = d;
339#ifdef TCTRLDEBUG
340			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
341#endif
342			if (sc->sc_rspoff == sc->sc_rsplen) {
343#ifdef TCTRLDEBUG
344				printf(" [idle]\n");
345#endif
346				sc->sc_state = TCTRL_IDLE;
347				sc->sc_wantdata = 0;
348			}
349			goto again;
350		default:
351			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
352			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
353			goto again;
354		}
355	}
356	if ((sc->sc_state == TCTRL_IDLE && sc->sc_wantdata == 0) ||
357	    sc->sc_flags & TCTRL_SEND_REQUEST) {
358		if (sc->sc_flags & TCTRL_SEND_REQUEST) {
359			sc->sc_flags &= ~TCTRL_SEND_REQUEST;
360			sc->sc_wantdata = 1;
361		}
362		if (sc->sc_cmdlen > 0) {
363			tctrl_write(sc, TS102_REG_UCTRL_INT,
364				tctrl_read(sc, TS102_REG_UCTRL_INT)
365				|TS102_UCTRL_INT_TXNF_MSK
366				|TS102_UCTRL_INT_TXNF_REQ);
367			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
368		}
369	}
370	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
371		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
372#ifdef TCTRLDEBUG
373		if (sc->sc_cmdoff == 1) {
374			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
375				sc->sc_cmdbuf[0], sc->sc_rsplen);
376		} else {
377			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
378				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
379		}
380#endif
381		if (sc->sc_cmdoff == sc->sc_cmdlen) {
382			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
383#ifdef TCTRLDEBUG
384			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
385#endif
386			if (sc->sc_cmdoff == 1) {
387				sc->sc_op = sc->sc_cmdbuf[0];
388			}
389			tctrl_write(sc, TS102_REG_UCTRL_INT,
390				tctrl_read(sc, TS102_REG_UCTRL_INT)
391				& (~TS102_UCTRL_INT_TXNF_MSK
392				   |TS102_UCTRL_INT_TXNF_REQ));
393		} else if (sc->sc_state == TCTRL_IDLE) {
394			sc->sc_op = sc->sc_cmdbuf[0];
395			sc->sc_state = TCTRL_ARGS;
396#ifdef TCTRLDEBUG
397			printf(" [args]");
398#endif
399		}
400	}
401	goto again;
402}
403
404static void
405tctrl_setup_bitport_nop(void)
406{
407	struct tctrl_softc *sc;
408	struct tctrl_req req;
409	int s;
410
411	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
412	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
413	req.cmdbuf[1] = 0xff;
414	req.cmdbuf[2] = 0;
415	req.cmdlen = 3;
416	req.rsplen = 2;
417	req.p = NULL;
418	tadpole_request(&req, 1);
419	s = splts102();
420	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
421	splx(s);
422}
423
424static void
425tctrl_setup_bitport(void)
426{
427	struct tctrl_softc *sc;
428	struct tctrl_req req;
429	int s;
430
431	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
432	s = splts102();
433	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
434	    || (!sc->sc_tft_on)) {
435		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
436	} else {
437		req.cmdbuf[2] = 0;
438	}
439	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
440	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
441	req.cmdlen = 3;
442	req.rsplen = 2;
443	req.p = NULL;
444	tadpole_request(&req, 1);
445	s = splts102();
446	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
447	splx(s);
448}
449
450/*
451 * The tadpole microcontroller is not preprogrammed with icon
452 * representations.  The machine boots with the DC-IN light as
453 * a blank (all 0x00) and the other lights, as 4 rows of horizontal
454 * bars.  The below code initializes the icons in the system to
455 * sane values.  Some of these icons could be used for any purpose
456 * desired, namely the pcmcia, LAN and WAN lights.  For the disk spinner,
457 * only the backslash is unprogrammed.  (sigh)
458 *
459 * programming the icons is simple.  It is a 5x8 matrix, which each row a
460 * bitfield in the order 0x10 0x08 0x04 0x02 0x01.
461 */
462
463static void
464tctrl_init_lcd(void)
465{
466	struct tctrl_req req;
467
468	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
469	req.cmdlen = 11;
470	req.rsplen = 1;
471	req.cmdbuf[1] = 0x08;	/*len*/
472	req.cmdbuf[2] = TS102_BLK_OFF_DEF_DC_GOOD;
473	req.cmdbuf[3] =  0x00;	/* ..... */
474	req.cmdbuf[4] =  0x00;	/* ..... */
475	req.cmdbuf[5] =  0x1f;	/* XXXXX */
476	req.cmdbuf[6] =  0x00;	/* ..... */
477	req.cmdbuf[7] =  0x15;	/* X.X.X */
478	req.cmdbuf[8] =  0x00;	/* ..... */
479	req.cmdbuf[9] =  0x00;	/* ..... */
480	req.cmdbuf[10] = 0x00;	/* ..... */
481	req.p = NULL;
482	tadpole_request(&req, 1);
483
484	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
485	req.cmdlen = 11;
486	req.rsplen = 1;
487	req.cmdbuf[1] = 0x08;	/*len*/
488	req.cmdbuf[2] = TS102_BLK_OFF_DEF_BACKSLASH;
489	req.cmdbuf[3] =  0x00;	/* ..... */
490	req.cmdbuf[4] =  0x10;	/* X.... */
491	req.cmdbuf[5] =  0x08;	/* .X... */
492	req.cmdbuf[6] =  0x04;	/* ..X.. */
493	req.cmdbuf[7] =  0x02;	/* ...X. */
494	req.cmdbuf[8] =  0x01;	/* ....X */
495	req.cmdbuf[9] =  0x00;	/* ..... */
496	req.cmdbuf[10] = 0x00;	/* ..... */
497	req.p = NULL;
498	tadpole_request(&req, 1);
499
500	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
501	req.cmdlen = 11;
502	req.rsplen = 1;
503	req.cmdbuf[1] = 0x08;	/*len*/
504	req.cmdbuf[2] = TS102_BLK_OFF_DEF_WAN1;
505	req.cmdbuf[3] =  0x0c;	/* .XXX. */
506	req.cmdbuf[4] =  0x16;	/* X.XX. */
507	req.cmdbuf[5] =  0x10;	/* X.... */
508	req.cmdbuf[6] =  0x15;	/* X.X.X */
509	req.cmdbuf[7] =  0x10;	/* X.... */
510	req.cmdbuf[8] =  0x16;	/* X.XX. */
511	req.cmdbuf[9] =  0x0c;	/* .XXX. */
512	req.cmdbuf[10] = 0x00;	/* ..... */
513	req.p = NULL;
514	tadpole_request(&req, 1);
515
516	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
517	req.cmdlen = 11;
518	req.rsplen = 1;
519	req.cmdbuf[1] = 0x08;	/*len*/
520	req.cmdbuf[2] = TS102_BLK_OFF_DEF_WAN2;
521	req.cmdbuf[3] =  0x0c;	/* .XXX. */
522	req.cmdbuf[4] =  0x0d;	/* .XX.X */
523	req.cmdbuf[5] =  0x01;	/* ....X */
524	req.cmdbuf[6] =  0x15;	/* X.X.X */
525	req.cmdbuf[7] =  0x01;	/* ....X */
526	req.cmdbuf[8] =  0x0d;	/* .XX.X */
527	req.cmdbuf[9] =  0x0c;	/* .XXX. */
528	req.cmdbuf[10] = 0x00;	/* ..... */
529	req.p = NULL;
530	tadpole_request(&req, 1);
531
532	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
533	req.cmdlen = 11;
534	req.rsplen = 1;
535	req.cmdbuf[1] = 0x08;	/*len*/
536	req.cmdbuf[2] = TS102_BLK_OFF_DEF_LAN1;
537	req.cmdbuf[3] =  0x00;	/* ..... */
538	req.cmdbuf[4] =  0x04;	/* ..X.. */
539	req.cmdbuf[5] =  0x08;	/* .X... */
540	req.cmdbuf[6] =  0x13;	/* X..XX */
541	req.cmdbuf[7] =  0x08;	/* .X... */
542	req.cmdbuf[8] =  0x04;	/* ..X.. */
543	req.cmdbuf[9] =  0x00;	/* ..... */
544	req.cmdbuf[10] = 0x00;	/* ..... */
545	req.p = NULL;
546	tadpole_request(&req, 1);
547
548	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
549	req.cmdlen = 11;
550	req.rsplen = 1;
551	req.cmdbuf[1] = 0x08;	/*len*/
552	req.cmdbuf[2] = TS102_BLK_OFF_DEF_LAN2;
553	req.cmdbuf[3] =  0x00;	/* ..... */
554	req.cmdbuf[4] =  0x04;	/* ..X.. */
555	req.cmdbuf[5] =  0x02;	/* ...X. */
556	req.cmdbuf[6] =  0x19;	/* XX..X */
557	req.cmdbuf[7] =  0x02;	/* ...X. */
558	req.cmdbuf[8] =  0x04;	/* ..X.. */
559	req.cmdbuf[9] =  0x00;	/* ..... */
560	req.cmdbuf[10] = 0x00;	/* ..... */
561	req.p = NULL;
562	tadpole_request(&req, 1);
563
564	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
565	req.cmdlen = 11;
566	req.rsplen = 1;
567	req.cmdbuf[1] = 0x08;	/*len*/
568	req.cmdbuf[2] = TS102_BLK_OFF_DEF_PCMCIA;
569	req.cmdbuf[3] =  0x00;	/* ..... */
570	req.cmdbuf[4] =  0x0c;	/* .XXX. */
571	req.cmdbuf[5] =  0x1f;	/* XXXXX */
572	req.cmdbuf[6] =  0x1f;	/* XXXXX */
573	req.cmdbuf[7] =  0x1f;	/* XXXXX */
574	req.cmdbuf[8] =  0x1f;	/* XXXXX */
575	req.cmdbuf[9] =  0x00;	/* ..... */
576	req.cmdbuf[10] = 0x00;	/* ..... */
577	req.p = NULL;
578	tadpole_request(&req, 1);
579}
580
581
582
583/*
584 * set the blinken-lights on the lcd.  what:
585 * what = 0 off,  what = 1 on,  what = 2 toggle
586 */
587
588void
589tctrl_set_lcd(what, which)
590	int what;
591	unsigned short which;
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	s = splts102();
599
600	/* provide a quick exit to save cpu time */
601	if ((what == 1 && sc->sc_lcdstate & which) ||
602	    (what == 0 && !(sc->sc_lcdstate & which))) {
603		splx(s);
604		return;
605	}
606	/*
607	 * the mask setup on this particular command is *very* bizzare
608	 * and totally undocumented.
609	 */
610	if ((what == 1) || (what == 2 && !(sc->sc_lcdstate & which))) {
611		req.cmdbuf[2] = (u_int8_t)(which&0xff);
612		req.cmdbuf[3] = (u_int8_t)(which>>8);
613	} else {
614		req.cmdbuf[2] = 0;
615		req.cmdbuf[3] = 0;
616	}
617	req.cmdbuf[0] = TS102_OP_CTL_LCD;
618	req.cmdbuf[4] = (u_int8_t)(~which>>8);
619	req.cmdbuf[1] = (u_int8_t)(~which&0xff);
620
621	/* XXX this thing is weird.... */
622	req.cmdlen = 3;
623	req.rsplen = 2;
624#if 0
625	req.cmdlen = 5;
626	req.rsplen = 4;
627#endif
628	req.p = NULL;
629	tadpole_request(&req, 1);
630	s = splts102();
631	sc->sc_lcdstate = (unsigned short)req.rspbuf[0];
632	splx(s);
633}
634
635static void
636tctrl_read_ext_status(void)
637{
638	struct tctrl_softc *sc;
639	struct tctrl_req req;
640	int s;
641
642	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
643	req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
644	req.cmdlen = 1;
645	req.rsplen = 3;
646	req.p = NULL;
647#ifdef TCTRLDEBUG
648	printf("pre read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
649#endif
650	tadpole_request(&req, 1);
651	s = splts102();
652	sc->sc_ext_status = req.rspbuf[0] * 256 + req.rspbuf[1];
653	splx(s);
654#ifdef TCTRLDEBUG
655	printf("post read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
656#endif
657}
658
659/*
660 * return 0 if the user will notice and handle the event,
661 * return 1 if the kernel driver should do so.
662 */
663static int
664tctrl_apm_record_event(sc, event_type)
665	struct tctrl_softc *sc;
666	u_int event_type;
667{
668	struct apm_event_info *evp;
669
670	if ((sc->sc_flags & TCTRL_APM_CTLOPEN) &&
671	    (sc->sc_event_count < APM_NEVENTS)) {
672		evp = &sc->sc_event_list[sc->sc_event_ptr];
673		sc->sc_event_count++;
674		sc->sc_event_ptr++;
675		sc->sc_event_ptr %= APM_NEVENTS;
676		evp->type = event_type;
677		evp->index = ++tctrl_apm_evindex;
678		selnotify(&sc->sc_rsel, 0);
679		return(sc->sc_flags & TCTRL_APM_CTLOPEN) ? 0 : 1;
680	}
681	return(1);
682}
683
684static void
685tctrl_read_event_status(arg)
686	void *arg;
687{
688	struct tctrl_softc *sc;
689	struct tctrl_req req;
690	int s;
691	unsigned int v;
692
693	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
694	req.cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
695	req.cmdlen = 1;
696	req.rsplen = 3;
697	req.p = NULL;
698	tadpole_request(&req, 1);
699	s = splts102();
700	v = req.rspbuf[0] * 256 + req.rspbuf[1];
701	if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
702		printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
703	}
704	if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
705/*printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);*/
706/* according to a tadpole header, and observation */
707#ifdef TCTRLDEBUG
708		printf("%s: Battery charge level change\n", sc->sc_dev.dv_xname);
709#endif
710	}
711	if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
712		if (tctrl_apm_record_event(sc, APM_BATTERY_LOW))
713			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
714	}
715	if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
716		splx(s);
717		tctrl_read_ext_status();
718		s = splts102();
719		if (tctrl_apm_record_event(sc, APM_POWER_CHANGE))
720			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
721			    (sc->sc_ext_status &
722			    TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ?
723			    "restored" : "removed");
724	}
725	if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
726		splx(s);
727		tctrl_read_ext_status();
728		tctrl_setup_bitport();
729#ifdef TCTRLDEBUG
730		printf("%s: lid %s\n", sc->sc_dev.dv_xname,
731		    (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
732		    ? "closed" : "opened");
733#endif
734	}
735	splx(s);
736}
737
738void
739tadpole_request(req, spin)
740	struct tctrl_req *req;
741	int spin;
742{
743	struct tctrl_softc *sc;
744	int i, s;
745
746	if (tctrl_cd.cd_devs == NULL
747	    || tctrl_cd.cd_ndevs == 0
748	    || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) {
749		return;
750	}
751
752	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
753	while (sc->sc_wantdata != 0) {
754		if (req->p != NULL)
755			tsleep(&sc->sc_wantdata, PLOCK, "tctrl_lock", 10);
756		else
757			DELAY(1);
758	}
759	if (spin)
760		s = splhigh();
761	else
762		s = splts102();
763	sc->sc_flags |= TCTRL_SEND_REQUEST;
764	memcpy(sc->sc_cmdbuf, req->cmdbuf, req->cmdlen);
765	sc->sc_wantdata = 1;
766	sc->sc_rsplen = req->rsplen;
767	sc->sc_cmdlen = req->cmdlen;
768	sc->sc_cmdoff = sc->sc_rspoff = 0;
769
770	/* we spin for certain commands, like poweroffs */
771	if (spin) {
772/*		for (i = 0; i < 30000; i++) {*/
773		while (sc->sc_wantdata == 1) {
774			tctrl_intr(sc);
775			DELAY(1);
776		}
777	} else {
778		tctrl_intr(sc);
779		i = 0;
780		while (((sc->sc_rspoff != sc->sc_rsplen) ||
781		    (sc->sc_cmdoff != sc->sc_cmdlen)) &&
782		    (i < (5 * sc->sc_rsplen + sc->sc_cmdlen)))
783			if (req->p != NULL) {
784				tsleep(sc, PWAIT, "tctrl_data", 15);
785				i++;
786			}
787			else
788				DELAY(1);
789	}
790	/*
791	 * we give the user a reasonable amount of time for a command
792	 * to complete.  If it doesn't complete in time, we hand them
793	 * garbage.  This is here to stop things like setting the
794	 * rsplen too long, and sleeping forever in a CMD_REQ ioctl.
795	 */
796	sc->sc_wantdata = 0;
797	memcpy(req->rspbuf, sc->sc_rspbuf, req->rsplen);
798	splx(s);
799}
800
801void
802tadpole_powerdown(void)
803{
804	struct tctrl_req req;
805
806	req.cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
807	req.cmdlen = 1;
808	req.rsplen = 1;
809	req.p = NULL;
810	tadpole_request(&req, 1);
811}
812
813void
814tadpole_set_video(enabled)
815	int enabled;
816{
817	struct tctrl_softc *sc;
818	struct tctrl_req req;
819	int s;
820
821	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
822	while (sc->sc_wantdata != 0)
823		DELAY(1);
824	s = splts102();
825	req.p = NULL;
826	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN && !enabled)
827	    || (sc->sc_tft_on)) {
828		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
829	} else {
830		req.cmdbuf[2] = 0;
831	}
832	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
833	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
834	req.cmdlen = 3;
835	req.rsplen = 2;
836
837	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
838		sc->sc_tft_on = enabled;
839		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
840			splx(s);
841			return;
842		}
843		tadpole_request(&req, 1);
844		sc->sc_bitport =
845		    (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
846	}
847	splx(s);
848}
849
850static void
851tctrl_write_data(sc, v)
852	struct tctrl_softc *sc;
853	u_int8_t v;
854{
855	unsigned int i;
856
857	for (i = 0; i < 100; i++)  {
858		if (TS102_UCTRL_STS_TXNF_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
859			break;
860	}
861	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
862}
863
864static u_int8_t
865tctrl_read_data(sc)
866	struct tctrl_softc *sc;
867{
868	unsigned int i, v;
869
870	for (i = 0; i < 100000; i++) {
871		if (TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
872			break;
873		DELAY(1);
874	}
875
876	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
877	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
878	return v;
879}
880
881static u_int8_t
882tctrl_read(sc, off)
883	struct tctrl_softc *sc;
884	bus_size_t off;
885{
886
887	sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off);
888	return sc->sc_junk;
889}
890
891static void
892tctrl_write(sc, off, v)
893	struct tctrl_softc *sc;
894	bus_size_t off;
895	u_int8_t v;
896{
897
898	sc->sc_junk = v;
899	bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v);
900}
901
902int
903tctrlopen(dev, flags, mode, p)
904	dev_t dev;
905	int flags, mode;
906	struct proc *p;
907{
908	int unit = (minor(dev)&0xf0);
909	int ctl = (minor(dev)&0x0f);
910	struct tctrl_softc *sc;
911
912	if (unit >= tctrl_cd.cd_ndevs)
913		return(ENXIO);
914	sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
915	if (!sc)
916		return(ENXIO);
917
918	switch (ctl) {
919	case TCTRL_STD_DEV:
920		break;
921	case TCTRL_APMCTL_DEV:
922		if (!(flags & FWRITE))
923			return(EINVAL);
924		if (sc->sc_flags & TCTRL_APM_CTLOPEN)
925			return(EBUSY);
926		sc->sc_flags |= TCTRL_APM_CTLOPEN;
927		break;
928	default:
929		return(ENXIO);
930		break;
931	}
932
933	return(0);
934}
935
936int
937tctrlclose(dev, flags, mode, p)
938	dev_t dev;
939	int flags, mode;
940	struct proc *p;
941{
942	int ctl = (minor(dev)&0x0f);
943	struct tctrl_softc *sc;
944
945	sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
946	if (!sc)
947		return(ENXIO);
948
949	switch (ctl) {
950	case TCTRL_STD_DEV:
951		break;
952	case TCTRL_APMCTL_DEV:
953		sc->sc_flags &= ~TCTRL_APM_CTLOPEN;
954		break;
955	}
956	return(0);
957}
958
959int
960tctrlioctl(dev, cmd, data, flags, p)
961        dev_t dev;
962        u_long cmd;
963        caddr_t data;
964        int flags;
965        struct proc *p;
966{
967	struct tctrl_req req, *reqn;
968	struct tctrl_pwr *pwrreq;
969	envsys_range_t *envrange;
970	envsys_temp_data_t *envdata;
971	envsys_temp_info_t *envinfo;
972	struct apm_power_info *powerp;
973	struct apm_event_info *evp;
974	struct tctrl_softc *sc;
975	int i;
976	u_int j;
977	u_int16_t a;
978	u_int8_t c;
979
980	if (tctrl_cd.cd_devs == NULL
981	    || tctrl_cd.cd_ndevs == 0
982	    || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) {
983		return ENXIO;
984	}
985	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
986        switch (cmd) {
987
988	case APM_IOC_STANDBY:
989		return(EOPNOTSUPP); /* for now */
990
991	case APM_IOC_SUSPEND:
992		return(EOPNOTSUPP); /* for now */
993
994	case OAPM_IOC_GETPOWER:
995	case APM_IOC_GETPOWER:
996		powerp = (struct apm_power_info *)data;
997		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_RATE;
998		req.cmdlen = 1;
999		req.rsplen = 2;
1000		req.p = p;
1001		tadpole_request(&req, 0);
1002		if (req.rspbuf[0] > 0x00)
1003			powerp->battery_state = APM_BATT_CHARGING;
1004		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_LEVEL;
1005		req.cmdlen = 1;
1006		req.rsplen = 3;
1007		req.p = p;
1008		tadpole_request(&req, 0);
1009		c = req.rspbuf[0];
1010		powerp->battery_life = c;
1011		if (c > 0x70)	/* the tadpole sometimes dips below zero, and */
1012			c = 0;	/* into the 255 range. */
1013		powerp->minutes_left = (45 * c) / 100; /* XXX based on 45 min */
1014		if (powerp->battery_state != APM_BATT_CHARGING) {
1015			if (c < 0x20)
1016				powerp->battery_state = APM_BATT_CRITICAL;
1017			else if (c < 0x40)
1018				powerp->battery_state = APM_BATT_LOW;
1019			else if (c < 0x66)
1020				powerp->battery_state = APM_BATT_HIGH;
1021			else
1022				powerp->battery_state = APM_BATT_UNKNOWN;
1023		}
1024		req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
1025		req.cmdlen = 1;
1026		req.rsplen = 3;
1027		req.p = p;
1028		tadpole_request(&req, 0);
1029		a = req.rspbuf[0] * 256 + req.rspbuf[1];
1030		if (a & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE)
1031			powerp->ac_state = APM_AC_ON;
1032		else
1033			powerp->ac_state = APM_AC_OFF;
1034		break;
1035
1036	case APM_IOC_NEXTEVENT:
1037		if (!sc->sc_event_count)
1038			return EAGAIN;
1039
1040		evp = (struct apm_event_info *)data;
1041		i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
1042		i %= APM_NEVENTS;
1043		*evp = sc->sc_event_list[i];
1044		sc->sc_event_count--;
1045		return(0);
1046
1047	/* this ioctl assumes the caller knows exactly what he is doing */
1048	case TCTRL_CMD_REQ:
1049		reqn = (struct tctrl_req *)data;
1050		if ((i = suser(p->p_ucred, &p->p_acflag)) != 0 &&
1051		    (reqn->cmdbuf[0] == TS102_OP_CTL_BITPORT ||
1052		    (reqn->cmdbuf[0] >= TS102_OP_CTL_WATCHDOG &&
1053		    reqn->cmdbuf[0] <= TS102_OP_CTL_SECURITY_KEY) ||
1054		    reqn->cmdbuf[0] == TS102_OP_CTL_TIMEZONE ||
1055		    reqn->cmdbuf[0] == TS102_OP_CTL_DIAGNOSTIC_MODE ||
1056		    reqn->cmdbuf[0] == TS102_OP_CMD_SOFTWARE_RESET ||
1057		    (reqn->cmdbuf[0] >= TS102_OP_CMD_SET_RTC &&
1058		    reqn->cmdbuf[0] < TS102_OP_RD_INT_CHARGE_LEVEL) ||
1059		    reqn->cmdbuf[0] > TS102_OP_RD_EXT_CHARGE_LEVEL))
1060			return(i);
1061		reqn->p = p;
1062		tadpole_request(reqn, 0);
1063		break;
1064
1065	case ENVSYS_VERSION:
1066		*(int32_t *)data = 1000;
1067		break;
1068
1069	case ENVSYS_GRANGE:
1070		envrange = (envsys_range_t *)data;
1071		i = 0;
1072		envrange->high = envrange->low = 0;
1073		for (j=0; j < ENVSYS_NUMSENSORS; j++) {
1074			if (!i && envrange->units == sc->sc_esensors[j].units) {
1075				envrange->low = j;
1076				i++;
1077			}
1078			if (i && envrange->units == sc->sc_esensors[j].units)
1079				envrange->high = j;
1080		}
1081		if (!i) {
1082			envrange->high = 0;
1083			envrange->low = 1;
1084		}
1085		break;
1086
1087	case ENVSYS_GTREDATA:
1088		envdata = (envsys_temp_data_t *)data;
1089		if (envdata->sensor >= ENVSYS_NUMSENSORS) {
1090			envdata->validflags = 0;
1091			break;
1092		}
1093		envdata->warnflags = ENVSYS_WARN_OK;
1094		if (envdata->sensor == 0) {
1095			envdata->validflags |= ENVSYS_FVALID;
1096			req.cmdbuf[0] = TS102_OP_RD_CURRENT_TEMP;
1097			req.cmdlen = 1;
1098			req.rsplen = 2;
1099			req.p = p;
1100			tadpole_request(&req, 0);
1101			envdata->cur.data_us =             /* 273160? */
1102			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000000/9+273150000);
1103			envdata->validflags |= ENVSYS_FCURVALID;
1104			req.cmdbuf[0] = TS102_OP_RD_MAX_TEMP;
1105			req.cmdlen = 1;
1106			req.rsplen = 2;
1107			req.p = p;
1108			tadpole_request(&req, 0);
1109			envdata->max.data_us =
1110			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000000/9+273150000);
1111			envdata->validflags |= ENVSYS_FMAXVALID;
1112			req.cmdbuf[0] = TS102_OP_RD_MIN_TEMP;
1113			req.cmdlen = 1;
1114			req.rsplen = 2;
1115			req.p = p;
1116			tadpole_request(&req, 0);
1117			envdata->min.data_us =
1118			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000000/9+273150000);
1119			envdata->validflags |= ENVSYS_FMINVALID;
1120			envdata->units = sc->sc_esensors[envdata->sensor].units;
1121			break;
1122		} else if (envdata->sensor == 1 || envdata->sensor == 2) {
1123			envdata->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
1124			envdata->units = sc->sc_esensors[envdata->sensor].units;
1125			if (envdata->sensor == 1)
1126				req.cmdbuf[0] = TS102_OP_RD_INT_BATT_VLT;
1127			else
1128				req.cmdbuf[0] = TS102_OP_RD_DC_IN_VLT;
1129			req.cmdlen = 1;
1130			req.rsplen = 2;
1131			req.p = p;
1132			tadpole_request(&req, 0);
1133			envdata->cur.data_s = (int32_t)req.rspbuf[0]*1000000/11;
1134			break;
1135		}
1136		break;
1137
1138        case ENVSYS_GTREINFO:
1139		envinfo = (envsys_temp_info_t *)data;
1140		if (envinfo->sensor >= ENVSYS_NUMSENSORS) {
1141			envinfo->validflags = 0;
1142			break;
1143		}
1144		envinfo->units = sc->sc_esensors[envinfo->sensor].units;
1145		memcpy(envinfo->desc, sc->sc_esensors[envinfo->sensor].desc,
1146		    sizeof(sc->sc_esensors[envinfo->sensor].desc) >
1147		    sizeof(envinfo->desc) ? sizeof(envinfo->desc) :
1148		    sizeof(sc->sc_esensors[envinfo->sensor].desc));
1149		if (envinfo->units == ENVSYS_STEMP) {
1150			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID|
1151			    ENVSYS_FMINVALID|ENVSYS_FMAXVALID;
1152		} else if (envinfo->units == ENVSYS_SVOLTS_DC) {
1153			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
1154		} else
1155			envinfo->validflags = 0;
1156                break;
1157
1158        case ENVSYS_STREINFO:
1159		envinfo = (envsys_temp_info_t *)data;
1160		if (envinfo->sensor >= ENVSYS_NUMSENSORS) {
1161			envinfo->validflags = 0;
1162			break;
1163		}
1164		if (envinfo->units == sc->sc_esensors[envinfo->sensor].units)
1165			memcpy(sc->sc_esensors[envinfo->sensor].desc,
1166			    envinfo->desc,
1167			    sizeof(envinfo->desc) > sizeof(char)*32 ?
1168			    sizeof(char)*32 : sizeof(envinfo->desc) );
1169		if (envinfo->units == ENVSYS_STEMP) {
1170			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID|
1171			    ENVSYS_FMINVALID|ENVSYS_FMAXVALID;
1172		} else if (envinfo->units == ENVSYS_SVOLTS_DC) {
1173			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
1174		} else
1175			envinfo->validflags = 0;
1176                break;
1177
1178	/* serial power mode (via auxiotwo) */
1179	case TCTRL_SERIAL_PWR:
1180		pwrreq = (struct tctrl_pwr *)data;
1181		if (pwrreq->rw)
1182			pwrreq->state = auxiotwoserialgetapm();
1183		else
1184			auxiotwoserialsetapm(pwrreq->state);
1185		break;
1186
1187	/* modem power mode (via auxio) */
1188	case TCTRL_MODEM_PWR:
1189		return(EOPNOTSUPP); /* for now */
1190		break;
1191
1192
1193        default:
1194                return (ENOTTY);
1195        }
1196        return (0);
1197}
1198
1199int
1200tctrlpoll(dev, events, p)
1201	dev_t dev;
1202	int events;
1203	struct proc *p;
1204{
1205	struct tctrl_softc *sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
1206	int revents = 0;
1207
1208	if (events & (POLLIN | POLLRDNORM)) {
1209		if (sc->sc_event_count)
1210			revents |= events & (POLLIN | POLLRDNORM);
1211		else
1212			selrecord(p, &sc->sc_rsel);
1213	}
1214
1215	return (revents);
1216}
1217
1218static void
1219filt_tctrlrdetach(struct knote *kn)
1220{
1221	struct tctrl_softc *sc = kn->kn_hook;
1222	int s;
1223
1224	s = splts102();
1225	SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
1226	splx(s);
1227}
1228
1229static int
1230filt_tctrlread(struct knote *kn, long hint)
1231{
1232	struct tctrl_softc *sc = kn->kn_hook;
1233
1234	kn->kn_data = sc->sc_event_count;
1235	return (kn->kn_data > 0);
1236}
1237
1238static const struct filterops tctrlread_filtops =
1239	{ 1, NULL, filt_tctrlrdetach, filt_tctrlread };
1240
1241int
1242tctrlkqfilter(dev_t dev, struct knote *kn)
1243{
1244	struct tctrl_softc *sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
1245	struct klist *klist;
1246	int s;
1247
1248	switch (kn->kn_filter) {
1249	case EVFILT_READ:
1250		klist = &sc->sc_rsel.sel_klist;
1251		kn->kn_fop = &tctrlread_filtops;
1252		break;
1253
1254	default:
1255		return (1);
1256	}
1257
1258	kn->kn_hook = sc;
1259
1260	s = splts102();
1261	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
1262	splx(s);
1263
1264	return (0);
1265}
1266
1267/* DO NOT SET THIS OPTION */
1268#ifdef TADPOLE_BLINK
1269void
1270cpu_disk_unbusy(busy)
1271        int busy;
1272{
1273	static struct timeval tctrl_ds_timestamp;
1274        struct timeval dv_time, diff_time;
1275	struct tctrl_softc *sc;
1276
1277	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
1278
1279	/* quickly bail */
1280	if (!(sc->sc_lcdstate & TS102_LCD_DISK_ACTIVE) || busy > 0)
1281		return;
1282
1283        /* we aren't terribly concerned with precision here */
1284        dv_time = mono_time;
1285        timersub(&dv_time, &tctrl_ds_timestamp, &diff_time);
1286
1287	if (diff_time.tv_sec > 0) {
1288                tctrl_set_lcd(0, TS102_LCD_DISK_ACTIVE);
1289		tctrl_ds_timestamp = mono_time;
1290	}
1291}
1292#endif
1293