]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/uart/uart_tty.c
add snps IP uart support / genaralize UART
[FreeBSD/FreeBSD.git] / sys / dev / uart / uart_tty.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2003 Marcel Moolenaar
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/conf.h>
36 #include <sys/cons.h>
37 #include <sys/fcntl.h>
38 #include <sys/interrupt.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/reboot.h>
42 #include <machine/bus.h>
43 #include <sys/rman.h>
44 #include <sys/tty.h>
45 #include <machine/resource.h>
46 #include <machine/stdarg.h>
47
48 #include <dev/uart/uart.h>
49 #include <dev/uart/uart_bus.h>
50 #include <dev/uart/uart_cpu.h>
51
52 #include "uart_if.h"
53
54 static cn_probe_t uart_cnprobe;
55 static cn_init_t uart_cninit;
56 static cn_init_t uart_cnresume;
57 static cn_term_t uart_cnterm;
58 static cn_getc_t uart_cngetc;
59 static cn_putc_t uart_cnputc;
60 static cn_grab_t uart_cngrab;
61 static cn_ungrab_t uart_cnungrab;
62
63 static tsw_open_t uart_tty_open;
64 static tsw_close_t uart_tty_close;
65 static tsw_outwakeup_t uart_tty_outwakeup;
66 static tsw_inwakeup_t uart_tty_inwakeup;
67 static tsw_ioctl_t uart_tty_ioctl;
68 static tsw_param_t uart_tty_param;
69 static tsw_modem_t uart_tty_modem;
70 static tsw_free_t uart_tty_free;
71 static tsw_busy_t uart_tty_busy;
72
73 CONSOLE_DRIVER(
74         uart,
75         .cn_resume = uart_cnresume,
76 );
77
78 static struct uart_devinfo uart_console;
79
80 static void
81 uart_cnprobe(struct consdev *cp)
82 {
83
84         cp->cn_pri = CN_DEAD;
85
86         KASSERT(uart_console.cookie == NULL, ("foo"));
87
88         if (uart_cpu_getdev(UART_DEV_CONSOLE, &uart_console))
89                 return;
90
91         if (uart_probe(&uart_console))
92                 return;
93
94         strlcpy(cp->cn_name, uart_driver_name, sizeof(cp->cn_name));
95         cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
96         cp->cn_arg = &uart_console;
97 }
98
99 static void
100 uart_cninit(struct consdev *cp)
101 {
102         struct uart_devinfo *di;
103
104         /*
105          * Yedi trick: we need to be able to define cn_dev before we go
106          * single- or multi-user. The problem is that we don't know at
107          * this time what the device will be. Hence, we need to link from
108          * the uart_devinfo to the consdev that corresponds to it so that
109          * we can define cn_dev in uart_bus_attach() when we find the
110          * device during bus enumeration. That's when we'll know what the
111          * the unit number will be.
112          */
113         di = cp->cn_arg;
114         KASSERT(di->cookie == NULL, ("foo"));
115         di->cookie = cp;
116         di->type = UART_DEV_CONSOLE;
117         uart_add_sysdev(di);
118         uart_init(di);
119 }
120
121 static void
122 uart_cnresume(struct consdev *cp)
123 {
124
125         uart_init(cp->cn_arg);
126 }
127
128 static void
129 uart_cnterm(struct consdev *cp)
130 {
131
132         uart_term(cp->cn_arg);
133 }
134
135 static void
136 uart_cngrab(struct consdev *cp)
137 {
138
139         uart_grab(cp->cn_arg);
140 }
141
142 static void
143 uart_cnungrab(struct consdev *cp)
144 {
145
146         uart_ungrab(cp->cn_arg);
147 }
148
149 static void
150 uart_cnputc(struct consdev *cp, int c)
151 {
152
153         uart_putc(cp->cn_arg, c);
154 }
155
156 static int
157 uart_cngetc(struct consdev *cp)
158 {
159
160         return (uart_poll(cp->cn_arg));
161 }
162
163 static int
164 uart_tty_open(struct tty *tp)
165 {
166         struct uart_softc *sc;
167
168         sc = tty_softc(tp);
169
170         if (sc == NULL || sc->sc_leaving)
171                 return (ENXIO);
172
173         sc->sc_opened = 1;
174         return (0);
175 }
176
177 static void
178 uart_tty_close(struct tty *tp)
179 {
180         struct uart_softc *sc;
181
182         sc = tty_softc(tp);
183         if (sc == NULL || sc->sc_leaving || !sc->sc_opened)
184                 return;
185
186         if (sc->sc_hwiflow)
187                 UART_IOCTL(sc, UART_IOCTL_IFLOW, 0);
188         if (sc->sc_hwoflow)
189                 UART_IOCTL(sc, UART_IOCTL_OFLOW, 0);
190         if (sc->sc_sysdev == NULL)
191                 UART_SETSIG(sc, SER_DDTR | SER_DRTS);
192
193         wakeup(sc);
194         sc->sc_opened = 0;
195 }
196
197 static void
198 uart_tty_outwakeup(struct tty *tp)
199 {
200         struct uart_softc *sc;
201
202         sc = tty_softc(tp);
203         if (sc == NULL || sc->sc_leaving)
204                 return;
205
206         if (sc->sc_txbusy)
207                 return;
208
209         /*
210          * Respect RTS/CTS (output) flow control if enabled and not already
211          * handled by hardware.
212          */
213         if ((tp->t_termios.c_cflag & CCTS_OFLOW) && !sc->sc_hwoflow &&
214             !(sc->sc_hwsig & SER_CTS))
215                 return;
216
217         sc->sc_txdatasz = ttydisc_getc(tp, sc->sc_txbuf, sc->sc_txfifosz);
218         if (sc->sc_txdatasz != 0)
219                 UART_TRANSMIT(sc);
220 }
221
222 static void
223 uart_tty_inwakeup(struct tty *tp)
224 {
225         struct uart_softc *sc;
226
227         sc = tty_softc(tp);
228         if (sc == NULL || sc->sc_leaving)
229                 return;
230
231         if (sc->sc_isquelch) {
232                 if ((tp->t_termios.c_cflag & CRTS_IFLOW) && !sc->sc_hwiflow)
233                         UART_SETSIG(sc, SER_DRTS|SER_RTS);
234                 sc->sc_isquelch = 0;
235                 uart_sched_softih(sc, SER_INT_RXREADY);
236         }
237 }
238
239 static int
240 uart_tty_ioctl(struct tty *tp, u_long cmd, caddr_t data,
241     struct thread *td __unused)
242 {
243         struct uart_softc *sc;
244
245         sc = tty_softc(tp);
246
247         switch (cmd) {
248         case TIOCSBRK:
249                 UART_IOCTL(sc, UART_IOCTL_BREAK, 1);
250                 return (0);
251         case TIOCCBRK:
252                 UART_IOCTL(sc, UART_IOCTL_BREAK, 0);
253                 return (0);
254         default:
255                 return pps_ioctl(cmd, data, &sc->sc_pps);
256         }
257 }
258
259 static int
260 uart_tty_param(struct tty *tp, struct termios *t)
261 {
262         struct uart_softc *sc;
263         int databits, parity, stopbits;
264
265         sc = tty_softc(tp);
266         if (sc == NULL || sc->sc_leaving)
267                 return (ENODEV);
268         if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0)
269                 return (EINVAL);
270         if (t->c_ospeed == 0) {
271                 UART_SETSIG(sc, SER_DDTR | SER_DRTS);
272                 return (0);
273         }
274         switch (t->c_cflag & CSIZE) {
275         case CS5:       databits = 5; break;
276         case CS6:       databits = 6; break;
277         case CS7:       databits = 7; break;
278         default:        databits = 8; break;
279         }
280         stopbits = (t->c_cflag & CSTOPB) ? 2 : 1;
281         if (t->c_cflag & PARENB)
282                 parity = (t->c_cflag & PARODD) ? UART_PARITY_ODD :
283                     UART_PARITY_EVEN;
284         else
285                 parity = UART_PARITY_NONE;
286         if (UART_PARAM(sc, t->c_ospeed, databits, stopbits, parity) != 0)
287                 return (EINVAL);
288         UART_SETSIG(sc, SER_DDTR | SER_DTR);
289         /* Set input flow control state. */
290         if (!sc->sc_hwiflow) {
291                 if ((t->c_cflag & CRTS_IFLOW) && sc->sc_isquelch)
292                         UART_SETSIG(sc, SER_DRTS);
293                 else
294                         UART_SETSIG(sc, SER_DRTS | SER_RTS);
295         } else
296                 UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW));
297         /* Set output flow control state. */
298         if (sc->sc_hwoflow)
299                 UART_IOCTL(sc, UART_IOCTL_OFLOW, (t->c_cflag & CCTS_OFLOW));
300
301         return (0);
302 }
303
304 static int
305 uart_tty_modem(struct tty *tp, int biton, int bitoff)
306 {
307         struct uart_softc *sc;
308
309         sc = tty_softc(tp);
310         if (biton != 0 || bitoff != 0)
311                 UART_SETSIG(sc, SER_DELTA(bitoff | biton) | biton);
312         return (sc->sc_hwsig);
313 }
314
315 void
316 uart_tty_intr(void *arg)
317 {
318         struct uart_softc *sc = arg;
319         struct tty *tp;
320         int c, err = 0, pend, sig, xc;
321
322         if (sc->sc_leaving)
323                 return;
324
325         pend = atomic_readandclear_32(&sc->sc_ttypend);
326         if (!(pend & SER_INT_MASK))
327                 return;
328
329         tp = sc->sc_u.u_tty.tp;
330         tty_lock(tp);
331
332         if (pend & SER_INT_RXREADY) {
333                 while (!uart_rx_empty(sc) && !sc->sc_isquelch) {
334                         xc = uart_rx_peek(sc);
335                         c = xc & 0xff;
336                         if (xc & UART_STAT_FRAMERR)
337                                 err |= TRE_FRAMING;
338                         if (xc & UART_STAT_OVERRUN)
339                                 err |= TRE_OVERRUN;
340                         if (xc & UART_STAT_PARERR)
341                                 err |= TRE_PARITY;
342                         if (ttydisc_rint(tp, c, err) != 0) {
343                                 sc->sc_isquelch = 1;
344                                 if ((tp->t_termios.c_cflag & CRTS_IFLOW) &&
345                                     !sc->sc_hwiflow)
346                                         UART_SETSIG(sc, SER_DRTS);
347                         } else
348                                 uart_rx_next(sc);
349                 }
350         }
351
352         if (pend & SER_INT_BREAK)
353                 ttydisc_rint(tp, 0, TRE_BREAK);
354
355         if (pend & SER_INT_SIGCHG) {
356                 sig = pend & SER_INT_SIGMASK;
357                 if (sig & SER_DDCD)
358                         ttydisc_modem(tp, sig & SER_DCD);
359                 if (sig & SER_DCTS)
360                         uart_tty_outwakeup(tp);
361         }
362
363         if (pend & SER_INT_TXIDLE)
364                 uart_tty_outwakeup(tp);
365         ttydisc_rint_done(tp);
366         tty_unlock(tp);
367 }
368
369 static void
370 uart_tty_free(void *arg __unused)
371 {
372
373         /*
374          * XXX: uart(4) could reuse the device unit number before it is
375          * being freed by the TTY layer. We should use this hook to free
376          * the device unit number, but unfortunately newbus does not
377          * seem to support such a construct.
378          */
379 }
380
381 static bool
382 uart_tty_busy(struct tty *tp)
383 {
384         struct uart_softc *sc;
385
386         sc = tty_softc(tp);
387         if (sc == NULL || sc->sc_leaving)
388                 return (FALSE);
389
390         return (sc->sc_txbusy);
391 }
392
393 static struct ttydevsw uart_tty_class = {
394         .tsw_flags      = TF_INITLOCK|TF_CALLOUT,
395         .tsw_open       = uart_tty_open,
396         .tsw_close      = uart_tty_close,
397         .tsw_outwakeup  = uart_tty_outwakeup,
398         .tsw_inwakeup   = uart_tty_inwakeup,
399         .tsw_ioctl      = uart_tty_ioctl,
400         .tsw_param      = uart_tty_param,
401         .tsw_modem      = uart_tty_modem,
402         .tsw_free       = uart_tty_free,
403         .tsw_busy       = uart_tty_busy,
404 };
405
406 int
407 uart_tty_attach(struct uart_softc *sc)
408 {
409         struct tty *tp;
410         int unit;
411
412         sc->sc_u.u_tty.tp = tp = tty_alloc(&uart_tty_class, sc);
413
414         unit = device_get_unit(sc->sc_dev);
415
416         if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
417                 sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name,
418                     "ttyu%r", unit);
419                 tty_init_console(tp, sc->sc_sysdev->baudrate);
420         }
421
422         swi_add(&tty_intr_event, uart_driver_name, uart_tty_intr, sc, SWI_TTY,
423             INTR_TYPE_TTY, &sc->sc_softih);
424
425         tty_makedev(tp, NULL, "u%r", unit);
426
427         return (0);
428 }
429
430 int
431 uart_tty_detach(struct uart_softc *sc)
432 {
433         struct tty *tp;
434
435         tp = sc->sc_u.u_tty.tp;
436
437         tty_lock(tp);
438         swi_remove(sc->sc_softih);
439         tty_rel_gone(tp);
440
441         return (0);
442 }
443
444 struct mtx *
445 uart_tty_getlock(struct uart_softc *sc)
446 {
447
448         if (sc->sc_u.u_tty.tp != NULL)
449                 return (tty_getlock(sc->sc_u.u_tty.tp));
450         else
451                 return (NULL);
452 }