Home | History | Annotate | Line # | Download | only in dev
tctrl.c revision 1.3
      1 /*	$NetBSD: tctrl.c,v 1.3 1999/11/21 15:23:02 pk 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 	if (sa->sa_nintr != 0) {
    174 		(void)bus_intr_establish(sc->sc_memt, sa->sa_pri,
    175 					 0, tctrl_intr, sc);
    176 		evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
    177 	}
    178 
    179 	/* See what the external status is
    180 	 */
    181 	sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
    182 	do {
    183 		tctrl_intr(sc);
    184 	} while (sc->sc_state != TCTRL_IDLE);
    185 
    186 	if (sc->sc_ext_status != 0) {
    187 		const char *sep;
    188 
    189 		printf("%s: ", sc->sc_dev.dv_xname);
    190 		v = sc->sc_ext_status;
    191 		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
    192 			if (v & 1) {
    193 				printf("%s%s", sep, tctrl_ext_statuses[i]);
    194 				sep = ", ";
    195 			}
    196 		}
    197 		printf("\n");
    198 	}
    199 
    200 	/* Get a current of the control bitport;
    201 	 */
    202 	sc->sc_pending |= TCTRL_SEND_BITPORT_NOP;
    203 	do {
    204 		tctrl_intr(sc);
    205 	} while (sc->sc_state != TCTRL_IDLE);
    206 
    207 	tctrl_write(sc, TS102_REG_UCTRL_INT,
    208 		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK);
    209 
    210 }
    211 
    212 static int
    213 tctrl_intr(void *arg)
    214 {
    215 	struct tctrl_softc *sc = arg;
    216 	unsigned int v, d;
    217 	int progress = 0;
    218 
    219     again:
    220 	/* find out the cause(s) of the interrupt */
    221 	v = tctrl_read(sc, TS102_REG_UCTRL_STS);
    222 
    223 	/* clear the cause(s) of the interrupt */
    224 	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
    225 
    226 	v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA);
    227 	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
    228 		v &= ~TS102_UCTRL_STS_TXNF_STA;
    229 	}
    230 	if ((v == 0) && (sc->sc_pending == 0 || sc->sc_state != TCTRL_IDLE)) {
    231 		return progress;
    232 	}
    233 
    234 	progress = 1;
    235 	if (v & TS102_UCTRL_STS_RXNE_STA) {
    236 		d = tctrl_read_data(sc);
    237 		switch (sc->sc_state) {
    238 		case TCTRL_IDLE:
    239 			if (d == 0xfa) {
    240 				sc->sc_pending |= TCTRL_SEND_RD_EVENT_STATUS;
    241 			} else {
    242 				printf("%s: (op=0x%02x): unexpected data (0x%02x)\n",
    243 					sc->sc_dev.dv_xname, sc->sc_op, d);
    244 			}
    245 			goto again;
    246 		case TCTRL_ACK:
    247 			if (d != 0xfe) {
    248 				printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n",
    249 					sc->sc_dev.dv_xname, sc->sc_op, d);
    250 			}
    251 #if 0
    252 			printf(" ack=0x%02x", d);
    253 #endif
    254 			sc->sc_rsplen--;
    255 			sc->sc_rspoff = 0;
    256 			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
    257 #if 0
    258 			if (sc->sc_rsplen > 0) {
    259 				printf(" [data(%u)]", sc->sc_rsplen);
    260 			} else {
    261 				printf(" [idle]\n");
    262 			}
    263 #endif
    264 			goto again;
    265 		case TCTRL_DATA:
    266 			sc->sc_rspbuf[sc->sc_rspoff++] = d;
    267 #if 0
    268 			printf(" [%d]=0x%02x", sc->sc_rspoff-1, d);
    269 #endif
    270 			if (sc->sc_rspoff == sc->sc_rsplen) {
    271 #if 0
    272 				printf(" [idle]\n");
    273 #endif
    274 				sc->sc_state = TCTRL_IDLE;
    275 				tctrl_process_response(sc);
    276 			}
    277 			goto again;
    278 		default:
    279 			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
    280 			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
    281 			goto again;
    282 		}
    283 	}
    284 	if (sc->sc_state == TCTRL_IDLE) {
    285 		sc->sc_cmdoff = 0;
    286 		sc->sc_cmdlen = 0;
    287 		if (sc->sc_pending & TCTRL_SEND_POWEROFF) {
    288 			sc->sc_pending &= ~TCTRL_SEND_POWEROFF;
    289 			sc->sc_cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
    290 			sc->sc_cmdlen = 1;
    291 			sc->sc_rsplen = 0;
    292 		} else if (sc->sc_pending & TCTRL_SEND_RD_EVENT_STATUS) {
    293 			sc->sc_pending &= ~TCTRL_SEND_RD_EVENT_STATUS;
    294 			sc->sc_cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
    295 			sc->sc_cmdlen = 1;
    296 			sc->sc_rsplen = 3;
    297 		} else if (sc->sc_pending & TCTRL_SEND_RD_EXT_STATUS) {
    298 			sc->sc_pending &= ~TCTRL_SEND_RD_EXT_STATUS;
    299 			sc->sc_cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
    300 			sc->sc_cmdlen = 1;
    301 			sc->sc_rsplen = 3;
    302 		} else if (sc->sc_pending & TCTRL_SEND_BITPORT_NOP) {
    303 			sc->sc_pending &= ~TCTRL_SEND_BITPORT_NOP;
    304 			tctrl_setup_bitport(sc, 1);
    305 		} else if (sc->sc_pending & TCTRL_SEND_BITPORT) {
    306 			sc->sc_pending &= ~TCTRL_SEND_BITPORT;
    307 			tctrl_setup_bitport(sc, 0);
    308 		}
    309 		if (sc->sc_cmdlen > 0) {
    310 			tctrl_write(sc, TS102_REG_UCTRL_INT,
    311 				tctrl_read(sc, TS102_REG_UCTRL_INT)
    312 				|TS102_UCTRL_INT_TXNF_MSK
    313 				|TS102_UCTRL_INT_TXNF_REQ);
    314 			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
    315 		}
    316 	}
    317 	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) {
    318 		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
    319 #if 0
    320 		if (sc->sc_cmdoff == 1) {
    321 			printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname,
    322 				sc->sc_cmdbuf[0], sc->sc_rsplen);
    323 		} else {
    324 			printf(" [%d]=0x%02x", sc->sc_cmdoff-1,
    325 				sc->sc_cmdbuf[sc->sc_cmdoff-1]);
    326 		}
    327 #endif
    328 		if (sc->sc_cmdoff == sc->sc_cmdlen) {
    329 			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
    330 #if 0
    331 			printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n");
    332 #endif
    333 			if (sc->sc_cmdoff == 1) {
    334 				sc->sc_op = sc->sc_cmdbuf[0];
    335 			}
    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 		} else if (sc->sc_state == TCTRL_IDLE) {
    341 			sc->sc_op = sc->sc_cmdbuf[0];
    342 			sc->sc_state = TCTRL_ARGS;
    343 #if 0
    344 			printf(" [args]");
    345 #endif
    346 		}
    347 	}
    348 	goto again;
    349 }
    350 
    351 static void
    352 tctrl_setup_bitport(struct tctrl_softc *sc, int nop)
    353 {
    354 	if (nop) {
    355 		sc->sc_cmdbuf[0] = TS102_OP_CTL_BITPORT;
    356 		sc->sc_cmdbuf[1] = 0xff;
    357 		sc->sc_cmdbuf[2] = 0;
    358 		sc->sc_cmdlen = 3;
    359 		sc->sc_rsplen = 2;
    360 	} else {
    361 		if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN)
    362 		    || (!sc->sc_tft_on)) {
    363 			sc->sc_cmdbuf[2] = TS102_BITPORT_TFTPWR;
    364 		} else {
    365 			sc->sc_cmdbuf[2] = 0;
    366 		}
    367 		sc->sc_cmdbuf[0] = TS102_OP_CTL_BITPORT;
    368 		sc->sc_cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
    369 		sc->sc_cmdlen = 3;
    370 		sc->sc_rsplen = 2;
    371 	}
    372 }
    373 
    374 static void
    375 tctrl_process_response(struct tctrl_softc *sc)
    376 {
    377 	switch (sc->sc_op) {
    378 	case TS102_OP_RD_EXT_STATUS: {
    379 		sc->sc_ext_status = sc->sc_rspbuf[0] * 256 + sc->sc_rspbuf[1];
    380 		break;
    381 	}
    382 	case TS102_OP_RD_EVENT_STATUS: {
    383 		unsigned int v = sc->sc_rspbuf[0] * 256 + sc->sc_rspbuf[1];
    384 		if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
    385 			printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
    386 		}
    387 		if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
    388 			printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
    389 		}
    390 		if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
    391 			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
    392 		}
    393 		if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
    394 			sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
    395 			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
    396 			       (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ? "removed" : "restored");
    397 		}
    398 		if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
    399 			sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
    400 			sc->sc_pending |= TCTRL_SEND_BITPORT;
    401 #if 0
    402 			printf("%s: lid %s\n", sc->sc_dev.dv_xname,
    403 			       (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) ? "opened" : "closed");
    404 #endif
    405 		}
    406 		break;
    407 	}
    408 	case TS102_OP_CTL_BITPORT:
    409 		sc->sc_bitport = (sc->sc_rspbuf[0] & sc->sc_cmdbuf[1]) ^ sc->sc_cmdbuf[2];
    410 		break;
    411 	default:
    412 		break;
    413 	}
    414 }
    415 
    416 void
    417 tadpole_powerdown(void)
    418 {
    419 	struct tctrl_softc *sc;
    420 	int i, s;
    421 
    422 	if (tctrl_cd.cd_devs == NULL
    423 	    || tctrl_cd.cd_ndevs == 0
    424 	    || tctrl_cd.cd_devs[0] == NULL) {
    425 		return;
    426 	}
    427 
    428 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[0];
    429 	s = splhigh();
    430 	sc->sc_pending |= TCTRL_SEND_POWEROFF;
    431 	for (i = 0; i < 10000; i++) {
    432 		tctrl_intr(sc);
    433 		DELAY(1);
    434 	}
    435 	splx(s);
    436 }
    437 
    438 void
    439 tadpole_set_video(int enabled)
    440 {
    441 	struct tctrl_softc *sc;
    442 	int s;
    443 
    444 	if (tctrl_cd.cd_devs == NULL
    445 	    || tctrl_cd.cd_ndevs == 0
    446 	    || tctrl_cd.cd_devs[0] == NULL) {
    447 		return;
    448 	}
    449 
    450 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[0];
    451 	s = splhigh();
    452 	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
    453 		sc->sc_tft_on = enabled;
    454 		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
    455 			splx(s);
    456 			return;
    457 		}
    458 		sc->sc_pending |= TCTRL_SEND_BITPORT;
    459 		tctrl_intr(sc);
    460 	}
    461 	splx(s);
    462 }
    463 
    464 static void
    465 tctrl_write_data(struct tctrl_softc *sc, u_int8_t v)
    466 {
    467 	unsigned int i;
    468 	for (i = 0; i < 100; i++)  {
    469 		if (TS102_UCTRL_STS_TXNF_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
    470 			break;
    471 	}
    472 	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
    473 }
    474 
    475 static u_int8_t
    476 tctrl_read_data(struct tctrl_softc *sc)
    477 {
    478 	unsigned int i, v;
    479 
    480 	for (i = 0; i < 100000; i++) {
    481 		if (TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS))
    482 			break;
    483 		DELAY(1);
    484 	}
    485 
    486 	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
    487 	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA);
    488 	return v;
    489 }
    490 
    491 static u_int8_t
    492 tctrl_read(struct tctrl_softc *sc, bus_size_t off)
    493 {
    494 	sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off);
    495 	return sc->sc_junk;
    496 }
    497 
    498 static void
    499 tctrl_write(struct tctrl_softc *sc, bus_size_t off, u_int8_t v)
    500 {
    501 	sc->sc_junk = v;
    502 	bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v);
    503 }
    504