/* * Copyright (c) 1992 OMRON Corporation. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * OMRON Corporation. * * %sccs.include.redist.c% * * @(#)sio.c 8.1 (Berkeley) 06/10/93 */ /* * sio.c -- NEC uPD7201A UART Device Driver * remaked by A.Fujita, NOV-5-1992 */ #include "sio.h" #if NSIO > 0 #include "bmc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include struct sio_portc *sio_port_assign(); struct sio_portc *sio_port_get(); int sioprobe(); int sioopen(); void siostart(); int sioparam(); int siointr(); struct driver siodriver = { sioprobe, "sio", }; struct sio_portc sio_portc[NPORT] = { { -1, -1, 0, (struct siodevice *) 0x51000000, (int (*)()) 0 }, { -1, -1, 1, (struct siodevice *) 0x51000004, (int (*)()) 0 } }; struct sio_softc sio_softc[NSIO]; int sio_init_done = 0; int siosoftCAR; int sio_active; int sioconsole = -1; int siodefaultrate = TTYDEF_SPEED; int siomajor = 12; struct tty sio_tty[NSIO]; struct speedtab siospeedtab[] = { 2400, WR4_BAUD24, 4800, WR4_BAUD48, 9600, WR4_BAUD96, }; #define siounit(x) minor(x) extern struct tty *constty; #ifdef KGDB /* * Kernel GDB support */ #include extern dev_t kgdb_dev; extern int kgdb_rate; extern int kgdb_debug_init; #endif /* * probe routines */ sioprobe(hd) register struct hp_device *hd; { int unit = hd->hp_unit; register struct sio_softc *sc = &sio_softc[unit]; register struct sio_portc *pc; if (sc->sc_pc != 0) { pc = sc->sc_pc; printf("sio%d: port %d, address 0x%x, intr 0x%x (console)\n", pc->pc_unit, pc->pc_port, pc->pc_addr, pc->pc_intr); return(1); } sc->sc_pc = pc = sio_port_assign(unit, siomajor, unit, siointr); printf("sio%d: port %d, address 0x%x, intr 0x%x\n", pc->pc_unit, pc->pc_port, pc->pc_addr, pc->pc_intr); sio_active |= 1 << unit; #ifdef KGDB if (major(kgdb_dev) == siomajor) { #ifdef notdef if (sioconsole == siounit(kgdb_dev)) { kgdb_dev = NODEV; /* can't debug over console port */ } else { #endif /* * The following could potentially be replaced * by the corresponding code in dcmcnprobe. */ if (kgdb_debug_init) { printf("sio%d: ", siounit(kgdb_dev)); kgdb_connect(1); } else printf("sio%d: kgdb enabled\n", siounit(kgdb_dev)); #ifdef notdef } #endif /* end could be replaced */ } #endif siosoftCAR |= 1 << unit; return(1); } struct sio_portc * sio_port_assign(port, major, unit, intr) int port, major, unit; int (*intr)(); { register struct sio_portc *pc; pc = &sio_portc[port]; pc->pc_major = major; pc->pc_intr = intr; pc->pc_unit = unit; return(pc); } struct sio_portc * sio_port_get(port) int port; { register struct sio_portc *pc; pc = &sio_portc[port]; return(pc); } int sio_port_info() { printf("sio_port_info[sio.c]:\t{%d} major = %d, unit = %d, intr = 0x%x\n", 0, sio_portc[0].pc_major, sio_portc[0].pc_unit, sio_portc[0].pc_intr); printf("sio_port_info[sio.c]:\t{%d} major = %d, unit = %d, intr = 0x%x\n", 1, sio_portc[1].pc_major, sio_portc[1].pc_unit, sio_portc[1].pc_intr); } /* * entry routines */ /* ARGSUSED */ #ifdef __STDC__ sioopen(dev_t dev, int flag, int mode, struct proc *p) #else sioopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; #endif { register struct tty *tp; register int unit; int error = 0; unit = siounit(dev); if (unit >= NSIO || (sio_active & (1 << unit)) == 0) return (ENXIO); tp = &sio_tty[unit]; tp->t_oproc = siostart; tp->t_param = sioparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; /* tp->t_cflag = TTYDEF_CFLAG; */ tp->t_cflag = (CREAD | CS8 | HUPCL); tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = siodefaultrate; } sioparam(tp, &tp->t_termios); ttsetwater(tp); } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); (void) siomctl(dev, WR5_DTR | WR5_RTS, DMSET); if ((siosoftCAR & (1 << unit)) || (siomctl(dev, 0, DMGET) & RR0_DCD)) tp->t_state |= TS_CARR_ON; (void) spltty(); while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && (tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ttopen, 0)) break; } (void) spl0(); if (error == 0) error = (*linesw[tp->t_line].l_open)(dev, tp); return (error); } /*ARGSUSED*/ sioclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct tty *tp; register int unit; unit = siounit(dev); tp = &sio_tty[unit]; (*linesw[tp->t_line].l_close)(tp, flag); (void) siomctl(dev, WR5_BREAK, DMBIS); if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || (tp->t_state&TS_ISOPEN) == 0) (void) siomctl(dev, 0, DMSET); ttyclose(tp); return (0); } sioread(dev, uio, flag) dev_t dev; struct uio *uio; { register struct tty *tp = &sio_tty[siounit(dev)]; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } siowrite(dev, uio, flag) dev_t dev; struct uio *uio; { register int unit = siounit(dev); register struct tty *tp = &sio_tty[unit]; if ((unit == sioconsole) && constty && (constty->t_state&(TS_CARR_ON|TS_ISOPEN))==(TS_CARR_ON|TS_ISOPEN)) tp = constty; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } /* * Stop output on a line. */ /*ARGSUSED*/ siostop(tp, flag) register struct tty *tp; { register int s; s = spltty(); if (tp->t_state & TS_BUSY) { if ((tp->t_state&TS_TTSTOP)==0) tp->t_state |= TS_FLUSH; } splx(s); } sioioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { register struct tty *tp; register int unit = siounit(dev); register int error; tp = &sio_tty[unit]; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return (error); switch (cmd) { case TIOCSBRK: (void) siomctl(dev, WR5_BREAK, DMBIS); break; case TIOCCBRK: (void) siomctl(dev, WR5_BREAK, DMBIC); break; case TIOCSDTR: (void) siomctl(dev, WR5_DTR | WR5_RTS, DMBIS); break; case TIOCCDTR: (void) siomctl(dev, WR5_DTR | WR5_RTS, DMBIC); break; case TIOCMSET: (void) siomctl(dev, *(int *)data, DMSET); break; case TIOCMBIS: (void) siomctl(dev, *(int *)data, DMBIS); break; case TIOCMBIC: (void) siomctl(dev, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = siomctl(dev, 0, DMGET); break; default: return (ENOTTY); } return (0); } /* * */ void siostart(tp) register struct tty *tp; { register struct siodevice *sio; register int rr; int s, unit, c; unit = siounit(tp->t_dev); sio = sio_softc[unit].sc_pc->pc_addr; s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } if (tp->t_outq.c_cc == 0) goto out; rr = siogetreg(sio); if (rr & RR_TXRDY) { c = getc(&tp->t_outq); tp->t_state |= TS_BUSY; sio->sio_data = c; } out: splx(s); } sioparam(tp, t) register struct tty *tp; register struct termios *t; { int unit = siounit(tp->t_dev); register struct siodevice *sio; register cflag = t->c_cflag; register u_char wr; int ospeed = ttspeedtab(t->c_ospeed, siospeedtab); sio = sio_softc[unit].sc_pc->pc_addr; switch (cflag & CSIZE) { case CS5: case CS6: case CS7: case CS8: break; } wr = ospeed; if (cflag & PARENB) { wr |= WR4_PARENAB; if ((cflag&PARODD) == 0) wr |= WR4_EPARITY; } if (cflag & CSTOPB) wr |= WR4_STOP2; /* 2 stop bit */ else wr |= WR4_STOP1; /* 1 stop bit */ (void) sioreg(sio, WR4, wr); return (0); } siomctl() { return (0); } /* * Interrupt handling */ void _siointr() { register int port; register struct sio_portc *pc; for (port = 0; port < NPORT; port++) { pc = &sio_portc[port]; if (pc->pc_major != -1) (pc->pc_intr)(pc->pc_unit); } } siointr(unit) register int unit; { register struct siodevice *sio = sio_softc[unit].sc_pc->pc_addr; register u_char code; register struct tty *tp; int s, rr; tp = &sio_tty[unit]; start: rr = siogetreg(sio); if (rr & RR_RXRDY) { if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) { sioeint(unit, rr, sio); goto start; } code = sio->sio_data; if ((tp->t_state & TS_ISOPEN) != 0) (*linesw[tp->t_line].l_rint)(code, tp); #ifdef KGDB else { if (code == FRAME_END && kgdb_dev == makedev(siomajor, unit)) kgdb_connect(0); /* trap into kgdb */ } #endif while ((rr = siogetreg(sio)) & RR_RXRDY) { code = sio->sio_data; if ((tp->t_state & TS_ISOPEN) != 0) (*linesw[tp->t_line].l_rint)(code, tp); #ifdef KGDB else { if (code == FRAME_END && kgdb_dev == makedev(siomajor, unit)) kgdb_connect(0); /* trap into kgdb */ } #endif } } if (rr & RR_TXRDY) { sio->sio_cmd = WR0_RSTPEND; tp->t_state &= ~(TS_BUSY|TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else siostart(tp); } } sioeint(unit, stat, sio) register int unit, stat; register struct siodevice *sio; { register struct tty *tp; register int code; tp = &sio_tty[unit]; code = sio->sio_data; sio->sio_cmd = WR0_ERRRST; if ((tp->t_state & TS_ISOPEN) == 0) { #ifdef KGDB /* we don't care about parity errors */ if (((stat & (RR_FRAMING|RR_PARITY)) == RR_PARITY) && kgdb_dev == makedev(siomajor, unit) && code == FRAME_END) kgdb_connect(0); /* trap into kgdb */ #endif return; } if (stat & RR_FRAMING) code |= TTY_FE; else if (stat & RR_PARITY) code |= TTY_PE; (*linesw[tp->t_line].l_rint)(code, tp); } /* * Following are all routines needed for SIO to act as console */ #include siocnprobe(cp) register struct consdev *cp; { register int unit = 0; /* locate the major number */ for (siomajor = 0; siomajor < nchrdev; siomajor++) if (cdevsw[siomajor].d_open == sioopen) break; /* initialize required fields */ cp->cn_dev = makedev(siomajor, unit); cp->cn_tp = &sio_tty[unit]; cp->cn_pri = CN_NORMAL; } siocninit(cp) struct consdev *cp; { int unit = siounit(cp->cn_dev); register struct sio_softc *sc = &sio_softc[unit]; sioinit((struct siodevice *) SIO_HARDADDR, siodefaultrate); /* port assign */ sc->sc_pc = sio_port_assign(SIO_PORT, siomajor, unit, siointr); sioconsole = unit; sio_active |= 1 << unit; siosoftCAR |= 1 << unit; } siocngetc(dev) dev_t dev; { struct sio_softc *sc = &sio_softc[siounit(dev)]; struct sio_portc *pc = sc->sc_pc; return(sio_imgetc(pc->pc_addr)); } siocnputc(dev, c) dev_t dev; int c; { struct sio_softc *sc = &sio_softc[siounit(dev)]; struct sio_portc *pc = sc->sc_pc; sio_imputc(pc->pc_addr, c); } /* * sio raw-level routines */ sioinit(sio0, rate) register struct siodevice *sio0; register int rate; { register struct siodevice *sio1; int s; rate = ttspeedtab(rate, siospeedtab); if (sio_init_done) return; sio1 = (struct siodevice *) ((u_long) sio0 + sizeof(struct siodevice)); s = splhigh(); sioreg(sio0, WR0, WR0_CHANRST); /* Channel-A Reset */ sioreg(sio0, WR2, (WR2_VEC86 | WR2_INTR_1)); /* Set CPU BUS Interface Mode */ sioreg(sio1, WR2, 0); /* Set Interrupt Vector */ sioreg(sio0, WR0, WR0_RSTINT); /* Reset E/S Interrupt */ sioreg(sio0, WR4, (rate | WR4_STOP1 | WR4_NPARITY)); /* Tx/Rx */ sioreg(sio0, WR3, (WR3_RX8BIT | WR3_RXENBL)); /* Rx */ sioreg(sio0, WR5, (WR5_TX8BIT | WR5_TXENBL)); /* Tx */ sioreg(sio0, WR0, WR0_RSTINT); /* Reset E/S Interrupt */ sioreg(sio0, WR1, (WR1_RXALLS | WR1_TXENBL)); sioreg(sio1, WR0, WR0_CHANRST); /* Channel-B Reset */ sioreg(sio1, WR0, WR0_RSTINT); /* Reset E/S Interrupt */ sioreg(sio1, WR4, (rate | WR4_STOP1 | WR4_NPARITY)); /* Tx/Rx */ sioreg(sio1, WR3, (WR3_RX8BIT | WR3_RXENBL)); /* Rx */ sioreg(sio1, WR5, (WR5_TX8BIT | WR5_TXENBL)); /* Tx */ sioreg(sio1, WR0, WR0_RSTINT); /* Reset E/S Interrupt */ sioreg(sio1, WR1, (WR1_RXALLS | WR1_TXENBL)); splx(s); sio_init_done = 1; } sio_imgetc(sio) register struct siodevice *sio; { register int rr0, rr1; int c, s; s = splhigh(); while (((rr0 = sioreg(sio, RR0, 0)) & RR0_RXAVAIL) == 0) ; c = sio->sio_data; sioreg(sio, WR0, WR0_RSTPEND); splx(s); return (c); } sio_imputc(sio, c) register struct siodevice *sio; int c; { register u_char code; register int rr; int s; s = splhigh(); sioreg(sio, WR1, WR1_RXALLS); do { DELAY(1); rr = siogetreg(sio); } while (!(rr & RR_TXRDY)); code = (c & 0xFF); sio->sio_data = code; do { DELAY(1); rr = siogetreg(sio); } while (!(rr & RR_TXRDY)); sioreg(sio, WR1, (WR1_RXALLS | WR1_TXENBL)); splx(s); } /* * uPD7201A register operation */ int siogetreg(sio) register struct siodevice *sio; { register int rr = 0; rr = sio->sio_stat; rr <<= 8; sio->sio_cmd = 1; /* Select RR1 */ rr |= sio->sio_stat; return(rr); } int sioreg(sio, reg, val) register struct siodevice *sio; register int reg, val; { if (isStatusReg(reg)) { if (reg != 0) sio->sio_cmd = reg; val = sio->sio_stat; } else { if (reg != 0) sio->sio_cmd = reg; sio->sio_cmd = val; } return(val); } #endif