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