1 /* $OpenBSD: siotty.c,v 1.16 2014/06/07 11:55:35 aoyama Exp $ */ 2 /* $NetBSD: siotty.c,v 1.9 2002/03/17 19:40:43 atatat Exp $ */ 3 4 /*- 5 * Copyright (c) 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Tohru Nishimura. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/device.h> 36 #include <sys/conf.h> 37 #include <sys/ioctl.h> 38 #include <sys/proc.h> 39 #include <sys/tty.h> 40 #include <sys/uio.h> 41 #include <sys/fcntl.h> 42 #include <dev/cons.h> 43 44 #include <machine/cpu.h> 45 46 #include <luna88k/dev/sioreg.h> 47 #include <luna88k/dev/siovar.h> 48 49 #define TIOCM_BREAK 01000 /* non standard use */ 50 51 static const u_int8_t ch0_regs[6] = { 52 WR0_RSTINT, /* reset E/S interrupt */ 53 WR1_RXALLS | WR1_TXENBL, /* Rx per char, Tx */ 54 0, /* */ 55 WR3_RX8BIT | WR3_RXENBL, /* Rx */ 56 WR4_BAUD96 | WR4_STOP1, /* Tx/Rx */ 57 WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */ 58 }; 59 60 static const struct speedtab siospeedtab[] = { 61 { 2400, WR4_BAUD24, }, 62 { 4800, WR4_BAUD48, }, 63 { 9600, WR4_BAUD96, }, 64 { -1, 0, }, 65 }; 66 67 struct siotty_softc { 68 struct device sc_dev; 69 struct tty *sc_tty; 70 struct sioreg *sc_ctl; 71 u_int sc_flags; 72 u_int8_t sc_wr[6]; 73 }; 74 75 cdev_decl(sio); 76 void siostart(struct tty *); 77 int sioparam(struct tty *, struct termios *); 78 void siottyintr(int); 79 int siomctl(struct siotty_softc *, int, int); 80 81 int siotty_match(struct device *, void *, void *); 82 void siotty_attach(struct device *, struct device *, void *); 83 84 const struct cfattach siotty_ca = { 85 sizeof(struct siotty_softc), siotty_match, siotty_attach 86 }; 87 88 struct cfdriver siotty_cd = { 89 NULL, "siotty", DV_TTY 90 }; 91 92 int 93 siotty_match(struct device *parent, void *cf, void *aux) 94 { 95 struct sio_attach_args *args = aux; 96 97 if (args->channel != 0) /* XXX allow tty on Ch.B XXX */ 98 return 0; 99 return 1; 100 } 101 102 void 103 siotty_attach(struct device *parent, struct device *self, void *aux) 104 { 105 struct sio_softc *scp = (void *)parent; 106 struct siotty_softc *sc = (void *)self; 107 struct sio_attach_args *args = aux; 108 109 sc->sc_ctl = (struct sioreg *)scp->scp_ctl + args->channel; 110 bcopy(ch0_regs, sc->sc_wr, sizeof(ch0_regs)); 111 scp->scp_intr[args->channel] = siottyintr; 112 113 if (args->hwflags == 1) { 114 printf(" (console)"); 115 sc->sc_flags = TIOCFLAG_SOFTCAR; 116 } 117 else { 118 setsioreg(sc->sc_ctl, WR0, WR0_CHANRST); 119 setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1); 120 setsioreg(sc->sc_ctl, WR2B, 0); 121 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); 122 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); 123 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); 124 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); 125 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); 126 } 127 setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */ 128 129 printf("\n"); 130 } 131 132 /*-------------------- low level routine --------------------*/ 133 134 void 135 siottyintr(int chan) 136 { 137 struct siotty_softc *sc; 138 struct sioreg *sio; 139 struct tty *tp; 140 unsigned int code; 141 int rr; 142 143 if (chan >= siotty_cd.cd_ndevs) 144 return; 145 sc = siotty_cd.cd_devs[chan]; 146 tp = sc->sc_tty; 147 sio = sc->sc_ctl; 148 rr = getsiocsr(sio); 149 if (rr & RR_RXRDY) { 150 do { 151 code = sio->sio_data; 152 if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) { 153 sio->sio_cmd = WR0_ERRRST; 154 if (sio->sio_stat & RR_FRAMING) 155 code |= TTY_FE; 156 else if (sio->sio_stat & RR_PARITY) 157 code |= TTY_PE; 158 } 159 if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) 160 continue; 161 #if 0 && defined(DDB) /* ?!?! fails to resume ?!?! */ 162 if ((rr & RR_BREAK) && tp->t_dev == cn_tab->cn_dev) { 163 if (db_console) 164 Debugger(); 165 return; 166 } 167 #endif 168 (*linesw[tp->t_line].l_rint)(code, tp); 169 } while ((rr = getsiocsr(sio)) & RR_RXRDY); 170 } 171 if (rr & RR_TXRDY) { 172 sio->sio_cmd = WR0_RSTPEND; 173 if (tp != NULL) { 174 tp->t_state &= ~(TS_BUSY|TS_FLUSH); 175 (*linesw[tp->t_line].l_start)(tp); 176 } 177 } 178 } 179 180 void 181 siostart(struct tty *tp) 182 { 183 struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; 184 int s, c; 185 186 s = spltty(); 187 if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP)) 188 goto out; 189 ttwakeupwr(tp); 190 if (tp->t_outq.c_cc == 0) 191 goto out; 192 193 tp->t_state |= TS_BUSY; 194 while (getsiocsr(sc->sc_ctl) & RR_TXRDY) { 195 if ((c = getc(&tp->t_outq)) == -1) 196 break; 197 sc->sc_ctl->sio_data = c; 198 } 199 out: 200 splx(s); 201 } 202 203 int 204 siostop(struct tty *tp, int flag) 205 { 206 int s; 207 208 s = spltty(); 209 if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) { 210 /* 211 * Device is transmitting; must stop it. 212 */ 213 tp->t_state |= TS_FLUSH; 214 } 215 splx(s); 216 return (0); 217 } 218 219 int 220 sioparam(struct tty *tp, struct termios *t) 221 { 222 struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; 223 int wr4, s; 224 225 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) 226 return EINVAL; 227 wr4 = ttspeedtab(t->c_ospeed, siospeedtab); 228 if (wr4 < 0) 229 return EINVAL; 230 231 if (sc->sc_flags & TIOCFLAG_SOFTCAR) { 232 t->c_cflag |= CLOCAL; 233 t->c_cflag &= ~HUPCL; 234 } 235 if (sc->sc_flags & TIOCFLAG_CLOCAL) 236 t->c_cflag |= CLOCAL; 237 238 /* 239 * If there were no changes, don't do anything. This avoids dropping 240 * input and improves performance when all we did was frob things like 241 * VMIN and VTIME. 242 */ 243 if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag) 244 return 0; 245 246 tp->t_ispeed = t->c_ispeed; 247 tp->t_ospeed = t->c_ospeed; 248 tp->t_cflag = t->c_cflag; 249 250 sc->sc_wr[WR3] &= 0x3f; 251 sc->sc_wr[WR5] &= 0x9f; 252 switch (tp->t_cflag & CSIZE) { 253 case CS7: 254 sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT; 255 break; 256 case CS8: 257 sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT; 258 break; 259 } 260 if (tp->t_cflag & PARENB) { 261 wr4 |= WR4_PARENAB; 262 if ((tp->t_cflag & PARODD) == 0) 263 wr4 |= WR4_EPARITY; 264 } 265 wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1; 266 sc->sc_wr[WR4] = wr4; 267 268 s = spltty(); 269 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); 270 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); 271 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); 272 splx(s); 273 274 return 0; 275 } 276 277 int 278 siomctl(struct siotty_softc *sc, int control, int op) 279 { 280 int val, s, wr5, rr; 281 282 val = 0; 283 if (control & TIOCM_BREAK) 284 val |= WR5_BREAK; 285 if (control & TIOCM_DTR) 286 val |= WR5_DTR; 287 if (control & TIOCM_RTS) 288 val |= WR5_RTS; 289 s = spltty(); 290 wr5 = sc->sc_wr[WR5]; 291 switch (op) { 292 case DMSET: 293 wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS); 294 /* FALLTHROUGH */ 295 case DMBIS: 296 wr5 |= val; 297 break; 298 case DMBIC: 299 wr5 &= ~val; 300 break; 301 case DMGET: 302 val = 0; 303 rr = getsiocsr(sc->sc_ctl); 304 if (wr5 & WR5_DTR) 305 val |= TIOCM_DTR; 306 if (wr5 & WR5_RTS) 307 val |= TIOCM_RTS; 308 if (rr & RR_CTS) 309 val |= TIOCM_CTS; 310 if (rr & RR_DCD) 311 val |= TIOCM_CD; 312 goto done; 313 } 314 sc->sc_wr[WR5] = wr5; 315 setsioreg(sc->sc_ctl, WR5, wr5); 316 val = 0; 317 done: 318 splx(s); 319 return val; 320 } 321 322 /*-------------------- cdevsw[] interface --------------------*/ 323 324 int 325 sioopen(dev_t dev, int flag, int mode, struct proc *p) 326 { 327 struct siotty_softc *sc; 328 struct tty *tp; 329 int error; 330 331 if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL) 332 return ENXIO; 333 if ((tp = sc->sc_tty) == NULL) { 334 tp = sc->sc_tty = ttymalloc(0); 335 } 336 else if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) 337 && suser(p, 0) != 0) 338 return EBUSY; 339 340 tp->t_oproc = siostart; 341 tp->t_param = sioparam; 342 tp->t_hwiflow = NULL /* XXX siohwiflow XXX */; 343 tp->t_dev = dev; 344 if ((tp->t_state & TS_ISOPEN) == 0) { 345 struct termios t; 346 347 t.c_ispeed = t.c_ospeed = TTYDEF_SPEED; 348 t.c_cflag = TTYDEF_CFLAG; 349 tp->t_ospeed = 0; /* force register update */ 350 (void)sioparam(tp, &t); 351 tp->t_iflag = TTYDEF_IFLAG; 352 tp->t_oflag = TTYDEF_OFLAG; 353 tp->t_lflag = TTYDEF_LFLAG; 354 ttychars(tp); 355 ttsetwater(tp); 356 /* raise RTS and DTR here; but, DTR lead is not wired */ 357 /* then check DCD condition; but, DCD lead is not wired */ 358 tp->t_state |= TS_CARR_ON; /* assume detected all the time */ 359 #if 0 360 if ((sc->sc_flags & TIOCFLAG_SOFTCAR) 361 || (tp->t_cflag & MDMBUF) 362 || (getsiocsr(sc->sc_ctl) & RR_DCD)) 363 tp->t_state |= TS_CARR_ON; 364 else 365 tp->t_state &= ~TS_CARR_ON; 366 #endif 367 } 368 369 error = ttyopen(dev, tp, p); 370 if (error > 0) 371 return error; 372 return (*linesw[tp->t_line].l_open)(dev, tp, p); 373 } 374 375 int 376 sioclose(dev_t dev, int flag, int mode, struct proc *p) 377 { 378 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 379 struct tty *tp = sc->sc_tty; 380 int s; 381 382 (*linesw[tp->t_line].l_close)(tp, flag, p); 383 384 s = spltty(); 385 siomctl(sc, TIOCM_BREAK, DMBIC); 386 #if 0 /* because unable to feed DTR signal */ 387 if ((tp->t_cflag & HUPCL) 388 || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) { 389 siomctl(sc, TIOCM_DTR, DMBIC); 390 /* Yield CPU time to others for 1 second, then ... */ 391 siomctl(sc, TIOCM_DTR, DMBIS); 392 } 393 #endif 394 splx(s); 395 return ttyclose(tp); 396 } 397 398 int 399 sioread(dev_t dev, struct uio *uio, int flag) 400 { 401 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 402 struct tty *tp = sc->sc_tty; 403 404 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 405 } 406 407 int 408 siowrite(dev_t dev, struct uio *uio, int flag) 409 { 410 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 411 struct tty *tp = sc->sc_tty; 412 413 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 414 } 415 416 #if 0 417 int 418 sioselect(dev, events, p) 419 dev_t dev; 420 int events; 421 struct proc *p; 422 { 423 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 424 struct tty *tp = sc->sc_tty; 425 426 return ((*linesw[tp->t_line].l_select)(tp, events, p)); 427 428 } 429 #endif 430 431 int 432 sioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 433 { 434 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 435 struct tty *tp = sc->sc_tty; 436 int error; 437 438 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 439 if (error >= 0) 440 return error; 441 442 error = ttioctl(tp, cmd, data, flag, p); 443 if (error >= 0) 444 return error; 445 446 /* the last resort for TIOC ioctl tranversing */ 447 switch (cmd) { 448 case TIOCSBRK: /* Set the hardware into BREAK condition */ 449 siomctl(sc, TIOCM_BREAK, DMBIS); 450 break; 451 case TIOCCBRK: /* Clear the hardware BREAK condition */ 452 siomctl(sc, TIOCM_BREAK, DMBIC); 453 break; 454 case TIOCSDTR: /* Assert DTR signal */ 455 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS); 456 break; 457 case TIOCCDTR: /* Clear DTR signal */ 458 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC); 459 break; 460 case TIOCMSET: /* Set modem state replacing current one */ 461 siomctl(sc, *(int *)data, DMSET); 462 break; 463 case TIOCMGET: /* Return current modem state */ 464 *(int *)data = siomctl(sc, 0, DMGET); 465 break; 466 case TIOCMBIS: /* Set individual bits of modem state */ 467 siomctl(sc, *(int *)data, DMBIS); 468 break; 469 case TIOCMBIC: /* Clear individual bits of modem state */ 470 siomctl(sc, *(int *)data, DMBIC); 471 break; 472 case TIOCSFLAGS: /* Instruct how serial port behaves */ 473 error = suser(p, 0); 474 if (error != 0) 475 return EPERM; 476 sc->sc_flags = *(int *)data; 477 break; 478 case TIOCGFLAGS: /* Return current serial port state */ 479 *(int *)data = sc->sc_flags; 480 break; 481 default: 482 return ENOTTY; 483 } 484 return 0; 485 } 486 487 /* ARSGUSED */ 488 struct tty * 489 siotty(dev_t dev) 490 { 491 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 492 493 return sc->sc_tty; 494 } 495 496 /*-------------------- miscellaneous routines --------------------*/ 497 498 /* EXPORT */ void 499 setsioreg(struct sioreg *sio, int regno, int val) 500 { 501 if (regno != 0) 502 sio->sio_cmd = regno; /* DELAY(); */ 503 sio->sio_cmd = val; /* DELAY(); */ 504 } 505 506 /* EXPORT */ int 507 getsiocsr(struct sioreg *sio) 508 { 509 int val; 510 511 val = sio->sio_stat << 8; /* DELAY(); */ 512 sio->sio_cmd = 1; /* DELAY(); */ 513 val |= sio->sio_stat; /* DELAY(); */ 514 return val; 515 } 516 517 /*--------------------- console interface ----------------------*/ 518 519 void syscnattach(int); 520 int syscngetc(dev_t); 521 void syscnputc(dev_t, int); 522 523 struct consdev syscons = { 524 NULL, 525 NULL, 526 syscngetc, 527 syscnputc, 528 nullcnpollc, 529 NULL, 530 NODEV, 531 CN_HIGHPRI, 532 }; 533 534 /* EXPORT */ void 535 syscnattach(int channel) 536 { 537 /* 538 * Channel A is immediately initialized with 9600N1 right after cold 539 * boot/reset/poweron. ROM monitor emits one line message on CH.A. 540 */ 541 struct sioreg *sio; 542 sio = (struct sioreg *)0x51000000 + channel; 543 544 syscons.cn_dev = makedev(12, channel); 545 cn_tab = &syscons; 546 547 #if 0 548 setsioreg(sio, WR0, WR0_CHANRST); 549 setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1); 550 setsioreg(sio, WR2B, 0); 551 setsioreg(sio, WR0, ch0_regs[WR0]); 552 setsioreg(sio, WR4, ch0_regs[WR4]); 553 setsioreg(sio, WR3, ch0_regs[WR3]); 554 setsioreg(sio, WR5, ch0_regs[WR5]); 555 setsioreg(sio, WR0, ch0_regs[WR0]); 556 #endif 557 } 558 559 /* EXPORT */ int 560 syscngetc(dev_t dev) 561 { 562 struct sioreg *sio; 563 int s, c; 564 565 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 566 s = splhigh(); 567 while ((getsiocsr(sio) & RR_RXRDY) == 0) 568 ; 569 c = sio->sio_data; 570 splx(s); 571 572 return c; 573 } 574 575 /* EXPORT */ void 576 syscnputc(dev_t dev, int c) 577 { 578 struct sioreg *sio; 579 int s; 580 581 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 582 s = splhigh(); 583 while ((getsiocsr(sio) & RR_TXRDY) == 0) 584 ; 585 sio->sio_cmd = WR0_RSTPEND; 586 sio->sio_data = c; 587 splx(s); 588 } 589