tctrl.c revision 1.50
1/*	$NetBSD: tctrl.c,v 1.50 2009/11/21 04:16:51 rmind Exp $	*/
2
3/*-
4 * Copyright (c) 1998, 2005, 2006 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: tctrl.c,v 1.50 2009/11/21 04:16:51 rmind Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/ioctl.h>
38#include <sys/select.h>
39#include <sys/tty.h>
40#include <sys/proc.h>
41#include <sys/conf.h>
42#include <sys/file.h>
43#include <sys/uio.h>
44#include <sys/kernel.h>
45#include <sys/kthread.h>
46#include <sys/syslog.h>
47#include <sys/types.h>
48#include <sys/device.h>
49#include <sys/envsys.h>
50#include <sys/poll.h>
51#include <sys/kauth.h>
52
53#include <machine/apmvar.h>
54#include <machine/autoconf.h>
55#include <machine/bus.h>
56#include <machine/intr.h>
57#include <machine/tctrl.h>
58
59#include <sparc/dev/ts102reg.h>
60#include <sparc/dev/tctrlvar.h>
61#include <sparc/sparc/auxiotwo.h>
62#include <sparc/sparc/auxreg.h>
63
64#include <dev/sysmon/sysmonvar.h>
65#include <dev/sysmon/sysmon_taskq.h>
66
67#include "sysmon_envsys.h"
68
69/*#define TCTRLDEBUG*/
70
71/* disk spinner */
72#include <sys/disk.h>
73#include <dev/scsipi/sdvar.h>
74
75/* ethernet carrier */
76#include <net/if.h>
77#include <net/if_dl.h>
78#include <net/if_ether.h>
79#include <net/if_media.h>
80#include <dev/ic/lancevar.h>
81
82extern struct cfdriver tctrl_cd;
83
84dev_type_open(tctrlopen);
85dev_type_close(tctrlclose);
86dev_type_ioctl(tctrlioctl);
87dev_type_poll(tctrlpoll);
88dev_type_kqfilter(tctrlkqfilter);
89
90const struct cdevsw tctrl_cdevsw = {
91	tctrlopen, tctrlclose, noread, nowrite, tctrlioctl,
92	nostop, notty, tctrlpoll, nommap, tctrlkqfilter,
93};
94
95static const char *tctrl_ext_statuses[16] = {
96	"main power available",
97	"internal battery attached",
98	"external battery attached",
99	"external VGA attached",
100	"external keyboard attached",
101	"external mouse attached",
102	"lid down",
103	"internal battery charging",
104	"external battery charging",
105	"internal battery discharging",
106	"external battery discharging",
107};
108
109struct tctrl_softc {
110	struct	device sc_dev;
111	bus_space_tag_t	sc_memt;
112	bus_space_handle_t	sc_memh;
113	unsigned int	sc_junk;
114	unsigned int	sc_ext_status;
115	unsigned int	sc_flags;
116#define TCTRL_SEND_REQUEST		0x0001
117#define TCTRL_APM_CTLOPEN		0x0002
118	uint32_t	sc_wantdata;
119	uint32_t	sc_ext_pending;
120	volatile uint16_t	sc_lcdstate;
121	uint16_t	sc_lcdwanted;
122
123	enum { TCTRL_IDLE, TCTRL_ARGS,
124		TCTRL_ACK, TCTRL_DATA } sc_state;
125	uint8_t		sc_cmdbuf[16];
126	uint8_t		sc_rspbuf[16];
127	uint8_t		sc_bitport;
128	uint8_t		sc_tft_on;
129	uint8_t		sc_op;
130	uint8_t		sc_cmdoff;
131	uint8_t		sc_cmdlen;
132	uint8_t		sc_rspoff;
133	uint8_t		sc_rsplen;
134	/* APM stuff */
135#define APM_NEVENTS 16
136	struct	apm_event_info sc_event_list[APM_NEVENTS];
137	int	sc_event_count;
138	int	sc_event_ptr;
139	struct	selinfo sc_rsel;
140
141	/* ENVSYS stuff */
142#define ENVSYS_NUMSENSORS 3
143	struct	evcnt sc_intrcnt;	/* interrupt counting */
144	struct	sysmon_envsys *sc_sme;
145	envsys_data_t sc_sensor[ENVSYS_NUMSENSORS];
146
147	struct	sysmon_pswitch sc_sm_pbutton;	/* power button */
148	struct	sysmon_pswitch sc_sm_lid;	/* lid state */
149	struct	sysmon_pswitch sc_sm_ac;	/* AC adaptor presence */
150	int	sc_powerpressed;
151
152	/* hardware status stuff */
153	int sc_lid;	/* 1 - open, 0 - closed */
154	int sc_power_state;
155	int sc_spl;
156
157	/*
158	 * we call this when we detect connection or removal of an external
159	 * monitor. 0 for no monitor, !=0 for monitor present
160	 */
161	void (*sc_video_callback)(void *, int);
162	void *sc_video_callback_cookie;
163	int sc_extvga;
164
165	uint32_t sc_events;
166	lwp_t *sc_thread;			/* event thread */
167	kmutex_t sc_requestlock;
168};
169
170#define TCTRL_STD_DEV		0
171#define TCTRL_APMCTL_DEV	8
172
173static int tctrl_match(struct device *, struct cfdata *, void *);
174static void tctrl_attach(struct device *, struct device *, void *);
175static void tctrl_write(struct tctrl_softc *, bus_size_t, uint8_t);
176static uint8_t tctrl_read(struct tctrl_softc *, bus_size_t);
177static void tctrl_write_data(struct tctrl_softc *, uint8_t);
178static uint8_t tctrl_read_data(struct tctrl_softc *);
179static int tctrl_intr(void *);
180static void tctrl_setup_bitport(void);
181static void tctrl_setup_bitport_nop(void);
182static void tctrl_read_ext_status(void);
183static void tctrl_read_event_status(struct tctrl_softc *);
184static int tctrl_apm_record_event(struct tctrl_softc *, u_int);
185static void tctrl_init_lcd(void);
186
187static void tctrl_sensor_setup(struct tctrl_softc *);
188static void tctrl_refresh(struct sysmon_envsys *, envsys_data_t *);
189
190static void tctrl_power_button_pressed(void *);
191static void tctrl_lid_state(struct tctrl_softc *);
192static void tctrl_ac_state(struct tctrl_softc *);
193
194static int tctrl_powerfail(void *);
195
196static void tctrl_event_thread(void *);
197void tctrl_update_lcd(struct tctrl_softc *);
198
199static void tctrl_lock(struct tctrl_softc *);
200static void tctrl_unlock(struct tctrl_softc *);
201
202CFATTACH_DECL(tctrl, sizeof(struct tctrl_softc),
203    tctrl_match, tctrl_attach, NULL, NULL);
204
205static int tadpole_request(struct tctrl_req *, int, int);
206
207/* XXX wtf is this? see i386/apm.c */
208int tctrl_apm_evindex;
209
210static int
211tctrl_match(struct device *parent, struct cfdata *cf, void *aux)
212{
213	union obio_attach_args *uoba = aux;
214	struct sbus_attach_args *sa = &uoba->uoba_sbus;
215
216	if (uoba->uoba_isobio4 != 0) {
217		return (0);
218	}
219
220	/* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
221	 * (who's interface is off the TS102 PCMCIA controller but there
222	 * exists a OpenProm for microcontroller interface).
223	 */
224	return strcmp("uctrl", sa->sa_name) == 0;
225}
226
227static void
228tctrl_attach(struct device *parent, struct device *self, void *aux)
229{
230	struct tctrl_softc *sc = device_private(self);
231	union obio_attach_args *uoba = aux;
232	struct sbus_attach_args *sa = &uoba->uoba_sbus;
233	unsigned int i, v;
234
235	/* We're living on a sbus slot that looks like an obio that
236	 * looks like an sbus slot.
237	 */
238	sc->sc_memt = sa->sa_bustag;
239	if (sbus_bus_map(sc->sc_memt,
240			 sa->sa_slot,
241			 sa->sa_offset - TS102_REG_UCTRL_INT,
242			 sa->sa_size,
243			 BUS_SPACE_MAP_LINEAR, &sc->sc_memh) != 0) {
244		printf(": can't map registers\n");
245		return;
246	}
247
248	printf("\n");
249
250	sc->sc_tft_on = 1;
251
252	/* clear any pending data.
253	 */
254	for (i = 0; i < 10000; i++) {
255		if ((TS102_UCTRL_STS_RXNE_STA &
256		    tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) {
257			break;
258		}
259		v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
260		tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
261	}
262
263	if (sa->sa_nintr != 0) {
264		(void)bus_intr_establish(sc->sc_memt, sa->sa_pri, IPL_NONE,
265					 tctrl_intr, sc);
266		evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
267				     sc->sc_dev.dv_xname, "intr");
268	}
269
270	/* See what the external status is */
271	sc->sc_ext_status = 0;
272	tctrl_read_ext_status();
273	if (sc->sc_ext_status != 0) {
274		const char *sep;
275
276		printf("%s: ", sc->sc_dev.dv_xname);
277		v = sc->sc_ext_status;
278		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
279			if (v & 1) {
280				printf("%s%s", sep, tctrl_ext_statuses[i]);
281				sep = ", ";
282			}
283		}
284		printf("\n");
285	}
286
287	/* Get a current of the control bitport */
288	tctrl_setup_bitport_nop();
289	tctrl_write(sc, TS102_REG_UCTRL_INT,
290		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK);
291	sc->sc_lid = (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) == 0;
292	sc->sc_power_state = PWR_RESUME;
293
294	sc->sc_extvga = (sc->sc_ext_status &
295	    TS102_EXT_STATUS_EXTERNAL_VGA_ATTACHED) != 0;
296	sc->sc_video_callback = NULL;
297
298
299	sc->sc_wantdata = 0;
300	sc->sc_event_count = 0;
301	sc->sc_ext_pending = 0;
302		sc->sc_ext_pending = 0;
303
304	mutex_init(&sc->sc_requestlock, MUTEX_DEFAULT, IPL_NONE);
305	selinit(&sc->sc_rsel);
306
307	/* setup sensors and register the power button */
308	tctrl_sensor_setup(sc);
309	tctrl_lid_state(sc);
310	tctrl_ac_state(sc);
311
312	/* initialize the LCD */
313	tctrl_init_lcd();
314
315	/* initialize sc_lcdstate */
316	sc->sc_lcdstate = 0;
317	sc->sc_lcdwanted = 0;
318	tadpole_set_lcd(2, 0);
319
320	/* fire up the LCD event thread */
321	sc->sc_events = 0;
322
323	if (kthread_create(PRI_NONE, 0, NULL, tctrl_event_thread, sc,
324	    &sc->sc_thread, "%s", sc->sc_dev.dv_xname) != 0) {
325		printf("%s: unable to create event kthread",
326		    sc->sc_dev.dv_xname);
327	}
328}
329
330static int
331tctrl_intr(void *arg)
332{
333	struct tctrl_softc *sc = arg;
334	unsigned int v, d;
335	int progress = 0;
336
337    again:
338	/* find out the cause(s) of the interrupt */
339	v = tctrl_read(sc, TS102_REG_UCTRL_STS) & TS102_UCTRL_STS_MASK;
340
341	/* clear the cause(s) of the interrupt */
342	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
343
344	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
345	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
346		v &= ~TS102_UCTRL_STS_TXNF_STA;
347		if (tctrl_read(sc, TS102_REG_UCTRL_INT) &
348		    TS102_UCTRL_INT_TXNF_REQ) {
349			tctrl_write(sc, TS102_REG_UCTRL_INT, 0);
350			progress = 1;
351		}
352	}
353	if ((v == 0) && ((sc->sc_flags & TCTRL_SEND_REQUEST) == 0 ||
354	    sc->sc_state != TCTRL_IDLE)) {
355		wakeup(sc);
356		return progress;
357	}
358
359	progress = 1;
360	if (v & TS102_UCTRL_STS_RXNE_STA) {
361		d = tctrl_read_data(sc);
362		switch (sc->sc_state) {
363		case TCTRL_IDLE:
364			if (d == 0xfa) {
365				/*
366				 * external event,
367				 * set a flag and wakeup the event thread
368				 */
369				sc->sc_ext_pending = 1;
370			} else {
371				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
372					sc->sc_dev.dv_xname, sc->sc_op, d);
373			}
374			goto again;
375		case TCTRL_ACK:
376			if (d != 0xfe) {
377				printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n",
378					sc->sc_dev.dv_xname, sc->sc_op, d);
379			}
380#ifdef TCTRLDEBUG
381			printf(" ack=0x%02x", d);
382#endif
383			sc->sc_rsplen--;
384			sc->sc_rspoff = 0;
385			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
386			sc->sc_wantdata = sc->sc_rsplen ? 1 : 0;
387#ifdef TCTRLDEBUG
388			if (sc->sc_rsplen > 0) {
389				printf(" [data(%u)]", sc->sc_rsplen);
390			} else {
391				printf(" [idle]\n");
392			}
393#endif
394			goto again;
395		case TCTRL_DATA:
396			sc->sc_rspbuf[sc->sc_rspoff++] = d;
397#ifdef TCTRLDEBUG
398			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
399#endif
400			if (sc->sc_rspoff == sc->sc_rsplen) {
401#ifdef TCTRLDEBUG
402				printf(" [idle]\n");
403#endif
404				sc->sc_state = TCTRL_IDLE;
405				sc->sc_wantdata = 0;
406			}
407			goto again;
408		default:
409			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
410			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
411			goto again;
412		}
413	}
414	if ((sc->sc_state == TCTRL_IDLE && sc->sc_wantdata == 0) ||
415	    sc->sc_flags & TCTRL_SEND_REQUEST) {
416		if (sc->sc_flags & TCTRL_SEND_REQUEST) {
417			sc->sc_flags &= ~TCTRL_SEND_REQUEST;
418			sc->sc_wantdata = 1;
419		}
420		if (sc->sc_cmdlen > 0) {
421			tctrl_write(sc, TS102_REG_UCTRL_INT,
422				tctrl_read(sc, TS102_REG_UCTRL_INT)
423				|TS102_UCTRL_INT_TXNF_MSK
424				|TS102_UCTRL_INT_TXNF_REQ);
425			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
426		}
427	}
428	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
429		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
430#ifdef TCTRLDEBUG
431		if (sc->sc_cmdoff == 1) {
432			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
433				sc->sc_cmdbuf[0], sc->sc_rsplen);
434		} else {
435			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
436				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
437		}
438#endif
439		if (sc->sc_cmdoff == sc->sc_cmdlen) {
440			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
441#ifdef TCTRLDEBUG
442			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
443#endif
444			if (sc->sc_cmdoff == 1) {
445				sc->sc_op = sc->sc_cmdbuf[0];
446			}
447			tctrl_write(sc, TS102_REG_UCTRL_INT,
448				tctrl_read(sc, TS102_REG_UCTRL_INT)
449				& (~TS102_UCTRL_INT_TXNF_MSK
450				   |TS102_UCTRL_INT_TXNF_REQ));
451		} else if (sc->sc_state == TCTRL_IDLE) {
452			sc->sc_op = sc->sc_cmdbuf[0];
453			sc->sc_state = TCTRL_ARGS;
454#ifdef TCTRLDEBUG
455			printf(" [args]");
456#endif
457		}
458	}
459	goto again;
460}
461
462static void
463tctrl_setup_bitport_nop(void)
464{
465	struct tctrl_softc *sc;
466	struct tctrl_req req;
467	int s;
468
469	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
470	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
471	req.cmdbuf[1] = 0xff;
472	req.cmdbuf[2] = 0x00;
473	req.cmdlen = 3;
474	req.rsplen = 2;
475	tadpole_request(&req, 1, 0);
476	s = splts102();
477	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
478	splx(s);
479}
480
481static void
482tctrl_setup_bitport(void)
483{
484	struct tctrl_softc *sc;
485	struct tctrl_req req;
486	int s;
487
488	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
489	s = splts102();
490	req.cmdbuf[2] = 0;
491	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
492	    || (!sc->sc_tft_on)) {
493		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
494	}
495	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
496	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
497	req.cmdlen = 3;
498	req.rsplen = 2;
499	tadpole_request(&req, 1, 0);
500	s = splts102();
501	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
502	splx(s);
503}
504
505/*
506 * The tadpole microcontroller is not preprogrammed with icon
507 * representations.  The machine boots with the DC-IN light as
508 * a blank (all 0x00) and the other lights, as 4 rows of horizontal
509 * bars.  The below code initializes the icons in the system to
510 * sane values.  Some of these icons could be used for any purpose
511 * desired, namely the pcmcia, LAN and WAN lights.  For the disk spinner,
512 * only the backslash is unprogrammed.  (sigh)
513 *
514 * programming the icons is simple.  It is a 5x8 matrix, which each row a
515 * bitfield in the order 0x10 0x08 0x04 0x02 0x01.
516 */
517
518static void
519tctrl_init_lcd(void)
520{
521	struct tctrl_req req;
522
523	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
524	req.cmdlen = 11;
525	req.rsplen = 1;
526	req.cmdbuf[1] = 0x08;	/*len*/
527	req.cmdbuf[2] = TS102_BLK_OFF_DEF_DC_GOOD;
528	req.cmdbuf[3] =  0x00;	/* ..... */
529	req.cmdbuf[4] =  0x00;	/* ..... */
530	req.cmdbuf[5] =  0x1f;	/* XXXXX */
531	req.cmdbuf[6] =  0x00;	/* ..... */
532	req.cmdbuf[7] =  0x15;	/* X.X.X */
533	req.cmdbuf[8] =  0x00;	/* ..... */
534	req.cmdbuf[9] =  0x00;	/* ..... */
535	req.cmdbuf[10] = 0x00;	/* ..... */
536	tadpole_request(&req, 1, 0);
537
538	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
539	req.cmdlen = 11;
540	req.rsplen = 1;
541	req.cmdbuf[1] = 0x08;	/*len*/
542	req.cmdbuf[2] = TS102_BLK_OFF_DEF_BACKSLASH;
543	req.cmdbuf[3] =  0x00;	/* ..... */
544	req.cmdbuf[4] =  0x10;	/* X.... */
545	req.cmdbuf[5] =  0x08;	/* .X... */
546	req.cmdbuf[6] =  0x04;	/* ..X.. */
547	req.cmdbuf[7] =  0x02;	/* ...X. */
548	req.cmdbuf[8] =  0x01;	/* ....X */
549	req.cmdbuf[9] =  0x00;	/* ..... */
550	req.cmdbuf[10] = 0x00;	/* ..... */
551	tadpole_request(&req, 1, 0);
552
553	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
554	req.cmdlen = 11;
555	req.rsplen = 1;
556	req.cmdbuf[1] = 0x08;	/*len*/
557	req.cmdbuf[2] = TS102_BLK_OFF_DEF_WAN1;
558	req.cmdbuf[3] =  0x0c;	/* .XXX. */
559	req.cmdbuf[4] =  0x16;	/* X.XX. */
560	req.cmdbuf[5] =  0x10;	/* X.... */
561	req.cmdbuf[6] =  0x15;	/* X.X.X */
562	req.cmdbuf[7] =  0x10;	/* X.... */
563	req.cmdbuf[8] =  0x16;	/* X.XX. */
564	req.cmdbuf[9] =  0x0c;	/* .XXX. */
565	req.cmdbuf[10] = 0x00;	/* ..... */
566	tadpole_request(&req, 1, 0);
567
568	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
569	req.cmdlen = 11;
570	req.rsplen = 1;
571	req.cmdbuf[1] = 0x08;	/*len*/
572	req.cmdbuf[2] = TS102_BLK_OFF_DEF_WAN2;
573	req.cmdbuf[3] =  0x0c;	/* .XXX. */
574	req.cmdbuf[4] =  0x0d;	/* .XX.X */
575	req.cmdbuf[5] =  0x01;	/* ....X */
576	req.cmdbuf[6] =  0x15;	/* X.X.X */
577	req.cmdbuf[7] =  0x01;	/* ....X */
578	req.cmdbuf[8] =  0x0d;	/* .XX.X */
579	req.cmdbuf[9] =  0x0c;	/* .XXX. */
580	req.cmdbuf[10] = 0x00;	/* ..... */
581	tadpole_request(&req, 1, 0);
582
583	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
584	req.cmdlen = 11;
585	req.rsplen = 1;
586	req.cmdbuf[1] = 0x08;	/*len*/
587	req.cmdbuf[2] = TS102_BLK_OFF_DEF_LAN1;
588	req.cmdbuf[3] =  0x00;	/* ..... */
589	req.cmdbuf[4] =  0x04;	/* ..X.. */
590	req.cmdbuf[5] =  0x08;	/* .X... */
591	req.cmdbuf[6] =  0x13;	/* X..XX */
592	req.cmdbuf[7] =  0x08;	/* .X... */
593	req.cmdbuf[8] =  0x04;	/* ..X.. */
594	req.cmdbuf[9] =  0x00;	/* ..... */
595	req.cmdbuf[10] = 0x00;	/* ..... */
596	tadpole_request(&req, 1, 0);
597
598	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
599	req.cmdlen = 11;
600	req.rsplen = 1;
601	req.cmdbuf[1] = 0x08;	/*len*/
602	req.cmdbuf[2] = TS102_BLK_OFF_DEF_LAN2;
603	req.cmdbuf[3] =  0x00;	/* ..... */
604	req.cmdbuf[4] =  0x04;	/* ..X.. */
605	req.cmdbuf[5] =  0x02;	/* ...X. */
606	req.cmdbuf[6] =  0x19;	/* XX..X */
607	req.cmdbuf[7] =  0x02;	/* ...X. */
608	req.cmdbuf[8] =  0x04;	/* ..X.. */
609	req.cmdbuf[9] =  0x00;	/* ..... */
610	req.cmdbuf[10] = 0x00;	/* ..... */
611	tadpole_request(&req, 1, 0);
612
613	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
614	req.cmdlen = 11;
615	req.rsplen = 1;
616	req.cmdbuf[1] = 0x08;	/*len*/
617	req.cmdbuf[2] = TS102_BLK_OFF_DEF_PCMCIA;
618	req.cmdbuf[3] =  0x00;	/* ..... */
619	req.cmdbuf[4] =  0x0c;	/* .XXX. */
620	req.cmdbuf[5] =  0x1f;	/* XXXXX */
621	req.cmdbuf[6] =  0x1f;	/* XXXXX */
622	req.cmdbuf[7] =  0x1f;	/* XXXXX */
623	req.cmdbuf[8] =  0x1f;	/* XXXXX */
624	req.cmdbuf[9] =  0x00;	/* ..... */
625	req.cmdbuf[10] = 0x00;	/* ..... */
626	tadpole_request(&req, 1, 0);
627}
628
629/* sc_lcdwanted -> lcd_state */
630void
631tctrl_update_lcd(struct tctrl_softc *sc)
632{
633	struct tctrl_req req;
634	int s;
635
636	s = splhigh();
637	if (sc->sc_lcdwanted == sc->sc_lcdstate) {
638		splx(s);
639		return;
640	}
641	sc->sc_lcdstate = sc->sc_lcdwanted;
642	splx(s);
643
644	/*
645	 * the mask setup on this particular command is *very* bizzare
646	 * and totally undocumented.
647	 */
648	req.cmdbuf[0] = TS102_OP_CTL_LCD;
649
650	/* leave caps-lock alone */
651	req.cmdbuf[2] = (u_int8_t)(sc->sc_lcdstate & 0xfe);
652	req.cmdbuf[3] = (u_int8_t)((sc->sc_lcdstate & 0x100)>>8);
653
654	req.cmdbuf[1] = 1;
655	req.cmdbuf[4] = 0;
656
657
658	/* XXX this thing is weird.... */
659	req.cmdlen = 3;
660	req.rsplen = 2;
661
662	/* below are the values one would expect but which won't work */
663#if 0
664	req.cmdlen = 5;
665	req.rsplen = 4;
666#endif
667	tadpole_request(&req, 1, 0);
668}
669
670
671/*
672 * set the blinken-lights on the lcd.  what:
673 * what = 0 off,  what = 1 on,  what = 2 toggle
674 */
675
676void
677tadpole_set_lcd(int what, unsigned short which)
678{
679	struct tctrl_softc *sc;
680	int s;
681
682	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
683
684	s = splhigh();
685	switch (what) {
686		case 0:
687			sc->sc_lcdwanted &= ~which;
688			break;
689		case 1:
690			sc->sc_lcdwanted |= which;
691			break;
692		case 2:
693			sc->sc_lcdwanted ^= which;
694			break;
695	}
696	splx(s);
697}
698
699static void
700tctrl_read_ext_status(void)
701{
702	struct tctrl_softc *sc;
703	struct tctrl_req req;
704	int s;
705
706	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
707	req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
708	req.cmdlen = 1;
709	req.rsplen = 3;
710#ifdef TCTRLDEBUG
711	printf("pre read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
712#endif
713	tadpole_request(&req, 1, 0);
714	s = splts102();
715	sc->sc_ext_status = (req.rspbuf[0] << 8) + req.rspbuf[1];
716	splx(s);
717#ifdef TCTRLDEBUG
718	printf("post read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
719#endif
720}
721
722/*
723 * return 0 if the user will notice and handle the event,
724 * return 1 if the kernel driver should do so.
725 */
726static int
727tctrl_apm_record_event(struct tctrl_softc *sc, u_int event_type)
728{
729	struct apm_event_info *evp;
730
731	if ((sc->sc_flags & TCTRL_APM_CTLOPEN) &&
732	    (sc->sc_event_count < APM_NEVENTS)) {
733		evp = &sc->sc_event_list[sc->sc_event_ptr];
734		sc->sc_event_count++;
735		sc->sc_event_ptr++;
736		sc->sc_event_ptr %= APM_NEVENTS;
737		evp->type = event_type;
738		evp->index = ++tctrl_apm_evindex;
739		selnotify(&sc->sc_rsel, 0, 0);
740		return(sc->sc_flags & TCTRL_APM_CTLOPEN) ? 0 : 1;
741	}
742	return(1);
743}
744
745static void
746tctrl_read_event_status(struct tctrl_softc *sc)
747{
748	struct tctrl_req req;
749	int s, lid;
750	uint32_t v;
751
752	req.cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
753	req.cmdlen = 1;
754	req.rsplen = 3;
755	tadpole_request(&req, 1, 0);
756	s = splts102();
757	v = req.rspbuf[0] * 256 + req.rspbuf[1];
758#ifdef TCTRLDEBUG
759	printf("event: %x\n",v);
760#endif
761	if (v & TS102_EVENT_STATUS_POWERON_BTN_PRESSED) {
762		printf("%s: Power button pressed\n",sc->sc_dev.dv_xname);
763		tctrl_powerfail(sc);
764	}
765	if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
766		printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
767		tctrl_powerfail(sc);
768	}
769	if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
770/*printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);*/
771/* according to a tadpole header, and observation */
772#ifdef TCTRLDEBUG
773		printf("%s: Battery charge level change\n",
774		    sc->sc_dev.dv_xname);
775#endif
776	}
777	if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
778		if (tctrl_apm_record_event(sc, APM_BATTERY_LOW))
779			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
780	}
781	if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
782		splx(s);
783		tctrl_read_ext_status();
784		tctrl_ac_state(sc);
785		s = splts102();
786		if (tctrl_apm_record_event(sc, APM_POWER_CHANGE))
787			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
788			    (sc->sc_ext_status &
789			    TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ?
790			    "restored" : "removed");
791	}
792	if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
793		splx(s);
794		tctrl_read_ext_status();
795		tctrl_lid_state(sc);
796		tctrl_setup_bitport();
797#ifdef TCTRLDEBUG
798		printf("%s: lid %s\n", sc->sc_dev.dv_xname,
799		    (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
800		    ? "closed" : "opened");
801#endif
802		lid = (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) == 0;
803	}
804	if (v & TS102_EVENT_STATUS_EXTERNAL_VGA_STATUS_CHANGE) {
805		int vga;
806		splx(s);
807		tctrl_read_ext_status();
808		vga = (sc->sc_ext_status &
809		    TS102_EXT_STATUS_EXTERNAL_VGA_ATTACHED) != 0;
810		if (vga != sc->sc_extvga) {
811			sc->sc_extvga = vga;
812			if (sc->sc_video_callback != NULL) {
813				sc->sc_video_callback(
814				    sc->sc_video_callback_cookie,
815				    sc->sc_extvga);
816			}
817		}
818	}
819#ifdef DIAGNOSTIC
820	if (v & TS102_EVENT_STATUS_EXT_MOUSE_STATUS_CHANGE) {
821		splx(s);
822		tctrl_read_ext_status();
823		if (sc->sc_ext_status &
824		    TS102_EXT_STATUS_EXTERNAL_MOUSE_ATTACHED) {
825			printf("tctrl: external mouse detected\n");
826		}
827	}
828#endif
829	sc->sc_ext_pending = 0;
830	splx(s);
831}
832
833static void
834tctrl_lock(struct tctrl_softc *sc)
835{
836
837	mutex_enter(&sc->sc_requestlock);
838}
839
840static void
841tctrl_unlock(struct tctrl_softc *sc)
842{
843
844	mutex_exit(&sc->sc_requestlock);
845}
846
847int
848tadpole_request(struct tctrl_req *req, int spin, int sleep)
849{
850	struct tctrl_softc *sc;
851	int i, s;
852
853	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
854	if (!sc)
855		return ENODEV;
856
857	tctrl_lock(sc);
858
859	if (spin)
860		s = splhigh();
861	else
862		s = splts102();
863	sc->sc_flags |= TCTRL_SEND_REQUEST;
864	memcpy(sc->sc_cmdbuf, req->cmdbuf, req->cmdlen);
865#ifdef DIAGNOSTIC
866	if (sc->sc_wantdata != 0) {
867		splx(s);
868		printf("tctrl: we lost the race\n");
869		tctrl_unlock(sc);
870		return EAGAIN;
871	}
872#endif
873	sc->sc_wantdata = 1;
874	sc->sc_rsplen = req->rsplen;
875	sc->sc_cmdlen = req->cmdlen;
876	sc->sc_cmdoff = sc->sc_rspoff = 0;
877
878	/* we spin for certain commands, like poweroffs */
879	if (spin) {
880/*		for (i = 0; i < 30000; i++) {*/
881		i = 0;
882		while ((sc->sc_wantdata == 1) && (i < 30000)) {
883			tctrl_intr(sc);
884			DELAY(1);
885			i++;
886		}
887#ifdef DIAGNOSTIC
888		if (i >= 30000) {
889			printf("tctrl: timeout busy waiting for micro controller request!\n");
890			sc->sc_wantdata = 0;
891			splx(s);
892			tctrl_unlock(sc);
893			return EAGAIN;
894		}
895#endif
896	} else {
897		int timeout = 5 * (sc->sc_rsplen + sc->sc_cmdlen);
898		tctrl_intr(sc);
899		i = 0;
900		while (((sc->sc_rspoff != sc->sc_rsplen) ||
901		    (sc->sc_cmdoff != sc->sc_cmdlen)) &&
902		    (i < timeout))
903			if (sleep) {
904				tsleep(sc, PWAIT, "tctrl_data", 15);
905				i++;
906			} else
907				DELAY(1);
908#ifdef DIAGNOSTIC
909		if (i >= timeout) {
910			printf("tctrl: timeout waiting for microcontroller request\n");
911			sc->sc_wantdata = 0;
912			splx(s);
913			tctrl_unlock(sc);
914			return EAGAIN;
915		}
916#endif
917	}
918	/*
919	 * we give the user a reasonable amount of time for a command
920	 * to complete.  If it doesn't complete in time, we hand them
921	 * garbage.  This is here to stop things like setting the
922	 * rsplen too long, and sleeping forever in a CMD_REQ ioctl.
923	 */
924	sc->sc_wantdata = 0;
925	memcpy(req->rspbuf, sc->sc_rspbuf, req->rsplen);
926	splx(s);
927
928	tctrl_unlock(sc);
929	return 0;
930}
931
932void
933tadpole_powerdown(void)
934{
935	struct tctrl_req req;
936
937	req.cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
938	req.cmdlen = 1;
939	req.rsplen = 1;
940	tadpole_request(&req, 1, 0);
941}
942
943void
944tadpole_set_video(int enabled)
945{
946	struct tctrl_softc *sc;
947	struct tctrl_req req;
948	int s;
949
950	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
951	while (sc->sc_wantdata != 0)
952		DELAY(1);
953	s = splts102();
954	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN && !enabled)
955	    || (sc->sc_tft_on)) {
956		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
957	} else {
958		req.cmdbuf[2] = 0;
959	}
960	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
961	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
962	req.cmdlen = 3;
963	req.rsplen = 2;
964
965	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
966		sc->sc_tft_on = enabled;
967		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
968			splx(s);
969			return;
970		}
971		tadpole_request(&req, 1, 0);
972		sc->sc_bitport =
973		    (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
974	}
975	splx(s);
976}
977
978static void
979tctrl_write_data(struct tctrl_softc *sc, uint8_t v)
980{
981	unsigned int i;
982
983	for (i = 0; i < 100; i++)  {
984		if (TS102_UCTRL_STS_TXNF_STA &
985		    tctrl_read(sc, TS102_REG_UCTRL_STS))
986			break;
987	}
988	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
989}
990
991static uint8_t
992tctrl_read_data(struct tctrl_softc *sc)
993{
994	unsigned int i, v;
995
996	for (i = 0; i < 100000; i++) {
997		if (TS102_UCTRL_STS_RXNE_STA &
998		    tctrl_read(sc, TS102_REG_UCTRL_STS))
999			break;
1000		DELAY(1);
1001	}
1002
1003	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
1004	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
1005	return v;
1006}
1007
1008static uint8_t
1009tctrl_read(struct tctrl_softc *sc, bus_size_t off)
1010{
1011
1012	sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off);
1013	return sc->sc_junk;
1014}
1015
1016static void
1017tctrl_write(struct tctrl_softc *sc, bus_size_t off, uint8_t v)
1018{
1019
1020	sc->sc_junk = v;
1021	bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v);
1022}
1023
1024int
1025tctrlopen(dev_t dev, int flags, int mode, struct lwp *l)
1026{
1027	int unit = (minor(dev)&0xf0);
1028	int ctl = (minor(dev)&0x0f);
1029	struct tctrl_softc *sc;
1030
1031	if (unit >= tctrl_cd.cd_ndevs)
1032		return(ENXIO);
1033	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
1034	if (!sc)
1035		return(ENXIO);
1036
1037	switch (ctl) {
1038	case TCTRL_STD_DEV:
1039		break;
1040	case TCTRL_APMCTL_DEV:
1041		if (!(flags & FWRITE))
1042			return(EINVAL);
1043		if (sc->sc_flags & TCTRL_APM_CTLOPEN)
1044			return(EBUSY);
1045		sc->sc_flags |= TCTRL_APM_CTLOPEN;
1046		break;
1047	default:
1048		return(ENXIO);
1049		break;
1050	}
1051
1052	return(0);
1053}
1054
1055int
1056tctrlclose(dev_t dev, int flags, int mode, struct lwp *l)
1057{
1058	int ctl = (minor(dev)&0x0f);
1059	struct tctrl_softc *sc;
1060
1061	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
1062	if (!sc)
1063		return(ENXIO);
1064
1065	switch (ctl) {
1066	case TCTRL_STD_DEV:
1067		break;
1068	case TCTRL_APMCTL_DEV:
1069		sc->sc_flags &= ~TCTRL_APM_CTLOPEN;
1070		break;
1071	}
1072	return(0);
1073}
1074
1075int
1076tctrlioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
1077{
1078	struct tctrl_req req, *reqn;
1079	struct tctrl_pwr *pwrreq;
1080	struct apm_power_info *powerp;
1081	struct apm_event_info *evp;
1082	struct tctrl_softc *sc;
1083	int i;
1084	uint8_t c;
1085
1086	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
1087	if (!sc)
1088		return ENXIO;
1089
1090        switch (cmd) {
1091
1092	case APM_IOC_STANDBY:
1093		/* turn off backlight and so on ? */
1094
1095		return 0; /* for now */
1096
1097	case APM_IOC_SUSPEND:
1098		/* not sure what to do here - we can't really suspend */
1099
1100		return 0; /* for now */
1101
1102	case OAPM_IOC_GETPOWER:
1103	case APM_IOC_GETPOWER:
1104		powerp = (struct apm_power_info *)data;
1105		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_RATE;
1106		req.cmdlen = 1;
1107		req.rsplen = 2;
1108		tadpole_request(&req, 0, l->l_proc ? 1 : 0);
1109		if (req.rspbuf[0] > 0x00)
1110			powerp->battery_state = APM_BATT_CHARGING;
1111		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_LEVEL;
1112		req.cmdlen = 1;
1113		req.rsplen = 3;
1114		tadpole_request(&req, 0, l->l_proc ? 1 : 0);
1115		c = req.rspbuf[0];
1116		powerp->battery_life = c;
1117		if (c > 0x70)	/* the tadpole sometimes dips below zero, and */
1118			c = 0;	/* into the 255 range. */
1119		powerp->minutes_left = (45 * c) / 100; /* XXX based on 45 min */
1120		if (powerp->battery_state != APM_BATT_CHARGING) {
1121			if (c < 0x20)
1122				powerp->battery_state = APM_BATT_CRITICAL;
1123			else if (c < 0x40)
1124				powerp->battery_state = APM_BATT_LOW;
1125			else if (c < 0x66)
1126				powerp->battery_state = APM_BATT_HIGH;
1127			else
1128				powerp->battery_state = APM_BATT_UNKNOWN;
1129		}
1130
1131		if (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE)
1132			powerp->ac_state = APM_AC_ON;
1133		else
1134			powerp->ac_state = APM_AC_OFF;
1135		break;
1136
1137	case APM_IOC_NEXTEVENT:
1138		if (!sc->sc_event_count)
1139			return EAGAIN;
1140
1141		evp = (struct apm_event_info *)data;
1142		i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
1143		i %= APM_NEVENTS;
1144		*evp = sc->sc_event_list[i];
1145		sc->sc_event_count--;
1146		return(0);
1147
1148	/* this ioctl assumes the caller knows exactly what he is doing */
1149	case TCTRL_CMD_REQ:
1150		reqn = (struct tctrl_req *)data;
1151		if ((i = kauth_authorize_generic(l->l_cred,
1152		    KAUTH_GENERIC_ISSUSER, NULL)) != 0 &&
1153		    (reqn->cmdbuf[0] == TS102_OP_CTL_BITPORT ||
1154		    (reqn->cmdbuf[0] >= TS102_OP_CTL_WATCHDOG &&
1155		    reqn->cmdbuf[0] <= TS102_OP_CTL_SECURITY_KEY) ||
1156		    reqn->cmdbuf[0] == TS102_OP_CTL_TIMEZONE ||
1157		    reqn->cmdbuf[0] == TS102_OP_CTL_DIAGNOSTIC_MODE ||
1158		    reqn->cmdbuf[0] == TS102_OP_CMD_SOFTWARE_RESET ||
1159		    (reqn->cmdbuf[0] >= TS102_OP_CMD_SET_RTC &&
1160		    reqn->cmdbuf[0] < TS102_OP_RD_INT_CHARGE_LEVEL) ||
1161		    reqn->cmdbuf[0] > TS102_OP_RD_EXT_CHARGE_LEVEL))
1162			return(i);
1163		tadpole_request(reqn, 0, l->l_proc ? 1 : 0);
1164		break;
1165	/* serial power mode (via auxiotwo) */
1166	case TCTRL_SERIAL_PWR:
1167		pwrreq = (struct tctrl_pwr *)data;
1168		if (pwrreq->rw)
1169			pwrreq->state = auxiotwoserialgetapm();
1170		else
1171			auxiotwoserialsetapm(pwrreq->state);
1172		break;
1173
1174	/* modem power mode (via auxio) */
1175	case TCTRL_MODEM_PWR:
1176		return(EOPNOTSUPP); /* for now */
1177		break;
1178
1179
1180        default:
1181                return (ENOTTY);
1182        }
1183        return (0);
1184}
1185
1186int
1187tctrlpoll(dev_t dev, int events, struct lwp *l)
1188{
1189	struct tctrl_softc *sc = device_lookup_private(&tctrl_cd,
1190						       TCTRL_STD_DEV);
1191	int revents = 0;
1192
1193	if (events & (POLLIN | POLLRDNORM)) {
1194		if (sc->sc_event_count)
1195			revents |= events & (POLLIN | POLLRDNORM);
1196		else
1197			selrecord(l, &sc->sc_rsel);
1198	}
1199
1200	return (revents);
1201}
1202
1203static void
1204filt_tctrlrdetach(struct knote *kn)
1205{
1206	struct tctrl_softc *sc = kn->kn_hook;
1207	int s;
1208
1209	s = splts102();
1210	SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
1211	splx(s);
1212}
1213
1214static int
1215filt_tctrlread(struct knote *kn, long hint)
1216{
1217	struct tctrl_softc *sc = kn->kn_hook;
1218
1219	kn->kn_data = sc->sc_event_count;
1220	return (kn->kn_data > 0);
1221}
1222
1223static const struct filterops tctrlread_filtops =
1224	{ 1, NULL, filt_tctrlrdetach, filt_tctrlread };
1225
1226int
1227tctrlkqfilter(dev_t dev, struct knote *kn)
1228{
1229	struct tctrl_softc *sc = device_lookup_private(&tctrl_cd,
1230						       TCTRL_STD_DEV);
1231	struct klist *klist;
1232	int s;
1233
1234	switch (kn->kn_filter) {
1235	case EVFILT_READ:
1236		klist = &sc->sc_rsel.sel_klist;
1237		kn->kn_fop = &tctrlread_filtops;
1238		break;
1239
1240	default:
1241		return (1);
1242	}
1243
1244	kn->kn_hook = sc;
1245
1246	s = splts102();
1247	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
1248	splx(s);
1249
1250	return (0);
1251}
1252
1253static void
1254tctrl_sensor_setup(struct tctrl_softc *sc)
1255{
1256	int i, error;
1257
1258	sc->sc_sme = sysmon_envsys_create();
1259
1260	/* case temperature */
1261	(void)strlcpy(sc->sc_sensor[0].desc, "Case temperature",
1262	    sizeof(sc->sc_sensor[0].desc));
1263	sc->sc_sensor[0].units = ENVSYS_STEMP;
1264
1265	/* battery voltage */
1266	(void)strlcpy(sc->sc_sensor[1].desc, "Internal battery voltage",
1267	    sizeof(sc->sc_sensor[1].desc));
1268	sc->sc_sensor[1].units = ENVSYS_SVOLTS_DC;
1269
1270	/* DC voltage */
1271	(void)strlcpy(sc->sc_sensor[2].desc, "DC-In voltage",
1272	    sizeof(sc->sc_sensor[2].desc));
1273	sc->sc_sensor[2].units = ENVSYS_SVOLTS_DC;
1274
1275	for (i = 0; i < ENVSYS_NUMSENSORS; i++) {
1276		if (sysmon_envsys_sensor_attach(sc->sc_sme,
1277						&sc->sc_sensor[i])) {
1278			sysmon_envsys_destroy(sc->sc_sme);
1279			return;
1280		}
1281	}
1282
1283	sc->sc_sme->sme_name = sc->sc_dev.dv_xname;
1284	sc->sc_sme->sme_cookie = sc;
1285	sc->sc_sme->sme_refresh = tctrl_refresh;
1286
1287	if ((error = sysmon_envsys_register(sc->sc_sme)) != 0) {
1288		printf("%s: couldn't register sensors (%d)\n",
1289		    sc->sc_dev.dv_xname, error);
1290		sysmon_envsys_destroy(sc->sc_sme);
1291		return;
1292	}
1293
1294	/* now register the power button */
1295
1296	sysmon_task_queue_init();
1297
1298	sc->sc_powerpressed = 0;
1299	memset(&sc->sc_sm_pbutton, 0, sizeof(struct sysmon_pswitch));
1300	sc->sc_sm_pbutton.smpsw_name = sc->sc_dev.dv_xname;
1301	sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
1302	if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0)
1303		printf("%s: unable to register power button with sysmon\n",
1304		    sc->sc_dev.dv_xname);
1305
1306	memset(&sc->sc_sm_lid, 0, sizeof(struct sysmon_pswitch));
1307	sc->sc_sm_lid.smpsw_name = sc->sc_dev.dv_xname;
1308	sc->sc_sm_lid.smpsw_type = PSWITCH_TYPE_LID;
1309	if (sysmon_pswitch_register(&sc->sc_sm_lid) != 0)
1310		printf("%s: unable to register lid switch with sysmon\n",
1311		    sc->sc_dev.dv_xname);
1312
1313	memset(&sc->sc_sm_ac, 0, sizeof(struct sysmon_pswitch));
1314	sc->sc_sm_ac.smpsw_name = sc->sc_dev.dv_xname;
1315	sc->sc_sm_ac.smpsw_type = PSWITCH_TYPE_ACADAPTER;
1316	if (sysmon_pswitch_register(&sc->sc_sm_ac) != 0)
1317		printf("%s: unable to register AC adaptor with sysmon\n",
1318		    sc->sc_dev.dv_xname);
1319}
1320
1321static void
1322tctrl_power_button_pressed(void *arg)
1323{
1324	struct tctrl_softc *sc = arg;
1325
1326	sysmon_pswitch_event(&sc->sc_sm_pbutton, PSWITCH_EVENT_PRESSED);
1327	sc->sc_powerpressed = 0;
1328}
1329
1330static void
1331tctrl_lid_state(struct tctrl_softc *sc)
1332{
1333	int state;
1334
1335	state = (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) ?
1336	    PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED;
1337	sysmon_pswitch_event(&sc->sc_sm_lid, state);
1338}
1339
1340static void
1341tctrl_ac_state(struct tctrl_softc *sc)
1342{
1343	int state;
1344
1345	state = (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ?
1346	    PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED;
1347	sysmon_pswitch_event(&sc->sc_sm_ac, state);
1348}
1349
1350static int
1351tctrl_powerfail(void *arg)
1352{
1353	struct tctrl_softc *sc = (struct tctrl_softc *)arg;
1354
1355	/*
1356	 * We lost power. Queue a callback with thread context to
1357	 * handle all the real work.
1358	 */
1359	if (sc->sc_powerpressed == 0) {
1360		sc->sc_powerpressed = 1;
1361		sysmon_task_queue_sched(0, tctrl_power_button_pressed, sc);
1362	}
1363	return (1);
1364}
1365
1366static void
1367tctrl_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
1368{
1369	/*struct tctrl_softc *sc = sme->sme_cookie;*/
1370	struct tctrl_req req;
1371	int sleepable;
1372	int i;
1373
1374	i = edata->sensor;
1375	sleepable = curlwp ? 1 : 0;
1376
1377	switch (i)
1378	{
1379		case 0:	/* case temperature */
1380			req.cmdbuf[0] = TS102_OP_RD_CURRENT_TEMP;
1381			req.cmdlen = 1;
1382			req.rsplen = 2;
1383			tadpole_request(&req, 0, sleepable);
1384			edata->value_cur =             /* 273160? */
1385			    (uint32_t)((int)((int)req.rspbuf[0] - 32) * 5000000
1386			    / 9 + 273150000);
1387			req.cmdbuf[0] = TS102_OP_RD_MAX_TEMP;
1388			req.cmdlen = 1;
1389			req.rsplen = 2;
1390			tadpole_request(&req, 0, sleepable);
1391			edata->value_max =
1392			    (uint32_t)((int)((int)req.rspbuf[0] - 32) * 5000000
1393			    / 9 + 273150000);
1394			edata->flags |= ENVSYS_FVALID_MAX;
1395			req.cmdbuf[0] = TS102_OP_RD_MIN_TEMP;
1396			req.cmdlen = 1;
1397			req.rsplen = 2;
1398			tadpole_request(&req, 0, sleepable);
1399			edata->value_min =
1400			    (uint32_t)((int)((int)req.rspbuf[0] - 32) * 5000000
1401			    / 9 + 273150000);
1402			edata->flags |= ENVSYS_FVALID_MIN;
1403			edata->units = ENVSYS_STEMP;
1404			break;
1405
1406		case 1: /* battery voltage */
1407			{
1408				edata->units = ENVSYS_SVOLTS_DC;
1409				req.cmdbuf[0] = TS102_OP_RD_INT_BATT_VLT;
1410				req.cmdlen = 1;
1411				req.rsplen = 2;
1412				tadpole_request(&req, 0, sleepable);
1413				edata->value_cur = (int32_t)req.rspbuf[0] *
1414				    1000000 / 11;
1415			}
1416			break;
1417		case 2: /* DC voltage */
1418			{
1419				edata->units = ENVSYS_SVOLTS_DC;
1420				req.cmdbuf[0] = TS102_OP_RD_DC_IN_VLT;
1421				req.cmdlen = 1;
1422				req.rsplen = 2;
1423				tadpole_request(&req, 0, sleepable);
1424				edata->value_cur = (int32_t)req.rspbuf[0] *
1425				    1000000 / 11;
1426			}
1427			break;
1428	}
1429	edata->state = ENVSYS_SVALID;
1430}
1431
1432static void
1433tctrl_event_thread(void *v)
1434{
1435	struct tctrl_softc *sc = v;
1436	struct device *dv;
1437	struct sd_softc *sd = NULL;
1438	struct lance_softc *le = NULL;
1439	int ticks = hz/2;
1440	int rcount, wcount;
1441	int s;
1442
1443	while (sd == NULL) {
1444		dv = device_find_by_xname("sd0");
1445		if (dv != NULL)
1446			sd = device_private(dv);
1447		else
1448			tsleep(&sc->sc_events, PWAIT, "probe_disk", hz);
1449	}
1450	dv = device_find_by_xname("le0");
1451	if (dv != NULL)
1452		le = device_private(dv);
1453	printf("found %s\n", device_xname(sd->sc_dev));
1454	rcount = sd->sc_dk.dk_stats->io_rxfer;
1455	wcount = sd->sc_dk.dk_stats->io_wxfer;
1456
1457	tctrl_read_event_status(sc);
1458
1459	while (1) {
1460		tsleep(&sc->sc_events, PWAIT, "tctrl_event", ticks);
1461		s = splhigh();
1462		if ((rcount != sd->sc_dk.dk_stats->io_rxfer) ||
1463		    (wcount != sd->sc_dk.dk_stats->io_wxfer)) {
1464			rcount = sd->sc_dk.dk_stats->io_rxfer;
1465			wcount = sd->sc_dk.dk_stats->io_wxfer;
1466			sc->sc_lcdwanted |= TS102_LCD_DISK_ACTIVE;
1467		} else
1468			sc->sc_lcdwanted &= ~TS102_LCD_DISK_ACTIVE;
1469		if (le != NULL) {
1470			if (le->sc_havecarrier != 0) {
1471				sc->sc_lcdwanted |= TS102_LCD_LAN_ACTIVE;
1472			} else
1473				sc->sc_lcdwanted &= ~TS102_LCD_LAN_ACTIVE;
1474		}
1475		splx(s);
1476		tctrl_update_lcd(sc);
1477		if (sc->sc_ext_pending)
1478			tctrl_read_event_status(sc);
1479	}
1480}
1481
1482void
1483tadpole_register_callback(void (*callback)(void *, int), void *cookie)
1484{
1485	struct tctrl_softc *sc;
1486
1487	sc = device_lookup_private(&tctrl_cd, TCTRL_STD_DEV);
1488	sc->sc_video_callback = callback;
1489	sc->sc_video_callback_cookie = cookie;
1490	if (sc->sc_video_callback != NULL) {
1491		sc->sc_video_callback(sc->sc_video_callback_cookie,
1492		    sc->sc_extvga);
1493	}
1494}
1495