tctrl.c revision 1.2 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