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