/* * ------------------------------------------------------------------- * shutdown () * * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. * ------------------------------------------------------------------- */ static void shutdown(struct dz_serial *info) { unsigned long flags; unsigned short tmp; if (!info->is_initialized) return; save_flags(flags); cli(); dz_stop(info->tty); info->cflags &= ~DZ_CREAD; /* turn off receive enable flag */ dz_out(info, DZ_LPR, info->cflags); if (info->xmit_buf) { /* free Tx buffer */ free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { tmp = dz_in(info, DZ_TCR); if (tmp & DZ_MODEM_DTR) { tmp &= ~DZ_MODEM_DTR; dz_out(info, DZ_TCR, tmp); } } if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->is_initialized = 0; restore_flags(flags); }
static int __init dz_console_setup(struct console *co, char *options) { struct dz_port *dport = &dz_ports[CONSOLE_LINE]; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; int ret; unsigned short mask, tmp; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); dz_reset(dport); ret = uart_set_options(&dport->port, co, baud, parity, bits, flow); if (ret == 0) { mask = 1 << dport->port.line; tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ if (!(tmp & mask)) { tmp |= mask; /* set the TX flag */ dz_out(dport, DZ_TCR, tmp); } } return ret; }
/* * This routine sends a break character out the serial port. */ static void send_break(struct dz_serial *info, int duration) { unsigned long flags; unsigned short tmp, mask; if (!info->port) return; mask = 1 << info->line; tmp = dz_in(info, DZ_TCR); tmp |= mask; current->state = TASK_INTERRUPTIBLE; save_flags(flags); cli(); dz_out(info, DZ_TCR, tmp); schedule_timeout(duration); tmp &= ~mask; dz_out(info, DZ_TCR, tmp); restore_flags(flags); }
/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ static unsigned int dz_tx_empty(struct uart_port *uport) { struct dz_port *dport = (struct dz_port *)uport; unsigned short status = dz_in(dport, DZ_LPR); /* FIXME: this appears to be obviously broken --rmk. */ return status ? TIOCSER_TEMT : 0; }
static void dz_reset(struct dz_port *dport) { dz_out(dport, DZ_CSR, DZ_CLR); while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); /* enable scanning */ dz_out(dport, DZ_CSR, DZ_MSE); }
/* * ------------------------------------------------------------------- * dz_tx_empty() -- get the transmitter empty status * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. * ------------------------------------------------------------------- */ static unsigned int dz_tx_empty(struct uart_port *uport) { struct dz_port *dport = (struct dz_port *)uport; unsigned short tmp, mask = 1 << dport->port.line; tmp = dz_in(dport, DZ_TCR); tmp &= mask; return tmp ? 0 : TIOCSER_TEMT; }
/* * ------------------------------------------------------------------- * startup () * * various initialization tasks * ------------------------------------------------------------------- */ static int startup(struct dz_serial *info) { unsigned long page, flags; unsigned short tmp; if (info->is_initialized) return 0; save_flags(flags); cli(); if (!info->port) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); restore_flags(flags); return -ENODEV; } if (!info->xmit_buf) { page = get_free_page(GFP_KERNEL); if (!page) { restore_flags(flags); return -ENOMEM; } info->xmit_buf = (unsigned char *) page; } if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); /* enable the interrupt and the scanning */ tmp = dz_in(info, DZ_CSR); tmp |= (DZ_RIE | DZ_TIE | DZ_MSE); dz_out(info, DZ_CSR, tmp); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* set up the speed */ change_speed(info); /* clear the line transmitter buffer I can't figure out why I need to do this - but its necessary - in order for the console portion and the interrupt portion to live happily side by side. */ /* clear the line transmitter buffer I can't figure out why I need to do this - but its necessary - in order for the console portion and the interrupt portion to live happily side by side. */ info->is_initialized = 1; restore_flags(flags); return 0; }
static void dz_start(struct tty_struct *tty) { struct dz_serial *info = (struct dz_serial *) tty->driver_data; unsigned short mask, tmp; mask = 1 << info->line; tmp = dz_in(info, DZ_TCR); /* read the TX flag */ tmp |= mask; /* set the TX flag */ dz_out(info, DZ_TCR, tmp); }
static void dz_start_tx(struct uart_port *uport) { struct dz_port *dport = (struct dz_port *)uport; unsigned short tmp, mask = 1 << dport->port.line; unsigned long flags; spin_lock_irqsave(&dport->port.lock, flags); tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ tmp |= mask; /* set the TX flag */ dz_out(dport, DZ_TCR, tmp); spin_unlock_irqrestore(&dport->port.lock, flags); }
static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) { struct dz_port *dport = (struct dz_port *)uport; unsigned short tmp; if (dport->port.line == DZ_MODEM) { tmp = dz_in(dport, DZ_TCR); if (mctrl & TIOCM_DTR) tmp &= ~DZ_MODEM_DTR; else tmp |= DZ_MODEM_DTR; dz_out(dport, DZ_TCR, tmp); } }
/* * ------------------------------------------------------------ * check_modem_status () * * Only valid for the MODEM line duh ! * ------------------------------------------------------------ */ static inline void check_modem_status(struct dz_serial *info) { unsigned short status; /* if not ne modem line just return */ if (info->line != DZ_MODEM) return; status = dz_in(info, DZ_MSR); /* it's easy, since DSR2 is the only bit in the register */ if (status) info->icount.dsr++; }
static unsigned int dz_get_mctrl(struct uart_port *uport) { struct dz_port *dport = (struct dz_port *)uport; unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; if (dport->port.line == DZ_MODEM) { /* * CHECKME: This is a guess from the other code... --rmk */ if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) mctrl &= ~TIOCM_DSR; } return mctrl; }
/* * ------------------------------------------------------------ * dz_interrupt () * * this is the main interrupt routine for the DZ chip. * It deals with the multiple ports. * ------------------------------------------------------------ */ static void dz_interrupt(int irq, void *dev, struct pt_regs *regs) { struct dz_serial *info; unsigned short status; /* get the reason why we just got an irq */ status = dz_in((struct dz_serial *) dev, DZ_CSR); info = lines[LINE(status)]; /* re-arrange info the proper port */ if (status & DZ_RDONE) receive_chars(info); /* the receive function */ if (status & DZ_TRDY) transmit_chars(info); }
static void dz_break_ctl(struct uart_port *uport, int break_state) { struct dz_port *dport = (struct dz_port *)uport; unsigned long flags; unsigned short tmp, mask = 1 << uport->line; spin_lock_irqsave(&uport->lock, flags); tmp = dz_in(dport, DZ_TCR); if (break_state) tmp |= mask; else tmp &= ~mask; dz_out(dport, DZ_TCR, tmp); spin_unlock_irqrestore(&uport->lock, flags); }
/* * ------------------------------------------------------------------- * dz_console_putchar() -- transmit a character * * Polled transmission. This is tricky. We need to mask transmit * interrupts so that they do not interfere, enable the transmitter * for the line requested and then wait till the transmit scanner * requests data for this line. But it may request data for another * line first, in which case we have to disable its transmitter and * repeat waiting till our line pops up. Only then the character may * be transmitted. Finally, the state of the transmitter mask is * restored. Welcome to the world of PDP-11! * ------------------------------------------------------------------- */ static void dz_console_putchar(struct uart_port *uport, int ch) { struct dz_port *dport = (struct dz_port *)uport; unsigned long flags; unsigned short csr, tcr, trdy, mask; int loops = 10000; spin_lock_irqsave(&dport->port.lock, flags); csr = dz_in(dport, DZ_CSR); dz_out(dport, DZ_CSR, csr & ~DZ_TIE); tcr = dz_in(dport, DZ_TCR); tcr |= 1 << dport->port.line; mask = tcr; dz_out(dport, DZ_TCR, mask); iob(); spin_unlock_irqrestore(&dport->port.lock, flags); while (loops--) { trdy = dz_in(dport, DZ_CSR); if (!(trdy & DZ_TRDY)) continue; trdy = (trdy & DZ_TLINE) >> 8; if (trdy == dport->port.line) break; mask &= ~(1 << trdy); dz_out(dport, DZ_TCR, mask); iob(); udelay(2); } if (loops) /* Cannot send otherwise. */ dz_out(dport, DZ_TDR, ch); dz_out(dport, DZ_TCR, tcr); dz_out(dport, DZ_CSR, csr); }
static unsigned int dz_get_mctrl(struct uart_port *uport) { /* * FIXME: Handle the 3100/5000 as appropriate. --macro */ struct dz_port *dport = (struct dz_port *)uport; unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; if (dport->port.line == DZ_MODEM) { if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) mctrl &= ~TIOCM_DSR; } return mctrl; }
static void dz_stop(struct tty_struct *tty) { struct dz_serial *info; unsigned short mask, tmp; if (tty == 0) return; info = (struct dz_serial *) tty->driver_data; mask = 1 << info->line; tmp = dz_in(info, DZ_TCR); /* read the TX flag */ tmp &= ~mask; /* clear the TX flag */ dz_out(info, DZ_TCR, tmp); }
/* * ------------------------------------------------------------ * dz_interrupt () * * this is the main interrupt routine for the DZ chip. * It deals with the multiple ports. * ------------------------------------------------------------ */ static irqreturn_t dz_interrupt(int irq, void *dev) { struct dz_port *dport = (struct dz_port *)dev; unsigned short status; /* get the reason why we just got an irq */ status = dz_in(dport, DZ_CSR); if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) dz_receive_chars(dport, regs); if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) dz_transmit_chars(dport); return IRQ_HANDLED; }
/* * ------------------------------------------------------------------- * startup () * * various initialization tasks * ------------------------------------------------------------------- */ static int dz_startup(struct uart_port *uport) { struct dz_port *dport = (struct dz_port *)uport; unsigned long flags; unsigned short tmp; spin_lock_irqsave(&dport->port.lock, flags); /* enable the interrupt and the scanning */ tmp = dz_in(dport, DZ_CSR); tmp |= DZ_RIE | DZ_TIE | DZ_MSE; dz_out(dport, DZ_CSR, tmp); spin_unlock_irqrestore(&dport->port.lock, flags); return 0; }
static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) { /* * FIXME: Handle the 3100/5000 as appropriate. --macro */ struct dz_port *dport = (struct dz_port *)uport; unsigned short tmp; if (dport->port.line == DZ_MODEM) { tmp = dz_in(dport, DZ_TCR); if (mctrl & TIOCM_DTR) tmp &= ~DZ_MODEM_DTR; else tmp |= DZ_MODEM_DTR; dz_out(dport, DZ_TCR, tmp); } }
static void dz_break_ctl(struct uart_port *uport, int break_state) { /* * FIXME: Can't access BREAK bits in TDR easily; * reuse the code for polled TX. --macro */ struct dz_port *dport = (struct dz_port *)uport; unsigned long flags; unsigned short tmp, mask = 1 << dport->port.line; spin_lock_irqsave(&uport->lock, flags); tmp = dz_in(dport, DZ_TCR); if (break_state) tmp |= mask; else tmp &= ~mask; dz_out(dport, DZ_TCR, tmp); spin_unlock_irqrestore(&uport->lock, flags); }
/* * ------------------------------------------------------------ * dz_interrupt () * * this is the main interrupt routine for the DZ chip. * It deals with the multiple ports. * ------------------------------------------------------------ */ static irqreturn_t dz_interrupt(int irq, void *dev, struct pt_regs *regs) { struct dz_port *dport; unsigned short status; /* get the reason why we just got an irq */ status = dz_in((struct dz_port *)dev, DZ_CSR); dport = &dz_ports[LINE(status)]; if (status & DZ_RDONE) dz_receive_chars(dport); if (status & DZ_TRDY) dz_transmit_chars(dport); /* FIXME: what about check modem status??? --rmk */ return IRQ_HANDLED; }
/* * ------------------------------------------------------------ * check_modem_status() * * DS 3100 & 5100: Only valid for the MODEM line, duh! * DS 5000/200: Valid for the MODEM and PRINTER line. * ------------------------------------------------------------ */ static inline void check_modem_status(struct dz_port *dport) { /* * FIXME: * 1. No status change interrupt; use a timer. * 2. Handle the 3100/5000 as appropriate. --macro */ unsigned short status; /* If not the modem line just return. */ if (dport->port.line != DZ_MODEM) return; status = dz_in(dport, DZ_MSR); /* it's easy, since DSR2 is the only bit in the register */ if (status) dport->port.icount.dsr++; }
static void dz_console_put_char(struct dz_port *dport, unsigned char ch) { unsigned long flags; int loops = 2500; unsigned short tmp = ch; /* this code sends stuff out to serial device - spinning its wheels and waiting. */ spin_lock_irqsave(&dport->port.lock, flags); /* spin our wheels */ while (((dz_in(dport, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) /* FIXME: cpu_relax, udelay? --rmk */ ; /* Actually transmit the character. */ dz_out(dport, DZ_TDR, tmp); spin_unlock_irqrestore(&dport->port.lock, flags); }
/* * ------------------------------------------------------------ * transmit_char () * * This routine deals with outputs to any lines. * ------------------------------------------------------------ */ static inline void dz_transmit_chars(struct dz_port *dport_in) { struct dz_port *dport; struct circ_buf *xmit; unsigned short status; unsigned char tmp; status = dz_in(dport_in, DZ_CSR); dport = &dz_ports[LINE(status)]; xmit = &dport->port.info->xmit; if (dport->port.x_char) { /* XON/XOFF chars */ dz_out(dport, DZ_TDR, dport->port.x_char); dport->port.icount.tx++; dport->port.x_char = 0; return; } /* If nothing to do or stopped or hardware stopped. */ if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { dz_stop_tx(&dport->port); return; } /* * If something to do... (remember the dz has no output fifo, * so we go one char at a time) :-< */ tmp = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); dz_out(dport, DZ_TDR, tmp); dport->port.icount.tx++; if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) uart_write_wakeup(&dport->port); /* Are we are done. */ if (uart_circ_empty(xmit)) dz_stop_tx(&dport->port); }
static void dz_console_put_char(unsigned char ch) { unsigned long flags; int loops = 2500; unsigned short tmp = ch; /* this code sends stuff out to serial device - spinning its wheels and waiting. */ /* force the issue - point it at lines[3] */ dz_console = &multi[CONSOLE_LINE]; save_flags(flags); cli(); /* spin our wheels */ while (((dz_in(dz_console, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--); /* Actually transmit the character. */ dz_out(dz_console, DZ_TDR, tmp); restore_flags(flags); }
/* * ------------------------------------------------------------------- * startup () * * various initialization tasks * ------------------------------------------------------------------- */ static int dz_startup(struct uart_port *uport) { struct dz_port *dport = (struct dz_port *)uport; unsigned long flags; unsigned short tmp; /* The dz lines for the mouse/keyboard must be * opened using their respective drivers. */ if ((dport->port.line == DZ_KEYBOARD) || (dport->port.line == DZ_MOUSE)) return -ENODEV; spin_lock_irqsave(&dport->port.lock, flags); /* enable the interrupt and the scanning */ tmp = dz_in(dport, DZ_CSR); tmp |= DZ_RIE | DZ_TIE | DZ_MSE; dz_out(dport, DZ_CSR, tmp); spin_unlock_irqrestore(&dport->port.lock, flags); return 0; }
/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ static int get_lsr_info(struct dz_serial *info, unsigned int *value) { unsigned short status = dz_in(info, DZ_LPR); return put_user(status, value); }
/* * ------------------------------------------------------------ * receive_char () * * This routine deals with inputs from any lines. * ------------------------------------------------------------ */ static inline void dz_receive_chars(struct dz_port *dport_in, struct pt_regs *regs) { struct dz_port *dport; struct tty_struct *tty = NULL; struct uart_icount *icount; int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; unsigned short status; unsigned char ch, flag; int i; while ((status = dz_in(dport_in, DZ_RBUF)) & DZ_DVAL) { dport = &dz_ports[LINE(status)]; tty = dport->port.info->tty; /* point to the proper dev */ ch = UCHAR(status); /* grab the char */ icount = &dport->port.icount; icount->rx++; flag = TTY_NORMAL; if (status & DZ_FERR) { /* frame error */ /* * There is no separate BREAK status bit, so * treat framing errors as BREAKs for Magic SysRq * and SAK; normally, otherwise. */ if (uart_handle_break(&dport->port)) continue; if (dport->port.flags & UPF_SAK) flag = TTY_BREAK; else flag = TTY_FRAME; } else if (status & DZ_OERR) /* overrun error */ flag = TTY_OVERRUN; else if (status & DZ_PERR) /* parity error */ flag = TTY_PARITY; /* keep track of the statistics */ switch (flag) { case TTY_FRAME: icount->frame++; break; case TTY_PARITY: icount->parity++; break; case TTY_OVERRUN: icount->overrun++; break; case TTY_BREAK: icount->brk++; break; default: break; } if (uart_handle_sysrq_char(&dport->port, ch, regs)) continue; if ((status & dport->port.ignore_status_mask) == 0) { uart_insert_char(&dport->port, status, DZ_OERR, ch, flag); lines_rx[LINE(status)] = 1; } } for (i = 0; i < DZ_NB_PORT; i++) if (lines_rx[i]) tty_flip_buffer_push(dz_ports[i].port.info->tty); }
/* * ------------------------------------------------------------ * receive_char () * * This routine deals with inputs from any lines. * ------------------------------------------------------------ */ static inline void dz_receive_chars(struct dz_port *dport) { struct tty_struct *tty = NULL; struct uart_icount *icount; int ignore = 0; unsigned short status, tmp; unsigned char ch, flag; /* this code is going to be a problem... the call to tty_flip_buffer is going to need to be rethought... */ do { status = dz_in(dport, DZ_RBUF); /* punt so we don't get duplicate characters */ if (!(status & DZ_DVAL)) goto ignore_char; ch = UCHAR(status); /* grab the char */ flag = TTY_NORMAL; #if 0 if (info->is_console) { if (ch == 0) return; /* it's a break ... */ } #endif tty = dport->port.info->tty;/* now tty points to the proper dev */ icount = &dport->port.icount; if (!tty) break; if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; icount->rx++; /* keep track of the statistics */ if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { if (status & DZ_PERR) /* parity error */ icount->parity++; else if (status & DZ_FERR) /* frame error */ icount->frame++; if (status & DZ_OERR) /* overrun error */ icount->overrun++; /* check to see if we should ignore the character and mask off conditions that should be ignored */ if (status & dport->port.ignore_status_mask) { if (++ignore > 100) break; goto ignore_char; } /* mask off the error conditions we want to ignore */ tmp = status & dport->port.read_status_mask; if (tmp & DZ_PERR) { flag = TTY_PARITY; #ifdef DEBUG_DZ debug_console("PERR\n", 5); #endif } else if (tmp & DZ_FERR) { flag = TTY_FRAME; #ifdef DEBUG_DZ debug_console("FERR\n", 5); #endif } if (tmp & DZ_OERR) { #ifdef DEBUG_DZ debug_console("OERR\n", 5); #endif tty_insert_flip_char(tty, ch, flag); ch = 0; flag = TTY_OVERRUN; } } tty_insert_flip_char(tty, ch, flag); ignore_char: } while (status & DZ_DVAL); if (tty) tty_flip_buffer_push(tty); } /* * ------------------------------------------------------------ * transmit_char () * * This routine deals with outputs to any lines. * ------------------------------------------------------------ */ static inline void dz_transmit_chars(struct dz_port *dport) { struct circ_buf *xmit = &dport->port.info->xmit; unsigned char tmp; if (dport->port.x_char) { /* XON/XOFF chars */ dz_out(dport, DZ_TDR, dport->port.x_char); dport->port.icount.tx++; dport->port.x_char = 0; return; } /* if nothing to do or stopped or hardware stopped */ if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { dz_stop_tx(&dport->port, 0); return; } /* * if something to do ... (rember the dz has no output fifo so we go * one char at a time :-< */ tmp = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); dz_out(dport, DZ_TDR, tmp); dport->port.icount.tx++; if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) uart_write_wakeup(&dport->port); /* Are we done */ if (uart_circ_empty(xmit)) dz_stop_tx(&dport->port, 0); } /* * ------------------------------------------------------------ * check_modem_status () * * Only valid for the MODEM line duh ! * ------------------------------------------------------------ */ static inline void check_modem_status(struct dz_port *dport) { unsigned short status; /* if not ne modem line just return */ if (dport->port.line != DZ_MODEM) return; status = dz_in(dport, DZ_MSR); /* it's easy, since DSR2 is the only bit in the register */ if (status) dport->port.icount.dsr++; }