1 /*	$OpenBSD: tctrl.c,v 1.18 2006/10/27 17:52:38 miod Exp $	*/
2 /*	$NetBSD: tctrl.c,v 1.2 1999/08/11 00:46:06 matt Exp $	*/
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Matt Thomas.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/conf.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/event.h>
46 #include <sys/fcntl.h>
47 #include <sys/ioctl.h>
48 #include <sys/proc.h>
49 #include <sys/timeout.h>
50 
51 #include <machine/apmvar.h>
52 #include <machine/autoconf.h>
53 #include <machine/conf.h>
54 #include <machine/cpu.h>
55 
56 #include <sparc/sparc/auxioreg.h>
57 
58 #include <sparc/dev/ts102reg.h>
59 #include <sparc/dev/tctrlvar.h>
60 
61 /*
62  * Flags to control kernel display
63  *	SCFLAG_NOPRINT:		do not output APM power messages due to
64  *				a power change event.
65  *
66  *	SCFLAG_PCTPRINT:	do not output APM power messages due to
67  *				to a power change event unless the battery
68  *				percentage changes.
69  */
70 
71 #define SCFLAG_NOPRINT	0x0008000
72 #define SCFLAG_PCTPRINT	0x0004000
73 #define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
74 
75 const char *tctrl_ext_status[16] = {
76 	"main power available",
77 	"internal battery attached",
78 	"external battery attached",
79 	"external VGA attached",
80 	"external keyboard attached",
81 	"external mouse attached",
82 	"lid down",
83 	"internal battery charging",
84 	"external battery charging",
85 	"internal battery discharging",
86 	"external battery discharging",
87 };
88 
89 /* Request "packet" */
90 struct tctrl_req {
91 	u_int8_t	cmdbuf[16];
92 	u_int		cmdlen;
93 	u_int8_t	rspbuf[16];
94 	u_int		rsplen;
95 };
96 
97 struct tctrl_softc {
98 	struct device sc_dev;
99 	struct uctrl_regs *sc_regs;
100 	struct intrhand sc_ih;
101 	u_int	sc_ext_status;
102 	u_int	sc_flags;
103 #define	TCTRL_SEND_REQUEST		0x0001
104 #define	TCTRL_ISXT			0x0002
105 	u_int	sc_wantdata;
106 	enum { TCTRL_IDLE, TCTRL_ARGS,
107 		TCTRL_ACK, TCTRL_DATA } sc_state;
108 	u_int8_t sc_cmdbuf[16];
109 	u_int8_t sc_rspbuf[16];
110 	u_int8_t sc_tft_on;
111 	u_int8_t sc_pcmcia_on;
112 	u_int8_t sc_brightness;
113 	u_int8_t sc_op;
114 	u_int	sc_cmdoff;
115 	u_int	sc_cmdlen;
116 	u_int	sc_rspoff;
117 	u_int	sc_rsplen;
118 	u_int	sc_rspack;
119 	u_int	sc_bellfreq;
120 	u_int	sc_bellvol;
121 
122 	struct timeout sc_tmo;
123 
124 	/* /dev/apm{,ctl} fields */
125 	struct klist sc_note;
126 	u_int	sc_apmflags;
127 
128 	/* external video control callback */
129 	void (*sc_evcb)(void *, int);
130 	void *sc_evdata;
131 };
132 
133 int	tctrl_match(struct device *, void *, void *);
134 void	tctrl_attach(struct device *, struct device *, void *);
135 
136 void	tctrl_bell(struct tctrl_softc *, int, int);
137 void	tctrl_brightness(struct tctrl_softc *, int, int);
138 void	tctrl_init_lcd(struct tctrl_softc *);
139 int	tctrl_intr(void *);
140 void	tctrl_lcd(struct tctrl_softc *, int, int);
141 u_int8_t tctrl_read_data(struct tctrl_softc *);
142 void	tctrl_read_event_status(void *);
143 void	tctrl_read_ext_status(struct tctrl_softc *);
144 int	tctrl_request(struct tctrl_softc *, struct tctrl_req *);
145 void	tctrl_write_data(struct tctrl_softc *, u_int8_t);
146 
147 struct cfattach tctrl_ca = {
148 	sizeof(struct tctrl_softc), tctrl_match, tctrl_attach
149 };
150 
151 struct cfdriver tctrl_cd = {
152 	NULL, "tctrl", DV_DULL
153 };
154 
155 int
tctrl_match(parent,vcf,aux)156 tctrl_match(parent, vcf, aux)
157 	struct device *parent;
158 	void *vcf;
159 	void *aux;
160 {
161 	struct confargs *ca = aux;
162 	struct romaux *ra = &ca->ca_ra;
163 
164 	/*
165 	 * Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
166 	 * (which is really part of the TS102 PCMCIA controller, but there
167 	 * exists a distinct OpenProm node for the microcontroller interface).
168 	 */
169 	if (strcmp("uctrl", ra->ra_name))
170 		return (0);
171 
172 	return (1);
173 }
174 
175 void
tctrl_attach(parent,self,aux)176 tctrl_attach(parent, self, aux)
177 	struct device *parent, *self;
178 	void *aux;
179 {
180 	struct confargs *ca = aux;
181 	struct tctrl_softc *sc = (void *)self;
182 	u_int i, v;
183 	int pri;
184 
185 	if (ca->ca_ra.ra_nintr != 1) {
186 		printf(": expected 1 interrupt, got %d\n",
187 		    ca->ca_ra.ra_nintr);
188 		return;
189 	}
190 	pri = ca->ca_ra.ra_intr[0].int_pri;
191 
192 	if (ca->ca_ra.ra_nreg != 1) {
193 		printf(": expected 1 register, got %d\n",
194 		    ca->ca_ra.ra_nreg);
195 		return;
196 	}
197 	sc->sc_regs = mapiodev(&(ca->ca_ra.ra_reg[0]), 0,
198 	    ca->ca_ra.ra_reg[0].rr_len);
199 
200 	printf(" pri %d\n", pri);
201 
202 	/*
203 	 * We need to check if we are running on the SPARCbook S3XT, which
204 	 * needs extra work to control the TFT power.
205 	 */
206 	sc->sc_flags = 0;
207 	if (strcmp(mainbus_model, "Tadpole_S3000XT") == 0)
208 		sc->sc_flags |= TCTRL_ISXT;
209 	sc->sc_tft_on = 1;
210 
211 	/* clear any pending data */
212 	for (i = 0; i < 10000; i++) {
213 		if ((TS102_UCTRL_STS_RXNE_STA & sc->sc_regs->stat) == 0)
214 			break;
215 		v = sc->sc_regs->data;
216 		sc->sc_regs->stat = TS102_UCTRL_STS_RXNE_STA;
217 	}
218 
219 	sc->sc_ih.ih_fun = tctrl_intr;
220 	sc->sc_ih.ih_arg = sc;
221 	intr_establish(pri, &sc->sc_ih, -1);
222 
223 	timeout_set(&sc->sc_tmo, tctrl_read_event_status, sc);
224 
225 	/* See what the external status is */
226 	tctrl_read_ext_status(sc);
227 	if (sc->sc_ext_status != 0) {
228 		const char *sep;
229 		u_int len;
230 
231 		v = sc->sc_ext_status;
232 		len = 0;
233 		sep = "";
234 		for (i = 0; v != 0; i++, v >>= 1) {
235 			if ((v & 1) == 0)
236 				continue;
237 			/* wrap to next line if necessary */
238 			if (len != 0 && len + strlen(sep) +
239 			    strlen(tctrl_ext_status[i]) > 80) {
240 				printf("\n");
241 				len = 0;
242 			}
243 			if (len == 0) {
244 				printf("%s: ", sc->sc_dev.dv_xname);
245 				len = 2 + strlen(sc->sc_dev.dv_xname);
246 				sep = "";
247 			}
248 			printf("%s%s", sep, tctrl_ext_status[i]);
249 			len += strlen(sep) + strlen(tctrl_ext_status[i]);
250 			sep = ", ";
251 		}
252 		if (len != 0)
253 			printf("\n");
254 	}
255 
256 	/* Get a few status values */
257 	tctrl_bell(sc, 0xff, 0);
258 	tctrl_brightness(sc, 0xff, 0);
259 
260 	sc->sc_regs->intr = TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK;
261 
262 	sc->sc_wantdata = 0;
263 
264 	/* Initialize the LCD icons */
265 	tctrl_init_lcd(sc);
266 }
267 
268 int
tctrl_intr(void * arg)269 tctrl_intr(void *arg)
270 {
271 	struct tctrl_softc *sc = arg;
272 	unsigned int v, d;
273 	int progress = 0;
274 
275 again:
276 	/* find out the cause(s) of the interrupt */
277 	v = sc->sc_regs->stat & TS102_UCTRL_STS_MASK;
278 
279 	/* clear the cause(s) of the interrupt */
280 	sc->sc_regs->stat = v;
281 
282 	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
283 	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
284 		v &= ~TS102_UCTRL_STS_TXNF_STA;
285 		if (sc->sc_regs->intr & TS102_UCTRL_INT_TXNF_REQ) {
286 			sc->sc_regs->intr = 0;
287 			progress = 1;
288 		}
289 	}
290 	if (v == 0 && ((sc->sc_flags & TCTRL_SEND_REQUEST) == 0 ||
291 	    sc->sc_state != TCTRL_IDLE)) {
292 		return (progress);
293 	}
294 
295 	progress = 1;
296 	if (v & TS102_UCTRL_STS_RXNE_STA) {
297 		d = tctrl_read_data(sc);
298 		switch (sc->sc_state) {
299 		case TCTRL_IDLE:
300 			if (d == TS102_UCTRL_INTR) {
301 				/* external event */
302 				timeout_add(&sc->sc_tmo, 1);
303 			} else {
304 				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
305 					sc->sc_dev.dv_xname, sc->sc_op, d);
306 			}
307 			goto again;
308 		case TCTRL_ACK:
309 #ifdef TCTRLDEBUG
310 			printf(" ack=0x%02x", d);
311 #endif
312 			switch (d) {
313 			case TS102_UCTRL_ACK:
314 				sc->sc_rspack = 1;
315 				sc->sc_rsplen--;
316 				sc->sc_rspoff = 0;
317 				sc->sc_state =
318 				    sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
319 				sc->sc_wantdata = sc->sc_rsplen ? 1 : 0;
320 #ifdef TCTRLDEBUG
321 				if (sc->sc_rsplen > 0) {
322 					printf(" [data(%u)]", sc->sc_rsplen);
323 				} else {
324 					printf(" [idle]\n");
325 				}
326 #endif
327 				goto again;
328 			default:
329 				printf("%s: (op=0x%02x): unexpected return value (0x%02x)\n",
330 					sc->sc_dev.dv_xname, sc->sc_op, d);
331 				/* FALLTHROUGH */
332 			case TS102_UCTRL_NACK:
333 				printf("%s: command %x failed\n",
334 				    sc->sc_dev.dv_xname, sc->sc_op);
335 				sc->sc_rspack = 0;
336 				sc->sc_wantdata = 0;
337 				sc->sc_state = TCTRL_IDLE;
338 				break;
339 			}
340 			break;
341 		case TCTRL_DATA:
342 			sc->sc_rspbuf[sc->sc_rspoff++] = d;
343 #ifdef TCTRLDEBUG
344 			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
345 #endif
346 			if (sc->sc_rspoff == sc->sc_rsplen) {
347 #ifdef TCTRLDEBUG
348 				printf(" [idle]\n");
349 #endif
350 				sc->sc_state = TCTRL_IDLE;
351 				sc->sc_wantdata = 0;
352 			}
353 			goto again;
354 		default:
355 			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
356 			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
357 			goto again;
358 		}
359 	}
360 	if ((sc->sc_state == TCTRL_IDLE && sc->sc_wantdata == 0) ||
361 	    (sc->sc_flags & TCTRL_SEND_REQUEST)) {
362 		if (sc->sc_flags & TCTRL_SEND_REQUEST) {
363 			sc->sc_flags &= ~TCTRL_SEND_REQUEST;
364 			sc->sc_wantdata = 1;
365 		}
366 		if (sc->sc_cmdlen > 0) {
367 			sc->sc_regs->intr =
368 			    sc->sc_regs->intr | TS102_UCTRL_INT_TXNF_MSK
369 				|TS102_UCTRL_INT_TXNF_REQ;
370 			v = sc->sc_regs->stat;
371 		}
372 	}
373 	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
374 		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
375 #ifdef TCTRLDEBUG
376 		if (sc->sc_cmdoff == 1) {
377 			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
378 				sc->sc_cmdbuf[0], sc->sc_rsplen);
379 		} else {
380 			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
381 				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
382 		}
383 #endif
384 		if (sc->sc_cmdoff == sc->sc_cmdlen) {
385 			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
386 #ifdef TCTRLDEBUG
387 			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
388 #endif
389 			if (sc->sc_cmdoff == 1) {
390 				sc->sc_op = sc->sc_cmdbuf[0];
391 			}
392 			sc->sc_regs->intr =
393 			    sc->sc_regs->intr & (~TS102_UCTRL_INT_TXNF_MSK
394 				   |TS102_UCTRL_INT_TXNF_REQ);
395 		} else if (sc->sc_state == TCTRL_IDLE) {
396 			sc->sc_op = sc->sc_cmdbuf[0];
397 			sc->sc_state = TCTRL_ARGS;
398 #ifdef TCTRLDEBUG
399 			printf(" [args]");
400 #endif
401 		}
402 	}
403 	goto again;
404 }
405 
406 /*
407  * The Tadpole microcontroller is not preprogrammed with icon
408  * representations.  The machine boots with the DC-IN light as
409  * a blank (all 0x00) and the other lights, as 4 rows of horizontal
410  * bars.  The below code initializes the few icons the system will use
411  * to sane values.
412  *
413  * Programming the icons is simple.  It is a 5x8 matrix, with each row a
414  * bitfield in the order 0x10 0x08 0x04 0x02 0x01.
415  */
416 
417 static void tctrl_set_glyph(struct tctrl_softc *, u_int, const u_int8_t *);
418 
419 static const u_int8_t
420     tctrl_glyph_dc[] = { 0x00, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x00, 0x00 },
421 #if 0
422     tctrl_glyph_bs[] = { 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00 },
423     tctrl_glyph_w1[] = { 0x0c, 0x16, 0x10, 0x15, 0x10, 0x16, 0x0c, 0x00 },
424     tctrl_glyph_w2[] = { 0x0c, 0x0d, 0x01, 0x15, 0x01, 0x0d, 0x0c, 0x00 },
425     tctrl_glyph_l1[] = { 0x00, 0x04, 0x08, 0x13, 0x08, 0x04, 0x00, 0x00 },
426     tctrl_glyph_l2[] = { 0x00, 0x04, 0x02, 0x19, 0x02, 0x04, 0x00, 0x00 },
427 #endif
428     tctrl_glyph_pc[] = { 0x00, 0x0e, 0x0e, 0x1f, 0x1f, 0x1f, 0x1f, 0x00 };
429 
430 void
tctrl_init_lcd(struct tctrl_softc * sc)431 tctrl_init_lcd(struct tctrl_softc *sc)
432 {
433 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_DC_GOOD, tctrl_glyph_dc);
434 #if 0
435 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_BACKSLASH, tctrl_glyph_bs);
436 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_WAN1, tctrl_glyph_w1);
437 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_WAN2, tctrl_glyph_w2);
438 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_LAN1, tctrl_glyph_l1);
439 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_LAN2, tctrl_glyph_l2);
440 #endif
441 	tctrl_set_glyph(sc, TS102_BLK_OFF_DEF_PCMCIA, tctrl_glyph_pc);
442 }
443 
444 static void
tctrl_set_glyph(struct tctrl_softc * sc,u_int glyph,const u_int8_t * data)445 tctrl_set_glyph(struct tctrl_softc *sc, u_int glyph, const u_int8_t *data)
446 {
447 	struct tctrl_req req;
448 
449 	req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR;
450 	req.cmdbuf[1] = 8;
451 	req.cmdbuf[2] = glyph;
452 	bcopy(data, req.cmdbuf + 3, 8);
453 	req.cmdlen = 3 + 8;
454 	req.rsplen = 1;
455 
456 	tctrl_request(sc, &req);
457 }
458 
459 void
tctrl_read_event_status(void * arg)460 tctrl_read_event_status(void *arg)
461 {
462 	struct tctrl_softc *sc = (struct tctrl_softc *)arg;
463 	struct tctrl_req req;
464 	unsigned int v;
465 
466 	req.cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
467 	req.cmdlen = 1;
468 	req.rsplen = 3;
469 
470 	tctrl_request(sc, &req);
471 
472 	v = req.rspbuf[0] * 256 + req.rspbuf[1];
473 
474 	/*
475 	 * Read the new external status value if necessary
476 	 */
477 	if (v & (TS102_EVENT_STATUS_DC_STATUS_CHANGE |
478 	    TS102_EVENT_STATUS_LID_STATUS_CHANGE |
479 	    TS102_EVENT_STATUS_EXTERNAL_VGA_STATUS_CHANGE))
480 		tctrl_read_ext_status(sc);
481 
482 	if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
483 		printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
484 	}
485 #ifdef TCTRLDEBUG
486 	/* Obviously status change */
487 	if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
488 		if (sc->sc_apmflags & SCFLAG_PCTPRINT)
489 			printf("%s: Battery level change\n",
490 			    sc->sc_dev.dv_xname);
491 	}
492 #endif
493 	if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
494 		if ((sc->sc_apmflags & SCFLAG_NOPRINT) == 0)
495 			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
496 	}
497 	if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
498 		if ((sc->sc_apmflags & SCFLAG_NOPRINT) == 0)
499 			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
500 			    (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ?
501 			      "restored" : "removed");
502 #if 0 /* automatically done for us */
503 		tctrl_lcd(sc, ~TS102_LCD_DC_OK,
504 		    sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE ?
505 		      TS102_LCD_DC_OK : 0);
506 #endif
507 	}
508 	if (v & TS102_EVENT_STATUS_EXTERNAL_VGA_STATUS_CHANGE) {
509 		printf("%s: external vga %s\n", sc->sc_dev.dv_xname,
510 		    sc->sc_ext_status & TS102_EXT_STATUS_EXTERNAL_VGA_ATTACHED ?
511 		      "attached" : "detached");
512 #ifdef TCTRLDEBUG
513 		req.cmdbuf[0] = TS102_OP_RD_EXT_VGA_PORT;
514 		req.cmdlen = 1;
515 		req.rsplen = 2;
516 		tctrl_request(sc, &req);
517 		printf("%s: vga status %x\n", sc->sc_dev.dv_xname,
518 		    req.rspbuf[0]);
519 #endif
520 		if (sc->sc_evcb != NULL)
521 			(*sc->sc_evcb)(sc->sc_evdata, sc->sc_ext_status &
522 			    TS102_EXT_STATUS_EXTERNAL_VGA_ATTACHED);
523 	}
524 }
525 
526 void
tctrl_read_ext_status(struct tctrl_softc * sc)527 tctrl_read_ext_status(struct tctrl_softc *sc)
528 {
529 	struct tctrl_req req;
530 
531 	req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
532 	req.cmdlen = 1;
533 	req.rsplen = 3;
534 #ifdef TCTRLDEBUG
535 	printf("tctrl_read_ext_status: before, ext_status = %x\n",
536 	    sc->sc_ext_status);
537 #endif
538 
539 	tctrl_request(sc, &req);
540 
541 	sc->sc_ext_status = req.rspbuf[0] * 256 + req.rspbuf[1];
542 
543 #ifdef TCTRLDEBUG
544 	printf("tctrl_read_ext_status: after, ext_status = %x\n",
545 	    sc->sc_ext_status);
546 #endif
547 }
548 
549 void
tctrl_bell(struct tctrl_softc * sc,int mask,int value)550 tctrl_bell(struct tctrl_softc *sc, int mask, int value)
551 {
552 	struct tctrl_req req;
553 
554 	req.cmdbuf[0] = TS102_OP_CTL_SPEAKER_VOLUME;
555 	req.cmdbuf[1] = mask;
556 	req.cmdbuf[2] = value;
557 	req.cmdlen = 3;
558 	req.rsplen = 2;
559 
560 	tctrl_request(sc, &req);
561 
562 	/*
563 	 * Note that rspbuf[0] returns the previous value, before any
564 	 * adjustment happened.
565 	 */
566 	if (mask == 0)
567 		sc->sc_bellvol = value;
568 	else
569 		sc->sc_bellvol = req.rspbuf[0];
570 }
571 
572 void
tctrl_brightness(struct tctrl_softc * sc,int mask,int value)573 tctrl_brightness(struct tctrl_softc *sc, int mask, int value)
574 {
575 	struct tctrl_req req;
576 
577 	req.cmdbuf[0] = TS102_OP_CTL_TFT_BRIGHTNESS;
578 	req.cmdbuf[1] = mask;
579 	req.cmdbuf[2] = value;
580 	req.cmdlen = 3;
581 	req.rsplen = 2;
582 
583 	tctrl_request(sc, &req);
584 
585 	/*
586 	 * Note that rspbuf[0] returns the previous value, before any
587 	 * adjustment happened.
588 	 */
589 	if (mask == 0)
590 		sc->sc_brightness = value;
591 	else
592 		sc->sc_brightness = req.rspbuf[0];
593 }
594 
595 void
tctrl_lcd(struct tctrl_softc * sc,int mask,int value)596 tctrl_lcd(struct tctrl_softc *sc, int mask, int value)
597 {
598 	struct tctrl_req req;
599 
600 	req.cmdbuf[0] = TS102_OP_CTL_LCD;
601 
602 	/*
603 	 * The mask setup for this particular command is *very* bizarre
604 	 * and totally undocumented.
605 	 * One would expect the cmdlen and rsplen to be 5 and 3,
606 	 * respectively, as well.  Though luck, they are not...
607 	 */
608 
609 	req.cmdbuf[1] = mask & 0xff;
610 	req.cmdbuf[4] = (mask >> 8) & 0x01;
611 
612 	req.cmdbuf[2] = value & 0xff;
613 	req.cmdbuf[3] = (value >> 8 & 0x01);
614 
615 	req.cmdlen = 3;
616 	req.rsplen = 2;
617 
618 	tctrl_request(sc, &req);
619 }
620 
621 int
tctrl_request(struct tctrl_softc * sc,struct tctrl_req * req)622 tctrl_request(struct tctrl_softc *sc, struct tctrl_req *req)
623 {
624 	int s, rv;
625 
626 	while (sc->sc_wantdata != 0) {
627 		DELAY(1);
628 	}
629 
630 	s = splhigh();
631 	sc->sc_flags |= TCTRL_SEND_REQUEST;
632 	bcopy(req->cmdbuf, sc->sc_cmdbuf, req->cmdlen);
633 	sc->sc_wantdata = 1;
634 	sc->sc_rsplen = req->rsplen;
635 	sc->sc_cmdlen = req->cmdlen;
636 	sc->sc_cmdoff = sc->sc_rspoff = 0;
637 
638 	do {
639 		tctrl_intr(sc);
640 	} while (sc->sc_state != TCTRL_IDLE);
641 
642 	sc->sc_wantdata = 0;	/* just in case... */
643 
644 	rv = sc->sc_rspack;
645 	if (rv != 0)
646 		bcopy(sc->sc_rspbuf, req->rspbuf, sc->sc_rsplen);
647 	else
648 		bzero(req->rspbuf, req->rsplen);	/* safety */
649 	splx(s);
650 
651 	return (rv);
652 }
653 
654 void
tctrl_write_data(sc,v)655 tctrl_write_data(sc, v)
656 	struct tctrl_softc *sc;
657 	u_int8_t v;
658 {
659 	unsigned int i;
660 
661 	for (i = 0; i < 100; i++)  {
662 		if (sc->sc_regs->stat & TS102_UCTRL_STS_TXNF_STA)
663 			break;
664 	}
665 	sc->sc_regs->data = v;
666 }
667 
668 u_int8_t
tctrl_read_data(sc)669 tctrl_read_data(sc)
670 	struct tctrl_softc *sc;
671 {
672 	unsigned int i, v;
673 
674 	for (i = 0; i < 100000; i++) {
675 		if (sc->sc_regs->stat & TS102_UCTRL_STS_RXNE_STA)
676 			break;
677 		DELAY(1);
678 	}
679 
680 	v = sc->sc_regs->data;
681 	sc->sc_regs->stat = TS102_UCTRL_STS_RXNE_STA;
682 	return v;
683 }
684 
685 /*
686  * External interfaces, used by the display and pcmcia drivers, as well
687  * as the powerdown code.
688  */
689 
690 void
tadpole_powerdown(void)691 tadpole_powerdown(void)
692 {
693 	struct tctrl_softc *sc;
694 	struct tctrl_req req;
695 
696 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
697 		return;
698 	}
699 
700 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
701 	req.cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
702 	req.cmdlen = 1;
703 	req.rsplen = 1;
704 
705 	tctrl_request(sc, &req);
706 }
707 
708 void
tadpole_set_brightness(int value)709 tadpole_set_brightness(int value)
710 {
711 	struct tctrl_softc *sc;
712 
713 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
714 		return;
715 	}
716 
717 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
718 	if (value != sc->sc_brightness)
719 		tctrl_brightness(sc, 0, value);
720 }
721 
722 int
tadpole_get_brightness()723 tadpole_get_brightness()
724 {
725 	struct tctrl_softc *sc;
726 
727 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
728 		return 0;
729 	}
730 
731 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
732 	return sc->sc_brightness;
733 }
734 
735 void
tadpole_set_video(int enabled)736 tadpole_set_video(int enabled)
737 {
738 	struct tctrl_softc *sc;
739 
740 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
741 		return;
742 	}
743 
744 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
745 	if (sc->sc_tft_on ^ enabled) {
746 		sc->sc_tft_on = enabled;
747 	}
748 }
749 
750 u_int
tadpole_get_video()751 tadpole_get_video()
752 {
753 	struct tctrl_softc *sc;
754 	unsigned int status;
755 
756 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
757 		return 0;
758 	}
759 
760 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
761 	status = sc->sc_tft_on ? TV_ON : 0;
762 
763 	return status;
764 }
765 
766 void
tadpole_register_extvideo(void (* cb)(void *,int),void * data)767 tadpole_register_extvideo(void (*cb)(void *, int), void *data)
768 {
769 	struct tctrl_softc *sc;
770 
771 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
772 		return;
773 	}
774 
775 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
776 	sc->sc_evcb = cb;
777 	sc->sc_evdata = data;
778 
779 	(*cb)(data, sc->sc_ext_status & TS102_EXT_STATUS_EXTERNAL_VGA_ATTACHED);
780 }
781 
782 void
tadpole_set_pcmcia(int slot,int enabled)783 tadpole_set_pcmcia(int slot, int enabled)
784 {
785 	struct tctrl_softc *sc;
786 	int mask;
787 
788 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
789 		return;
790 	}
791 
792 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
793 	mask = 1 << slot;
794 	enabled = enabled ? mask : 0;
795 	if ((sc->sc_pcmcia_on ^ enabled) & mask) {
796 		sc->sc_pcmcia_on ^= mask;
797 		tctrl_lcd(sc, ~TS102_LCD_PCMCIA_ACTIVE,
798 		    sc->sc_pcmcia_on ? TS102_LCD_PCMCIA_ACTIVE : 0);
799 	}
800 }
801 
802 int
tadpole_bell(u_int duration,u_int freq,u_int volume)803 tadpole_bell(u_int duration, u_int freq, u_int volume)
804 {
805 	struct tctrl_softc *sc;
806 	struct tctrl_req req;
807 
808 	if (tctrl_cd.cd_ndevs == 0 || tctrl_cd.cd_devs[0] == NULL) {
809 		return (0);
810 	}
811 
812 	sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0];
813 
814 	/* Adjust frequency if necessary (first time or frequence change) */
815 	if (freq > 0 && freq <= 0xffff && freq != sc->sc_bellfreq) {
816 		req.cmdbuf[0] = TS102_OP_CMD_SET_BELL_FREQ;
817 		req.cmdbuf[1] = (freq >> 8) & 0xff;
818 		req.cmdbuf[2] = freq & 0xff;
819 		req.cmdlen = 3;
820 		req.rsplen = 1;
821 
822 		tctrl_request(sc, &req);
823 
824 		sc->sc_bellfreq = freq;
825 	}
826 
827 	/* Adjust volume if necessary */
828 	if (volume >= 0 && volume <= 100) {
829 		volume = (volume * 255) / 100;
830 		if (volume != sc->sc_bellvol)
831 			tctrl_bell(sc, 0, volume);
832 
833 	}
834 
835 	req.cmdbuf[0] = TS102_OP_CMD_RING_BELL;
836 	req.cmdbuf[1] = (duration >> 8) & 0xff;
837 	req.cmdbuf[2] = duration & 0xff;
838 	req.cmdlen = 3;
839 	req.rsplen = 1;
840 
841 	tctrl_request(sc, &req);
842 
843 	return (1);
844 }
845