xref: /netbsd-src/sys/arch/sparc/dev/tctrl.c (revision 3b435a73967be44dfb4a27315acd72bfacde430c)
1 /*	$NetBSD: tctrl.c,v 1.2 1999/08/11 00:46:06 matt 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 
54 #include <machine/autoconf.h>
55 #include <machine/cpu.h>
56 #include <machine/bus.h>
57 
58 #include <sparc/dev/ts102reg.h>
59 #include <sparc/dev/tctrlvar.h>
60 
61 static const char *tctrl_ext_statuses[16] = {
62 	"main power available",
63 	"internal battery attached",
64 	"external battery attached",
65 	"external VGA attached",
66 	"external keyboard attached",
67 	"external mouse attached",
68 	"lid down",
69 	"internal battery charging",
70 	"external battery charging",
71 	"internal battery discharging",
72 	"external battery discharging",
73 };
74 
75 struct tctrl_softc {
76 	struct device sc_dev;
77 	bus_space_tag_t sc_memt;
78 	bus_space_handle_t sc_memh;
79 	unsigned int sc_junk;
80 	unsigned int sc_ext_status;
81 	unsigned int sc_pending;
82 #define	TCTRL_SEND_BITPORT		0x0001
83 #define	TCTRL_SEND_POWEROFF		0x0002
84 #define	TCTRL_SEND_RD_EXT_STATUS	0x0004
85 #define	TCTRL_SEND_RD_EVENT_STATUS	0x0008
86 #define	TCTRL_SEND_BITPORT_NOP		0x0010
87 	enum { TCTRL_IDLE, TCTRL_ARGS,
88 		TCTRL_ACK, TCTRL_DATA } sc_state;
89 	u_int8_t sc_cmdbuf[16];
90 	u_int8_t sc_rspbuf[16];
91 	u_int8_t sc_bitport;
92 	u_int8_t sc_tft_on;
93 	u_int8_t sc_op;
94 	u_int8_t sc_cmdoff;
95 	u_int8_t sc_cmdlen;
96 	u_int8_t sc_rspoff;
97 	u_int8_t sc_rsplen;
98 
99 	struct evcnt sc_intrcnt;	/* interrupt counting */
100 };
101 
102 static int tctrl_match(struct device *parent, struct cfdata *cf, void *aux);
103 static void tctrl_attach(struct device *parent, struct device *self, void *aux);
104 
105 static void tctrl_write(struct tctrl_softc *sc, bus_size_t off, u_int8_t v);
106 static u_int8_t tctrl_read(struct tctrl_softc *sc, bus_size_t off);
107 static void tctrl_write_data(struct tctrl_softc *sc, u_int8_t v);
108 static u_int8_t tctrl_read_data(struct tctrl_softc *sc);
109 static int tctrl_intr(void *arg);
110 static void tctrl_setup_bitport(struct tctrl_softc *sc, int nop);
111 static void tctrl_process_response(struct tctrl_softc *sc);
112 
113 struct cfattach tctrl_ca = {
114 	sizeof(struct tctrl_softc), tctrl_match, tctrl_attach
115 };
116 
117 extern struct cfdriver tctrl_cd;
118 
119 static int
120 tctrl_match(struct device *parent, struct cfdata *cf, void *aux)
121 {
122 	union obio_attach_args *uoba = aux;
123 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
124 
125 	if (uoba->uoba_isobio4 != 0) {
126 		return (0);
127 	}
128 
129 	/* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
130 	 * (who's interface is off the TS102 PCMCIA controller but there
131 	 * exists a OpenProm for microcontroller interface).
132 	 */
133 	return strcmp("uctrl", sa->sa_name) == 0;
134 }
135 
136 static void
137 tctrl_attach(struct device *parent, struct device *self, void *aux)
138 {
139 	struct tctrl_softc *sc = (void *)self;
140 	union obio_attach_args *uoba = aux;
141 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
142 	unsigned int i, v;
143 #if 0
144 	unsigned int ack, msb, lsb;
145 #endif
146 
147 	/* We're living on a sbus slot that looks like an obio that
148 	 * looks like an sbus slot.
149 	 */
150 	sc->sc_memt = sa->sa_bustag;
151 	if (sbus_bus_map(sc->sc_memt, sa->sa_slot,
152 			 sa->sa_offset - TS102_REG_UCTRL_INT, sa->sa_size,
153 			 BUS_SPACE_MAP_LINEAR, 0,
154 			 &sc->sc_memh) != 0) {
155 		printf(": can't map registers\n");
156 		return;
157 	}
158 
159 	printf("\n");
160 
161 	sc->sc_tft_on = 1;
162 
163 	/* clear any pending data.
164 	 */
165 	for (i = 0; i < 10000; i++) {
166 		if ((TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) {
167 			break;
168 		}
169 		v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
170 		tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
171 	}
172 
173 	(void)bus_intr_establish(sc->sc_memt, sa->sa_pri, 0, tctrl_intr, sc);
174 	evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
175 
176 	/* See what the external status is
177 	 */
178 	sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
179 	do {
180 		tctrl_intr(sc);
181 	} while (sc->sc_state != TCTRL_IDLE);
182 
183 	if (sc->sc_ext_status != 0) {
184 		const char *sep;
185 
186 		printf("%s: ", sc->sc_dev.dv_xname);
187 		v = sc->sc_ext_status;
188 		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
189 			if (v & 1) {
190 				printf("%s%s", sep, tctrl_ext_statuses[i]);
191 				sep = ", ";
192 			}
193 		}
194 		printf("\n");
195 	}
196 
197 	/* Get a current of the control bitport;
198 	 */
199 	sc->sc_pending |= TCTRL_SEND_BITPORT_NOP;
200 	do {
201 		tctrl_intr(sc);
202 	} while (sc->sc_state != TCTRL_IDLE);
203 
204 	tctrl_write(sc, TS102_REG_UCTRL_INT,
205 		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK);
206 
207 }
208 
209 static int
210 tctrl_intr(void *arg)
211 {
212 	struct tctrl_softc *sc = arg;
213 	unsigned int v, d;
214 	int progress = 0;
215 
216     again:
217 	/* find out the cause(s) of the interrupt */
218 	v = tctrl_read(sc, TS102_REG_UCTRL_STS);
219 
220 	/* clear the cause(s) of the interrupt */
221 	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
222 
223 	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
224 	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
225 		v &= ~TS102_UCTRL_STS_TXNF_STA;
226 	}
227 	if ((v == 0) && (sc->sc_pending == 0 || sc->sc_state != TCTRL_IDLE)) {
228 		return progress;
229 	}
230 
231 	progress = 1;
232 	if (v & TS102_UCTRL_STS_RXNE_STA) {
233 		d = tctrl_read_data(sc);
234 		switch (sc->sc_state) {
235 		case TCTRL_IDLE:
236 			if (d == 0xfa) {
237 				sc->sc_pending |= TCTRL_SEND_RD_EVENT_STATUS;
238 			} else {
239 				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
240 					sc->sc_dev.dv_xname, sc->sc_op, d);
241 			}
242 			goto again;
243 		case TCTRL_ACK:
244 			if (d != 0xfe) {
245 				printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n",
246 					sc->sc_dev.dv_xname, sc->sc_op, d);
247 			}
248 #if 0
249 			printf(" ack=0x%02x", d);
250 #endif
251 			sc->sc_rsplen--;
252 			sc->sc_rspoff = 0;
253 			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
254 #if 0
255 			if (sc->sc_rsplen > 0) {
256 				printf(" [data(%u)]", sc->sc_rsplen);
257 			} else {
258 				printf(" [idle]\n");
259 			}
260 #endif
261 			goto again;
262 		case TCTRL_DATA:
263 			sc->sc_rspbuf[sc->sc_rspoff++] = d;
264 #if 0
265 			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
266 #endif
267 			if (sc->sc_rspoff == sc->sc_rsplen) {
268 #if 0
269 				printf(" [idle]\n");
270 #endif
271 				sc->sc_state = TCTRL_IDLE;
272 				tctrl_process_response(sc);
273 			}
274 			goto again;
275 		default:
276 			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
277 			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
278 			goto again;
279 		}
280 	}
281 	if (sc->sc_state == TCTRL_IDLE) {
282 		sc->sc_cmdoff = 0;
283 		sc->sc_cmdlen = 0;
284 		if (sc->sc_pending & TCTRL_SEND_POWEROFF) {
285 			sc->sc_pending &= ~TCTRL_SEND_POWEROFF;
286 			sc->sc_cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
287 			sc->sc_cmdlen = 1;
288 			sc->sc_rsplen = 0;
289 		} else if (sc->sc_pending & TCTRL_SEND_RD_EVENT_STATUS) {
290 			sc->sc_pending &= ~TCTRL_SEND_RD_EVENT_STATUS;
291 			sc->sc_cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
292 			sc->sc_cmdlen = 1;
293 			sc->sc_rsplen = 3;
294 		} else if (sc->sc_pending & TCTRL_SEND_RD_EXT_STATUS) {
295 			sc->sc_pending &= ~TCTRL_SEND_RD_EXT_STATUS;
296 			sc->sc_cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
297 			sc->sc_cmdlen = 1;
298 			sc->sc_rsplen = 3;
299 		} else if (sc->sc_pending & TCTRL_SEND_BITPORT_NOP) {
300 			sc->sc_pending &= ~TCTRL_SEND_BITPORT_NOP;
301 			tctrl_setup_bitport(sc, 1);
302 		} else if (sc->sc_pending & TCTRL_SEND_BITPORT) {
303 			sc->sc_pending &= ~TCTRL_SEND_BITPORT;
304 			tctrl_setup_bitport(sc, 0);
305 		}
306 		if (sc->sc_cmdlen > 0) {
307 			tctrl_write(sc, TS102_REG_UCTRL_INT,
308 				tctrl_read(sc, TS102_REG_UCTRL_INT)
309 				|TS102_UCTRL_INT_TXNF_MSK
310 				|TS102_UCTRL_INT_TXNF_REQ);
311 			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
312 		}
313 	}
314 	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
315 		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
316 #if 0
317 		if (sc->sc_cmdoff == 1) {
318 			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
319 				sc->sc_cmdbuf[0], sc->sc_rsplen);
320 		} else {
321 			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
322 				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
323 		}
324 #endif
325 		if (sc->sc_cmdoff == sc->sc_cmdlen) {
326 			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
327 #if 0
328 			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
329 #endif
330 			if (sc->sc_cmdoff == 1) {
331 				sc->sc_op = sc->sc_cmdbuf[0];
332 			}
333 			tctrl_write(sc, TS102_REG_UCTRL_INT,
334 				tctrl_read(sc, TS102_REG_UCTRL_INT)
335 				& (~TS102_UCTRL_INT_TXNF_MSK
336 				   |TS102_UCTRL_INT_TXNF_REQ));
337 		} else if (sc->sc_state == TCTRL_IDLE) {
338 			sc->sc_op = sc->sc_cmdbuf[0];
339 			sc->sc_state = TCTRL_ARGS;
340 #if 0
341 			printf(" [args]");
342 #endif
343 		}
344 	}
345 	goto again;
346 }
347 
348 static void
349 tctrl_setup_bitport(struct tctrl_softc *sc, int nop)
350 {
351 	if (nop) {
352 		sc->sc_cmdbuf[0] = TS102_OP_CTL_BITPORT;
353 		sc->sc_cmdbuf[1] = 0xff;
354 		sc->sc_cmdbuf[2] = 0;
355 		sc->sc_cmdlen = 3;
356 		sc->sc_rsplen = 2;
357 	} else {
358 		if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
359 		    || (!sc->sc_tft_on)) {
360 			sc->sc_cmdbuf[2] = TS102_BITPORT_TFTPWR;
361 		} else {
362 			sc->sc_cmdbuf[2] = 0;
363 		}
364 		sc->sc_cmdbuf[0] = TS102_OP_CTL_BITPORT;
365 		sc->sc_cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
366 		sc->sc_cmdlen = 3;
367 		sc->sc_rsplen = 2;
368 	}
369 }
370 
371 static void
372 tctrl_process_response(struct tctrl_softc *sc)
373 {
374 	switch (sc->sc_op) {
375 	case TS102_OP_RD_EXT_STATUS: {
376 		sc->sc_ext_status = sc->sc_rspbuf[0] * 256 + sc->sc_rspbuf[1];
377 		break;
378 	}
379 	case TS102_OP_RD_EVENT_STATUS: {
380 		unsigned int v = sc->sc_rspbuf[0] * 256 + sc->sc_rspbuf[1];
381 		if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
382 			printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
383 		}
384 		if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
385 			printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
386 		}
387 		if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
388 			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
389 		}
390 		if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
391 			sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
392 			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
393 			       (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ? "removed" : "restored");
394 		}
395 		if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
396 			sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
397 			sc->sc_pending |= TCTRL_SEND_BITPORT;
398 #if 0
399 			printf("%s: lid %s\n", sc->sc_dev.dv_xname,
400 			       (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) ? "opened" : "closed");
401 #endif
402 		}
403 		break;
404 	}
405 	case TS102_OP_CTL_BITPORT:
406 		sc->sc_bitport = (sc->sc_rspbuf[0] & sc->sc_cmdbuf[1]) ^ sc->sc_cmdbuf[2];
407 		break;
408 	default:
409 		break;
410 	}
411 }
412 
413 void
414 tadpole_powerdown(void)
415 {
416 	struct tctrl_softc *sc;
417 	int i, s;
418 
419 	if (tctrl_cd.cd_devs == NULL
420 	    || tctrl_cd.cd_ndevs == 0
421 	    || tctrl_cd.cd_devs[0] == NULL) {
422 		return;
423 	}
424 
425 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[0];
426 	s = splhigh();
427 	sc->sc_pending |= TCTRL_SEND_POWEROFF;
428 	for (i = 0; i < 10000; i++) {
429 		tctrl_intr(sc);
430 		DELAY(1);
431 	}
432 	splx(s);
433 }
434 
435 void
436 tadpole_set_video(int enabled)
437 {
438 	struct tctrl_softc *sc;
439 	int s;
440 
441 	if (tctrl_cd.cd_devs == NULL
442 	    || tctrl_cd.cd_ndevs == 0
443 	    || tctrl_cd.cd_devs[0] == NULL) {
444 		return;
445 	}
446 
447 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[0];
448 	s = splhigh();
449 	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
450 		sc->sc_tft_on = enabled;
451 		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
452 			splx(s);
453 			return;
454 		}
455 		sc->sc_pending |= TCTRL_SEND_BITPORT;
456 		tctrl_intr(sc);
457 	}
458 	splx(s);
459 }
460 
461 static void
462 tctrl_write_data(struct tctrl_softc *sc, u_int8_t v)
463 {
464 	unsigned int i;
465 	for (i = 0; i < 100; i++)  {
466 		if (TS102_UCTRL_STS_TXNF_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
467 			break;
468 	}
469 	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
470 }
471 
472 static u_int8_t
473 tctrl_read_data(struct tctrl_softc *sc)
474 {
475 	unsigned int i, v;
476 
477 	for (i = 0; i < 100000; i++) {
478 		if (TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
479 			break;
480 		DELAY(1);
481 	}
482 
483 	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
484 	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
485 	return v;
486 }
487 
488 static u_int8_t
489 tctrl_read(struct tctrl_softc *sc, bus_size_t off)
490 {
491 	sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off);
492 	return sc->sc_junk;
493 }
494 
495 static void
496 tctrl_write(struct tctrl_softc *sc, bus_size_t off, u_int8_t v)
497 {
498 	sc->sc_junk = v;
499 	bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v);
500 }
501