1 /* $NetBSD: siotty.c,v 1.8 2001/05/02 10:32:22 scw 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.8 2001/05/02 10:32:22 scw 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 >= 0) 484 return error; 485 error = ttioctl(tp, cmd, data, flag, p); 486 if (error >= 0) 487 return error; 488 489 /* the last resort for TIOC ioctl tranversing */ 490 switch (cmd) { 491 case TIOCSBRK: /* Set the hardware into BREAK condition */ 492 siomctl(sc, TIOCM_BREAK, DMBIS); 493 break; 494 case TIOCCBRK: /* Clear the hardware BREAK condition */ 495 siomctl(sc, TIOCM_BREAK, DMBIC); 496 break; 497 case TIOCSDTR: /* Assert DTR signal */ 498 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS); 499 break; 500 case TIOCCDTR: /* Clear DTR signal */ 501 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC); 502 break; 503 case TIOCMSET: /* Set modem state replacing current one */ 504 siomctl(sc, *(int *)data, DMSET); 505 break; 506 case TIOCMGET: /* Return current modem state */ 507 *(int *)data = siomctl(sc, 0, DMGET); 508 break; 509 case TIOCMBIS: /* Set individual bits of modem state */ 510 siomctl(sc, *(int *)data, DMBIS); 511 break; 512 case TIOCMBIC: /* Clear individual bits of modem state */ 513 siomctl(sc, *(int *)data, DMBIC); 514 break; 515 case TIOCSFLAGS: /* Instruct how serial port behaves */ 516 sc->sc_flags = *(int *)data; 517 break; 518 case TIOCGFLAGS: /* Return current serial port state */ 519 *(int *)data = sc->sc_flags; 520 break; 521 default: 522 return ENOTTY; 523 } 524 return 0; 525 } 526 527 /* ARSGUSED */ 528 struct tty * 529 siotty(dev) 530 dev_t dev; 531 { 532 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 533 534 return sc->sc_tty; 535 } 536 537 /*-------------------- miscelleneous routine --------------------*/ 538 539 /* EXPORT */ void 540 setsioreg(sio, regno, val) 541 struct sioreg *sio; 542 int regno, val; 543 { 544 if (regno != 0) 545 sio->sio_cmd = regno; /* DELAY(); */ 546 sio->sio_cmd = val; /* DELAY(); */ 547 } 548 549 /* EXPORT */ int 550 getsiocsr(sio) 551 struct sioreg *sio; 552 { 553 int val; 554 555 val = sio->sio_stat << 8; /* DELAY(); */ 556 sio->sio_cmd = 1; /* DELAY(); */ 557 val |= sio->sio_stat; /* DELAY(); */ 558 return val; 559 } 560 561 /*--------------------- console interface ----------------------*/ 562 563 void syscnattach __P((int)); 564 int syscngetc __P((dev_t)); 565 void syscnputc __P((dev_t, int)); 566 567 struct consdev syscons = { 568 NULL, 569 NULL, 570 syscngetc, 571 syscnputc, 572 nullcnpollc, 573 NULL, 574 NODEV, 575 CN_REMOTE, 576 }; 577 578 /* EXPORT */ void 579 syscnattach(channel) 580 int channel; 581 { 582 /* 583 * Channel A is immediately initialized with 9600N1 right after cold 584 * boot/reset/poweron. ROM monitor emits one line message on CH.A. 585 */ 586 struct sioreg *sio; 587 sio = (struct sioreg *)0x51000000 + channel; 588 589 syscons.cn_dev = makedev(7, channel); 590 cn_tab = &syscons; 591 592 setsioreg(sio, WR0, WR0_CHANRST); 593 setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1); 594 setsioreg(sio, WR2B, 0); 595 setsioreg(sio, WR0, ch0_regs[WR0]); 596 setsioreg(sio, WR4, ch0_regs[WR4]); 597 setsioreg(sio, WR3, ch0_regs[WR3]); 598 setsioreg(sio, WR5, ch0_regs[WR5]); 599 setsioreg(sio, WR0, ch0_regs[WR0]); 600 } 601 602 /* EXPORT */ int 603 syscngetc(dev) 604 dev_t dev; 605 { 606 struct sioreg *sio; 607 int s, c; 608 609 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 610 s = splhigh(); 611 while ((getsiocsr(sio) & RR_RXRDY) == 0) 612 ; 613 c = sio->sio_data; 614 splx(s); 615 616 return c; 617 } 618 619 /* EXPORT */ void 620 syscnputc(dev, c) 621 dev_t dev; 622 int c; 623 { 624 struct sioreg *sio; 625 int s; 626 627 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 628 s = splhigh(); 629 while ((getsiocsr(sio) & RR_TXRDY) == 0) 630 ; 631 sio->sio_cmd = WR0_RSTPEND; 632 sio->sio_data = c; 633 splx(s); 634 } 635