Home | History | Annotate | Line # | Download | only in dev
tctrl.c revision 1.1
      1 /*	$NetBSD: tctrl.c,v 1.1 1999/08/09 18:39:58 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 	enum { TCTRL_IDLE, TCTRL_ARGS,
     87 		TCTRL_ACK, TCTRL_DATA } sc_state;
     88 	u_int8_t sc_cmdbuf[16];
     89 	u_int8_t sc_rspbuf[16];
     90 	u_int8_t sc_bitport;
     91 	u_int8_t sc_tft_on;
     92 	u_int8_t sc_op;
     93 	u_int8_t sc_cmdoff;
     94 	u_int8_t sc_cmdlen;
     95 	u_int8_t sc_rspoff;
     96 	u_int8_t sc_rsplen;
     97 
     98 	struct evcnt sc_intrcnt;	/* interrupt counting */
     99 };
    100 
    101 static int tctrl_match(struct device *parent, struct cfdata *cf, void *aux);
    102 static void tctrl_attach(struct device *parent, struct device *self, void *aux);
    103 
    104 static void tctrl_write(struct tctrl_softc *sc, bus_size_t off, u_int8_t v);
    105 static u_int8_t tctrl_read(struct tctrl_softc *sc, bus_size_t off);
    106 static void tctrl_write_data(struct tctrl_softc *sc, u_int8_t v);
    107 static u_int8_t tctrl_read_data(struct tctrl_softc *sc);
    108 static int tctrl_intr(void *arg);
    109 static void tctrl_setup_bitport(struct tctrl_softc *sc);
    110 static void tctrl_process_response(struct tctrl_softc *sc);
    111 
    112 struct cfattach tctrl_ca = {
    113 	sizeof(struct tctrl_softc), tctrl_match, tctrl_attach
    114 };
    115 
    116 extern struct cfdriver tctrl_cd;
    117 
    118 static int
    119 tctrl_match(struct device *parent, struct cfdata *cf, void *aux)
    120 {
    121 	union obio_attach_args *uoba = aux;
    122 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
    123 
    124 	if (uoba->uoba_isobio4 != 0) {
    125 		return (0);
    126 	}
    127 
    128 	/* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller
    129 	 * (who's interface is off the TS102 PCMCIA controller but there
    130 	 * exists a OpenProm for microcontroller interface).
    131 	 */
    132 	return strcmp("uctrl", sa->sa_name) == 0;
    133 }
    134 
    135 static void
    136 tctrl_attach(struct device *parent, struct device *self, void *aux)
    137 {
    138 	struct tctrl_softc *sc = (void *)self;
    139 	union obio_attach_args *uoba = aux;
    140 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
    141 	unsigned int ack, msb, lsb;
    142 	unsigned int i, v;
    143 	const char *sep;
    144 
    145 	/* We're living on a sbus slot that looks like an obio that
    146 	 * looks like an sbus slot.
    147 	 */
    148 	sc->sc_memt = sa->sa_bustag;
    149 	if (sbus_bus_map(sc->sc_memt, sa->sa_slot,
    150 			 sa->sa_offset - TS102_REG_UCTRL_INT, sa->sa_size,
    151 			 BUS_SPACE_MAP_LINEAR, 0,
    152 			 &sc->sc_memh) != 0) {
    153 		printf(": can't map registers\n");
    154 		return;
    155 	}
    156 
    157 	sc->sc_tft_on = 1;
    158 	/* clear any pending data.
    159 	 */
    160 	for (i = 0; i < 10000; i++) {
    161 		if ((TS102_UCTRL_STS_RXNE_REQ & tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) {
    162 			break;
    163 		}
    164 		v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
    165 		tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_REQ);
    166 	}
    167 
    168 	printf("\n");
    169 
    170 	tctrl_write_data(sc, TS102_OP_RD_EXT_STATUS);
    171 	ack = tctrl_read_data(sc);
    172 	msb = tctrl_read_data(sc);
    173 	lsb = tctrl_read_data(sc);
    174 	sc->sc_ext_status = v = msb * 256 + lsb;
    175 
    176 	if (sc->sc_ext_status) {
    177 		printf("%s: ", sc->sc_dev.dv_xname);
    178 		for (i = 0, sep = ""; v != 0; i++, v >>= 1) {
    179 			if (v & 1) {
    180 				printf("%s%s", sep, tctrl_ext_statuses[i]);
    181 				sep = ", ";
    182 			}
    183 		}
    184 		printf("\n");
    185 	}
    186 
    187 	sc->sc_pending |= TCTRL_SEND_BITPORT;
    188 
    189 	(void)bus_intr_establish(sc->sc_memt, sa->sa_pri, 0, tctrl_intr, sc);
    190 	evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
    191 
    192 	tctrl_write(sc, TS102_REG_UCTRL_STS,
    193 		    tctrl_read(sc, TS102_REG_UCTRL_STS)
    194 		    |TS102_UCTRL_INT_RXNE_MSK|TS102_UCTRL_INT_TXNF_MSK);
    195 	tctrl_write(sc, TS102_REG_UCTRL_INT,
    196 		    TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_TXNF_REQ|
    197 		    TS102_UCTRL_INT_RXNE_MSK|TS102_UCTRL_INT_TXNF_MSK);
    198 #if 0
    199 	printf("%s: int=0x%02x sts=0x%02x\n", sc->sc_dev.dv_xname,
    200 		tctrl_read(sc, TS102_REG_UCTRL_INT),
    201 		tctrl_read(sc, TS102_REG_UCTRL_STS));
    202 #endif
    203 }
    204 
    205 static int
    206 tctrl_intr(void *arg)
    207 {
    208 	struct tctrl_softc *sc = arg;
    209 	unsigned int v, d;
    210 	int progress = 0;
    211 
    212     again:
    213 	/* find out the cause(s) of the interrupt */
    214 	v = tctrl_read(sc, TS102_REG_UCTRL_STS);
    215 
    216 	/* clear the cause(s) of the interrupt */
    217 	tctrl_write(sc, TS102_REG_UCTRL_STS, v);
    218 
    219 	v &= ~(TS102_UCTRL_STS_RXO_REQ|TS102_UCTRL_STS_TXE_REQ);
    220 	if (sc->sc_cmdoff >= sc->sc_cmdlen) {
    221 		v &= ~TS102_UCTRL_STS_TXNF_REQ;
    222 	}
    223 	if ((v == 0) && (sc->sc_pending == 0 || sc->sc_state != TCTRL_IDLE)) {
    224 		return progress;
    225 	}
    226 
    227 	progress = 1;
    228 	if (v & TS102_UCTRL_STS_RXNE_REQ) {
    229 		d = tctrl_read_data(sc);
    230 		switch (sc->sc_state) {
    231 		case TCTRL_IDLE:
    232 			if (d == 0xfb) {
    233 				sc->sc_pending |= TCTRL_SEND_RD_EVENT_STATUS;
    234 			}
    235 			break;
    236 		case TCTRL_ACK:
    237 			if (d != 0xfe) {
    238 				printf("%s: (op=0x%02x): unexpected value for ack (0x%02x)\n",
    239 					sc->sc_dev.dv_xname, sc->sc_op, d);
    240 			}
    241 			sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE;
    242 			sc->sc_rspoff = 0;
    243 			sc->sc_rsplen--;
    244 			break;
    245 		case TCTRL_DATA:
    246 			sc->sc_rspbuf[sc->sc_rspoff++] = d;
    247 			if (sc->sc_rspoff == sc->sc_rsplen) {
    248 				sc->sc_state = TCTRL_IDLE;
    249 				if (sc->sc_rsplen > 0) {
    250 					tctrl_process_response(sc);
    251 				}
    252 			}
    253 			break;
    254 		default:
    255 			printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n",
    256 			       sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state);
    257 			break;
    258 		}
    259 	}
    260 	if (sc->sc_state == TCTRL_IDLE) {
    261 		sc->sc_cmdoff = 0;
    262 		if (sc->sc_pending & TCTRL_SEND_POWEROFF) {
    263 			sc->sc_pending &= ~TCTRL_SEND_POWEROFF;
    264 			sc->sc_cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF;
    265 			sc->sc_cmdlen = 1;
    266 			sc->sc_rsplen = 0;
    267 		} else if (sc->sc_pending & TCTRL_SEND_RD_EVENT_STATUS) {
    268 			sc->sc_pending &= ~TCTRL_SEND_RD_EVENT_STATUS;
    269 			sc->sc_cmdbuf[0] = TS102_OP_RD_EVENT_STATUS;
    270 			sc->sc_cmdlen = 1;
    271 			sc->sc_rsplen = 3;
    272 		} else if (sc->sc_pending & TCTRL_SEND_RD_EXT_STATUS) {
    273 			sc->sc_pending &= ~TCTRL_SEND_RD_EXT_STATUS;
    274 			sc->sc_cmdbuf[0] = TS102_OP_RD_EXT_STATUS;
    275 			sc->sc_cmdlen = 1;
    276 			sc->sc_rsplen = 3;
    277 		} else if (sc->sc_pending & TCTRL_SEND_BITPORT) {
    278 			sc->sc_pending &= ~TCTRL_SEND_BITPORT;
    279 			tctrl_setup_bitport(sc);
    280 		}
    281 		if (sc->sc_cmdlen > 0) {
    282 			tctrl_write(sc, TS102_REG_UCTRL_INT,
    283 				tctrl_read(sc, TS102_REG_UCTRL_INT)
    284 				|TS102_UCTRL_INT_TXNF_MSK
    285 				|TS102_UCTRL_INT_TXNF_REQ);
    286 			v = tctrl_read(sc, TS102_REG_UCTRL_STS);
    287 		}
    288 	}
    289 	if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_REQ)) {
    290 		tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]);
    291 		if (sc->sc_cmdoff == sc->sc_cmdlen) {
    292 			sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE;
    293 			tctrl_write(sc, TS102_REG_UCTRL_INT,
    294 				tctrl_read(sc, TS102_REG_UCTRL_INT)
    295 				& (~TS102_UCTRL_INT_TXNF_MSK
    296 				   |TS102_UCTRL_INT_TXNF_REQ));
    297 		} else if (sc->sc_state == TCTRL_IDLE) {
    298 			sc->sc_op = sc->sc_cmdbuf[0];
    299 			sc->sc_state = TCTRL_ARGS;
    300 		}
    301 	}
    302 	goto again;
    303 }
    304 
    305 static void
    306 tctrl_setup_bitport(struct tctrl_softc *sc)
    307 {
    308 	sc->sc_cmdbuf[0] = TS102_OP_CTL_BITPORT;
    309 	sc->sc_cmdbuf[1] = ~TS102_BITPORT_TFTPWR;
    310 	if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
    311 		sc->sc_cmdbuf[2] = 0;
    312 	} else {
    313 		sc->sc_cmdbuf[2] = sc->sc_tft_on ? TS102_BITPORT_TFTPWR : 0;
    314 	}
    315 	sc->sc_cmdlen = 3;
    316 	sc->sc_rsplen = 2;
    317 }
    318 
    319 static void
    320 tctrl_process_response(struct tctrl_softc *sc)
    321 {
    322 	switch (sc->sc_op) {
    323 	case TS102_OP_RD_EXT_STATUS: {
    324 		sc->sc_ext_status = sc->sc_rspbuf[0] * 256 + sc->sc_rspbuf[1];
    325 		break;
    326 	}
    327 	case TS102_OP_RD_EVENT_STATUS: {
    328 		unsigned int v = sc->sc_rspbuf[0] * 256 + sc->sc_rspbuf[1];
    329 		if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) {
    330 			printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname);
    331 		}
    332 		if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) {
    333 			printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
    334 		}
    335 		if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) {
    336 			printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname);
    337 		}
    338 		if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) {
    339 			sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
    340 			printf("%s: main power %s\n", sc->sc_dev.dv_xname,
    341 			       (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ? "removed" : "restored");
    342 		}
    343 		if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) {
    344 			sc->sc_pending |= TCTRL_SEND_RD_EXT_STATUS;
    345 			sc->sc_pending |= TCTRL_SEND_BITPORT;
    346 		}
    347 		break;
    348 	}
    349 	case TS102_OP_CTL_BITPORT:
    350 		sc->sc_bitport = sc->sc_rspbuf[0];
    351 		break;
    352 	default:
    353 		break;
    354 	}
    355 }
    356 
    357 void
    358 tadpole_powerdown(void)
    359 {
    360 	struct tctrl_softc *sc;
    361 	int i, s;
    362 
    363 	if (tctrl_cd.cd_devs == NULL
    364 	    || tctrl_cd.cd_ndevs == 0
    365 	    || tctrl_cd.cd_devs[0] == NULL) {
    366 		return;
    367 	}
    368 
    369 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[0];
    370 	s = splhigh();
    371 	sc->sc_pending |= TCTRL_SEND_POWEROFF;
    372 	for (i = 0; i < 10000; i++) {
    373 		tctrl_intr(sc);
    374 		DELAY(1);
    375 	}
    376 	splx(s);
    377 }
    378 
    379 void
    380 tadpole_set_video(int enabled)
    381 {
    382 	struct tctrl_softc *sc;
    383 	int s;
    384 
    385 	if (tctrl_cd.cd_devs == NULL
    386 	    || tctrl_cd.cd_ndevs == 0
    387 	    || tctrl_cd.cd_devs[0] == NULL) {
    388 		return;
    389 	}
    390 
    391 	sc = (struct tctrl_softc *) tctrl_cd.cd_devs[0];
    392 	s = splhigh();
    393 	if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) {
    394 		sc->sc_tft_on = enabled;
    395 		if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) {
    396 			splx(s);
    397 			return;
    398 		}
    399 		sc->sc_pending |= TCTRL_SEND_BITPORT;
    400 		tctrl_intr(sc);
    401 	}
    402 	splx(s);
    403 }
    404 
    405 static void
    406 tctrl_write_data(struct tctrl_softc *sc, u_int8_t v)
    407 {
    408 	unsigned int i;
    409 	for (i = 0; i < 100; i++)  {
    410 		if (TS102_UCTRL_STS_TXNF_REQ & tctrl_read(sc, TS102_REG_UCTRL_STS))
    411 			break;
    412 	}
    413 	tctrl_write(sc, TS102_REG_UCTRL_DATA, v);
    414 }
    415 
    416 static u_int8_t
    417 tctrl_read_data(struct tctrl_softc *sc)
    418 {
    419 	unsigned int i, v;
    420 
    421 	for (i = 0; i < 100000; i++) {
    422 		if (TS102_UCTRL_STS_RXNE_REQ & tctrl_read(sc, TS102_REG_UCTRL_STS))
    423 			break;
    424 		DELAY(1);
    425 	}
    426 
    427 	v = tctrl_read(sc, TS102_REG_UCTRL_DATA);
    428 	tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_REQ);
    429 	return v;
    430 }
    431 
    432 static u_int8_t
    433 tctrl_read(struct tctrl_softc *sc, bus_size_t off)
    434 {
    435 	sc->sc_junk = bus_space_read_4(sc->sc_memt, sc->sc_memh, off) >> 24;
    436 	return sc->sc_junk;
    437 }
    438 
    439 static void
    440 tctrl_write(struct tctrl_softc *sc, bus_size_t off, u_int8_t v)
    441 {
    442 	sc->sc_junk = v;
    443 	bus_space_write_4(sc->sc_memt, sc->sc_memh, off, v << 24);
    444 }
    445