1 /* $OpenBSD: amluart.c,v 1.4 2022/07/15 17:14:49 kettenis Exp $ */
2 /*
3 * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/fcntl.h>
21 #include <sys/proc.h>
22 #include <sys/systm.h>
23 #include <sys/tty.h>
24
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27
28 #include <dev/cons.h>
29
30 #include <dev/ofw/fdt.h>
31 #include <dev/ofw/openfirm.h>
32
33 #define UART_WFIFO 0x0000
34 #define UART_RFIFO 0x0004
35 #define UART_CONTROL 0x0008
36 #define UART_CONTROL_TX_INT (1 << 28)
37 #define UART_CONTROL_RX_INT (1 << 27)
38 #define UART_CONTROL_CLEAR_ERROR (1 << 24)
39 #define UART_STATUS 0x000c
40 #define UART_STATUS_RX_FIFO_OVERFLOW (1 << 24)
41 #define UART_STATUS_TX_FIFO_FULL (1 << 21)
42 #define UART_STATUS_RX_FIFO_EMPTY (1 << 20)
43 #define UART_STATUS_FRAME_ERROR (1 << 17)
44 #define UART_STATUS_PARITY_ERROR (1 << 16)
45 #define UART_STATUS_ERROR (1 << 24 | 0x7 << 16)
46 #define UART_MISC 0x0010
47 #define UART_MISC_TX_INT_CNT_MASK (0xff << 16)
48 #define UART_MISC_TX_INT_CNT_SHIFT 16
49 #define UART_MISC_RX_INT_CNT_MASK (0xff << 0)
50 #define UART_MISC_RX_INT_CNT_SHIFT 0
51
52 #define UART_SPACE 24
53
54 #define HREAD4(sc, reg) \
55 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
56 #define HWRITE4(sc, reg, val) \
57 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
58 #define HSET4(sc, reg, bits) \
59 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
60 #define HCLR4(sc, reg, bits) \
61 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
62
63 cdev_decl(com);
64 cdev_decl(amluart);
65
66 #define DEVUNIT(x) (minor(x) & 0x7f)
67 #define DEVCUA(x) (minor(x) & 0x80)
68
69 struct cdevsw amluartdev = cdev_tty_init(3, amluart);
70
71 struct amluart_softc {
72 struct device sc_dev;
73 bus_space_tag_t sc_iot;
74 bus_space_handle_t sc_ioh;
75
76 struct soft_intrhand *sc_si;
77 void *sc_ih;
78
79 struct tty *sc_tty;
80 int sc_conspeed;
81 int sc_floods;
82 int sc_overflows;
83 int sc_halt;
84 int sc_cua;
85 int *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
86 #define AMLUART_IBUFSIZE 128
87 #define AMLUART_IHIGHWATER 100
88 int sc_ibufs[2][AMLUART_IBUFSIZE];
89 };
90
91 int amluart_match(struct device *, void *, void *);
92 void amluart_attach(struct device *, struct device *, void *);
93
94 struct cfdriver amluart_cd = {
95 NULL, "amluart", DV_TTY
96 };
97
98 const struct cfattach amluart_ca = {
99 sizeof(struct amluart_softc), amluart_match, amluart_attach
100 };
101
102 bus_space_tag_t amluartconsiot;
103 bus_space_handle_t amluartconsioh;
104
105 struct amluart_softc *amluart_sc(dev_t);
106
107 int amluart_intr(void *);
108 void amluart_softintr(void *);
109 void amluart_start(struct tty *);
110
111 int amluartcnattach(bus_space_tag_t, bus_addr_t);
112 int amluartcngetc(dev_t);
113 void amluartcnputc(dev_t, int);
114 void amluartcnpollc(dev_t, int);
115
116 void
amluart_init_cons(void)117 amluart_init_cons(void)
118 {
119 struct fdt_reg reg;
120 void *node;
121
122 if ((node = fdt_find_cons("amlogic,meson-gx-uart")) == NULL)
123 return;
124 if (fdt_get_reg(node, 0, ®))
125 return;
126
127 amluartcnattach(fdt_cons_bs_tag, reg.addr);
128 }
129
130 int
amluart_match(struct device * parent,void * match,void * aux)131 amluart_match(struct device *parent, void *match, void *aux)
132 {
133 struct fdt_attach_args *faa = aux;
134
135 return OF_is_compatible(faa->fa_node, "amlogic,meson-gx-uart");
136 }
137
138 void
amluart_attach(struct device * parent,struct device * self,void * aux)139 amluart_attach(struct device *parent, struct device *self, void *aux)
140 {
141 struct amluart_softc *sc = (struct amluart_softc *)self;
142 struct fdt_attach_args *faa = aux;
143 uint32_t reg;
144 int maj;
145
146 if (faa->fa_nreg < 1) {
147 printf(": no registers\n");
148 return;
149 }
150
151 sc->sc_iot = faa->fa_iot;
152 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
153 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
154 printf(": can't map registers\n");
155 return;
156 }
157
158 if (faa->fa_node == stdout_node) {
159 /* Locate the major number. */
160 for (maj = 0; maj < nchrdev; maj++)
161 if (cdevsw[maj].d_open == amluartopen)
162 break;
163 cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
164 sc->sc_conspeed = stdout_speed;
165 printf(": console");
166 }
167
168 sc->sc_si = softintr_establish(IPL_TTY, amluart_softintr, sc);
169 if (sc->sc_si == NULL) {
170 printf(": can't establish soft interrupt\n");
171 return;
172 }
173
174 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY,
175 amluart_intr, sc, sc->sc_dev.dv_xname);
176 if (sc->sc_ih == NULL) {
177 printf(": can't establish hard interrupt\n");
178 return;
179 }
180
181 printf("\n");
182
183 /*
184 * Generate interrupts if the Tx FIFO is half-empty or if
185 * there is anything in the Rx FIFO.
186 */
187 reg = HREAD4(sc, UART_MISC);
188 reg &= ~UART_MISC_TX_INT_CNT_MASK;
189 reg |= (32 << UART_MISC_TX_INT_CNT_SHIFT);
190 reg &= ~UART_MISC_RX_INT_CNT_MASK;
191 reg |= (1 << UART_MISC_RX_INT_CNT_SHIFT);
192 HWRITE4(sc, UART_MISC, reg);
193 }
194
195 int
amluart_intr(void * arg)196 amluart_intr(void *arg)
197 {
198 struct amluart_softc *sc = arg;
199 struct tty *tp = sc->sc_tty;
200 int *p;
201 u_int32_t stat;
202 u_char c;
203 int handled = 0;
204
205 if (tp == NULL)
206 return 0;
207
208 stat = HREAD4(sc, UART_STATUS);
209 if (!ISSET(stat, UART_STATUS_TX_FIFO_FULL) &&
210 ISSET(tp->t_state, TS_BUSY)) {
211 CLR(tp->t_state, TS_BUSY | TS_FLUSH);
212 if (sc->sc_halt > 0)
213 wakeup(&tp->t_outq);
214 (*linesw[tp->t_line].l_start)(tp);
215 handled = 1;
216 }
217
218 p = sc->sc_ibufp;
219 while (!ISSET(stat, UART_STATUS_RX_FIFO_EMPTY)) {
220 c = HREAD4(sc, UART_RFIFO);
221 if (ISSET(stat, UART_STATUS_FRAME_ERROR))
222 c |= TTY_FE;
223 if (ISSET(stat, UART_STATUS_PARITY_ERROR))
224 c |= TTY_PE;
225 if (ISSET(stat, UART_STATUS_RX_FIFO_OVERFLOW))
226 sc->sc_overflows++;
227
228 if (p >= sc->sc_ibufend)
229 sc->sc_floods++;
230 else
231 *p++ = c;
232
233 if (stat & UART_STATUS_ERROR)
234 HSET4(sc, UART_CONTROL, UART_CONTROL_CLEAR_ERROR);
235 stat = HREAD4(sc, UART_STATUS);
236 handled = 1;
237 }
238 if (sc->sc_ibufp != p) {
239 sc->sc_ibufp = p;
240 softintr_schedule(sc->sc_si);
241 }
242
243 return handled;
244 }
245
246 void
amluart_softintr(void * arg)247 amluart_softintr(void *arg)
248 {
249 struct amluart_softc *sc = arg;
250 struct tty *tp = sc->sc_tty;
251 int *ibufp, *ibufend;
252 int s;
253
254 if (sc->sc_ibufp == sc->sc_ibuf)
255 return;
256
257 s = spltty();
258
259 ibufp = sc->sc_ibuf;
260 ibufend = sc->sc_ibufp;
261
262 if (ibufp == ibufend) {
263 splx(s);
264 return;
265 }
266
267 sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
268 sc->sc_ibufs[1] : sc->sc_ibufs[0];
269 sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
270 sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
271
272 if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
273 splx(s);
274 return;
275 }
276
277 splx(s);
278
279 while (ibufp < ibufend) {
280 int i = *ibufp++;
281 #ifdef DDB
282 if (tp->t_dev == cn_tab->cn_dev) {
283 int j = db_rint(i);
284
285 if (j == 1) /* Escape received, skip */
286 continue;
287 if (j == 2) /* Second char wasn't 'D' */
288 (*linesw[tp->t_line].l_rint)(27, tp);
289 }
290 #endif
291 (*linesw[tp->t_line].l_rint)(i, tp);
292 }
293 }
294
295 int
amluart_param(struct tty * tp,struct termios * t)296 amluart_param(struct tty *tp, struct termios *t)
297 {
298 struct amluart_softc *sc = amluart_sc(tp->t_dev);
299 int ospeed = t->c_ospeed;
300
301 /* Check requested parameters. */
302 if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
303 return EINVAL;
304
305 switch (ISSET(t->c_cflag, CSIZE)) {
306 case CS5:
307 case CS6:
308 case CS7:
309 return EINVAL;
310 case CS8:
311 break;
312 }
313
314 if (ospeed != 0) {
315 while (ISSET(tp->t_state, TS_BUSY)) {
316 int error;
317
318 sc->sc_halt++;
319 error = ttysleep(tp, &tp->t_outq,
320 TTOPRI | PCATCH, "amluprm");
321 sc->sc_halt--;
322 if (error) {
323 amluart_start(tp);
324 return error;
325 }
326 }
327 }
328
329 tp->t_ispeed = t->c_ispeed;
330 tp->t_ospeed = t->c_ospeed;
331 tp->t_cflag = t->c_cflag;
332
333 /* Just to be sure... */
334 amluart_start(tp);
335 return 0;
336 }
337
338 void
amluart_start(struct tty * tp)339 amluart_start(struct tty *tp)
340 {
341 struct amluart_softc *sc = amluart_sc(tp->t_dev);
342 int stat;
343 int s;
344
345 s = spltty();
346 if (ISSET(tp->t_state, TS_BUSY))
347 goto out;
348 if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
349 goto out;
350 ttwakeupwr(tp);
351 if (tp->t_outq.c_cc == 0)
352 goto out;
353 SET(tp->t_state, TS_BUSY);
354
355 stat = HREAD4(sc, UART_STATUS);
356 while ((stat & UART_STATUS_TX_FIFO_FULL) == 0) {
357 HWRITE4(sc, UART_WFIFO, getc(&tp->t_outq));
358 stat = HREAD4(sc, UART_STATUS);
359 }
360 out:
361 splx(s);
362 }
363
364 int
amluartopen(dev_t dev,int flag,int mode,struct proc * p)365 amluartopen(dev_t dev, int flag, int mode, struct proc *p)
366 {
367 struct amluart_softc *sc = amluart_sc(dev);
368 struct tty *tp;
369 int error;
370 int s;
371
372 if (sc == NULL)
373 return ENXIO;
374
375 s = spltty();
376 if (sc->sc_tty == NULL)
377 tp = sc->sc_tty = ttymalloc(0);
378 else
379 tp = sc->sc_tty;
380 splx(s);
381
382 tp->t_oproc = amluart_start;
383 tp->t_param = amluart_param;
384 tp->t_dev = dev;
385
386 if (!ISSET(tp->t_state, TS_ISOPEN)) {
387 SET(tp->t_state, TS_WOPEN);
388 ttychars(tp);
389 tp->t_iflag = TTYDEF_IFLAG;
390 tp->t_oflag = TTYDEF_OFLAG;
391 tp->t_cflag = TTYDEF_CFLAG;
392 tp->t_lflag = TTYDEF_LFLAG;
393 tp->t_ispeed = tp->t_ospeed =
394 sc->sc_conspeed ? sc->sc_conspeed : B115200;
395
396 s = spltty();
397
398 amluart_param(tp, &tp->t_termios);
399 ttsetwater(tp);
400
401 sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
402 sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
403 sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
404
405 /* Enable interrupts */
406 HSET4(sc, UART_CONTROL,
407 UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
408
409 /* No carrier detect support. */
410 SET(tp->t_state, TS_CARR_ON);
411 } else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
412 return EBUSY;
413 else
414 s = spltty();
415
416 if (DEVCUA(dev)) {
417 if (ISSET(tp->t_state, TS_ISOPEN)) {
418 /* Ah, but someone already is dialed in... */
419 splx(s);
420 return EBUSY;
421 }
422 sc->sc_cua = 1; /* We go into CUA mode. */
423 } else {
424 if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) {
425 /* Opening TTY non-blocking... but the CUA is busy. */
426 splx(s);
427 return EBUSY;
428 } else {
429 while (sc->sc_cua) {
430 SET(tp->t_state, TS_WOPEN);
431 error = ttysleep(tp, &tp->t_rawq,
432 TTIPRI | PCATCH, ttopen);
433 /*
434 * If TS_WOPEN has been reset, that means the
435 * cua device has been closed.
436 * We don't want to fail in that case,
437 * so just go around again.
438 */
439 if (error && ISSET(tp->t_state, TS_WOPEN)) {
440 CLR(tp->t_state, TS_WOPEN);
441 splx(s);
442 return error;
443 }
444 }
445 }
446 }
447 splx(s);
448
449 return (*linesw[tp->t_line].l_open)(dev, tp, p);
450 }
451
452 int
amluartclose(dev_t dev,int flag,int mode,struct proc * p)453 amluartclose(dev_t dev, int flag, int mode, struct proc *p)
454 {
455 struct amluart_softc *sc = amluart_sc(dev);
456 struct tty *tp = sc->sc_tty;
457 int s;
458
459 if (!ISSET(tp->t_state, TS_ISOPEN))
460 return 0;
461
462 (*linesw[tp->t_line].l_close)(tp, flag, p);
463 s = spltty();
464 if (!ISSET(tp->t_state, TS_WOPEN)) {
465 /* Disable interrupts */
466 HCLR4(sc, UART_CONTROL,
467 UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
468 }
469 CLR(tp->t_state, TS_BUSY | TS_FLUSH);
470 sc->sc_cua = 0;
471 splx(s);
472 ttyclose(tp);
473
474 return 0;
475 }
476
477 int
amluartread(dev_t dev,struct uio * uio,int flag)478 amluartread(dev_t dev, struct uio *uio, int flag)
479 {
480 struct tty *tp = amluarttty(dev);
481
482 if (tp == NULL)
483 return ENODEV;
484
485 return (*linesw[tp->t_line].l_read)(tp, uio, flag);
486 }
487
488 int
amluartwrite(dev_t dev,struct uio * uio,int flag)489 amluartwrite(dev_t dev, struct uio *uio, int flag)
490 {
491 struct tty *tp = amluarttty(dev);
492
493 if (tp == NULL)
494 return ENODEV;
495
496 return (*linesw[tp->t_line].l_write)(tp, uio, flag);
497 }
498
499 int
amluartioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)500 amluartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
501 {
502 struct amluart_softc *sc = amluart_sc(dev);
503 struct tty *tp;
504 int error;
505
506 if (sc == NULL)
507 return ENODEV;
508
509 tp = sc->sc_tty;
510 if (tp == NULL)
511 return ENXIO;
512
513 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
514 if (error >= 0)
515 return error;
516
517 error = ttioctl(tp, cmd, data, flag, p);
518 if (error >= 0)
519 return error;
520
521 switch(cmd) {
522 case TIOCSBRK:
523 case TIOCCBRK:
524 case TIOCSDTR:
525 case TIOCCDTR:
526 case TIOCMSET:
527 case TIOCMBIS:
528 case TIOCMBIC:
529 case TIOCMGET:
530 case TIOCGFLAGS:
531 break;
532 case TIOCSFLAGS:
533 error = suser(p);
534 if (error != 0)
535 return EPERM;
536 break;
537 default:
538 return ENOTTY;
539 }
540
541 return 0;
542 }
543
544 int
amluartstop(struct tty * tp,int flag)545 amluartstop(struct tty *tp, int flag)
546 {
547 return 0;
548 }
549
550 struct tty *
amluarttty(dev_t dev)551 amluarttty(dev_t dev)
552 {
553 struct amluart_softc *sc = amluart_sc(dev);
554
555 if (sc == NULL)
556 return NULL;
557 return sc->sc_tty;
558 }
559
560 struct amluart_softc *
amluart_sc(dev_t dev)561 amluart_sc(dev_t dev)
562 {
563 int unit = DEVUNIT(dev);
564
565 if (unit >= amluart_cd.cd_ndevs)
566 return NULL;
567 return (struct amluart_softc *)amluart_cd.cd_devs[unit];
568 }
569
570 int
amluartcnattach(bus_space_tag_t iot,bus_addr_t iobase)571 amluartcnattach(bus_space_tag_t iot, bus_addr_t iobase)
572 {
573 static struct consdev amluartcons = {
574 NULL, NULL, amluartcngetc, amluartcnputc, amluartcnpollc, NULL,
575 NODEV, CN_MIDPRI
576 };
577 int maj;
578
579 amluartconsiot = iot;
580 if (bus_space_map(iot, iobase, UART_SPACE, 0, &amluartconsioh))
581 return ENOMEM;
582
583 /* Look for major of com(4) to replace. */
584 for (maj = 0; maj < nchrdev; maj++)
585 if (cdevsw[maj].d_open == comopen)
586 break;
587 if (maj == nchrdev)
588 return ENXIO;
589
590 cn_tab = &amluartcons;
591 cn_tab->cn_dev = makedev(maj, 0);
592 cdevsw[maj] = amluartdev; /* KLUDGE */
593
594 return 0;
595 }
596
597 int
amluartcngetc(dev_t dev)598 amluartcngetc(dev_t dev)
599 {
600 uint8_t c;
601
602 while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
603 UART_STATUS_RX_FIFO_EMPTY)
604 CPU_BUSY_CYCLE();
605 c = bus_space_read_4(amluartconsiot, amluartconsioh, UART_RFIFO);
606 return c;
607 }
608
609 void
amluartcnputc(dev_t dev,int c)610 amluartcnputc(dev_t dev, int c)
611 {
612 while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
613 UART_STATUS_TX_FIFO_FULL)
614 CPU_BUSY_CYCLE();
615 bus_space_write_4(amluartconsiot, amluartconsioh, UART_WFIFO, c);
616 }
617
618 void
amluartcnpollc(dev_t dev,int on)619 amluartcnpollc(dev_t dev, int on)
620 {
621 }
622