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