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