1 /* $OpenBSD: amluart.c,v 1.1 2019/08/26 09:10:22 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 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 (*linesw[tp->t_line].l_rint)(*ibufp++, tp); 281 } 282 283 int 284 amluart_param(struct tty *tp, struct termios *t) 285 { 286 struct amluart_softc *sc = amluart_sc(tp->t_dev); 287 int ospeed = t->c_ospeed; 288 289 /* Check requested parameters. */ 290 if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) 291 return EINVAL; 292 293 switch (ISSET(t->c_cflag, CSIZE)) { 294 case CS5: 295 case CS6: 296 case CS7: 297 return EINVAL; 298 case CS8: 299 break; 300 } 301 302 if (ospeed != 0) { 303 while (ISSET(tp->t_state, TS_BUSY)) { 304 int error; 305 306 sc->sc_halt++; 307 error = ttysleep(tp, &tp->t_outq, 308 TTOPRI | PCATCH, "amluprm"); 309 sc->sc_halt--; 310 if (error) { 311 amluart_start(tp); 312 return error; 313 } 314 } 315 } 316 317 tp->t_ispeed = t->c_ispeed; 318 tp->t_ospeed = t->c_ospeed; 319 tp->t_cflag = t->c_cflag; 320 321 /* Just to be sure... */ 322 amluart_start(tp); 323 return 0; 324 } 325 326 void 327 amluart_start(struct tty *tp) 328 { 329 struct amluart_softc *sc = amluart_sc(tp->t_dev); 330 int stat; 331 int s; 332 333 s = spltty(); 334 if (ISSET(tp->t_state, TS_BUSY)) 335 goto out; 336 if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0) 337 goto out; 338 ttwakeupwr(tp); 339 if (tp->t_outq.c_cc == 0) 340 goto out; 341 SET(tp->t_state, TS_BUSY); 342 343 stat = HREAD4(sc, UART_STATUS); 344 while ((stat & UART_STATUS_TX_FIFO_FULL) == 0) { 345 HWRITE4(sc, UART_WFIFO, getc(&tp->t_outq)); 346 stat = HREAD4(sc, UART_STATUS); 347 } 348 out: 349 splx(s); 350 } 351 352 int 353 amluartopen(dev_t dev, int flag, int mode, struct proc *p) 354 { 355 struct amluart_softc *sc = amluart_sc(dev); 356 struct tty *tp; 357 int error; 358 int s; 359 360 if (sc == NULL) 361 return ENXIO; 362 363 s = spltty(); 364 if (sc->sc_tty == NULL) 365 tp = sc->sc_tty = ttymalloc(0); 366 else 367 tp = sc->sc_tty; 368 splx(s); 369 370 tp->t_oproc = amluart_start; 371 tp->t_param = amluart_param; 372 tp->t_dev = dev; 373 374 if (!ISSET(tp->t_state, TS_ISOPEN)) { 375 SET(tp->t_state, TS_WOPEN); 376 ttychars(tp); 377 tp->t_iflag = TTYDEF_IFLAG; 378 tp->t_oflag = TTYDEF_OFLAG; 379 tp->t_cflag = TTYDEF_CFLAG; 380 tp->t_lflag = TTYDEF_LFLAG; 381 tp->t_ispeed = tp->t_ospeed = 382 sc->sc_conspeed ? sc->sc_conspeed : B115200; 383 384 s = spltty(); 385 386 amluart_param(tp, &tp->t_termios); 387 ttsetwater(tp); 388 389 sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; 390 sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER; 391 sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE; 392 393 /* Enable interrupts */ 394 HSET4(sc, UART_CONTROL, 395 UART_CONTROL_TX_INT | UART_CONTROL_RX_INT); 396 397 /* No carrier detect support. */ 398 SET(tp->t_state, TS_CARR_ON); 399 } else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) 400 return EBUSY; 401 else 402 s = spltty(); 403 404 if (DEVCUA(dev)) { 405 if (ISSET(tp->t_state, TS_ISOPEN)) { 406 /* Ah, but someone already is dialed in... */ 407 splx(s); 408 return EBUSY; 409 } 410 sc->sc_cua = 1; /* We go into CUA mode. */ 411 } else { 412 if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) { 413 /* Opening TTY non-blocking... but the CUA is busy. */ 414 splx(s); 415 return EBUSY; 416 } else { 417 while (sc->sc_cua) { 418 SET(tp->t_state, TS_WOPEN); 419 error = ttysleep(tp, &tp->t_rawq, 420 TTIPRI | PCATCH, ttopen); 421 /* 422 * If TS_WOPEN has been reset, that means the 423 * cua device has been closed. 424 * We don't want to fail in that case, 425 * so just go around again. 426 */ 427 if (error && ISSET(tp->t_state, TS_WOPEN)) { 428 CLR(tp->t_state, TS_WOPEN); 429 splx(s); 430 return error; 431 } 432 } 433 } 434 } 435 splx(s); 436 437 return (*linesw[tp->t_line].l_open)(dev, tp, p); 438 } 439 440 int 441 amluartclose(dev_t dev, int flag, int mode, struct proc *p) 442 { 443 struct amluart_softc *sc = amluart_sc(dev); 444 struct tty *tp = sc->sc_tty; 445 int s; 446 447 if (!ISSET(tp->t_state, TS_ISOPEN)) 448 return 0; 449 450 (*linesw[tp->t_line].l_close)(tp, flag, p); 451 s = spltty(); 452 if (!ISSET(tp->t_state, TS_WOPEN)) { 453 /* Disable interrupts */ 454 HCLR4(sc, UART_CONTROL, 455 UART_CONTROL_TX_INT | UART_CONTROL_RX_INT); 456 } 457 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 458 sc->sc_cua = 0; 459 splx(s); 460 ttyclose(tp); 461 462 return 0; 463 } 464 465 int 466 amluartread(dev_t dev, struct uio *uio, int flag) 467 { 468 struct tty *tp = amluarttty(dev); 469 470 if (tp == NULL) 471 return ENODEV; 472 473 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 474 } 475 476 int 477 amluartwrite(dev_t dev, struct uio *uio, int flag) 478 { 479 struct tty *tp = amluarttty(dev); 480 481 if (tp == NULL) 482 return ENODEV; 483 484 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 485 } 486 487 int 488 amluartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 489 { 490 struct amluart_softc *sc = amluart_sc(dev); 491 struct tty *tp; 492 int error; 493 494 if (sc == NULL) 495 return ENODEV; 496 497 tp = sc->sc_tty; 498 if (tp == NULL) 499 return ENXIO; 500 501 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 502 if (error >= 0) 503 return error; 504 505 error = ttioctl(tp, cmd, data, flag, p); 506 if (error >= 0) 507 return error; 508 509 switch(cmd) { 510 case TIOCSBRK: 511 case TIOCCBRK: 512 case TIOCSDTR: 513 case TIOCCDTR: 514 case TIOCMSET: 515 case TIOCMBIS: 516 case TIOCMBIC: 517 case TIOCMGET: 518 case TIOCGFLAGS: 519 break; 520 case TIOCSFLAGS: 521 error = suser(p); 522 if (error != 0) 523 return EPERM; 524 break; 525 default: 526 return ENOTTY; 527 } 528 529 return 0; 530 } 531 532 int 533 amluartstop(struct tty *tp, int flag) 534 { 535 return 0; 536 } 537 538 struct tty * 539 amluarttty(dev_t dev) 540 { 541 struct amluart_softc *sc = amluart_sc(dev); 542 543 if (sc == NULL) 544 return NULL; 545 return sc->sc_tty; 546 } 547 548 struct amluart_softc * 549 amluart_sc(dev_t dev) 550 { 551 int unit = DEVUNIT(dev); 552 553 if (unit >= amluart_cd.cd_ndevs) 554 return NULL; 555 return (struct amluart_softc *)amluart_cd.cd_devs[unit]; 556 } 557 558 int 559 amluartcnattach(bus_space_tag_t iot, bus_addr_t iobase) 560 { 561 static struct consdev amluartcons = { 562 NULL, NULL, amluartcngetc, amluartcnputc, amluartcnpollc, NULL, 563 NODEV, CN_MIDPRI 564 }; 565 int maj; 566 567 amluartconsiot = iot; 568 if (bus_space_map(iot, iobase, UART_SPACE, 0, &amluartconsioh)) 569 return ENOMEM; 570 571 /* Look for major of com(4) to replace. */ 572 for (maj = 0; maj < nchrdev; maj++) 573 if (cdevsw[maj].d_open == comopen) 574 break; 575 if (maj == nchrdev) 576 return ENXIO; 577 578 cn_tab = &amluartcons; 579 cn_tab->cn_dev = makedev(maj, 0); 580 cdevsw[maj] = amluartdev; /* KLUDGE */ 581 582 return 0; 583 } 584 585 int 586 amluartcngetc(dev_t dev) 587 { 588 uint8_t c; 589 590 while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) & 591 UART_STATUS_RX_FIFO_EMPTY) 592 CPU_BUSY_CYCLE(); 593 c = bus_space_read_4(amluartconsiot, amluartconsioh, UART_RFIFO); 594 return c; 595 } 596 597 void 598 amluartcnputc(dev_t dev, int c) 599 { 600 while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) & 601 UART_STATUS_TX_FIFO_FULL) 602 CPU_BUSY_CYCLE(); 603 bus_space_write_4(amluartconsiot, amluartconsioh, UART_WFIFO, c); 604 } 605 606 void 607 amluartcnpollc(dev_t dev, int on) 608 { 609 } 610