📄 8250.c
字号:
struct tty_struct *tty = up->port.info->tty; unsigned char ch, lsr = *status; int max_count = 256; char flag; do { /* The following is not allowed by the tty layer and unsafe. It should be fixed ASAP */ if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { if (tty->low_latency) { spin_unlock(&up->port.lock); tty_flip_buffer_push(tty); spin_lock(&up->port.lock); } /* If this failed then we will throw away the bytes but must do so to clear interrupts */ } ch = serial_inp(up, UART_RX); flag = TTY_NORMAL; up->port.icount.rx++;#ifdef CONFIG_SERIAL_8250_CONSOLE /* * Recover the break flag from console xmit */ if (up->port.line == up->port.cons->index) { lsr |= up->lsr_break_flag; up->lsr_break_flag = 0; }#endif if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE))) { /* * For statistics only */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); up->port.icount.brk++; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ if (uart_handle_break(&up->port)) goto ignore_char; } else if (lsr & UART_LSR_PE) up->port.icount.parity++; else if (lsr & UART_LSR_FE) up->port.icount.frame++; if (lsr & UART_LSR_OE) up->port.icount.overrun++; /* * Mask off conditions which should be ingored. */ lsr &= up->port.read_status_mask; if (lsr & UART_LSR_BI) { DEBUG_INTR("handling break...."); flag = TTY_BREAK; } else if (lsr & UART_LSR_PE) flag = TTY_PARITY; else if (lsr & UART_LSR_FE) flag = TTY_FRAME; } if (uart_handle_sysrq_char(&up->port, ch, regs)) goto ignore_char; if ((lsr & up->port.ignore_status_mask) == 0) { tty_insert_flip_char(tty, ch, flag); } if ((lsr & UART_LSR_OE) && tty->flip.count < TTY_FLIPBUF_SIZE) { /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character. */ tty_insert_flip_char(tty, 0, TTY_OVERRUN); } ignore_char: lsr = serial_inp(up, UART_LSR); } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); spin_unlock(&up->port.lock); tty_flip_buffer_push(tty); spin_lock(&up->port.lock); *status = lsr;}static _INLINE_ void transmit_chars(struct uart_8250_port *up){ struct circ_buf *xmit = &up->port.info->xmit; int count; if (up->port.x_char) { serial_outp(up, UART_TX, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { serial8250_stop_tx(&up->port, 0); return; } count = up->tx_loadsz; do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; if (uart_circ_empty(xmit)) break; } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); DEBUG_INTR("THRE..."); if (uart_circ_empty(xmit)) serial8250_stop_tx(&up->port, 0);}static _INLINE_ void check_modem_status(struct uart_8250_port *up){ int status; status = serial_in(up, UART_MSR); if ((status & UART_MSR_ANY_DELTA) == 0) return; if (status & UART_MSR_TERI) up->port.icount.rng++; if (status & UART_MSR_DDSR) up->port.icount.dsr++; if (status & UART_MSR_DDCD) uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); wake_up_interruptible(&up->port.info->delta_msr_wait);}/* * This handles the interrupt from one port. */static inline voidserial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs){ unsigned int status = serial_inp(up, UART_LSR); DEBUG_INTR("status = %x...", status); if (status & UART_LSR_DR) receive_chars(up, &status, regs); check_modem_status(up); if (status & UART_LSR_THRE) transmit_chars(up);}/*! * This is the 16C652 serial driver's interrupt routine modified for MXC. This * function is modified to handle interrupt pin connectivity requirements. * * @param irq The Interrupt number * @param dev_id Driver private data * @param regs Holds a snapshot of the processors context before the * processor entered the interrupt code * * @result This function returns \b IRQ_HANDLED. * \b IRQ_HANDLED is defined in include/linux/interrupt.h. * *//* * This is the serial driver's interrupt routine. * * Arjan thinks the old way was overly complex, so it got simplified. * Alan disagrees, saying that need the complexity to handle the weird * nature of ISA shared interrupts. (This is a special exception.) * * In order to handle ISA shared interrupts properly, we need to check * that all ports have been serviced, and therefore the ISA interrupt * line has been de-asserted. * * This means we need to loop through all ports. checking that they * don't have an interrupt pending. */static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct irq_info *i = dev_id; struct list_head *l, *end = NULL; int pass_counter = 0, handled = 0; DEBUG_INTR("serial8250_interrupt(%d)...", irq); spin_lock(&i->lock); l = i->head; do { struct uart_8250_port *up; unsigned int iir; up = list_entry(l, struct uart_8250_port, list);#if defined(CONFIG_ARCH_ARGONPLUS) || \ defined(CONFIG_ARCH_SCMA11) || \ defined(CONFIG_ARCH_MX3) || \ defined(CONFIG_MACH_ZEUSEVB) /* * Make sure INTA-B are active in the MCR, otherwise the DUART will * tri-state the INT lines */ serial_out(up, UART_MCR, serial_inp(up, UART_MCR) | UART_MCR_OUT2); /* * Get the EDIO interrupt, clear if set. */ extuart_intr_clear(up->port.line);#endif iir = serial_in(up, UART_IIR); if (!(iir & UART_IIR_NO_INT)) { spin_lock(&up->port.lock); serial8250_handle_port(up, regs); spin_unlock(&up->port.lock); handled = 1; end = NULL; } else if (end == NULL) end = l; l = l->next; if (l == i->head && pass_counter++ > PASS_LIMIT) { /* If we hit this, we're dead. */ printk(KERN_ERR "serial8250: too much work for " "irq%d\n", irq); break; } } while (l != end); spin_unlock(&i->lock); DEBUG_INTR("end.\n"); //return IRQ_RETVAL(handled); return IRQ_HANDLED; /* FIXME: iir status not ready on 1510 */}/* * To support ISA shared interrupts, we need to have one interrupt * handler that ensures that the IRQ line has been deasserted * before returning. Failing to do this will result in the IRQ * line being stuck active, and, since ISA irqs are edge triggered, * no more IRQs will be seen. */static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up){ spin_lock_irq(&i->lock); if (!list_empty(i->head)) { if (i->head == &up->list) i->head = i->head->next; list_del(&up->list); } else { BUG_ON(i->head != &up->list); i->head = NULL; } spin_unlock_irq(&i->lock);}/*! * This function registers UART interrupt service routine, if not already * registered. If Interrupt type flags contains UPF_SHARE_IRQ, interrupt * sharing is enabled. This function inserts irq info into irq_lists. This * function handles interrupt pin configurations required for MXC boards. * * @param up the 8250 UART port structure * * @result This function returns zero if irq_lists is already initialized * or the return value from request_irq(). */static int serial_link_irq_chain(struct uart_8250_port *up){ struct irq_info *i = irq_lists + up->port.irq; int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0; spin_lock_irq(&i->lock); if (i->head) { list_add(&up->list, i->head); spin_unlock_irq(&i->lock); ret = 0; } else { INIT_LIST_HEAD(&up->list); i->head = &up->list; spin_unlock_irq(&i->lock);#if defined(CONFIG_ARCH_SCMA11) || \ defined(CONFIG_ARCH_MX3) || \ defined(CONFIG_ARCH_ARGONPLUS) ret = extuart_intr_setup(up->port.line); /* * Register the ISR with the GPIO driver, * return after registering */ if (ret == 1) { ret = gpio_request_irq(2, 30, GPIO_HIGH_PRIO, serial8250_interrupt, irq_flags, "serial", i); if (ret != 0) { gpio_free_irq(2, 30, GPIO_HIGH_PRIO); serial_do_unlink(i, up); } return ret; }#endif#if defined(CONFIG_MACH_ZEUSEVB) ret = extuart_intr_setup(up->port.line, up->port.irq, serial8250_interrupt, irq_flags, "serial", i);#else ret = request_irq(up->port.irq, serial8250_interrupt, irq_flags, "serial", i);#endif if (ret < 0) serial_do_unlink(i, up); } return ret;}static void serial_unlink_irq_chain(struct uart_8250_port *up){ struct irq_info *i = irq_lists + up->port.irq; BUG_ON(i->head == NULL);#if defined(CONFIG_ARCH_ARGONPLUS) || \ defined(CONFIG_ARCH_SCMA11) || \ defined(CONFIG_ARCH_MX3) if (list_empty(i->head)) { if (extuart_intr_cleanup(up->port.line)) { gpio_free_irq(2, 30, GPIO_HIGH_PRIO); } else { free_irq(up->port.irq, i); } serial_do_unlink(i, up); return; }#endif if (list_empty(i->head))#if defined(CONFIG_MACH_ZEUSEVB) extuart_intr_cleanup(up->port.line, up->port.irq, i);#else free_irq(up->port.irq, i);#endif serial_do_unlink(i, up);}/* * This function is used to handle ports that do not have an * interrupt. This doesn't work very well for 16450's, but gives * barely passable results for a 16550A. (Although at the expense * of much CPU overhead). */static void serial8250_timeout(unsigned long data){ struct uart_8250_port *up = (struct uart_8250_port *)data; unsigned int timeout; unsigned int iir; iir = serial_in(up, UART_IIR); if (!(iir & UART_IIR_NO_INT)) { spin_lock(&up->port.lock); serial8250_handle_port(up, NULL); spin_unlock(&up->port.lock); } timeout = up->port.timeout; timeout = timeout > 6 ? (timeout / 2 - 2) : 1; mod_timer(&up->timer, jiffies + timeout);}static unsigned int serial8250_tx_empty(struct uart_port *port){ struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; unsigned int ret; spin_lock_irqsave(&up->port.lock, flags); ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; spin_unlock_irqrestore(&up->port.lock, flags); return ret;}static unsigned int serial8250_get_mctrl(struct uart_port *port){ struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; unsigned char status; unsigned int ret; spin_lock_irqsave(&up->port.lock, flags); status = serial_in(up, UART_MSR); spin_unlock_irqrestore(&up->port.lock, flags); ret = 0; if (status & UART_MSR_DCD) ret |= TIOCM_CAR; if (status & UART_MSR_RI) ret |= TIOCM_RNG; if (status & UART_MSR_DSR) ret |= TIOCM_DSR; if (status & UART_MSR_CTS) ret |= TIOCM_CTS; return ret;}static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl){ struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned char mcr = 0; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; if (mctrl & TIOCM_DTR) mcr |= UART_MCR_DTR; if (mctrl & TIOCM_OUT1) mcr |= UART_MCR_OUT1; if (mctrl & TIOCM_OUT2) mcr |= UART_MCR_OUT2; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; serial_out(up, UART_MCR, mcr);}static void serial8250_break_ctl(struct uart_port *port, int break_state){ struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; else up->lcr &= ~UART_LCR_SBC; serial_out(up, UART_LCR, up->lcr); spin_unlock_irqrestore(&up->port.lock, flags);}static int serial8250_startup(struct uart_port *port){ struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; int retval; if (up->port.line == kgdb8250_ttyS) return -EBUSY; up->capabilities = uart_config[up->port.type].flags; up->mcr = 0; if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_IER, 0); serial_outp(up, UART_LCR, 0); serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_LCR, 0); }#ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the * higher speed clock. */ enable_rsa(up);#endif /* * Clear the FIFO buffers and disable them. * (they will be reeanbled in set_termios()) */ serial8250_clear_fifos(up); /* * Clear the interrupt registers. */ (void) serial_inp(up, UART_LSR); (void) serial_inp(up, UART_RX); (void) serial_inp(up, UART_IIR); (void) serial_inp(up, UART_MSR); /* * At this point, there's no way the LSR could still be 0xff; * if it is, then bail out, because there's likely no UART * here. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -