wmcom.c revision 1.8 1 /* $NetBSD: wmcom.c,v 1.8 2019/11/10 21:16:25 chs Exp $ */
2 /*
3 * Copyright (c) 2012 KIYOHARA Takashi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: wmcom.c,v 1.8 2019/11/10 21:16:25 chs Exp $");
29
30 #include "rnd.h"
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/device.h>
36 #include <sys/errno.h>
37 #include <sys/fcntl.h>
38 #include <sys/intr.h>
39 #include <sys/kauth.h>
40 #include <sys/lwp.h>
41 #include <sys/malloc.h>
42 #include <sys/systm.h>
43 #include <sys/termios.h>
44 #include <sys/tty.h>
45 #include <sys/types.h>
46
47 #include <epoc32/windermere/windermerereg.h>
48 #include <epoc32/windermere/windermerevar.h>
49
50 #include <dev/cons.h>
51
52 #ifdef RND_COM
53 #include <sys/rndsource.h>
54 #endif
55
56 #include "ioconf.h"
57 #include "locators.h"
58
59 #define COMUNIT(x) TTUNIT(x)
60 #define COMDIALOUT(x) TTDIALOUT(x)
61
62 #define WMCOM_RING_SIZE 2048
63
64 struct wmcom_softc {
65 device_t sc_dev;
66 bus_space_tag_t sc_iot;
67 bus_space_handle_t sc_ioh;
68
69 void *sc_si;
70
71 struct tty *sc_tty;
72
73 u_char *sc_tba;
74 u_int sc_tbc;
75 u_char *sc_rbuf;
76 char *volatile sc_rbget;
77 char *volatile sc_rbput;
78 volatile int sc_rbavail;
79
80 int sc_tx_done;
81 int sc_rx_ready;
82
83 int sc_hwflags;
84 #define COM_HW_CONSOLE (1 << 0)
85 #define COM_HW_DEV_OK (1 << 1)
86 #define COM_HW_KGDB (1 << 2)
87 int sc_swflags;
88
89 int sc_flags;
90 #define WMCOM_IRDA (1 << 0)
91
92 #ifdef RND_COM
93 krandsource_t rnd_source;
94 #endif
95 };
96
97 static int wmcom_match(device_t, cfdata_t, void *);
98 static void wmcom_attach(device_t, device_t, void *);
99
100 static int wmcom_intr(void *);
101 static void wmcom_soft(void *);
102
103 static void wmcom_start(struct tty *);
104 static int wmcom_param(struct tty *, struct termios *);
105 static int wmcom_hwiflow(struct tty *, int);
106
107 dev_type_open(wmcomopen);
108 dev_type_close(wmcomclose);
109 dev_type_read(wmcomread);
110 dev_type_write(wmcomwrite);
111 dev_type_ioctl(wmcomioctl);
112 dev_type_stop(wmcomstop);
113 dev_type_tty(wmcomtty);
114 dev_type_poll(wmcompoll);
115
116 static void wmcom_iflush(struct wmcom_softc *);
117 static void wmcom_shutdown(struct wmcom_softc *);
118 static void wmcom_break(struct wmcom_softc *, int);
119
120 static void wmcom_rxsoft(struct wmcom_softc *, struct tty *);
121
122 static inline uint32_t wmcom_rate2lcr(int);
123 static uint8_t wmcom_cflag2fcr(tcflag_t);
124
125 static int wmcom_cngetc(dev_t);
126 static void wmcom_cnputc(dev_t, int);
127 static void wmcom_cnpollc(dev_t, int);
128
129 CFATTACH_DECL_NEW(wmcom, sizeof(struct wmcom_softc),
130 wmcom_match, wmcom_attach, NULL, NULL);
131
132 const struct cdevsw wmcom_cdevsw = {
133 .d_open = wmcomopen,
134 .d_close = wmcomclose,
135 .d_read = wmcomread,
136 .d_write = wmcomwrite,
137 .d_ioctl = wmcomioctl,
138 .d_stop = wmcomstop,
139 .d_tty = wmcomtty,
140 .d_poll = wmcompoll,
141 .d_mmap = nommap,
142 .d_kqfilter = ttykqfilter,
143 .d_discard = nodiscard,
144 .d_flag = D_TTY
145 };
146
147 static struct cnm_state wmcom_cnm_state;
148 static vaddr_t wmcom_cnaddr;
149 static int wmcom_cnrate;
150 static tcflag_t wmcom_cncflag;
151
152
153 /* ARGSUSED */
154 static int
155 wmcom_match(device_t parent, cfdata_t match, void *aux)
156 {
157 struct windermere_attach_args *aa = aux;
158
159 /* Wildcard not accept */
160 if (aa->aa_offset == WINDERMERECF_OFFSET_DEFAULT ||
161 aa->aa_irq == WINDERMERECF_IRQ_DEFAULT)
162 return 0;
163
164 aa->aa_size = UART_SIZE;
165 return 1;
166 }
167
168 /* ARGSUSED */
169 static void
170 wmcom_attach(device_t parent, device_t self, void *aux)
171 {
172 struct wmcom_softc *sc = device_private(self);
173 struct windermere_attach_args *aa = aux;
174
175 aprint_naive("\n");
176 aprint_normal("\n");
177
178 sc->sc_dev = self;
179 if (windermere_bus_space_subregion(aa->aa_iot, *aa->aa_ioh,
180 aa->aa_offset, aa->aa_size, &sc->sc_ioh) != 0) {
181 aprint_error_dev(self, "can't map registers\n");
182 return;
183 }
184 sc->sc_iot = aa->aa_iot;
185 if (intr_establish(aa->aa_irq, IPL_SERIAL, 0, wmcom_intr, sc) == NULL) {
186 aprint_error_dev(self, "can't establish interrupt\n");
187 return;
188 }
189
190 if (aa->aa_offset == (wmcom_cnaddr & 0xfff))
191 SET(sc->sc_hwflags, COM_HW_CONSOLE);
192
193 if (aa->aa_offset == WINDERMERE_COM0_OFFSET)
194 SET(sc->sc_flags, WMCOM_IRDA);
195
196 sc->sc_tty = tty_alloc();
197 sc->sc_tty->t_oproc = wmcom_start;
198 sc->sc_tty->t_param = wmcom_param;
199 sc->sc_tty->t_hwiflow = wmcom_hwiflow;
200
201 sc->sc_tbc = 0;
202 sc->sc_rbuf = malloc(WMCOM_RING_SIZE << 1, M_DEVBUF, M_WAITOK);
203 sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
204 sc->sc_rbavail = WMCOM_RING_SIZE;
205
206 tty_attach(sc->sc_tty);
207
208 if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
209 int maj = cdevsw_lookup_major(&wmcom_cdevsw);
210
211 sc->sc_tty->t_dev = makedev(maj, device_unit(sc->sc_dev));
212 cn_tab->cn_dev = sc->sc_tty->t_dev;
213
214 aprint_normal_dev(self, "console\n");
215 }
216
217 sc->sc_si = softint_establish(SOFTINT_SERIAL, wmcom_soft, sc);
218
219 #ifdef RND_COM
220 rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
221 RND_TYPE_TTY, RND_FLAG_DEFAULT);
222 #endif
223
224 SET(sc->sc_hwflags, COM_HW_DEV_OK);
225 }
226
227 static int
228 wmcom_intr(void *arg)
229 {
230 struct wmcom_softc *sc = arg;
231 bus_space_tag_t iot = sc->sc_iot;
232 bus_space_handle_t ioh = sc->sc_ioh;
233 int cc;
234 uint32_t data;
235 uint8_t fr, intm;
236 u_char *put;
237
238 if (!device_is_active(sc->sc_dev))
239 return 0;
240
241 fr = bus_space_read_1(iot, ioh, UARTFR);
242 intm = bus_space_read_1(iot, ioh, UARTINTM);
243 if (bus_space_read_1(iot, ioh, UARTINT) & INT_RXINT) {
244 put = sc->sc_rbput;
245 cc = sc->sc_rbavail;
246 while (cc > 0) {
247 if (ISSET(fr, FR_RXFE))
248 break;
249 data = bus_space_read_4(iot, ioh, UARTDR);
250 cn_check_magic(sc->sc_tty->t_dev, data & 0xff,
251 wmcom_cnm_state);
252
253 put[0] = data & 0xff;
254 put[1] = (data >> 8) & 0xff;
255 put += 2;
256 if (put >= sc->sc_rbuf + (WMCOM_RING_SIZE << 1))
257 put = sc->sc_rbuf;
258 cc--;
259 sc->sc_rx_ready = 1;
260
261 fr = bus_space_read_1(iot, ioh, UARTFR);
262 }
263
264 /*
265 * Current string of incoming characters ended because
266 * no more data was available or we ran out of space.
267 * Schedule a receive event if any data was received.
268 * If we're out of space, turn off receive interrupts.
269 */
270 sc->sc_rbput = put;
271 sc->sc_rbavail = cc;
272
273 /*
274 * See if we are in danger of overflowing a buffer. If
275 * so, use hardware flow control to ease the pressure.
276 */
277
278 /* but wmcom cannot. X-( */
279
280 /*
281 * If we're out of space, disable receive interrupts
282 * until the queue has drained a bit.
283 */
284 if (cc <= 0)
285 CLR(intm, INT_RXINT);
286 }
287
288 /*
289 * Done handling any receive interrupts. See if data can be
290 * transmitted as well. Schedule tx done event if no data left
291 * and tty was marked busy.
292 */
293
294 if (!ISSET(fr, FR_TXFF)) {
295 /* Output the next chunk of the contiguous buffer, if any. */
296 if (sc->sc_tbc > 0) {
297 while (sc->sc_tbc > 0 && !ISSET(fr, FR_TXFF)) {
298 bus_space_write_1(iot, ioh, UARTDR,
299 *sc->sc_tba);
300 sc->sc_tba++;
301 sc->sc_tbc--;
302 fr = bus_space_read_1(iot, ioh, UARTFR);
303 }
304 } else if (!ISSET(fr, FR_BUSY) && ISSET(intm, INT_TXINT)) {
305 CLR(intm, INT_TXINT);
306 sc->sc_tx_done = 1;
307 }
308 }
309
310 bus_space_write_1(iot, ioh, UARTINTM, intm);
311
312 /* Wake up the poller. */
313 softint_schedule(sc->sc_si);
314
315 return 1;
316 }
317
318 static void
319 wmcom_soft(void *arg)
320 {
321 struct wmcom_softc *sc = arg;
322 struct tty *tp = sc->sc_tty;
323
324 if (!device_is_active(sc->sc_dev))
325 return;
326
327 if (sc->sc_rx_ready) {
328 sc->sc_rx_ready = 0;
329 wmcom_rxsoft(sc, tp);
330 }
331 if (sc->sc_tx_done) {
332 sc->sc_tx_done = 0;
333 CLR(tp->t_state, TS_BUSY);
334 if (ISSET(tp->t_state, TS_FLUSH))
335 CLR(tp->t_state, TS_FLUSH);
336 else
337 ndflush(&tp->t_outq,
338 (int)(sc->sc_tba - tp->t_outq.c_cf));
339 (*tp->t_linesw->l_start)(tp);
340 }
341 }
342
343 static void
344 wmcom_start(struct tty *tp)
345 {
346 struct wmcom_softc *sc
347 = device_lookup_private(&wmcom_cd, COMUNIT(tp->t_dev));
348 bus_space_tag_t iot = sc->sc_iot;
349 bus_space_handle_t ioh = sc->sc_ioh;
350 int s, n;
351 uint8_t intm;
352
353 if (!device_is_active(sc->sc_dev))
354 return;
355
356 s = spltty();
357 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
358 goto out;
359 if (!ttypull(tp))
360 goto out;
361
362 /* Grab the first contiguous region of buffer space. */
363 {
364 u_char *tba;
365 int tbc;
366
367 tba = tp->t_outq.c_cf;
368 tbc = ndqb(&tp->t_outq, 0);
369
370 (void)splserial();
371
372 sc->sc_tba = tba;
373 sc->sc_tbc = tbc;
374 }
375
376 SET(tp->t_state, TS_BUSY);
377
378 intm = bus_space_read_1(iot, ioh, UARTINTM);
379 if (!ISSET(intm, INT_TXINT)) {
380 bus_space_write_1(iot, ioh, UARTINTM, intm | INT_TXINT);
381
382 /* Output the first chunk of the contiguous buffer. */
383 n = uimin(sc->sc_tbc, UART_FIFO_SIZE);
384 bus_space_write_multi_1(iot, ioh, UARTDR, sc->sc_tba, n);
385 sc->sc_tba += n;
386 sc->sc_tbc -= n;
387 }
388 out:
389 splx(s);
390 return;
391 }
392
393 static int
394 wmcom_param(struct tty *tp, struct termios *t)
395 {
396 struct wmcom_softc *sc =
397 device_lookup_private(&wmcom_cd, COMUNIT(tp->t_dev));
398 bus_space_tag_t iot = sc->sc_iot;
399 bus_space_handle_t ioh = sc->sc_ioh;
400 int s;
401
402 if (!device_is_active(sc->sc_dev))
403 return ENXIO;
404 if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
405 return EINVAL;
406
407 /*
408 * For the console, always force CLOCAL and !HUPCL, so that the port
409 * is always active.
410 */
411 if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
412 ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
413 SET(t->c_cflag, CLOCAL);
414 CLR(t->c_cflag, HUPCL);
415 }
416
417 /*
418 * If there were no changes, don't do anything. This avoids dropping
419 * input and improves performance when all we did was frob things like
420 * VMIN and VTIME.
421 */
422 if (tp->t_ospeed == t->c_ospeed &&
423 tp->t_cflag == t->c_cflag)
424 return 0;
425
426 s = splserial();
427 bus_space_write_4(iot, ioh, UARTLCR, wmcom_rate2lcr(t->c_ospeed));
428 bus_space_write_1(iot, ioh, UARTFCR, wmcom_cflag2fcr(t->c_cflag));
429
430 /* And copy to tty. */
431 tp->t_ispeed = 0;
432 tp->t_ospeed = t->c_ospeed;
433 tp->t_cflag = t->c_cflag;
434 splx(s);
435
436 /*
437 * Update the tty layer's idea of the carrier bit.
438 * We tell tty the carrier is always on.
439 */
440 (*tp->t_linesw->l_modem)(tp, 1);
441
442 return 0;
443 }
444
445 static int
446 wmcom_hwiflow(struct tty *tp, int block)
447 {
448 /* Nothing */
449 return 0;
450 }
451
452 /* ARGSUSED */
453 int
454 wmcomopen(dev_t dev, int flag, int mode, struct lwp *l)
455 {
456 struct wmcom_softc *sc;
457 struct tty *tp;
458 int error, s, s2;
459 uint8_t con;
460
461 sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
462 if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK))
463 return ENXIO;
464 if (!device_is_active(sc->sc_dev))
465 return ENXIO;
466
467 #ifdef KGDB
468 /*
469 * If this is the kgdb port, no other use is permitted.
470 */
471 if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
472 return EBUSY;
473 #endif
474
475 tp = sc->sc_tty;
476
477 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
478 return EBUSY;
479
480 s = spltty();
481
482 /*
483 * Do the following iff this is a first open.
484 */
485 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
486 struct termios t;
487
488 tp->t_dev = dev;
489
490 /* Enable and turn on interrupt */
491 con = CON_UARTEN;
492 if (ISSET(sc->sc_flags, WMCOM_IRDA))
493 con |= CON_IRTXM;
494 bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTCON, con);
495 bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTINTM, INT_RXINT);
496
497 /*
498 * Initialize the termios status to the defaults. Add in the
499 * sticky bits from TIOCSFLAGS.
500 */
501 t.c_ispeed = 0;
502 if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
503 t.c_ospeed = wmcom_cnrate;
504 t.c_cflag = wmcom_cncflag;
505 } else {
506 t.c_ospeed = TTYDEF_SPEED;
507 t.c_cflag = TTYDEF_CFLAG;
508 }
509 if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
510 SET(t.c_cflag, CLOCAL);
511 if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
512 SET(t.c_cflag, CRTSCTS);
513 if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
514 SET(t.c_cflag, MDMBUF);
515 /* Make sure wmcom_param() we do something */
516 tp->t_ospeed = 0;
517 wmcom_param(tp, &t);
518 tp->t_iflag = TTYDEF_IFLAG;
519 tp->t_oflag = TTYDEF_OFLAG;
520 tp->t_lflag = TTYDEF_LFLAG;
521 ttychars(tp);
522 ttsetwater(tp);
523
524 s2 = splserial();
525
526 /* Clear the input ring. */
527 sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
528 sc->sc_rbavail = WMCOM_RING_SIZE;
529 wmcom_iflush(sc);
530
531 splx(s2);
532 }
533
534 splx(s);
535
536 error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
537 if (error)
538 goto bad;
539
540 error = (*tp->t_linesw->l_open)(dev, tp);
541 if (error)
542 goto bad;
543 return 0;
544
545 bad:
546 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
547 /*
548 * We failed to open the device, and nobody else had it opened.
549 * Clean up the state as appropriate.
550 */
551 wmcom_shutdown(sc);
552
553 /* Disable UART */
554 if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
555 bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTCON, 0);
556 }
557
558 return error;
559 }
560
561 /* ARGSUSED */
562 int
563 wmcomclose(dev_t dev, int flag, int mode, struct lwp *l)
564 {
565 struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
566 struct tty *tp = sc->sc_tty;
567
568 /* XXXX This is for cons.c. */
569 if (!ISSET(tp->t_state, TS_ISOPEN))
570 return 0;
571
572 (*tp->t_linesw->l_close)(tp, flag);
573 ttyclose(tp);
574
575 if (!device_is_active(sc->sc_dev))
576 return 0;
577
578 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
579 /*
580 * Although we got a last close, the device may still be in
581 * use; e.g. if this was the dialout node, and there are still
582 * processes waiting for carrier on the non-dialout node.
583 */
584 wmcom_shutdown(sc);
585
586 /* Disable UART */
587 if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
588 bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTCON, 0);
589 }
590
591 return 0;
592 }
593
594 int
595 wmcomread(dev_t dev, struct uio *uio, int flag)
596 {
597 struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
598 struct tty *tp = sc->sc_tty;
599
600 if (!device_is_active(sc->sc_dev))
601 return EIO;
602
603 return (*tp->t_linesw->l_read)(tp, uio, flag);
604 }
605
606 int
607 wmcomwrite(dev_t dev, struct uio *uio, int flag)
608 {
609 struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
610 struct tty *tp = sc->sc_tty;
611
612 if (!device_is_active(sc->sc_dev))
613 return EIO;
614
615 return (*tp->t_linesw->l_write)(tp, uio, flag);
616 }
617
618 int
619 wmcomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
620 {
621 struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
622 struct tty *tp = sc->sc_tty;
623 int error, s;
624
625 if (!device_is_active(sc->sc_dev))
626 return EIO;
627
628 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
629 if (error != EPASSTHROUGH)
630 return error;
631
632 error = ttioctl(tp, cmd, data, flag, l);
633 if (error != EPASSTHROUGH)
634 return error;
635
636 switch (cmd) {
637 case TIOCSFLAGS:
638 error = kauth_authorize_device_tty(l->l_cred,
639 KAUTH_DEVICE_TTY_PRIVSET, tp);
640 break;
641 default:
642 break;
643 }
644 if (error)
645 return error;
646
647 s = splserial();
648 error = 0;
649 switch (cmd) {
650 case TIOCSBRK:
651 wmcom_break(sc, 1);
652 break;
653
654 case TIOCCBRK:
655 wmcom_break(sc, 0);
656 break;
657
658 case TIOCGFLAGS:
659 *(int *)data = sc->sc_swflags;
660 break;
661
662 case TIOCSFLAGS:
663 sc->sc_swflags = *(int *)data;
664 break;
665
666 default:
667 error = EPASSTHROUGH;
668 break;
669 }
670 splx(s);
671 return error;
672 }
673
674 int
675 wmcompoll(dev_t dev, int events, struct lwp *l)
676 {
677 struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
678 struct tty *tp = sc->sc_tty;
679
680 if (!device_is_active(sc->sc_dev))
681 return EIO;
682
683 return (*tp->t_linesw->l_poll)(tp, events, l);
684 }
685
686 struct tty *
687 wmcomtty(dev_t dev)
688 {
689 struct wmcom_softc *sc = device_lookup_private(&wmcom_cd, COMUNIT(dev));
690
691 return sc->sc_tty;
692 }
693
694 void
695 wmcomstop(struct tty *tp, int flag)
696 {
697 int s;
698
699 s = splserial();
700 if (ISSET(tp->t_state, TS_BUSY)) {
701 /* Stop transmitting at the next chunk. */
702 if (!ISSET(tp->t_state, TS_TTSTOP))
703 SET(tp->t_state, TS_FLUSH);
704 }
705 splx(s);
706 }
707
708
709 static void
710 wmcom_iflush(struct wmcom_softc *sc)
711 {
712 bus_space_tag_t iot = sc->sc_iot;
713 bus_space_handle_t ioh = sc->sc_ioh;
714 int timo;
715
716 timo = 50000;
717 while ((bus_space_read_1(iot, ioh, UARTFR) & FR_RXFE) == 0 &&
718 timo--)
719 bus_space_read_1(iot, ioh, UARTDR);
720 if (timo == 0)
721 printf("%s: iflush timeout\n", device_xname(sc->sc_dev));
722 }
723
724 static void
725 wmcom_shutdown(struct wmcom_softc *sc)
726 {
727 int s;
728
729 s = splserial();
730
731 /* Turn off interrupt */
732 bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTINTM, 0);
733
734 /* Clear any break condition set with TIOCSBRK. */
735 wmcom_break(sc, 0);
736
737 splx(s);
738 }
739
740 static void
741 wmcom_break(struct wmcom_softc *sc, int onoff)
742 {
743 int s;
744 uint8_t fcr;
745
746 s = splserial();
747 fcr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, UARTFCR);
748 if (onoff)
749 SET(fcr, FCR_BREAK);
750 else
751 CLR(fcr, FCR_BREAK);
752 bus_space_write_1(sc->sc_iot, sc->sc_ioh, UARTFCR, fcr);
753 splx(s);
754 }
755
756 static void
757 wmcom_rxsoft(struct wmcom_softc *sc, struct tty *tp)
758 {
759 bus_space_tag_t iot = sc->sc_iot;
760 bus_space_handle_t ioh = sc->sc_ioh;
761 int code, s;
762 u_int cc, scc;
763 uint8_t intm;
764 u_char sts, *get;
765
766 get = sc->sc_rbget;
767 scc = cc = WMCOM_RING_SIZE - sc->sc_rbavail;
768 while (cc) {
769 code = get[0];
770 sts = get[1];
771 if (ISSET(sts, RSR_FE | RSR_PE | RSR_OE)) {
772 if (ISSET(sts, (RSR_FE)))
773 SET(code, TTY_FE);
774 if (ISSET(sts, RSR_PE))
775 SET(code, TTY_PE);
776 if (ISSET(sts, RSR_OE))
777 ; /* XXXXX: Overrun */
778 }
779 if ((*tp->t_linesw->l_rint)(code, tp) == -1) {
780 /*
781 * The line discipline's buffer is out of space.
782 */
783 /*
784 * We're either not using flow control, or the
785 * line discipline didn't tell us to block for
786 * some reason. Either way, we have no way to
787 * know when there's more space available, so
788 * just drop the rest of the data.
789 */
790 get += cc << 1;
791 if (get >= sc->sc_rbuf + (WMCOM_RING_SIZE << 1))
792 get -= (WMCOM_RING_SIZE << 1);
793 cc = 0;
794 break;
795 }
796 get += 2;
797 if (get >= sc->sc_rbuf + (WMCOM_RING_SIZE << 1))
798 get = sc->sc_rbuf;
799 cc--;
800 }
801
802 if (cc != scc) {
803 sc->sc_rbget = get;
804 s = splserial();
805
806 cc = sc->sc_rbavail += scc - cc;
807 /* Buffers should be ok again, release possible block. */
808 if (cc >= 1) {
809 intm = bus_space_read_1(iot, ioh, UARTINTM);
810 SET(intm, INT_RXINT);
811 bus_space_write_1(iot, ioh, UARTINTM, intm);
812 }
813 splx(s);
814 }
815 }
816
817 static inline uint32_t
818 wmcom_rate2lcr(int rate)
819 {
820
821 return 7372800 / (16 * rate) - 1;
822 }
823
824 static uint8_t
825 wmcom_cflag2fcr(tcflag_t cflag)
826 {
827 int8_t fcr = FCR_UFIFOEN;
828
829 switch (cflag & CSIZE) {
830 case CS5: SET(fcr, FCR_WLEN_5); break;
831 case CS6: SET(fcr, FCR_WLEN_6); break;
832 case CS7: SET(fcr, FCR_WLEN_7); break;
833 case CS8: SET(fcr, FCR_WLEN_8); break;
834 default: SET(fcr, FCR_WLEN_8); break;
835 }
836 if (cflag & CSTOPB)
837 SET(fcr, FCR_XSTOP);
838 if (cflag & PARENB) {
839 SET(fcr, (FCR_PRTEN | FCR_EVENPRT));
840 if (cflag & PARODD)
841 CLR(fcr, FCR_EVENPRT);
842 }
843 return fcr;
844 }
845
846 #define WMCOM_CNREAD_1(offset) \
847 (*(volatile uint8_t *)(wmcom_cnaddr + ((offset) << 2)))
848 #define WMCOM_CNREAD_4(offset) \
849 (*(volatile uint32_t *)(wmcom_cnaddr + ((offset) << 2)))
850 #define WMCOM_CNWRITE_1(offset, val) \
851 (*(volatile uint8_t *)(wmcom_cnaddr + ((offset) << 2)) = val)
852 #define WMCOM_CNWRITE_4(offset, val) \
853 (*(volatile uint32_t *)(wmcom_cnaddr + ((offset) << 2)) = val)
854
855 static struct consdev wmcomcons = {
856 NULL, NULL, wmcom_cngetc, wmcom_cnputc, wmcom_cnpollc, NULL, NULL, NULL,
857 NODEV, CN_NORMAL
858 };
859
860 int
861 wmcom_cnattach(vaddr_t addr, int rate, tcflag_t cflag, int irda)
862 {
863
864 wmcom_cnaddr = addr;
865 wmcom_cnrate = rate;
866 wmcom_cncflag = cflag;
867 WMCOM_CNWRITE_4(UARTLCR, wmcom_rate2lcr(rate));
868 WMCOM_CNWRITE_1(UARTFCR, wmcom_cflag2fcr(cflag));
869 if (irda)
870 WMCOM_CNWRITE_1(UARTCON, CON_UARTEN | CON_IRTXM);
871 else
872 WMCOM_CNWRITE_1(UARTCON, CON_UARTEN);
873
874 cn_tab = &wmcomcons;
875 cn_init_magic(&wmcom_cnm_state);
876 cn_set_magic("\047\001"); /* default magic is BREAK */
877
878 return 0;
879 }
880
881 /* ARGSUSED */
882 static int
883 wmcom_cngetc(dev_t dev)
884 {
885 int s = splserial();
886 char ch;
887
888 while (WMCOM_CNREAD_1(UARTFR) & FR_RXFE);
889
890 ch = WMCOM_CNREAD_4(UARTDR);
891
892 {
893 #ifdef DDB
894 extern int db_active;
895 if (!db_active)
896 #endif
897 cn_check_magic(dev, ch, wmcom_cnm_state);
898 }
899
900 splx(s);
901 return ch;
902 }
903
904 /* ARGSUSED */
905 static void
906 wmcom_cnputc(dev_t dev, int c)
907 {
908 int s = splserial();
909
910 while (WMCOM_CNREAD_1(UARTFR) & FR_TXFF);
911
912 WMCOM_CNWRITE_1(UARTDR, c);
913
914 /* Make sure output. */
915 while (WMCOM_CNREAD_1(UARTFR) & FR_BUSY);
916
917 splx(s);
918 }
919
920 /* ARGSUSED */
921 static void
922 wmcom_cnpollc(dev_t dev, int on)
923 {
924 /* Nothing */
925 }
926