tctrl.c revision 1.5
1/*	$NetBSD: tctrl.c,v 1.5 1999/12/17 00:32:25 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	enum { TCTRL_IDLE, TCTRL_ARGS,
94		TCTRL_ACK, TCTRL_DATA } sc_state;
95	u_int8_t	sc_cmdbuf[16];
96	u_int8_t	sc_rspbuf[16];
97	u_int8_t	sc_bitport;
98	u_int8_t	sc_tft_on;
99	u_int8_t	sc_op;
100	u_int8_t	sc_cmdoff;
101	u_int8_t	sc_cmdlen;
102	u_int8_t	sc_rspoff;
103	u_int8_t	sc_rsplen;
104	/* APM stuff */
105#define APM_NEVENTS 16
106	struct	apm_event_info sc_event_list[APM_NEVENTS];
107	int	sc_event_count;
108	int	sc_event_ptr;
109	struct	selinfo sc_rsel;
110	/* ENVSYS stuff */
111#define ENVSYS_NUMSENSORS 3
112	struct	envsys_sensor sc_esensors[ENVSYS_NUMSENSORS];
113
114	struct	evcnt sc_intrcnt;	/* interrupt counting */
115};
116
117#define TCTRL_STD_DEV		0
118#define TCTRL_APMCTL_DEV	8
119
120static int tctrl_match __P((struct device *parent, struct cfdata *cf,
121	void *aux));
122static void tctrl_attach __P((struct device *parent, struct device *self,
123	void *aux));
124static void tctrl_write __P((struct tctrl_softc *sc, bus_size_t off,
125	u_int8_t v));
126static u_int8_t tctrl_read __P((struct tctrl_softc *sc, bus_size_t off));
127static void tctrl_write_data __P((struct tctrl_softc *sc, u_int8_t v));
128static u_int8_t tctrl_read_data __P((struct tctrl_softc *sc));
129static int tctrl_intr __P((void *arg));
130static void tctrl_setup_bitport __P((void));
131static void tctrl_setup_bitport_nop __P((void));
132static void tctrl_read_ext_status __P((void));
133static void tctrl_read_event_status __P((void *arg));
134static int tctrl_apm_record_event __P((struct tctrl_softc *sc,
135	u_int event_type));
136
137struct cfattach tctrl_ca = {
138	sizeof(struct tctrl_softc), tctrl_match, tctrl_attach
139};
140
141extern struct cfdriver tctrl_cd;
142/* XXX wtf is this? see i386/apm.c */
143int tctrl_apm_evindex;
144
145static int
146tctrl_match(parent, cf, aux)
147	struct device *parent;
148	struct cfdata *cf;
149	void *aux;
150{
151	union obio_attach_args *uoba = aux;
152	struct sbus_attach_args *sa = &uoba->uoba_sbus;
153
154	if (uoba->uoba_isobio4 != 0) {
155		return (0);
156	}
157
158	/* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
159	 * (who's interface is off the TS102 PCMCIA controller but there
160	 * exists a OpenProm for microcontroller interface).
161	 */
162	return strcmp("uctrl", sa->sa_name) == 0;
163}
164
165static void
166tctrl_attach(parent, self, aux)
167	struct device *parent;
168	struct device *self;
169	void *aux;
170{
171	struct tctrl_softc *sc = (void *)self;
172	union obio_attach_args *uoba = aux;
173	struct sbus_attach_args *sa = &uoba->uoba_sbus;
174	unsigned int i, v;
175#if 0
176	unsigned int ack, msb, lsb;
177#endif
178
179	/* We're living on a sbus slot that looks like an obio that
180	 * looks like an sbus slot.
181	 */
182	sc->sc_memt = sa->sa_bustag;
183	if (sbus_bus_map(sc->sc_memt, sa->sa_slot,
184			 sa->sa_offset - TS102_REG_UCTRL_INT, sa->sa_size,
185			 BUS_SPACE_MAP_LINEAR, 0,
186			 &sc->sc_memh) != 0) {
187		printf(": can't map registers\n");
188		return;
189	}
190
191	printf("\n");
192
193	sc->sc_tft_on = 1;
194
195	/* clear any pending data.
196	 */
197	for (i = 0; i < 10000; i++) {
198		if ((TS102_UCTRL_STS_RXNE_STA &
199		    tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) {
200			break;
201		}
202		v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
203		tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
204	}
205
206	if (sa->sa_nintr != 0) {
207		(void)bus_intr_establish(sc->sc_memt, sa->sa_pri,
208		    0, tctrl_intr, sc);
209		evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
210	}
211
212	/* See what the external status is
213	 */
214
215	tctrl_read_ext_status();
216	if (sc->sc_ext_status != 0) {
217		const char *sep;
218
219		printf("%s: ", sc->sc_dev.dv_xname);
220		v = sc->sc_ext_status;
221		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
222			if (v & 1) {
223				printf("%s%s", sep, tctrl_ext_statuses[i]);
224				sep = ", ";
225			}
226		}
227		printf("\n");
228	}
229
230	/* Get a current of the control bitport;
231	 */
232	tctrl_setup_bitport_nop();
233	tctrl_write(sc, TS102_REG_UCTRL_INT,
234		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK);
235
236	sc->sc_wantdata = 0;
237	sc->sc_event_count = 0;
238
239	/* prime the sensor data */
240	sprintf(sc->sc_esensors[0].desc, "%s", "Internal Unit Temperature");
241	sc->sc_esensors[0].units = ENVSYS_STEMP;
242	sprintf(sc->sc_esensors[1].desc, "%s", "Internal Battery Voltage");
243	sc->sc_esensors[1].units = ENVSYS_SVOLTS_DC;
244	sprintf(sc->sc_esensors[2].desc, "%s", "DC-In Voltage");
245	sc->sc_esensors[2].units = ENVSYS_SVOLTS_DC;
246}
247
248static int
249tctrl_intr(arg)
250	void *arg;
251{
252	struct tctrl_softc *sc = arg;
253	unsigned int v, d;
254	int progress = 0;
255
256    again:
257	/* find out the cause(s) of the interrupt */
258	v = tctrl_read(sc, TS102_REG_UCTRL_STS);
259
260	/* clear the cause(s) of the interrupt */
261	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
262
263	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
264	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
265		v &= ~TS102_UCTRL_STS_TXNF_STA;
266		if (tctrl_read(sc, TS102_REG_UCTRL_INT) & TS102_UCTRL_INT_TXNF_REQ) {
267			tctrl_write(sc, TS102_REG_UCTRL_INT, 0);
268			progress = 1;
269		}
270	}
271	if ((v == 0) && ((sc->sc_flags & TCTRL_SEND_REQUEST) == 0 ||
272	    sc->sc_state != TCTRL_IDLE)) {
273		wakeup(sc);
274		return progress;
275	}
276
277	progress = 1;
278	if (v & TS102_UCTRL_STS_RXNE_STA) {
279		d = tctrl_read_data(sc);
280		switch (sc->sc_state) {
281		case TCTRL_IDLE:
282			if (d == 0xfa) {
283				/* external event */
284				timeout(tctrl_read_event_status, (void *)0, 1);
285			} else {
286				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
287					sc->sc_dev.dv_xname, sc->sc_op, d);
288			}
289			goto again;
290		case TCTRL_ACK:
291			if (d != 0xfe) {
292				printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n",
293					sc->sc_dev.dv_xname, sc->sc_op, d);
294			}
295#ifdef TCTRLDEBUG
296			printf(" ack=0x%02x", d);
297#endif
298			sc->sc_rsplen--;
299			sc->sc_rspoff = 0;
300			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
301			sc->sc_wantdata = sc->sc_rsplen ? 1 : 0;
302#ifdef TCTRLDEBUG
303			if (sc->sc_rsplen > 0) {
304				printf(" [data(%u)]", sc->sc_rsplen);
305			} else {
306				printf(" [idle]\n");
307			}
308#endif
309			goto again;
310		case TCTRL_DATA:
311			sc->sc_rspbuf[sc->sc_rspoff++] = d;
312#ifdef TCTRLDEBUG
313			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
314#endif
315			if (sc->sc_rspoff == sc->sc_rsplen) {
316#ifdef TCTRLDEBUG
317				printf(" [idle]\n");
318#endif
319				sc->sc_state = TCTRL_IDLE;
320				sc->sc_wantdata = 0;
321			}
322			goto again;
323		default:
324			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
325			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
326			goto again;
327		}
328	}
329	if ((sc->sc_state == TCTRL_IDLE && sc->sc_wantdata == 0) ||
330	    sc->sc_flags & TCTRL_SEND_REQUEST) {
331		if (sc->sc_flags & TCTRL_SEND_REQUEST) {
332			sc->sc_flags &= ~TCTRL_SEND_REQUEST;
333			sc->sc_wantdata = 1;
334		}
335		if (sc->sc_cmdlen > 0) {
336			tctrl_write(sc, TS102_REG_UCTRL_INT,
337				tctrl_read(sc, TS102_REG_UCTRL_INT)
338				|TS102_UCTRL_INT_TXNF_MSK
339				|TS102_UCTRL_INT_TXNF_REQ);
340			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
341		}
342	}
343	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
344		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
345#ifdef TCTRLDEBUG
346		if (sc->sc_cmdoff == 1) {
347			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
348				sc->sc_cmdbuf[0], sc->sc_rsplen);
349		} else {
350			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
351				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
352		}
353#endif
354		if (sc->sc_cmdoff == sc->sc_cmdlen) {
355			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
356#ifdef TCTRLDEBUG
357			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
358#endif
359			if (sc->sc_cmdoff == 1) {
360				sc->sc_op = sc->sc_cmdbuf[0];
361			}
362			tctrl_write(sc, TS102_REG_UCTRL_INT,
363				tctrl_read(sc, TS102_REG_UCTRL_INT)
364				& (~TS102_UCTRL_INT_TXNF_MSK
365				   |TS102_UCTRL_INT_TXNF_REQ));
366		} else if (sc->sc_state == TCTRL_IDLE) {
367			sc->sc_op = sc->sc_cmdbuf[0];
368			sc->sc_state = TCTRL_ARGS;
369#ifdef TCTRLDEBUG
370			printf(" [args]");
371#endif
372		}
373	}
374	goto again;
375}
376
377static void
378tctrl_setup_bitport_nop(void)
379{
380	struct tctrl_softc *sc;
381	struct tctrl_req req;
382	int s;
383
384	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
385	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
386	req.cmdbuf[1] = 0xff;
387	req.cmdbuf[2] = 0;
388	req.cmdlen = 3;
389	req.rsplen = 2;
390	req.p = NULL;
391	tadpole_request(&req, 1);
392	s = splts102();
393	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
394	splx(s);
395}
396
397static void
398tctrl_setup_bitport(void)
399{
400	struct tctrl_softc *sc;
401	struct tctrl_req req;
402	int s;
403
404	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
405	s = splts102();
406	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
407	    || (!sc->sc_tft_on)) {
408		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
409	} else {
410		req.cmdbuf[2] = 0;
411	}
412	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
413	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
414	req.cmdlen = 3;
415	req.rsplen = 2;
416	req.p = NULL;
417	tadpole_request(&req, 1);
418	s = splts102();
419	sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
420	splx(s);
421}
422
423static void
424tctrl_read_ext_status(void)
425{
426	struct tctrl_softc *sc;
427	struct tctrl_req req;
428	int s;
429
430	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
431	req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
432	req.cmdlen = 1;
433	req.rsplen = 3;
434	req.p = NULL;
435#ifdef TCTRLDEBUG
436	printf("pre read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
437#endif
438	tadpole_request(&req, 1);
439	s = splts102();
440	sc->sc_ext_status = req.rspbuf[0] * 256 + req.rspbuf[1];
441	splx(s);
442#ifdef TCTRLDEBUG
443	printf("post read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status);
444#endif
445}
446
447/*
448 * return 0 if the user will notice and handle the event,
449 * return 1 if the kernel driver should do so.
450 */
451static int
452tctrl_apm_record_event(sc, event_type)
453	struct tctrl_softc *sc;
454	u_int event_type;
455{
456	struct apm_event_info *evp;
457
458	if ((sc->sc_flags & TCTRL_APM_CTLOPEN) &&
459	    (sc->sc_event_count < APM_NEVENTS)) {
460		evp = &sc->sc_event_list[sc->sc_event_ptr];
461		sc->sc_event_count++;
462		sc->sc_event_ptr++;
463		sc->sc_event_ptr %= APM_NEVENTS;
464		evp->type = event_type;
465		evp->index = ++tctrl_apm_evindex;
466		selwakeup(&sc->sc_rsel);
467		return(sc->sc_flags & TCTRL_APM_CTLOPEN) ? 0 : 1;
468	}
469	return(1);
470}
471
472static void
473tctrl_read_event_status(arg)
474	void *arg;
475{
476	struct tctrl_softc *sc;
477	struct tctrl_req req;
478	int s;
479	unsigned int v;
480
481	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
482	req.cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
483	req.cmdlen = 1;
484	req.rsplen = 3;
485	req.p = NULL;
486	tadpole_request(&req, 1);
487	s = splts102();
488	v = req.rspbuf[0] * 256 + req.rspbuf[1];
489	if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
490		printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
491	}
492	if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
493/*printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);*/
494/* according to a tadpole header, and observation */
495#ifdef TCTRLDEBUG
496		printf("%s: Battery charge level change\n", sc->sc_dev.dv_xname);
497#endif
498	}
499	if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
500		if (tctrl_apm_record_event(sc, APM_BATTERY_LOW))
501			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
502	}
503	if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
504		splx(s);
505		tctrl_read_ext_status();
506		s = splts102();
507		if (tctrl_apm_record_event(sc, APM_POWER_CHANGE))
508			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
509			    (sc->sc_ext_status &
510			    TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ?
511			    "restored" : "removed");
512	}
513	if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
514		splx(s);
515		tctrl_read_ext_status();
516		tctrl_setup_bitport();
517#ifdef TCTRLDEBUG
518		printf("%s: lid %s\n", sc->sc_dev.dv_xname,
519		    (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
520		    ? "closed" : "opened");
521#endif
522	}
523	splx(s);
524}
525
526void
527tadpole_request(req, spin)
528	struct tctrl_req *req;
529	int spin;
530{
531	struct tctrl_softc *sc;
532	int i, s;
533
534	if (tctrl_cd.cd_devs == NULL
535	    || tctrl_cd.cd_ndevs == 0
536	    || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) {
537		return;
538	}
539
540	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
541	while (sc->sc_wantdata != 0) {
542		if (req->p != NULL)
543			tsleep(&sc->sc_wantdata, PLOCK, "tctrl_lock", 10);
544		else
545			DELAY(1);
546	}
547	if (spin)
548		s = splhigh();
549	else
550		s = splts102();
551	sc->sc_flags |= TCTRL_SEND_REQUEST;
552	memcpy(sc->sc_cmdbuf, req->cmdbuf, req->cmdlen);
553	sc->sc_wantdata = 1;
554	sc->sc_rsplen = req->rsplen;
555	sc->sc_cmdlen = req->cmdlen;
556	sc->sc_cmdoff = sc->sc_rspoff = 0;
557
558	/* we spin for certain commands, like poweroffs */
559	if (spin) {
560		for (i = 0; i < 30000; i++) {
561			tctrl_intr(sc);
562			DELAY(1);
563		}
564	} else {
565		tctrl_intr(sc);
566		i = 0;
567		while (((sc->sc_rspoff != sc->sc_rsplen) ||
568		    (sc->sc_cmdoff != sc->sc_cmdlen)) &&
569		    (i < (5 * sc->sc_rsplen + sc->sc_cmdlen)))
570			if (req->p != NULL) {
571				tsleep(sc, PWAIT, "tctrl_data", 15);
572				i++;
573			}
574			else
575				DELAY(1);
576	}
577	/*
578	 * we give the user a reasonable amount of time for a command
579	 * to complete.  If it doesn't complete in time, we hand them
580	 * garbage.  This is here to stop things like setting the
581	 * rsplen too long, and sleeping forever in a CMD_REQ ioctl.
582	 */
583	sc->sc_wantdata = 0;
584	memcpy(req->rspbuf, sc->sc_rspbuf, req->rsplen);
585	splx(s);
586}
587
588void
589tadpole_powerdown(void)
590{
591	struct tctrl_req req;
592
593	req.cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
594	req.cmdlen = 1;
595	req.rsplen = 1;
596	req.p = NULL;
597	tadpole_request(&req, 1);
598}
599
600void
601tadpole_set_video(enabled)
602	int enabled;
603{
604	struct tctrl_softc *sc;
605	struct tctrl_req req;
606	int s;
607
608	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
609	while (sc->sc_wantdata != 0)
610		DELAY(1);
611	s = splts102();
612	req.p = NULL;
613	if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN && !enabled)
614	    || (sc->sc_tft_on)) {
615		req.cmdbuf[2] = TS102_BITPORT_TFTPWR;
616	} else {
617		req.cmdbuf[2] = 0;
618	}
619	req.cmdbuf[0] = TS102_OP_CTL_BITPORT;
620	req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
621	req.cmdlen = 3;
622	req.rsplen = 2;
623
624	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
625		sc->sc_tft_on = enabled;
626		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
627			splx(s);
628			return;
629		}
630		tadpole_request(&req, 1);
631		sc->sc_bitport =
632		    (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2];
633	}
634	splx(s);
635}
636
637static void
638tctrl_write_data(sc, v)
639	struct tctrl_softc *sc;
640	u_int8_t v;
641{
642	unsigned int i;
643
644	for (i = 0; i < 100; i++)  {
645		if (TS102_UCTRL_STS_TXNF_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
646			break;
647	}
648	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
649}
650
651static u_int8_t
652tctrl_read_data(sc)
653	struct tctrl_softc *sc;
654{
655	unsigned int i, v;
656
657	for (i = 0; i < 100000; i++) {
658		if (TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
659			break;
660		DELAY(1);
661	}
662
663	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
664	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
665	return v;
666}
667
668static u_int8_t
669tctrl_read(sc, off)
670	struct tctrl_softc *sc;
671	bus_size_t off;
672{
673
674	sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off);
675	return sc->sc_junk;
676}
677
678static void
679tctrl_write(sc, off, v)
680	struct tctrl_softc *sc;
681	bus_size_t off;
682	u_int8_t v;
683{
684
685	sc->sc_junk = v;
686	bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v);
687}
688
689int
690tctrlopen(dev, flags, mode, p)
691	dev_t dev;
692	int flags, mode;
693	struct proc *p;
694{
695	int unit = (minor(dev)&0xf0);
696	int ctl = (minor(dev)&0x0f);
697	struct tctrl_softc *sc;
698
699	if (unit >= tctrl_cd.cd_ndevs)
700		return(ENXIO);
701	sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
702	if (!sc)
703		return(ENXIO);
704
705	switch (ctl) {
706	case TCTRL_STD_DEV:
707		break;
708	case TCTRL_APMCTL_DEV:
709		if (!(flags & FWRITE))
710			return(EINVAL);
711		if (sc->sc_flags & TCTRL_APM_CTLOPEN)
712			return(EBUSY);
713		sc->sc_flags |= TCTRL_APM_CTLOPEN;
714		break;
715	default:
716		return(ENXIO);
717		break;
718	}
719
720	return(0);
721}
722
723int
724tctrlclose(dev, flags, mode, p)
725	dev_t dev;
726	int flags, mode;
727	struct proc *p;
728{
729	int ctl = (minor(dev)&0x0f);
730	struct tctrl_softc *sc;
731
732	sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
733	if (!sc)
734		return(ENXIO);
735
736	switch (ctl) {
737	case TCTRL_STD_DEV:
738		break;
739	case TCTRL_APMCTL_DEV:
740		sc->sc_flags &= ~TCTRL_APM_CTLOPEN;
741		break;
742	}
743	return(0);
744}
745
746int
747tctrlioctl(dev, cmd, data, flags, p)
748        dev_t dev;
749        u_long cmd;
750        caddr_t data;
751        int flags;
752        struct proc *p;
753{
754	struct tctrl_req req, *reqn;
755	envsys_range_t *envrange;
756	envsys_temp_data_t *envdata;
757	envsys_temp_info_t *envinfo;
758	struct apm_power_info *powerp;
759	struct apm_event_info *evp;
760	struct tctrl_softc *sc;
761	int i;
762	u_int j;
763	u_int16_t a;
764	u_int8_t c;
765
766	if (tctrl_cd.cd_devs == NULL
767	    || tctrl_cd.cd_ndevs == 0
768	    || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) {
769		return ENXIO;
770	}
771	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV];
772        switch (cmd) {
773
774	case APM_IOC_STANDBY:
775		return(EOPNOTSUPP); /* for now */
776
777	case APM_IOC_SUSPEND:
778		return(EOPNOTSUPP); /* for now */
779
780	case APM_IOC_GETPOWER:
781		powerp = (struct apm_power_info *)data;
782		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_RATE;
783		req.cmdlen = 1;
784		req.rsplen = 2;
785		req.p = p;
786		tadpole_request(&req, 0);
787		if (req.rspbuf[0] > 0x00)
788			powerp->battery_state = APM_BATT_CHARGING;
789		req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_LEVEL;
790		req.cmdlen = 1;
791		req.rsplen = 3;
792		req.p = p;
793		tadpole_request(&req, 0);
794		c = req.rspbuf[0];
795		powerp->battery_life = c;
796		powerp->minutes_left = (45 * c) / 100; /* XXX based on 45 min */
797		if (powerp->battery_state != APM_BATT_CHARGING) {
798			if (c < 0x20)
799				powerp->battery_state = APM_BATT_CRITICAL;
800			else if (c < 0x40)
801				powerp->battery_state = APM_BATT_LOW;
802			else if (c < 0x66)
803				powerp->battery_state = APM_BATT_HIGH;
804			else
805				powerp->battery_state = APM_BATT_UNKNOWN;
806		}
807		req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
808		req.cmdlen = 1;
809		req.rsplen = 3;
810		req.p = p;
811		tadpole_request(&req, 0);
812		a = req.rspbuf[0] * 256 + req.rspbuf[1];
813		if (a & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE)
814			powerp->ac_state = APM_AC_ON;
815		else
816			powerp->ac_state = APM_AC_OFF;
817		break;
818
819	case APM_IOC_NEXTEVENT:
820		if (!sc->sc_event_count)
821			return EAGAIN;
822
823		evp = (struct apm_event_info *)data;
824		i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
825		i %= APM_NEVENTS;
826		*evp = sc->sc_event_list[i];
827		sc->sc_event_count--;
828		return(0);
829
830	/* this ioctl assumes the caller knows exactly what he is doing */
831	case TCTRL_CMD_REQ:
832		reqn = (struct tctrl_req *)data;
833		if ((i = suser(p->p_ucred, &p->p_acflag)) != 0 &&
834		    (reqn->cmdbuf[0] == TS102_OP_CTL_BITPORT ||
835		    (reqn->cmdbuf[0] >= TS102_OP_CTL_WATCHDOG &&
836		    reqn->cmdbuf[0] <= TS102_OP_CTL_SECURITY_KEY) ||
837		    reqn->cmdbuf[0] == TS102_OP_CTL_TIMEZONE ||
838		    reqn->cmdbuf[0] == TS102_OP_CTL_DIAGNOSTIC_MODE ||
839		    reqn->cmdbuf[0] == TS102_OP_CMD_SOFTWARE_RESET ||
840		    (reqn->cmdbuf[0] >= TS102_OP_CMD_SET_RTC &&
841		    reqn->cmdbuf[0] < TS102_OP_RD_INT_CHARGE_LEVEL) ||
842		    reqn->cmdbuf[0] > TS102_OP_RD_EXT_CHARGE_LEVEL))
843			return(i);
844		reqn->p = p;
845		tadpole_request(reqn, 0);
846		break;
847
848	case ENVSYS_VERSION:
849		*(int32_t *)data = 1000;
850		break;
851
852	case ENVSYS_GRANGE:
853		envrange = (envsys_range_t *)data;
854		i = 0;
855		envrange->high = envrange->low = 0;
856		for (j=0; j < ENVSYS_NUMSENSORS; j++) {
857			if (!i && envrange->units == sc->sc_esensors[j].units) {
858				envrange->low = j;
859				i++;
860			}
861			if (i && envrange->units == sc->sc_esensors[j].units)
862				envrange->high = j;
863		}
864		if (!i) {
865			envrange->high = 0;
866			envrange->low = 1;
867		}
868		break;
869
870	case ENVSYS_GTREDATA:
871		envdata = (envsys_temp_data_t *)data;
872		if (envdata->sensor >= ENVSYS_NUMSENSORS) {
873			envdata->validflags = 0;
874			break;
875		}
876		envdata->warnflags = ENVSYS_WARN_OK;
877		if (envdata->sensor == 0) {
878			envdata->validflags |= ENVSYS_FVALID;
879			req.cmdbuf[0] = TS102_OP_RD_CURRENT_TEMP;
880			req.cmdlen = 1;
881			req.rsplen = 2;
882			req.p = p;
883			tadpole_request(&req, 0);
884			envdata->cur.data_us =             /* 273160? */
885			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000/9+273000);
886			envdata->validflags |= ENVSYS_FCURVALID;
887			req.cmdbuf[0] = TS102_OP_RD_MAX_TEMP;
888			req.cmdlen = 1;
889			req.rsplen = 2;
890			req.p = p;
891			tadpole_request(&req, 0);
892			envdata->max.data_us =
893			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000/9+273000);
894			envdata->validflags |= ENVSYS_FMAXVALID;
895			req.cmdbuf[0] = TS102_OP_RD_MIN_TEMP;
896			req.cmdlen = 1;
897			req.rsplen = 2;
898			req.p = p;
899			tadpole_request(&req, 0);
900			envdata->min.data_us =
901			    (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000/9+273000);
902			envdata->validflags |= ENVSYS_FMINVALID;
903			envdata->units = sc->sc_esensors[envdata->sensor].units;
904			break;
905		} else if (envdata->sensor == 1 || envdata->sensor == 2) {
906			envdata->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
907			envdata->units = sc->sc_esensors[envdata->sensor].units;
908			if (envdata->sensor == 1)
909				req.cmdbuf[0] = TS102_OP_RD_INT_BATT_VLT;
910			else
911				req.cmdbuf[0] = TS102_OP_RD_DC_IN_VLT;
912			req.cmdlen = 1;
913			req.rsplen = 2;
914			req.p = p;
915			tadpole_request(&req, 0);
916			envdata->cur.data_s = (int32_t)req.rspbuf[0]*1000/11;
917			break;
918		}
919		break;
920
921        case ENVSYS_GTREINFO:
922		envinfo = (envsys_temp_info_t *)data;
923		if (envinfo->sensor >= ENVSYS_NUMSENSORS) {
924			envinfo->validflags = 0;
925			break;
926		}
927		envinfo->units = sc->sc_esensors[envinfo->sensor].units;
928		memcpy(envinfo->desc, sc->sc_esensors[envinfo->sensor].desc,
929		    sizeof(sc->sc_esensors[envinfo->sensor].desc) >
930		    sizeof(envinfo->desc) ? sizeof(envinfo->desc) :
931		    sizeof(sc->sc_esensors[envinfo->sensor].desc));
932		if (envinfo->units == ENVSYS_STEMP) {
933			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID|
934			    ENVSYS_FMINVALID|ENVSYS_FMAXVALID;
935		} else if (envinfo->units == ENVSYS_SVOLTS_DC) {
936			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
937		} else
938			envinfo->validflags = 0;
939                break;
940
941        case ENVSYS_STREINFO:
942		envinfo = (envsys_temp_info_t *)data;
943		if (envinfo->sensor >= ENVSYS_NUMSENSORS) {
944			envinfo->validflags = 0;
945			break;
946		}
947		if (envinfo->units == sc->sc_esensors[envinfo->sensor].units)
948			memcpy(sc->sc_esensors[envinfo->sensor].desc,
949			    envinfo->desc,
950			    sizeof(envinfo->desc) > sizeof(char)*32 ?
951			    sizeof(char)*32 : sizeof(envinfo->desc) );
952		if (envinfo->units == ENVSYS_STEMP) {
953			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID|
954			    ENVSYS_FMINVALID|ENVSYS_FMAXVALID;
955		} else if (envinfo->units == ENVSYS_SVOLTS_DC) {
956			envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID;
957		} else
958			envinfo->validflags = 0;
959                break;
960
961
962        default:
963                return (ENOTTY);
964        }
965        return (0);
966}
967
968int
969tctrlpoll(dev, events, p)
970	dev_t dev;
971	int events;
972	struct proc *p;
973{
974	struct tctrl_softc *sc = tctrl_cd.cd_devs[TCTRL_STD_DEV];
975	int revents = 0;
976
977	if (events & (POLLIN | POLLRDNORM)) {
978		if (sc->sc_event_count)
979			revents |= events & (POLLIN | POLLRDNORM);
980		else
981			selrecord(p, &sc->sc_rsel);
982	}
983
984	return (revents);
985}
986