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 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 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 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 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 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 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 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 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 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 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 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 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 545 amluartstop(struct tty *tp, int flag) 546 { 547 return 0; 548 } 549 550 struct tty * 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 * 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 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 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 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 619 amluartcnpollc(dev_t dev, int on) 620 { 621 } 622