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