1 /* $NetBSD: siotty.c,v 1.12 2002/10/23 09:11:25 jdolecek 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.12 2002/10/23 09:11:25 jdolecek 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 static void siostart __P((struct tty *)); 91 static int sioparam __P((struct tty *, struct termios *)); 92 static void siottyintr __P((int)); 93 static int siomctl __P((struct siotty_softc *, int, int)); 94 95 static int siotty_match __P((struct device *, struct cfdata *, void *)); 96 static void siotty_attach __P((struct device *, struct device *, void *)); 97 98 CFATTACH_DECL(siotty, sizeof(struct siotty_softc), 99 siotty_match, siotty_attach, NULL, NULL); 100 extern struct cfdriver siotty_cd; 101 102 dev_type_open(sioopen); 103 dev_type_close(sioclose); 104 dev_type_read(sioread); 105 dev_type_write(siowrite); 106 dev_type_ioctl(sioioctl); 107 dev_type_stop(siostop); 108 dev_type_tty(siotty); 109 dev_type_poll(siopoll); 110 111 const struct cdevsw siotty_cdevsw = { 112 sioopen, sioclose, sioread, siowrite, sioioctl, 113 siostop, siotty, siopoll, nommap, ttykqfilter, D_TTY 114 }; 115 116 static int 117 siotty_match(parent, cf, aux) 118 struct device *parent; 119 struct cfdata *cf; 120 void *aux; 121 { 122 struct sio_attach_args *args = aux; 123 124 if (args->channel != 0) /* XXX allow tty on Ch.B XXX */ 125 return 0; 126 return 1; 127 } 128 129 static void 130 siotty_attach(parent, self, aux) 131 struct device *parent, *self; 132 void *aux; 133 { 134 struct sio_softc *scp = (void *)parent; 135 struct siotty_softc *sc = (void *)self; 136 struct sio_attach_args *args = aux; 137 138 sc->sc_ctl = (struct sioreg *)scp->scp_ctl + args->channel; 139 bcopy(ch0_regs, sc->sc_wr, sizeof(ch0_regs)); 140 scp->scp_intr[args->channel] = siottyintr; 141 142 if (args->hwflags == 1) { 143 printf(" (console)"); 144 sc->sc_flags = TIOCFLAG_SOFTCAR; 145 } 146 else { 147 setsioreg(sc->sc_ctl, WR0, WR0_CHANRST); 148 setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1); 149 setsioreg(sc->sc_ctl, WR2B, 0); 150 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); 151 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); 152 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); 153 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); 154 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); 155 } 156 setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */ 157 158 printf("\n"); 159 } 160 161 /*-------------------- low level routine --------------------*/ 162 163 static void 164 siottyintr(chan) 165 int chan; 166 { 167 struct siotty_softc *sc; 168 struct sioreg *sio; 169 struct tty *tp; 170 unsigned int code; 171 int rr; 172 173 if (chan >= siotty_cd.cd_ndevs) 174 return; 175 sc = siotty_cd.cd_devs[chan]; 176 tp = sc->sc_tty; 177 sio = sc->sc_ctl; 178 rr = getsiocsr(sio); 179 if (rr & RR_RXRDY) { 180 do { 181 code = sio->sio_data; 182 if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) { 183 sio->sio_cmd = WR0_ERRRST; 184 if (sio->sio_stat & RR_FRAMING) 185 code |= TTY_FE; 186 else if (sio->sio_stat & RR_PARITY) 187 code |= TTY_PE; 188 } 189 if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) 190 continue; 191 #if 0 && defined(DDB) /* ?!?! fails to resume ?!?! */ 192 if ((rr & RR_BREAK) && tp->t_dev == cn_tab->cn_dev) { 193 cpu_Debugger(); 194 return; 195 } 196 #endif 197 (*tp->t_linesw->l_rint)(code, tp); 198 } while ((rr = getsiocsr(sio)) & RR_RXRDY); 199 } 200 if (rr & RR_TXRDY) { 201 sio->sio_cmd = WR0_RSTPEND; 202 if (tp != NULL) { 203 tp->t_state &= ~(TS_BUSY|TS_FLUSH); 204 (*tp->t_linesw->l_start)(tp); 205 } 206 } 207 } 208 209 static void 210 siostart(tp) 211 struct tty *tp; 212 { 213 struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; 214 int s, c; 215 216 s = spltty(); 217 if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP)) 218 goto out; 219 if (tp->t_outq.c_cc <= tp->t_lowat) { 220 if (tp->t_state & TS_ASLEEP) { 221 tp->t_state &= ~TS_ASLEEP; 222 wakeup((caddr_t)&tp->t_outq); 223 } 224 selwakeup(&tp->t_wsel); 225 } 226 if (tp->t_outq.c_cc == 0) 227 goto out; 228 229 tp->t_state |= TS_BUSY; 230 while (getsiocsr(sc->sc_ctl) & RR_TXRDY) { 231 if ((c = getc(&tp->t_outq)) == -1) 232 break; 233 sc->sc_ctl->sio_data = c; 234 } 235 out: 236 splx(s); 237 } 238 239 void 240 siostop(tp, flag) 241 struct tty *tp; 242 int flag; 243 { 244 int s; 245 246 s = spltty(); 247 if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) { 248 /* 249 * Device is transmitting; must stop it. 250 */ 251 tp->t_state |= TS_FLUSH; 252 } 253 splx(s); 254 } 255 256 static int 257 sioparam(tp, t) 258 struct tty *tp; 259 struct termios *t; 260 { 261 struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; 262 int wr4, s; 263 264 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) 265 return EINVAL; 266 wr4 = ttspeedtab(t->c_ospeed, siospeedtab); 267 if (wr4 < 0) 268 return EINVAL; 269 270 if (sc->sc_flags & TIOCFLAG_SOFTCAR) { 271 t->c_cflag |= CLOCAL; 272 t->c_cflag &= ~HUPCL; 273 } 274 if (sc->sc_flags & TIOCFLAG_CLOCAL) 275 t->c_cflag |= CLOCAL; 276 277 /* 278 * If there were no changes, don't do anything. This avoids dropping 279 * input and improves performance when all we did was frob things like 280 * VMIN and VTIME. 281 */ 282 if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag) 283 return 0; 284 285 tp->t_ispeed = t->c_ispeed; 286 tp->t_ospeed = t->c_ospeed; 287 tp->t_cflag = t->c_cflag; 288 289 sc->sc_wr[WR3] &= 0x3f; 290 sc->sc_wr[WR5] &= 0x9f; 291 switch (tp->t_cflag & CSIZE) { 292 case CS7: 293 sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT; 294 break; 295 case CS8: 296 sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT; 297 break; 298 } 299 if (tp->t_cflag & PARENB) { 300 wr4 |= WR4_PARENAB; 301 if ((tp->t_cflag & PARODD) == 0) 302 wr4 |= WR4_EPARITY; 303 } 304 wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1; 305 sc->sc_wr[WR4] = wr4; 306 307 s = spltty(); 308 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); 309 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); 310 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); 311 splx(s); 312 313 return 0; 314 } 315 316 static int 317 siomctl(sc, control, op) 318 struct siotty_softc *sc; 319 int control, op; 320 { 321 int val, s, wr5, rr; 322 323 val = 0; 324 if (control & TIOCM_BREAK) 325 val |= WR5_BREAK; 326 if (control & TIOCM_DTR) 327 val |= WR5_DTR; 328 if (control & TIOCM_RTS) 329 val |= WR5_RTS; 330 s = spltty(); 331 wr5 = sc->sc_wr[WR5]; 332 switch (op) { 333 case DMSET: 334 wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS); 335 /* FALLTHRU */ 336 case DMBIS: 337 wr5 |= val; 338 break; 339 case DMBIC: 340 wr5 &= ~val; 341 break; 342 case DMGET: 343 val = 0; 344 rr = getsiocsr(sc->sc_ctl); 345 if (wr5 & WR5_DTR) 346 val |= TIOCM_DTR; 347 if (wr5 & WR5_RTS) 348 val |= TIOCM_RTS; 349 if (rr & RR_CTS) 350 val |= TIOCM_CTS; 351 if (rr & RR_DCD) 352 val |= TIOCM_CD; 353 goto done; 354 } 355 sc->sc_wr[WR5] = wr5; 356 setsioreg(sc->sc_ctl, WR5, wr5); 357 val = 0; 358 done: 359 splx(s); 360 return val; 361 } 362 363 /*-------------------- cdevsw[] interface --------------------*/ 364 365 int 366 sioopen(dev, flag, mode, p) 367 dev_t dev; 368 int flag, mode; 369 struct proc *p; 370 { 371 struct siotty_softc *sc; 372 struct tty *tp; 373 int error; 374 375 if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL) 376 return ENXIO; 377 if ((tp = sc->sc_tty) == NULL) { 378 tp = sc->sc_tty = ttymalloc(); 379 tty_attach(tp); 380 } 381 else if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) 382 && p->p_ucred->cr_uid != 0) 383 return EBUSY; 384 385 tp->t_oproc = siostart; 386 tp->t_param = sioparam; 387 tp->t_hwiflow = NULL /* XXX siohwiflow XXX */; 388 tp->t_dev = dev; 389 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { 390 struct termios t; 391 392 t.c_ispeed = t.c_ospeed = TTYDEF_SPEED; 393 t.c_cflag = TTYDEF_CFLAG; 394 tp->t_ospeed = 0; /* force register update */ 395 (void)sioparam(tp, &t); 396 tp->t_iflag = TTYDEF_IFLAG; 397 tp->t_oflag = TTYDEF_OFLAG; 398 tp->t_lflag = TTYDEF_LFLAG; 399 ttychars(tp); 400 ttsetwater(tp); 401 /* raise RTS and DTR here; but, DTR lead is not wired */ 402 /* then check DCD condition; but, DCD lead is not wired */ 403 tp->t_state |= TS_CARR_ON; /* assume detected all the time */ 404 #if 0 405 if ((sc->sc_flags & TIOCFLAG_SOFTCAR) 406 || (tp->t_cflag & MDMBUF) 407 || (getsiocsr(sc->sc_ctl) & RR_DCD)) 408 tp->t_state |= TS_CARR_ON; 409 else 410 tp->t_state &= ~TS_CARR_ON; 411 #endif 412 } 413 414 error = ttyopen(tp, 0, (flag & O_NONBLOCK)); 415 if (error > 0) 416 return error; 417 return (*tp->t_linesw->l_open)(dev, tp); 418 } 419 420 int 421 sioclose(dev, flag, mode, p) 422 dev_t dev; 423 int flag, mode; 424 struct proc *p; 425 { 426 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 427 struct tty *tp = sc->sc_tty; 428 int s; 429 430 (*tp->t_linesw->l_close)(tp, flag); 431 432 s = spltty(); 433 siomctl(sc, TIOCM_BREAK, DMBIC); 434 #if 0 /* because unable to feed DTR signal */ 435 if ((tp->t_cflag & HUPCL) 436 || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) { 437 siomctl(sc, TIOCM_DTR, DMBIC); 438 /* Yield CPU time to others for 1 second, then ... */ 439 siomctl(sc, TIOCM_DTR, DMBIS); 440 } 441 #endif 442 splx(s); 443 return ttyclose(tp); 444 } 445 446 int 447 sioread(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_read)(tp, uio, flag); 456 } 457 458 int 459 siowrite(dev, uio, flag) 460 dev_t dev; 461 struct uio *uio; 462 int flag; 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_write)(tp, uio, flag); 468 } 469 470 int 471 siopoll(dev, events, p) 472 dev_t dev; 473 int events; 474 struct proc *p; 475 { 476 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 477 struct tty *tp = sc->sc_tty; 478 479 return ((*tp->t_linesw->l_poll)(tp, events, p)); 480 } 481 482 int 483 sioioctl(dev, cmd, data, flag, p) 484 dev_t dev; 485 u_long cmd; 486 caddr_t data; 487 int flag; 488 struct proc *p; 489 { 490 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 491 struct tty *tp = sc->sc_tty; 492 int error; 493 494 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); 495 if (error != EPASSTHROUGH) 496 return error; 497 498 error = ttioctl(tp, cmd, data, flag, p); 499 if (error != EPASSTHROUGH) 500 return error; 501 502 /* the last resort for TIOC ioctl tranversing */ 503 switch (cmd) { 504 case TIOCSBRK: /* Set the hardware into BREAK condition */ 505 siomctl(sc, TIOCM_BREAK, DMBIS); 506 break; 507 case TIOCCBRK: /* Clear the hardware BREAK condition */ 508 siomctl(sc, TIOCM_BREAK, DMBIC); 509 break; 510 case TIOCSDTR: /* Assert DTR signal */ 511 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS); 512 break; 513 case TIOCCDTR: /* Clear DTR signal */ 514 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC); 515 break; 516 case TIOCMSET: /* Set modem state replacing current one */ 517 siomctl(sc, *(int *)data, DMSET); 518 break; 519 case TIOCMGET: /* Return current modem state */ 520 *(int *)data = siomctl(sc, 0, DMGET); 521 break; 522 case TIOCMBIS: /* Set individual bits of modem state */ 523 siomctl(sc, *(int *)data, DMBIS); 524 break; 525 case TIOCMBIC: /* Clear individual bits of modem state */ 526 siomctl(sc, *(int *)data, DMBIC); 527 break; 528 case TIOCSFLAGS: /* Instruct how serial port behaves */ 529 sc->sc_flags = *(int *)data; 530 break; 531 case TIOCGFLAGS: /* Return current serial port state */ 532 *(int *)data = sc->sc_flags; 533 break; 534 default: 535 return EPASSTHROUGH; 536 } 537 return 0; 538 } 539 540 /* ARSGUSED */ 541 struct tty * 542 siotty(dev) 543 dev_t dev; 544 { 545 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 546 547 return sc->sc_tty; 548 } 549 550 /*-------------------- miscelleneous routine --------------------*/ 551 552 /* EXPORT */ void 553 setsioreg(sio, regno, val) 554 struct sioreg *sio; 555 int regno, val; 556 { 557 if (regno != 0) 558 sio->sio_cmd = regno; /* DELAY(); */ 559 sio->sio_cmd = val; /* DELAY(); */ 560 } 561 562 /* EXPORT */ int 563 getsiocsr(sio) 564 struct sioreg *sio; 565 { 566 int val; 567 568 val = sio->sio_stat << 8; /* DELAY(); */ 569 sio->sio_cmd = 1; /* DELAY(); */ 570 val |= sio->sio_stat; /* DELAY(); */ 571 return val; 572 } 573 574 /*--------------------- console interface ----------------------*/ 575 576 void syscnattach __P((int)); 577 int syscngetc __P((dev_t)); 578 void syscnputc __P((dev_t, int)); 579 580 struct consdev syscons = { 581 NULL, 582 NULL, 583 syscngetc, 584 syscnputc, 585 nullcnpollc, 586 NULL, 587 NODEV, 588 CN_REMOTE, 589 }; 590 591 /* EXPORT */ void 592 syscnattach(channel) 593 int channel; 594 { 595 /* 596 * Channel A is immediately initialized with 9600N1 right after cold 597 * boot/reset/poweron. ROM monitor emits one line message on CH.A. 598 */ 599 struct sioreg *sio; 600 sio = (struct sioreg *)0x51000000 + channel; 601 602 syscons.cn_dev = makedev(cdevsw_lookup_major(&siotty_cdevsw), 603 channel); 604 cn_tab = &syscons; 605 606 setsioreg(sio, WR0, WR0_CHANRST); 607 setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1); 608 setsioreg(sio, WR2B, 0); 609 setsioreg(sio, WR0, ch0_regs[WR0]); 610 setsioreg(sio, WR4, ch0_regs[WR4]); 611 setsioreg(sio, WR3, ch0_regs[WR3]); 612 setsioreg(sio, WR5, ch0_regs[WR5]); 613 setsioreg(sio, WR0, ch0_regs[WR0]); 614 } 615 616 /* EXPORT */ int 617 syscngetc(dev) 618 dev_t dev; 619 { 620 struct sioreg *sio; 621 int s, c; 622 623 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 624 s = splhigh(); 625 while ((getsiocsr(sio) & RR_RXRDY) == 0) 626 ; 627 c = sio->sio_data; 628 splx(s); 629 630 return c; 631 } 632 633 /* EXPORT */ void 634 syscnputc(dev, c) 635 dev_t dev; 636 int c; 637 { 638 struct sioreg *sio; 639 int s; 640 641 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 642 s = splhigh(); 643 while ((getsiocsr(sio) & RR_TXRDY) == 0) 644 ; 645 sio->sio_cmd = WR0_RSTPEND; 646 sio->sio_data = c; 647 splx(s); 648 } 649