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