jcs's openbsd hax
openbsd
at jcs 607 lines 13 kB view raw
1/* $OpenBSD: qcuart.c,v 1.1 2026/01/29 11:23:35 kettenis Exp $ */ 2/* 3 * Copyright (c) 2026 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 27#include <dev/cons.h> 28 29#include <dev/ic/qcuartvar.h> 30 31#define GENI_STATUS 0x040 32#define GENI_STATUS_M_CMD_ACTIVE (1U << 0) 33#define GENI_STATUS_S_CMD_ACTIVE (1U << 12) 34#define GENI_UART_TX_TRANS_LEN 0x270 35#define GENI_M_CMD0 0x600 36#define GENI_M_CMD0_OPCODE_UART_START_TX (1U << 27) 37#define GENI_M_IRQ_STATUS 0x610 38#define GENI_M_IRQ_EN 0x614 39#define GENI_M_IRQ_CLEAR 0x618 40#define GENI_M_IRQ_CMD_DONE (1U << 0) 41#define GENI_M_IRQ_TX_FIFO_WATERMARK (1U << 30) 42#define GENI_M_IRQ_SEC_IRQ (1U << 31) 43#define GENI_S_CMD0 0x630 44#define GENI_S_CMD0_OPCODE_UART_START_RX (1U << 27) 45#define GENI_S_IRQ_STATUS 0x640 46#define GENI_S_IRQ_EN 0x644 47#define GENI_S_IRQ_CLEAR 0x648 48#define GENI_S_IRQ_RX_FIFO_WATERMARK (1U << 26) 49#define GENI_S_IRQ_RX_FIFO_LAST (1U << 27) 50#define GENI_TX_FIFO 0x700 51#define GENI_RX_FIFO 0x780 52#define GENI_TX_FIFO_STATUS 0x800 53#define GENI_TX_FIFO_STATUS_WC_MASK 0xfffffff 54#define GENI_RX_FIFO_STATUS 0x804 55#define GENI_RX_FIFO_STATUS_WC_MASK 0x1ffffff 56#define GENI_TX_FIFO_WATERMARK 0x80c 57 58#define GENI_SPACE 0x1000 59 60#define QCUART_TX_WATERMARK 2 61 62#define HREAD4(sc, reg) \ 63 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 64#define HWRITE4(sc, reg, val) \ 65 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 66#define HSET4(sc, reg, bits) \ 67 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 68#define HCLR4(sc, reg, bits) \ 69 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 70 71cdev_decl(com); 72cdev_decl(qcuart); 73 74#define DEVUNIT(x) (minor(x) & 0x7f) 75#define DEVCUA(x) (minor(x) & 0x80) 76 77struct cdevsw qcuartdev = cdev_tty_init(3, qcuart); 78 79struct cfdriver qcuart_cd = { 80 NULL, "qcuart", DV_TTY 81}; 82 83bus_space_tag_t qcuartconsiot; 84bus_space_handle_t qcuartconsioh; 85 86struct qcuart_softc *qcuart_sc(dev_t); 87 88void qcuart_softintr(void *); 89void qcuart_start(struct tty *); 90 91int qcuartcnattach(bus_space_tag_t, bus_addr_t); 92int qcuartcngetc(dev_t); 93void qcuartcnputc(dev_t, int); 94void qcuartcnpollc(dev_t, int); 95 96void 97qcuart_attach_common(struct qcuart_softc *sc, int console) 98{ 99 int maj; 100 101 if (console) { 102 /* Locate the major number. */ 103 for (maj = 0; maj < nchrdev; maj++) 104 if (cdevsw[maj].d_open == qcuartopen) 105 break; 106 cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit); 107 printf(": console"); 108 } 109 110 /* Disable all interrupts. */ 111 HWRITE4(sc, GENI_M_IRQ_EN, 0); 112 HWRITE4(sc, GENI_S_IRQ_EN, 0); 113 114 sc->sc_si = softintr_establish(IPL_TTY, qcuart_softintr, sc); 115 if (sc->sc_si == NULL) { 116 printf(": can't establish soft interrupt\n"); 117 return; 118 } 119 120 printf("\n"); 121} 122 123void 124qcuart_tx_intr(struct qcuart_softc *sc) 125{ 126 struct tty *tp = sc->sc_tty; 127 128 if (ISSET(tp->t_state, TS_BUSY)) { 129 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 130 if (sc->sc_halt > 0) 131 wakeup(&tp->t_outq); 132 (*linesw[tp->t_line].l_start)(tp); 133 } 134} 135 136void 137qcuart_rx_intr(struct qcuart_softc *sc) 138{ 139 uint32_t stat; 140 u_char c; 141 int *p; 142 143 p = sc->sc_ibufp; 144 for (;;) { 145 stat = HREAD4(sc, GENI_RX_FIFO_STATUS); 146 if ((stat & GENI_RX_FIFO_STATUS_WC_MASK) == 0) 147 break; 148 149 c = HREAD4(sc, GENI_RX_FIFO); 150 if (p >= sc->sc_ibufend) 151 sc->sc_floods++; 152 else 153 *p++ = c; 154 } 155 if (sc->sc_ibufp != p) { 156 sc->sc_ibufp = p; 157 softintr_schedule(sc->sc_si); 158 } 159} 160 161int 162qcuart_intr(void *arg) 163{ 164 struct qcuart_softc *sc = arg; 165 struct tty *tp = sc->sc_tty; 166 uint32_t m_stat, s_stat; 167 168 m_stat = HREAD4(sc, GENI_M_IRQ_STATUS); 169 s_stat = HREAD4(sc, GENI_S_IRQ_STATUS); 170 HWRITE4(sc, GENI_M_IRQ_CLEAR, m_stat); 171 HWRITE4(sc, GENI_S_IRQ_CLEAR, s_stat); 172 m_stat &= HREAD4(sc, GENI_M_IRQ_EN); 173 174 if (tp == NULL) 175 return 0; 176 177 if (m_stat & GENI_M_IRQ_CMD_DONE) 178 qcuart_tx_intr(sc); 179 180 if (m_stat & GENI_M_IRQ_SEC_IRQ) 181 qcuart_rx_intr(sc); 182 183 return m_stat ? 1 : 0; 184} 185 186void 187qcuart_softintr(void *arg) 188{ 189 struct qcuart_softc *sc = arg; 190 struct tty *tp = sc->sc_tty; 191 int *ibufp, *ibufend; 192 int s; 193 194 if (sc->sc_ibufp == sc->sc_ibuf) 195 return; 196 197 s = spltty(); 198 199 ibufp = sc->sc_ibuf; 200 ibufend = sc->sc_ibufp; 201 202 if (ibufp == ibufend) { 203 splx(s); 204 return; 205 } 206 207 sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? 208 sc->sc_ibufs[1] : sc->sc_ibufs[0]; 209 sc->sc_ibufhigh = sc->sc_ibuf + QCUART_IHIGHWATER; 210 sc->sc_ibufend = sc->sc_ibuf + QCUART_IBUFSIZE; 211 212 if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) { 213 splx(s); 214 return; 215 } 216 217 splx(s); 218 219 while (ibufp < ibufend) { 220 int i = *ibufp++; 221#ifdef DDB 222 if (tp->t_dev == cn_tab->cn_dev) { 223 int j = db_rint(i); 224 225 if (j == 1) /* Escape received, skip */ 226 continue; 227 if (j == 2) /* Second char wasn't 'D' */ 228 (*linesw[tp->t_line].l_rint)(27, tp); 229 } 230#endif 231 (*linesw[tp->t_line].l_rint)(i, tp); 232 } 233} 234 235int 236qcuart_param(struct tty *tp, struct termios *t) 237{ 238 struct qcuart_softc *sc = qcuart_sc(tp->t_dev); 239 int ospeed = t->c_ospeed; 240 241 /* Check requested parameters. */ 242 if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) 243 return EINVAL; 244 245 switch (ISSET(t->c_cflag, CSIZE)) { 246 case CS5: 247 case CS6: 248 case CS7: 249 return EINVAL; 250 case CS8: 251 break; 252 } 253 254 if (ospeed != 0) { 255 while (ISSET(tp->t_state, TS_BUSY)) { 256 int error; 257 258 sc->sc_halt++; 259 error = ttysleep(tp, &tp->t_outq, 260 TTOPRI | PCATCH, "qcuprm"); 261 sc->sc_halt--; 262 if (error) { 263 qcuart_start(tp); 264 return error; 265 } 266 } 267 } 268 269 tp->t_ispeed = t->c_ispeed; 270 tp->t_ospeed = t->c_ospeed; 271 tp->t_cflag = t->c_cflag; 272 273 /* Just to be sure... */ 274 qcuart_start(tp); 275 return 0; 276} 277 278void 279qcuart_start(struct tty *tp) 280{ 281 struct qcuart_softc *sc = qcuart_sc(tp->t_dev); 282 int s; 283 284 s = spltty(); 285 if (ISSET(tp->t_state, TS_BUSY)) 286 goto out; 287 if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0) 288 goto out; 289 ttwakeupwr(tp); 290 if (tp->t_outq.c_cc == 0) 291 goto out; 292 SET(tp->t_state, TS_BUSY); 293 294 /* Enable Tx completion interrupts. */ 295 HSET4(sc, GENI_M_IRQ_EN, GENI_M_IRQ_CMD_DONE); 296 297 /* Send a single character. */ 298 HWRITE4(sc, GENI_UART_TX_TRANS_LEN, 1); 299 HWRITE4(sc, GENI_M_CMD0, GENI_M_CMD0_OPCODE_UART_START_TX); 300 HWRITE4(sc, GENI_TX_FIFO, getc(&tp->t_outq)); 301 302out: 303 splx(s); 304} 305 306int 307qcuartopen(dev_t dev, int flag, int mode, struct proc *p) 308{ 309 struct qcuart_softc *sc = qcuart_sc(dev); 310 struct tty *tp; 311 int error; 312 int s; 313 314 if (sc == NULL) 315 return ENXIO; 316 317 s = spltty(); 318 if (sc->sc_tty == NULL) 319 tp = sc->sc_tty = ttymalloc(0); 320 else 321 tp = sc->sc_tty; 322 splx(s); 323 324 tp->t_oproc = qcuart_start; 325 tp->t_param = qcuart_param; 326 tp->t_dev = dev; 327 328 if (!ISSET(tp->t_state, TS_ISOPEN)) { 329 SET(tp->t_state, TS_WOPEN); 330 ttychars(tp); 331 tp->t_iflag = TTYDEF_IFLAG; 332 tp->t_oflag = TTYDEF_OFLAG; 333 tp->t_cflag = TTYDEF_CFLAG; 334 tp->t_lflag = TTYDEF_LFLAG; 335 tp->t_ispeed = tp->t_ospeed = 336 sc->sc_conspeed ? sc->sc_conspeed : B115200; 337 338 s = spltty(); 339 340 qcuart_param(tp, &tp->t_termios); 341 ttsetwater(tp); 342 343 sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; 344 sc->sc_ibufhigh = sc->sc_ibuf + QCUART_IHIGHWATER; 345 sc->sc_ibufend = sc->sc_ibuf + QCUART_IBUFSIZE; 346 347 /* Enable Rx interrupts. */ 348 HSET4(sc, GENI_S_IRQ_EN, 349 GENI_S_IRQ_RX_FIFO_WATERMARK | GENI_S_IRQ_RX_FIFO_LAST); 350 HSET4(sc, GENI_M_IRQ_EN, GENI_M_IRQ_SEC_IRQ); 351 352 /* Start Rx engine. */ 353 HWRITE4(sc, GENI_S_CMD0, GENI_S_CMD0_OPCODE_UART_START_RX); 354 355 /* No carrier detect support. */ 356 SET(tp->t_state, TS_CARR_ON); 357 } else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0) 358 return EBUSY; 359 else 360 s = spltty(); 361 362 if (DEVCUA(dev)) { 363 if (ISSET(tp->t_state, TS_ISOPEN)) { 364 /* Ah, but someone already is dialed in... */ 365 splx(s); 366 return EBUSY; 367 } 368 sc->sc_cua = 1; /* We go into CUA mode. */ 369 } else { 370 if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) { 371 /* Opening TTY non-blocking... but the CUA is busy. */ 372 splx(s); 373 return EBUSY; 374 } else { 375 while (sc->sc_cua) { 376 SET(tp->t_state, TS_WOPEN); 377 error = ttysleep(tp, &tp->t_rawq, 378 TTIPRI | PCATCH, ttopen); 379 /* 380 * If TS_WOPEN has been reset, that means the 381 * cua device has been closed. 382 * We don't want to fail in that case, 383 * so just go around again. 384 */ 385 if (error && ISSET(tp->t_state, TS_WOPEN)) { 386 CLR(tp->t_state, TS_WOPEN); 387 splx(s); 388 return error; 389 } 390 } 391 } 392 } 393 splx(s); 394 395 return (*linesw[tp->t_line].l_open)(dev, tp, p); 396} 397 398int 399qcuartclose(dev_t dev, int flag, int mode, struct proc *p) 400{ 401 struct qcuart_softc *sc = qcuart_sc(dev); 402 struct tty *tp = sc->sc_tty; 403 int s; 404 405 if (!ISSET(tp->t_state, TS_ISOPEN)) 406 return 0; 407 408 (*linesw[tp->t_line].l_close)(tp, flag, p); 409 s = spltty(); 410 if (!ISSET(tp->t_state, TS_WOPEN)) { 411 /* Disable interrupts. */ 412 HWRITE4(sc, GENI_M_IRQ_EN, 0); 413 HWRITE4(sc, GENI_S_IRQ_EN, 0); 414 } 415 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 416 sc->sc_cua = 0; 417 splx(s); 418 ttyclose(tp); 419 420 return 0; 421} 422 423int 424qcuartread(dev_t dev, struct uio *uio, int flag) 425{ 426 struct tty *tp = qcuarttty(dev); 427 428 if (tp == NULL) 429 return ENODEV; 430 431 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 432} 433 434int 435qcuartwrite(dev_t dev, struct uio *uio, int flag) 436{ 437 struct tty *tp = qcuarttty(dev); 438 439 if (tp == NULL) 440 return ENODEV; 441 442 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 443} 444 445int 446qcuartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 447{ 448 struct qcuart_softc *sc = qcuart_sc(dev); 449 struct tty *tp; 450 int error; 451 452 if (sc == NULL) 453 return ENODEV; 454 455 tp = sc->sc_tty; 456 if (tp == NULL) 457 return ENXIO; 458 459 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 460 if (error >= 0) 461 return error; 462 463 error = ttioctl(tp, cmd, data, flag, p); 464 if (error >= 0) 465 return error; 466 467 switch(cmd) { 468 case TIOCSBRK: 469 case TIOCCBRK: 470 case TIOCSDTR: 471 case TIOCCDTR: 472 case TIOCMSET: 473 case TIOCMBIS: 474 case TIOCMBIC: 475 case TIOCMGET: 476 case TIOCGFLAGS: 477 break; 478 case TIOCSFLAGS: 479 error = suser(p); 480 if (error != 0) 481 return EPERM; 482 break; 483 default: 484 return ENOTTY; 485 } 486 487 return 0; 488} 489 490int 491qcuartstop(struct tty *tp, int flag) 492{ 493 return 0; 494} 495 496struct tty * 497qcuarttty(dev_t dev) 498{ 499 struct qcuart_softc *sc = qcuart_sc(dev); 500 501 if (sc == NULL) 502 return NULL; 503 return sc->sc_tty; 504} 505 506struct qcuart_softc * 507qcuart_sc(dev_t dev) 508{ 509 int unit = DEVUNIT(dev); 510 511 if (unit >= qcuart_cd.cd_ndevs) 512 return NULL; 513 return (struct qcuart_softc *)qcuart_cd.cd_devs[unit]; 514} 515 516int 517qcuartcnattach(bus_space_tag_t iot, bus_addr_t iobase) 518{ 519 static struct consdev qcuartcons = { 520 NULL, NULL, qcuartcngetc, qcuartcnputc, qcuartcnpollc, NULL, 521 NODEV, CN_MIDPRI 522 }; 523 int maj; 524 525 qcuartconsiot = iot; 526 if (bus_space_map(iot, iobase, GENI_SPACE, 0, &qcuartconsioh)) 527 return ENOMEM; 528 529 /* Look for major of com(4) to replace. */ 530 for (maj = 0; maj < nchrdev; maj++) 531 if (cdevsw[maj].d_open == comopen) 532 break; 533 if (maj == nchrdev) 534 return ENXIO; 535 536 cn_tab = &qcuartcons; 537 cn_tab->cn_dev = makedev(maj, 0); 538 cdevsw[maj] = qcuartdev; /* KLUDGE */ 539 540 return 0; 541} 542 543int 544qcuartcngetc(dev_t dev) 545{ 546 bus_space_tag_t iot = qcuartconsiot; 547 bus_space_handle_t ioh = qcuartconsioh; 548 uint32_t stat; 549 uint8_t c; 550 551 bus_space_write_4(iot, ioh, GENI_S_CMD0, 552 GENI_S_CMD0_OPCODE_UART_START_RX); 553 554 stat = bus_space_read_4(iot, ioh, GENI_M_IRQ_STATUS); 555 bus_space_write_4(iot, ioh, GENI_M_IRQ_CLEAR, stat); 556 stat = bus_space_read_4(iot, ioh, GENI_S_IRQ_STATUS); 557 bus_space_write_4(iot, ioh, GENI_S_IRQ_CLEAR, stat); 558 559 for (;;) { 560 stat = bus_space_read_4(iot, ioh, GENI_RX_FIFO_STATUS); 561 if (stat & GENI_RX_FIFO_STATUS_WC_MASK) 562 break; 563 CPU_BUSY_CYCLE(); 564 } 565 566 c = bus_space_read_4(iot, ioh, GENI_RX_FIFO); 567 return c; 568} 569 570void 571qcuartcnputc(dev_t dev, int c) 572{ 573 bus_space_tag_t iot = qcuartconsiot; 574 bus_space_handle_t ioh = qcuartconsioh; 575 uint32_t stat; 576 577 bus_space_write_4(iot, ioh, GENI_TX_FIFO_WATERMARK, 578 QCUART_TX_WATERMARK); 579 580 bus_space_write_4(iot, ioh, GENI_UART_TX_TRANS_LEN, 1); 581 bus_space_write_4(iot, ioh, GENI_M_CMD0, 582 GENI_M_CMD0_OPCODE_UART_START_TX); 583 584 for (;;) { 585 stat = bus_space_read_4(iot, ioh, GENI_M_IRQ_STATUS); 586 if (stat & GENI_M_IRQ_TX_FIFO_WATERMARK) 587 break; 588 CPU_BUSY_CYCLE(); 589 } 590 bus_space_write_4(iot, ioh, GENI_TX_FIFO, c); 591 592 bus_space_write_4(iot, ioh, GENI_M_IRQ_CLEAR, 593 GENI_M_IRQ_TX_FIFO_WATERMARK); 594 595 for (;;) { 596 stat = bus_space_read_4(iot, ioh, GENI_M_IRQ_STATUS); 597 if (stat & GENI_M_IRQ_CMD_DONE) 598 break; 599 CPU_BUSY_CYCLE(); 600 } 601 bus_space_write_4(iot, ioh, GENI_M_IRQ_CLEAR, GENI_M_IRQ_CMD_DONE); 602} 603 604void 605qcuartcnpollc(dev_t dev, int on) 606{ 607}