1/* $OpenBSD: mvuart.c,v 1.4 2021/10/24 17:52:26 mpi Exp $ */ 2/* 3 * Copyright (c) 2005 Dale Rahn <drahn@motorola.com> 4 * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and 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#include <sys/param.h> 20#include <sys/ioctl.h> 21#include <sys/proc.h> 22#include <sys/tty.h> 23#include <sys/systm.h> 24#include <sys/device.h> 25#include <sys/conf.h> 26#include <sys/fcntl.h> 27 28#include <machine/bus.h> 29#include <machine/fdt.h> 30 31#include <dev/cons.h> 32 33#include <dev/ofw/openfirm.h> 34#include <dev/ofw/ofw_clock.h> 35#include <dev/ofw/ofw_pinctrl.h> 36#include <dev/ofw/fdt.h> 37 38#define MVUART_RBR 0x00 39#define MVUART_TSH 0x04 40#define MVUART_CTRL 0x08 41#define MVUART_CTRL_RX_RDY_INT (1 << 4) 42#define MVUART_CTRL_TX_RDY_INT (1 << 5) 43#define MVUART_STAT 0x0c 44#define MVUART_STAT_STD_OVR_ERR (1 << 0) 45#define MVUART_STAT_STD_PAR_ERR (1 << 1) 46#define MVUART_STAT_STD_FRM_ERR (1 << 2) 47#define MVUART_STAT_STD_BRK_DET (1 << 3) 48#define MVUART_STAT_STD_ERROR_MASK (0xf << 0) 49#define MVUART_STAT_STD_RX_RDY (1 << 4) 50#define MVUART_STAT_STD_TX_RDY (1 << 5) 51#define MVUART_STAT_STD_TX_EMPTY (1 << 6) 52#define MVUART_STAT_STD_TX_FIFO_FULL (1 << 11) 53#define MVUART_STAT_STD_TX_FIFO_EMPTY (1 << 13) 54#define MVUART_BAUD_RATE_DIV 0x10 55#define MVUART_BAUD_RATE_DIV_MASK 0x3ff 56 57#define DEVUNIT(x) (minor(x) & 0x7f) 58#define DEVCUA(x) (minor(x) & 0x80) 59 60#define HREAD4(sc, reg) \ 61 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 62#define HWRITE4(sc, reg, val) \ 63 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 64#define HSET4(sc, reg, bits) \ 65 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 66#define HCLR4(sc, reg, bits) \ 67 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 68 69struct mvuart_softc { 70 struct device sc_dev; 71 bus_space_tag_t sc_iot; 72 bus_space_handle_t sc_ioh; 73 int sc_node; 74 struct soft_intrhand *sc_si; 75 void *sc_ih; 76 struct tty *sc_tty; 77 int sc_floods; 78 int sc_errors; 79 int sc_halt; 80 uint8_t sc_hwflags; 81#define COM_HW_NOIEN 0x01 82#define COM_HW_FIFO 0x02 83#define COM_HW_SIR 0x20 84#define COM_HW_CONSOLE 0x40 85 uint8_t sc_cua; 86 uint16_t *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; 87#define MVUART_IBUFSIZE 128 88#define MVUART_IHIGHWATER 100 89 uint16_t sc_ibufs[2][MVUART_IBUFSIZE]; 90}; 91 92int mvuart_match(struct device *, void *, void *); 93void mvuart_attach(struct device *, struct device *, void *); 94 95void mvuartcnprobe(struct consdev *cp); 96void mvuartcninit(struct consdev *cp); 97int mvuartcnattach(bus_space_tag_t, bus_addr_t, int, tcflag_t); 98int mvuartcngetc(dev_t dev); 99void mvuartcnputc(dev_t dev, int c); 100void mvuartcnpollc(dev_t dev, int on); 101int mvuart_param(struct tty *, struct termios *); 102void mvuart_start(struct tty *); 103void mvuart_softint(void *arg); 104 105struct mvuart_softc *mvuart_sc(dev_t dev); 106 107int mvuart_intr(void *); 108int mvuart_intr_rx(struct mvuart_softc *); 109int mvuart_intr_tx(struct mvuart_softc *); 110 111/* XXX - we imitate 'com' serial ports and take over their entry points */ 112/* XXX: These belong elsewhere */ 113cdev_decl(com); 114cdev_decl(mvuart); 115 116struct cfdriver mvuart_cd = { 117 NULL, "mvuart", DV_TTY 118}; 119 120const struct cfattach mvuart_ca = { 121 sizeof(struct mvuart_softc), mvuart_match, mvuart_attach 122}; 123 124bus_space_tag_t mvuartconsiot; 125bus_space_handle_t mvuartconsioh; 126bus_addr_t mvuartconsaddr; 127 128struct cdevsw mvuartdev = 129 cdev_tty_init(3/*XXX NMVUART */, mvuart); /* 12: serial port */ 130 131void 132mvuart_init_cons(void) 133{ 134 struct fdt_reg reg; 135 void *node; 136 137 if ((node = fdt_find_cons("marvell,armada-3700-uart")) == NULL) 138 return; 139 140 if (fdt_get_reg(node, 0, ®)) 141 return; 142 143 mvuartcnattach(fdt_cons_bs_tag, reg.addr, B115200, TTYDEF_CFLAG); 144} 145 146int 147mvuart_match(struct device *parent, void *match, void *aux) 148{ 149 struct fdt_attach_args *faa = aux; 150 151 return OF_is_compatible(faa->fa_node, "marvell,armada-3700-uart"); 152} 153 154void 155mvuart_attach(struct device *parent, struct device *self, void *aux) 156{ 157 struct mvuart_softc *sc = (struct mvuart_softc *)self; 158 struct fdt_attach_args *faa = aux; 159 int maj; 160 161 if (faa->fa_nreg < 1) 162 return; 163 164 pinctrl_byname(faa->fa_node, "default"); 165 166 sc->sc_node = faa->fa_node; 167 sc->sc_iot = faa->fa_iot; 168 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 169 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 170 panic("%s: bus_space_map failed", sc->sc_dev.dv_xname); 171 return; 172 } 173 174 if (faa->fa_reg[0].addr == mvuartconsaddr) { 175 /* Locate the major number. */ 176 for (maj = 0; maj < nchrdev; maj++) 177 if (cdevsw[maj].d_open == mvuartopen) 178 break; 179 cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit); 180 181 printf(": console"); 182 } 183 184 printf("\n"); 185 186 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY, 187 mvuart_intr, sc, sc->sc_dev.dv_xname); 188 if (sc->sc_ih == NULL) 189 panic("%s: can't establish hard interrupt", 190 sc->sc_dev.dv_xname); 191 192 sc->sc_si = softintr_establish(IPL_TTY, mvuart_softint, sc); 193 if (sc->sc_si == NULL) 194 panic("%s: can't establish soft interrupt", 195 sc->sc_dev.dv_xname); 196} 197 198int 199mvuart_intr(void *arg) 200{ 201 struct mvuart_softc *sc = arg; 202 uint32_t stat; 203 int ret = 0; 204 205 if (sc->sc_tty == NULL) 206 return 0; 207 208 stat = HREAD4(sc, MVUART_STAT); 209 210 if ((stat & MVUART_STAT_STD_RX_RDY) != 0) 211 ret |= mvuart_intr_rx(sc); 212 213 if ((stat & MVUART_STAT_STD_TX_RDY) != 0) 214 ret |= mvuart_intr_tx(sc); 215 216 return ret; 217} 218 219int 220mvuart_intr_rx(struct mvuart_softc *sc) 221{ 222 uint32_t stat; 223 uint16_t *p, c; 224 225 p = sc->sc_ibufp; 226 227 stat = HREAD4(sc, MVUART_STAT); 228 while ((stat & MVUART_STAT_STD_RX_RDY) != 0) { 229 c = HREAD4(sc, MVUART_RBR); 230 c |= (stat & MVUART_STAT_STD_ERROR_MASK) << 8; 231 if (p >= sc->sc_ibufend) { 232 sc->sc_floods++; 233 } else { 234 *p++ = c; 235 } 236 stat = HREAD4(sc, MVUART_STAT); 237 } 238 sc->sc_ibufp = p; 239 240 softintr_schedule(sc->sc_si); 241 return 1; 242} 243 244int 245mvuart_intr_tx(struct mvuart_softc *sc) 246{ 247 struct tty *tp = sc->sc_tty; 248 249 HCLR4(sc, MVUART_CTRL, MVUART_CTRL_TX_RDY_INT); 250 if (ISSET(tp->t_state, TS_BUSY)) { 251 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 252 if (sc->sc_halt > 0) 253 wakeup(&tp->t_outq); 254 (*linesw[tp->t_line].l_start)(tp); 255 } 256 257 return 1; 258} 259 260int 261mvuart_param(struct tty *tp, struct termios *t) 262{ 263 struct mvuart_softc *sc = mvuart_sc(tp->t_dev); 264 int error, ospeed = t->c_ospeed; 265 tcflag_t oldcflag; 266 267 if (t->c_ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) 268 return EINVAL; 269 270 switch (ISSET(t->c_cflag, CSIZE)) { 271 case CS5: 272 case CS6: 273 case CS7: 274 return EINVAL; 275 case CS8: 276 break; 277 } 278 279 if (ospeed != 0) { 280 while (ISSET(tp->t_state, TS_BUSY)) { 281 ++sc->sc_halt; 282 error = ttysleep(tp, &tp->t_outq, 283 TTOPRI | PCATCH, "mvuartprm"); 284 --sc->sc_halt; 285 if (error) { 286 mvuart_start(tp); 287 return (error); 288 } 289 } 290 } 291 292 /* and copy to tty */ 293 tp->t_ispeed = t->c_ispeed; 294 tp->t_ospeed = t->c_ospeed; 295 oldcflag = tp->t_cflag; 296 tp->t_cflag = t->c_cflag; 297 298 mvuart_start(tp); 299 return 0; 300} 301 302void 303mvuart_start(struct tty *tp) 304{ 305 struct mvuart_softc *sc = mvuart_sc(tp->t_dev); 306 uint8_t buf; 307 int i, n, s; 308 309 s = spltty(); 310 if (ISSET(tp->t_state, TS_BUSY)) { 311 splx(s); 312 return; 313 } 314 if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) 315 goto out; 316 if (tp->t_outq.c_cc <= tp->t_lowat) { 317 if (ISSET(tp->t_state, TS_ASLEEP)) { 318 CLR(tp->t_state, TS_ASLEEP); 319 wakeup(&tp->t_outq); 320 } 321 if (tp->t_outq.c_cc == 0) 322 goto out; 323 selwakeup(&tp->t_wsel); 324 } 325 SET(tp->t_state, TS_BUSY); 326 327 for (i = 0; i < 32; i++) { 328 n = q_to_b(&tp->t_outq, &buf, 1); 329 if (n < 1) 330 break; 331 HWRITE4(sc, MVUART_TSH, buf); 332 if (HREAD4(sc, MVUART_STAT) & MVUART_STAT_STD_TX_FIFO_FULL) 333 break; 334 } 335 HSET4(sc, MVUART_CTRL, MVUART_CTRL_TX_RDY_INT); 336 337out: 338 splx(s); 339} 340 341void 342mvuart_softint(void *arg) 343{ 344 struct mvuart_softc *sc = arg; 345 struct tty *tp; 346 uint16_t *ibufp; 347 uint16_t *ibufend; 348 int c, err, s; 349 350 if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf) 351 return; 352 353 tp = sc->sc_tty; 354 s = spltty(); 355 356 ibufp = sc->sc_ibuf; 357 ibufend = sc->sc_ibufp; 358 359 if (ibufp == ibufend || tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) { 360 splx(s); 361 return; 362 } 363 364 sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? 365 sc->sc_ibufs[1] : sc->sc_ibufs[0]; 366 sc->sc_ibufhigh = sc->sc_ibuf + MVUART_IHIGHWATER; 367 sc->sc_ibufend = sc->sc_ibuf + MVUART_IBUFSIZE; 368 369 splx(s); 370 371 while (ibufp < ibufend) { 372 err = 0; 373 c = *ibufp++; 374 if (ISSET(c, (MVUART_STAT_STD_PAR_ERR << 8))) 375 err |= TTY_PE; 376 if (ISSET(c, (MVUART_STAT_STD_FRM_ERR << 8))) 377 err |= TTY_FE; 378 c = (c & 0xff) | err; 379 (*linesw[tp->t_line].l_rint)(c, tp); 380 } 381} 382 383int 384mvuartopen(dev_t dev, int flag, int mode, struct proc *p) 385{ 386 struct mvuart_softc *sc; 387 struct tty *tp; 388 int s, error = 0; 389 390 sc = mvuart_sc(dev); 391 if (sc == NULL) 392 return ENXIO; 393 394 s = spltty(); 395 if (sc->sc_tty == NULL) 396 tp = sc->sc_tty = ttymalloc(0); 397 else 398 tp = sc->sc_tty; 399 400 splx(s); 401 402 tp->t_oproc = mvuart_start; 403 tp->t_param = mvuart_param; 404 tp->t_dev = dev; 405 406 if (!ISSET(tp->t_state, TS_ISOPEN)) { 407 SET(tp->t_state, TS_WOPEN); 408 ttychars(tp); 409 tp->t_iflag = TTYDEF_IFLAG; 410 tp->t_oflag = TTYDEF_OFLAG; 411 412 tp->t_cflag = TTYDEF_CFLAG; 413 tp->t_lflag = TTYDEF_LFLAG; 414 tp->t_ispeed = tp->t_ospeed = B115200; 415 416 s = spltty(); 417 418 mvuart_param(tp, &tp->t_termios); 419 ttsetwater(tp); 420 sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; 421 sc->sc_ibufhigh = sc->sc_ibuf + MVUART_IHIGHWATER; 422 sc->sc_ibufend = sc->sc_ibuf + MVUART_IBUFSIZE; 423 424 /* Enable interrupts */ 425 HSET4(sc, MVUART_CTRL, MVUART_CTRL_RX_RDY_INT); 426 427 SET(tp->t_state, TS_CARR_ON); /* XXX */ 428 } else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0) 429 return EBUSY; 430 else 431 s = spltty(); 432 433 if (DEVCUA(dev)) { 434 if (ISSET(tp->t_state, TS_ISOPEN)) { 435 splx(s); 436 return EBUSY; 437 } 438 sc->sc_cua = 1; 439 } else { 440 /* tty (not cua) device; wait for carrier if necessary */ 441 if (ISSET(flag, O_NONBLOCK)) { 442 if (sc->sc_cua) { 443 /* Opening TTY non-blocking... but the CUA is busy */ 444 splx(s); 445 return EBUSY; 446 } 447 } else { 448 while (sc->sc_cua || 449 (!ISSET(tp->t_cflag, CLOCAL) && 450 !ISSET(tp->t_state, TS_CARR_ON))) { 451 SET(tp->t_state, TS_WOPEN); 452 error = ttysleep(tp, &tp->t_rawq, 453 TTIPRI | PCATCH, ttopen); 454 /* 455 * If TS_WOPEN has been reset, that means the 456 * cua device has been closed. We don't want 457 * to fail in that case, 458 * so just go around again. 459 */ 460 if (error && ISSET(tp->t_state, TS_WOPEN)) { 461 CLR(tp->t_state, TS_WOPEN); 462 splx(s); 463 return error; 464 } 465 } 466 } 467 } 468 splx(s); 469 return (*linesw[tp->t_line].l_open)(dev,tp,p); 470} 471 472int 473mvuartclose(dev_t dev, int flag, int mode, struct proc *p) 474{ 475 struct mvuart_softc *sc = mvuart_sc(dev); 476 struct tty *tp = sc->sc_tty; 477 int s; 478 479 if (!ISSET(tp->t_state, TS_ISOPEN)) 480 return 0; 481 482 (*linesw[tp->t_line].l_close)(tp, flag, p); 483 s = spltty(); 484 if (!ISSET(tp->t_state, TS_WOPEN)) { 485 /* Disable interrupts */ 486 HCLR4(sc, MVUART_CTRL, MVUART_CTRL_RX_RDY_INT | 487 MVUART_CTRL_TX_RDY_INT); 488 } 489 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 490 sc->sc_cua = 0; 491 splx(s); 492 ttyclose(tp); 493 494 return 0; 495} 496 497int 498mvuartread(dev_t dev, struct uio *uio, int flag) 499{ 500 struct tty *tty; 501 502 tty = mvuarttty(dev); 503 if (tty == NULL) 504 return ENODEV; 505 506 return((*linesw[tty->t_line].l_read)(tty, uio, flag)); 507} 508 509int 510mvuartwrite(dev_t dev, struct uio *uio, int flag) 511{ 512 struct tty *tty; 513 514 tty = mvuarttty(dev); 515 if (tty == NULL) 516 return ENODEV; 517 518 return((*linesw[tty->t_line].l_write)(tty, uio, flag)); 519} 520 521int 522mvuartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 523{ 524 struct mvuart_softc *sc; 525 struct tty *tp; 526 int error; 527 528 sc = mvuart_sc(dev); 529 if (sc == NULL) 530 return (ENODEV); 531 532 tp = sc->sc_tty; 533 if (tp == NULL) 534 return (ENXIO); 535 536 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 537 if (error >= 0) 538 return (error); 539 540 error = ttioctl(tp, cmd, data, flag, p); 541 if (error >= 0) 542 return (error); 543 544 switch(cmd) { 545 case TIOCSBRK: 546 case TIOCCBRK: 547 case TIOCSDTR: 548 case TIOCCDTR: 549 case TIOCMSET: 550 case TIOCMBIS: 551 case TIOCMBIC: 552 case TIOCMGET: 553 case TIOCGFLAGS: 554 break; 555 case TIOCSFLAGS: 556 error = suser(p); 557 if (error != 0) 558 return(EPERM); 559 break; 560 default: 561 return (ENOTTY); 562 } 563 564 return 0; 565} 566 567int 568mvuartstop(struct tty *tp, int flag) 569{ 570 return 0; 571} 572 573struct tty * 574mvuarttty(dev_t dev) 575{ 576 struct mvuart_softc *sc; 577 sc = mvuart_sc(dev); 578 if (sc == NULL) 579 return NULL; 580 return sc->sc_tty; 581} 582 583struct mvuart_softc * 584mvuart_sc(dev_t dev) 585{ 586 int unit; 587 unit = DEVUNIT(dev); 588 if (unit >= mvuart_cd.cd_ndevs) 589 return NULL; 590 return (struct mvuart_softc *)mvuart_cd.cd_devs[unit]; 591} 592 593/* serial console */ 594void 595mvuartcnprobe(struct consdev *cp) 596{ 597} 598 599void 600mvuartcninit(struct consdev *cp) 601{ 602} 603 604int 605mvuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, tcflag_t cflag) 606{ 607 static struct consdev mvuartcons = { 608 NULL, NULL, mvuartcngetc, mvuartcnputc, mvuartcnpollc, NULL, 609 NODEV, CN_MIDPRI 610 }; 611 int maj; 612 613 if (bus_space_map(iot, iobase, 0x200, 0, &mvuartconsioh)) 614 return ENOMEM; 615 616 /* Look for major of com(4) to replace. */ 617 for (maj = 0; maj < nchrdev; maj++) 618 if (cdevsw[maj].d_open == comopen) 619 break; 620 if (maj == nchrdev) 621 return ENXIO; 622 623 cn_tab = &mvuartcons; 624 cn_tab->cn_dev = makedev(maj, 0); 625 cdevsw[maj] = mvuartdev; /* KLUDGE */ 626 627 mvuartconsiot = iot; 628 mvuartconsaddr = iobase; 629 630 return 0; 631} 632 633int 634mvuartcngetc(dev_t dev) 635{ 636 int c; 637 int s; 638 s = splhigh(); 639 while ((bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_STAT) & 640 MVUART_STAT_STD_RX_RDY) == 0) 641 ; 642 c = bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_RBR); 643 splx(s); 644 return c; 645} 646 647void 648mvuartcnputc(dev_t dev, int c) 649{ 650 int s; 651 s = splhigh(); 652 while ((bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_STAT) & 653 MVUART_STAT_STD_TX_FIFO_FULL) != 0) 654 ; 655 bus_space_write_4(mvuartconsiot, mvuartconsioh, MVUART_TSH, (uint8_t)c); 656 while ((bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_STAT) & 657 MVUART_STAT_STD_TX_FIFO_EMPTY) != 0) 658 ; 659 splx(s); 660} 661 662void 663mvuartcnpollc(dev_t dev, int on) 664{ 665} 666