1 /* $OpenBSD: cduart.c,v 1.1 2021/04/24 07:49:11 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Visa Hankala 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Driver for Cadence UART. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/conf.h> 26 #include <sys/device.h> 27 #include <sys/fcntl.h> 28 #include <sys/tty.h> 29 #include <sys/syslog.h> 30 31 #include <machine/bus.h> 32 #include <machine/fdt.h> 33 34 #include <dev/cons.h> 35 36 #include <dev/ofw/fdt.h> 37 #include <dev/ofw/openfirm.h> 38 39 #define CDUART_CR 0x0000 40 #define CDUART_CR_STOPBRK (1 << 8) 41 #define CDUART_CR_STARTBRK (1 << 7) 42 #define CDUART_CR_TORST (1 << 6) 43 #define CDUART_CR_TXDIS (1 << 5) 44 #define CDUART_CR_TXEN (1 << 4) 45 #define CDUART_CR_RXDIS (1 << 3) 46 #define CDUART_CR_RXEN (1 << 2) 47 #define CDUART_CR_TXRST (1 << 1) 48 #define CDUART_CR_RXRST (1 << 0) 49 #define CDUART_MR 0x0004 50 #define CDUART_MR_CHMODE_MASK (0x3 << 8) 51 #define CDUART_MR_NBSTOP_MASK (0x3 << 6) 52 #define CDUART_MR_PAR_MASK (0x7 << 3) 53 #define CDUART_MR_PAR_NO (0x4 << 3) 54 #define CDUART_MR_PAR_FORCED1 (0x3 << 3) 55 #define CDUART_MR_PAR_FORCED0 (0x2 << 3) 56 #define CDUART_MR_PAR_ODD (0x1 << 3) 57 #define CDUART_MR_PAR_EVEN (0x0 << 3) 58 #define CDUART_MR_CHRL_MASK (0x3 << 1) 59 #define CDUART_MR_CHRL_C6 (0x3 << 1) 60 #define CDUART_MR_CHRL_C7 (0x2 << 1) 61 #define CDUART_MR_CHRL_C8 (0x0 << 1) 62 #define CDUART_MR_CLKSEL (1 << 0) 63 #define CDUART_IER 0x0008 64 #define CDUART_IDR 0x000c 65 #define CDUART_IMR 0x0010 66 #define CDUART_ISR 0x0014 67 #define CDUART_ISR_TXOVR (1 << 12) 68 #define CDUART_ISR_TNFUL (1 << 11) 69 #define CDUART_ISR_TTRIG (1 << 10) 70 #define CDUART_IXR_DMS (1 << 9) 71 #define CDUART_IXR_TOUT (1 << 8) 72 #define CDUART_IXR_PARITY (1 << 7) 73 #define CDUART_IXR_FRAMING (1 << 6) 74 #define CDUART_IXR_RXOVR (1 << 5) 75 #define CDUART_IXR_TXFULL (1 << 4) 76 #define CDUART_IXR_TXEMPTY (1 << 3) 77 #define CDUART_IXR_RXFULL (1 << 2) 78 #define CDUART_IXR_RXEMPTY (1 << 1) 79 #define CDUART_IXR_RTRIG (1 << 0) 80 #define CDUART_RXTOUT 0x001c 81 #define CDUART_RXWM 0x0020 82 #define CDUART_SR 0x002c 83 #define CDUART_SR_TNFUL (1 << 14) 84 #define CDUART_SR_TTRIG (1 << 13) 85 #define CDUART_SR_FLOWDEL (1 << 12) 86 #define CDUART_SR_TACTIVE (1 << 11) 87 #define CDUART_SR_RACTIVE (1 << 10) 88 #define CDUART_SR_TXFULL (1 << 4) 89 #define CDUART_SR_TXEMPTY (1 << 3) 90 #define CDUART_SR_RXFULL (1 << 2) 91 #define CDUART_SR_RXEMPTY (1 << 1) 92 #define CDUART_SR_RXOVR (1 << 0) 93 #define CDUART_FIFO 0x0030 94 95 #define CDUART_SPACE_SIZE 0x0048 96 97 #define CDUART_FIFOSIZE 64 98 #define CDUART_IBUFSIZE 128 99 100 struct cduart_softc { 101 struct device sc_dev; 102 bus_space_tag_t sc_iot; 103 bus_space_handle_t sc_ioh; 104 void *sc_ih; 105 void *sc_si; 106 107 struct tty *sc_tty; 108 uint8_t sc_cua; 109 110 struct timeout sc_diag_tmo; 111 int sc_overflows; 112 int sc_floods; 113 int sc_errors; 114 115 int sc_ibufs[2][CDUART_IBUFSIZE]; 116 int *sc_ibuf; 117 int *sc_ibufend; 118 int *sc_ibufp; 119 }; 120 121 int cduart_match(struct device *, void *, void *); 122 void cduart_attach(struct device *, struct device *, void *); 123 void cduart_diag(void *); 124 int cduart_intr(void *); 125 void cduart_softintr(void *); 126 127 struct tty *cduarttty(dev_t); 128 struct cduart_softc *cduart_sc(dev_t); 129 130 int cduartparam(struct tty *, struct termios *); 131 void cduartstart(struct tty *); 132 133 int cduartcnattach(bus_space_tag_t, bus_addr_t, int, tcflag_t); 134 void cduartcnprobe(struct consdev *); 135 void cduartcninit(struct consdev *); 136 int cduartcngetc(dev_t); 137 void cduartcnputc(dev_t, int); 138 void cduartcnpollc(dev_t, int); 139 140 cdev_decl(com); 141 cdev_decl(cduart); 142 143 const struct cfattach cduart_ca = { 144 sizeof(struct cduart_softc), cduart_match, cduart_attach 145 }; 146 147 struct cfdriver cduart_cd = { 148 NULL, "cduart", DV_DULL 149 }; 150 151 bus_space_tag_t cduartconsiot; 152 bus_space_handle_t cduartconsioh; 153 int cduartconsrate; 154 tcflag_t cduartconscflag; 155 struct cdevsw cduartdev = cdev_tty_init(3, cduart); 156 157 #define DEVUNIT(x) (minor(x) & 0x7f) 158 #define DEVCUA(x) (minor(x) & 0x80) 159 160 static inline uint32_t 161 cduart_read(struct cduart_softc *sc, uint32_t reg) 162 { 163 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg); 164 } 165 166 static inline void 167 cduart_write(struct cduart_softc *sc, uint32_t reg, uint32_t val) 168 { 169 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val); 170 } 171 172 void 173 cduart_init_cons(void) 174 { 175 struct fdt_reg reg; 176 void *node; 177 178 if ((node = fdt_find_cons("cdns,uart-r1p8")) == NULL && 179 (node = fdt_find_cons("cdns,uart-r1p12")) == NULL) 180 return; 181 if (fdt_get_reg(node, 0, ®) != 0) 182 return; 183 184 cduartcnattach(fdt_cons_bs_tag, reg.addr, B115200, TTYDEF_CFLAG); 185 } 186 187 int 188 cduart_match(struct device *parent, void *match, void *aux) 189 { 190 struct fdt_attach_args *faa = aux; 191 192 return OF_is_compatible(faa->fa_node, "cdns,uart-r1p8") || 193 OF_is_compatible(faa->fa_node, "cdns,uart-r1p12"); 194 } 195 196 void 197 cduart_attach(struct device *parent, struct device *self, void *aux) 198 { 199 struct fdt_attach_args *faa = aux; 200 struct cduart_softc *sc = (struct cduart_softc *)self; 201 uint32_t cr, isr; 202 int maj; 203 204 if (faa->fa_nreg < 1) { 205 printf(": no registers\n"); 206 return; 207 } 208 209 sc->sc_iot = faa->fa_iot; 210 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 211 faa->fa_reg[0].size, 0, &sc->sc_ioh) != 0) { 212 printf(": can't map registers\n"); 213 return; 214 } 215 216 /* Disable all interrupts. */ 217 cduart_write(sc, CDUART_IDR, ~0U); 218 219 /* Clear any pending interrupts. */ 220 isr = cduart_read(sc, CDUART_ISR); 221 cduart_write(sc, CDUART_ISR, isr); 222 223 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY, 224 cduart_intr, sc, sc->sc_dev.dv_xname); 225 if (sc->sc_ih == NULL) { 226 printf(": can't establish interrupt\n"); 227 goto fail; 228 } 229 230 timeout_set(&sc->sc_diag_tmo, cduart_diag, sc); 231 sc->sc_si = softintr_establish(IPL_TTY, cduart_softintr, sc); 232 if (sc->sc_si == NULL) { 233 printf(": can't establish soft interrupt\n"); 234 goto fail; 235 } 236 237 if (faa->fa_node == stdout_node) { 238 /* Locate the major number. */ 239 for (maj = 0; maj < nchrdev; maj++) { 240 if (cdevsw[maj].d_open == cduartopen) 241 break; 242 } 243 KASSERT(maj < nchrdev); 244 cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit); 245 printf(": console"); 246 } 247 248 /* Enable transmitter and receiver. */ 249 cr = cduart_read(sc, CDUART_CR); 250 cr &= ~(CDUART_CR_TXDIS | CDUART_CR_RXDIS); 251 cr |= CDUART_CR_TXEN | CDUART_CR_RXEN; 252 cduart_write(sc, CDUART_CR, cr); 253 254 printf("\n"); 255 256 return; 257 258 fail: 259 if (sc->sc_si != NULL) 260 softintr_disestablish(sc->sc_si); 261 if (sc->sc_ih != NULL) 262 fdt_intr_disestablish(sc->sc_ih); 263 if (sc->sc_ioh != 0) 264 bus_space_unmap(sc->sc_iot, sc->sc_ioh, CDUART_SPACE_SIZE); 265 } 266 267 int 268 cduart_intr(void *arg) 269 { 270 struct cduart_softc *sc = arg; 271 struct tty *tp = sc->sc_tty; 272 int *ibufp; 273 uint32_t isr, sr; 274 int c, handled = 0; 275 276 if (tp == NULL) 277 return 0; 278 279 isr = cduart_read(sc, CDUART_ISR); 280 cduart_write(sc, CDUART_ISR, isr); 281 282 if ((isr & CDUART_IXR_TXEMPTY) && (tp->t_state & TS_BUSY)) { 283 tp->t_state &= ~TS_BUSY; 284 (*linesw[tp->t_line].l_start)(tp); 285 handled = 1; 286 } 287 288 if (isr & (CDUART_IXR_TOUT | CDUART_IXR_RTRIG)) { 289 ibufp = sc->sc_ibufp; 290 for (;;) { 291 sr = cduart_read(sc, CDUART_SR); 292 if (sr & CDUART_SR_RXEMPTY) 293 break; 294 c = cduart_read(sc, CDUART_FIFO) & 0xff; 295 296 if (ibufp < sc->sc_ibufend) { 297 *ibufp++ = c; 298 } else { 299 sc->sc_floods++; 300 if (sc->sc_errors++ == 0) 301 timeout_add_sec(&sc->sc_diag_tmo, 60); 302 } 303 } 304 if (sc->sc_ibufp != ibufp) { 305 sc->sc_ibufp = ibufp; 306 softintr_schedule(sc->sc_si); 307 } 308 handled = 1; 309 } 310 311 if (isr & CDUART_IXR_RXOVR) { 312 sc->sc_overflows++; 313 if (sc->sc_errors++ == 0) 314 timeout_add_sec(&sc->sc_diag_tmo, 60); 315 handled = 1; 316 } 317 318 return handled; 319 } 320 321 void 322 cduart_softintr(void *arg) 323 { 324 struct cduart_softc *sc = arg; 325 struct tty *tp = sc->sc_tty; 326 int *ibufend, *ibufp; 327 int s; 328 329 s = spltty(); 330 331 ibufp = sc->sc_ibuf; 332 ibufend = sc->sc_ibufp; 333 334 if (ibufp == ibufend) { 335 splx(s); 336 return; 337 } 338 339 sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? 340 sc->sc_ibufs[1] : sc->sc_ibufs[0]; 341 sc->sc_ibufend = sc->sc_ibuf + CDUART_IBUFSIZE; 342 343 if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) { 344 splx(s); 345 return; 346 } 347 348 splx(s); 349 350 while (ibufp < ibufend) 351 (*linesw[tp->t_line].l_rint)(*ibufp++, tp); 352 } 353 354 void 355 cduart_diag(void *arg) 356 { 357 struct cduart_softc *sc = arg; 358 int overflows, floods; 359 int s; 360 361 s = spltty(); 362 sc->sc_errors = 0; 363 overflows = sc->sc_overflows; 364 sc->sc_overflows = 0; 365 floods = sc->sc_floods; 366 sc->sc_floods = 0; 367 splx(s); 368 log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n", 369 sc->sc_dev.dv_xname, 370 overflows, overflows == 1 ? "" : "s", 371 floods, floods == 1 ? "" : "s"); 372 } 373 374 int 375 cduartopen(dev_t dev, int flag, int mode, struct proc *p) 376 { 377 struct cduart_softc *sc; 378 struct tty *tp; 379 uint32_t sr; 380 int error, s; 381 382 sc = cduart_sc(dev); 383 if (sc == NULL) 384 return ENXIO; 385 386 s = spltty(); 387 388 if (sc->sc_tty == NULL) 389 sc->sc_tty = ttymalloc(0); 390 391 tp = sc->sc_tty; 392 tp->t_oproc = cduartstart; 393 tp->t_param = cduartparam; 394 tp->t_dev = dev; 395 if ((tp->t_state & TS_ISOPEN) == 0) { 396 tp->t_state |= TS_WOPEN | TS_CARR_ON; 397 ttychars(tp); 398 tp->t_iflag = TTYDEF_IFLAG; 399 tp->t_oflag = TTYDEF_OFLAG; 400 tp->t_cflag = TTYDEF_CFLAG; 401 tp->t_lflag = TTYDEF_LFLAG; 402 tp->t_ispeed = B115200; /* XXX */ 403 tp->t_ospeed = tp->t_ispeed; 404 ttsetwater(tp); 405 406 sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; 407 sc->sc_ibufend = sc->sc_ibuf + CDUART_IBUFSIZE; 408 409 cduart_write(sc, CDUART_RXTOUT, 10); 410 cduart_write(sc, CDUART_RXWM, CDUART_FIFOSIZE / 2); 411 412 /* Clear any pending I/O. */ 413 for (;;) { 414 sr = cduart_read(sc, CDUART_SR); 415 if (sr & CDUART_SR_RXEMPTY) 416 break; 417 (void)cduart_read(sc, CDUART_FIFO); 418 } 419 420 cduart_write(sc, CDUART_IER, 421 CDUART_IXR_TOUT | CDUART_IXR_RXOVR | CDUART_IXR_RTRIG); 422 } else if ((tp->t_state & TS_XCLUDE) && suser(p) != 0) { 423 splx(s); 424 return EBUSY; 425 } 426 427 if (DEVCUA(dev)) { 428 if (tp->t_state & TS_ISOPEN) { 429 splx(s); 430 return EBUSY; 431 } 432 sc->sc_cua = 1; 433 } else { 434 if ((flag & O_NONBLOCK) && sc->sc_cua) { 435 splx(s); 436 return EBUSY; 437 } else { 438 while (sc->sc_cua) { 439 tp->t_state |= TS_WOPEN; 440 error = ttysleep(tp, &tp->t_rawq, 441 TTIPRI | PCATCH, ttopen); 442 if (error != 0 && (tp->t_state & TS_WOPEN)) { 443 tp->t_state &= ~TS_WOPEN; 444 splx(s); 445 return error; 446 } 447 } 448 } 449 } 450 451 splx(s); 452 453 return (*linesw[tp->t_line].l_open)(dev, tp, p); 454 } 455 456 int 457 cduartclose(dev_t dev, int flag, int mode, struct proc *p) 458 { 459 struct cduart_softc *sc; 460 struct tty *tp; 461 int s; 462 463 sc = cduart_sc(dev); 464 tp = sc->sc_tty; 465 466 if ((tp->t_state & TS_ISOPEN) == 0) 467 return 0; 468 469 (*linesw[tp->t_line].l_close)(tp, flag, p); 470 s = spltty(); 471 /* Disable interrupts. */ 472 cduart_write(sc, CDUART_IDR, ~0U); 473 sc->sc_cua = 0; 474 splx(s); 475 ttyclose(tp); 476 477 return 0; 478 } 479 480 int 481 cduartread(dev_t dev, struct uio *uio, int flag) 482 { 483 struct tty *tp; 484 485 tp = cduarttty(dev); 486 if (tp == NULL) 487 return ENODEV; 488 489 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 490 } 491 492 int 493 cduartwrite(dev_t dev, struct uio *uio, int flag) 494 { 495 struct tty *tp; 496 497 tp = cduarttty(dev); 498 if (tp == NULL) 499 return ENODEV; 500 501 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 502 } 503 504 int 505 cduartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 506 { 507 struct cduart_softc *sc; 508 struct tty *tp; 509 int error; 510 511 sc = cduart_sc(dev); 512 if (sc == NULL) 513 return ENODEV; 514 515 tp = sc->sc_tty; 516 if (tp == NULL) 517 return ENXIO; 518 519 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 520 if (error >= 0) 521 return error; 522 523 error = ttioctl(tp, cmd, data, flag, p); 524 if (error >= 0) 525 return error; 526 527 /* XXX */ 528 switch (cmd) { 529 case TIOCSBRK: 530 case TIOCCBRK: 531 case TIOCSDTR: 532 case TIOCCDTR: 533 case TIOCMSET: 534 case TIOCMBIC: 535 case TIOCMGET: 536 case TIOCGFLAGS: 537 break; 538 case TIOCSFLAGS: 539 error = suser(p); 540 if (error != 0) 541 return EPERM; 542 break; 543 default: 544 return ENOTTY; 545 } 546 547 return 0; 548 } 549 550 int 551 cduartparam(struct tty *tp, struct termios *t) 552 { 553 return 0; 554 } 555 556 void 557 cduartstart(struct tty *tp) 558 { 559 struct cduart_softc *sc; 560 uint32_t sr; 561 int s; 562 563 sc = cduart_sc(tp->t_dev); 564 565 s = spltty(); 566 if (tp->t_state & TS_BUSY) 567 goto out; 568 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) 569 goto stopped; 570 ttwakeupwr(tp); 571 if (tp->t_outq.c_cc == 0) 572 goto stopped; 573 tp->t_state |= TS_BUSY; 574 575 cduart_write(sc, CDUART_ISR, CDUART_IXR_TXEMPTY); 576 577 sr = cduart_read(sc, CDUART_SR); 578 while ((sr & CDUART_SR_TXFULL) == 0 && tp->t_outq.c_cc != 0) { 579 cduart_write(sc, CDUART_FIFO, getc(&tp->t_outq)); 580 sr = cduart_read(sc, CDUART_SR); 581 } 582 583 cduart_write(sc, CDUART_IER, CDUART_IXR_TXEMPTY); 584 out: 585 splx(s); 586 return; 587 stopped: 588 cduart_write(sc, CDUART_IDR, CDUART_IXR_TXEMPTY); 589 splx(s); 590 } 591 592 int 593 cduartstop(struct tty *tp, int flag) 594 { 595 return 0; 596 } 597 598 struct tty * 599 cduarttty(dev_t dev) 600 { 601 struct cduart_softc *sc; 602 603 sc = cduart_sc(dev); 604 if (sc == NULL) 605 return NULL; 606 607 return sc->sc_tty; 608 } 609 610 struct cduart_softc * 611 cduart_sc(dev_t dev) 612 { 613 int unit = DEVUNIT(dev); 614 615 if (unit >= cduart_cd.cd_ndevs) 616 return NULL; 617 return (struct cduart_softc *)cduart_cd.cd_devs[unit]; 618 } 619 620 struct consdev cduartcons = { 621 .cn_probe = NULL, 622 .cn_init = NULL, 623 .cn_getc = cduartcngetc, 624 .cn_putc = cduartcnputc, 625 .cn_pollc = cduartcnpollc, 626 .cn_bell = NULL, 627 .cn_dev = NODEV, 628 .cn_pri = CN_MIDPRI, 629 }; 630 631 int 632 cduartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, 633 tcflag_t cflag) 634 { 635 bus_space_handle_t ioh; 636 int maj; 637 638 /* Look for major of com(4) to replace. */ 639 for (maj = 0; maj < nchrdev; maj++) { 640 if (cdevsw[maj].d_open == comopen) 641 break; 642 } 643 if (maj == nchrdev) 644 return ENXIO; 645 646 if (bus_space_map(iot, iobase, CDUART_SPACE_SIZE, 0, &ioh) != 0) 647 return ENOMEM; 648 649 cn_tab = &cduartcons; 650 cn_tab->cn_dev = makedev(maj, 0); 651 cdevsw[maj] = cduartdev; 652 653 cduartconsiot = iot; 654 cduartconsioh = ioh; 655 cduartconsrate = rate; 656 cduartconscflag = cflag; 657 658 return 0; 659 } 660 661 void 662 cduartcnprobe(struct consdev *cp) 663 { 664 } 665 666 void 667 cduartcninit(struct consdev *cp) 668 { 669 } 670 671 int 672 cduartcngetc(dev_t dev) 673 { 674 int s; 675 uint8_t c; 676 677 s = splhigh(); 678 while (bus_space_read_4(cduartconsiot, cduartconsioh, 679 CDUART_SR) & CDUART_SR_RXEMPTY) 680 CPU_BUSY_CYCLE(); 681 c = bus_space_read_4(cduartconsiot, cduartconsioh, CDUART_FIFO); 682 splx(s); 683 684 return c; 685 } 686 687 void 688 cduartcnputc(dev_t dev, int c) 689 { 690 int s; 691 692 s = splhigh(); 693 while (bus_space_read_4(cduartconsiot, cduartconsioh, 694 CDUART_SR) & CDUART_SR_TXFULL) 695 CPU_BUSY_CYCLE(); 696 bus_space_write_4(cduartconsiot, cduartconsioh, CDUART_FIFO, c); 697 splx(s); 698 } 699 700 void 701 cduartcnpollc(dev_t dev, int on) 702 { 703 } 704