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