/* * Interrupt handler */ static irqreturn_t atmel_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; unsigned int status, pending, pass_counter = 0; status = UART_GET_CSR(port); pending = status & UART_GET_IMR(port); while (pending) { /* Interrupt receive */ if (pending & ATMEL_US_RXRDY) atmel_rx_chars(port); // TODO: All reads to CSR will clear these interrupts! if (pending & ATMEL_US_RIIC) port->icount.rng++; if (pending & ATMEL_US_DSRIC) port->icount.dsr++; if (pending & ATMEL_US_DCDIC) uart_handle_dcd_change(port, !(status & ATMEL_US_DCD)); if (pending & ATMEL_US_CTSIC) uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC)) wake_up_interruptible(&port->info->delta_msr_wait); /* Interrupt transmit */ if (pending & ATMEL_US_TXRDY) atmel_tx_chars(port); if (pass_counter++ > ATMEL_ISR_PASS_LIMIT) break; status = UART_GET_CSR(port); pending = status & UART_GET_IMR(port); } return IRQ_HANDLED; }
/* * Interrupt handler */ static irqreturn_t atmel_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; unsigned int status, pending, pass_counter = 0; status = UART_GET_CSR(port); pending = status & UART_GET_IMR(port); while (pending) { /* PDC receive */ if (pending & ATMEL_US_ENDRX) atmel_pdc_endrx(port); if (pending & ATMEL_US_TIMEOUT) atmel_pdc_timeout(port); if (atmel_port->use_dma_rx && pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | ATMEL_US_FRAME | ATMEL_US_PARE)) atmel_pdc_rxerr(port, pending); /* Interrupt receive */ if (pending & ATMEL_US_RXRDY) atmel_rx_chars(port); else if (pending & ATMEL_US_RXBRK) { /* * End of break detected. If it came along * with a character, atmel_rx_chars will * handle it. */ UART_PUT_CR(port, ATMEL_US_RSTSTA); UART_PUT_IDR(port, ATMEL_US_RXBRK); atmel_port->break_active = 0; } // TODO: All reads to CSR will clear these interrupts! if (pending & ATMEL_US_RIIC) port->icount.rng++; if (pending & ATMEL_US_DSRIC) port->icount.dsr++; if (pending & ATMEL_US_DCDIC) uart_handle_dcd_change(port, !(status & ATMEL_US_DCD)); if (pending & ATMEL_US_CTSIC) uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC)) wake_up_interruptible(&port->info->delta_msr_wait); /* PDC transmit */ if (pending & ATMEL_US_ENDTX) atmel_pdc_endtx(port); if (pending & ATMEL_US_TXBUFE) atmel_pdc_txbufe(port); /* Interrupt transmit */ if (pending & ATMEL_US_TXRDY) atmel_tx_chars(port); if (pass_counter++ > ATMEL_ISR_PASS_LIMIT) break; status = UART_GET_CSR(port); pending = status & UART_GET_IMR(port); } return IRQ_HANDLED; }
/* * Characters received (called from interrupt handler) */ static void at91_rx_chars(struct uart_port *port, struct pt_regs *regs) { struct tty_struct *tty = port->info->tty; unsigned int status, ch, flg; status = UART_GET_CSR(port) & port->read_status_mask; while (status & (AT91_US_RXRDY)) { ch = UART_GET_CHAR(port); port->icount.rx++; flg = TTY_NORMAL; /* * note that the error handling code is * out of the main execution path */ if (unlikely(status & (AT91_US_PARE | AT91_US_FRAME | AT91_US_OVRE))) { UART_PUT_CR(port, AT91_US_RSTSTA); /* clear error */ if (status & (AT91_US_PARE)) port->icount.parity++; if (status & (AT91_US_FRAME)) port->icount.frame++; if (status & (AT91_US_OVRE)) port->icount.overrun++; if (status & AT91_US_PARE) flg = TTY_PARITY; else if (status & AT91_US_FRAME) flg = TTY_FRAME; if (status & AT91_US_OVRE) { /* * overrun does *not* affect the character * we read from the FIFO */ tty_insert_flip_char(tty, ch, flg); ch = 0; flg = TTY_OVERRUN; } #ifdef SUPPORT_SYSRQ port->sysrq = 0; #endif } if (uart_handle_sysrq_char(port, ch, regs)) goto ignore_char; tty_insert_flip_char(tty, ch, flg); ignore_char: status = UART_GET_CSR(port) & port->read_status_mask; } tty_flip_buffer_push(tty); }
/* * Characters received (called from interrupt handler) */ static void atmel_rx_chars(struct uart_port *port) { struct tty_struct *tty = port->info->tty; unsigned int status, ch, flg; status = UART_GET_CSR(port); while (status & ATMEL_US_RXRDY) { ch = UART_GET_CHAR(port); port->icount.rx++; flg = TTY_NORMAL; /* * note that the error handling code is * out of the main execution path */ if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME | ATMEL_US_OVRE | ATMEL_US_RXBRK))) { UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */ if (status & ATMEL_US_RXBRK) { status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */ port->icount.brk++; if (uart_handle_break(port)) goto ignore_char; } if (status & ATMEL_US_PARE) port->icount.parity++; if (status & ATMEL_US_FRAME) port->icount.frame++; if (status & ATMEL_US_OVRE) port->icount.overrun++; status &= port->read_status_mask; if (status & ATMEL_US_RXBRK) flg = TTY_BREAK; else if (status & ATMEL_US_PARE) flg = TTY_PARITY; else if (status & ATMEL_US_FRAME) flg = TTY_FRAME; } if (uart_handle_sysrq_char(port, ch)) goto ignore_char; uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg); ignore_char: status = UART_GET_CSR(port); } tty_flip_buffer_push(tty); }
/* * Transmit characters (called from interrupt handler) */ static void atmel_tx_chars(struct uart_port *port) { struct circ_buf *xmit = &port->info->xmit; if (port->x_char) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { atmel_stop_tx(port); return; } while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) atmel_stop_tx(port); }
static ssize_t sam_write(struct file *file, const char *buf, size_t count, loff_t * ppos) { unsigned long flags; unsigned int CSR_status=0; unsigned int i; int data; char *tmpBuf = (char *)kmalloc(count, GFP_KERNEL); if (!tmpBuf) return -ENOMEM; // Move User Space Memory to Kernel Space if (copy_from_user(tmpBuf, buf, count)) { kfree(tmpBuf); return -EFAULT; } // SPIN Lock to protect writing to SAM spin_lock_irqsave(&sam_spinlock, flags); // Part 1, clear receive buffer while (((CSR_status = UART_GET_CSR(base)) & AT91C_US_RXRDY)) { data = (UART_GET_CHAR(base) & 0x1FF); } // Part 2, write to UART's THR for (i=0; i<count; i++) { // Wait unit TX ready while (!((CSR_status = UART_GET_CSR(base)) & AT91C_US_TXRDY)); // copy buffer UART_PUT_CHAR(base, (tmpBuf[i] & 0xFF)); // Wait unit TX empty while (!((CSR_status = UART_GET_CSR(base)) & AT91C_US_TXEMPTY)); } spin_unlock_irqrestore(&sam_spinlock, flags); // SPIN Lock end // free kernel memory kfree(tmpBuf); return 0; }
/* * Interrupt handler */ static irqreturn_t at91_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct uart_port *port = dev_id; unsigned int status, pending, pass_counter = 0; status = UART_GET_CSR(port); pending = status & port->read_status_mask; if (pending) { do { if (pending & AT91_US_RXRDY) at91_rx_chars(port, regs); /* Clear the relevent break bits */ if (pending & AT91_US_RXBRK) { UART_PUT_CR(port, AT91_US_RSTSTA); port->icount.brk++; uart_handle_break(port); } // TODO: All reads to CSR will clear these interrupts! if (pending & AT91_US_RIIC) port->icount.rng++; if (pending & AT91_US_DSRIC) port->icount.dsr++; if (pending & AT91_US_DCDIC) uart_handle_dcd_change(port, !(status & AT91_US_DCD)); if (pending & AT91_US_CTSIC) uart_handle_cts_change(port, !(status & AT91_US_CTS)); if (pending & (AT91_US_RIIC | AT91_US_DSRIC | AT91_US_DCDIC | AT91_US_CTSIC)) wake_up_interruptible(&port->info->delta_msr_wait); if (pending & AT91_US_TXRDY) at91_tx_chars(port); if (pass_counter++ > AT91_ISR_PASS_LIMIT) break; status = UART_GET_CSR(port); pending = status & port->read_status_mask; } while (pending); } return IRQ_HANDLED; }
/* * Interrupts are disabled on entering */ static void at91_console_write(struct console *co, const char *s, u_int count) { struct uart_port *port = at91_ports + co->index; unsigned int status, i, imr; /* * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); /* get interrupt mask */ UART_PUT_IDR(port, AT91_US_RXRDY | AT91_US_TXRDY); /* * Now, do each character */ for (i = 0; i < count; i++) { do { status = UART_GET_CSR(port); } while (!(status & AT91_US_TXRDY)); UART_PUT_CHAR(port, s[i]); if (s[i] == '\n') { do { status = UART_GET_CSR(port); } while (!(status & AT91_US_TXRDY)); UART_PUT_CHAR(port, '\r'); } } /* * Finally, wait for transmitter to become empty * and restore IMR */ do { status = UART_GET_CSR(port); } while (!(status & AT91_US_TXRDY)); UART_PUT_IER(port, imr); /* set interrupts back the way they were */ }
/* * AT91_iso_getchar * * Utility to retrieve one char from USART controller * * by polling UART_CSR register, if ready, get UART_RHR. */ char AT91_iso_getchar(unsigned int nWait100cycle) { unsigned int CSR_status=0; while (!((CSR_status = UART_GET_CSR(base)) & AT91C_US_RXRDY)) { if (nWait100cycle>0) { udelay(20); // cycle time?? nWait100cycle--; if (nWait100cycle <= 0) { // timeout, break loop printk(KERN_INFO "[sam.ko] AT91_iso_getchar(): timeout\n"); return 0; } } } return (UART_GET_CHAR(base) & 0x1FF); }
static ssize_t sam_read(struct file *file, char *buf, size_t count, loff_t *ptr) { unsigned long flags; unsigned int CSR_status=0; unsigned int i; int timeout=0; char *tmpBuf = (char *)kmalloc(count, GFP_KERNEL); int retval=0; if (!tmpBuf) return -ENOMEM; // SPIN Lock to protect reading from SAM spin_lock_irqsave(&sam_spinlock, flags); if (DEBUG) { printk(KERN_INFO "SAM Read count = %d\n", count); } for (i=0; i<count; i++) { timeout = 10000; while (!((CSR_status = UART_GET_CSR(base)) & AT91C_US_RXRDY)) { timeout--; if (timeout == 0) { printk(KERN_INFO "SAM Read Timeout == 0!\n"); break; } } tmpBuf[i] = (UART_GET_CHAR(base) & 0x1FF); if (DEBUG) printk(KERN_INFO "[%.2X]", tmpBuf[i]); } if (DEBUG) { if (timeout == 0) printk(KERN_INFO "SAM Read Timeout == 0!\n"); } spin_unlock_irqrestore(&sam_spinlock, flags); // SPIN Lock end // Move Kernel Space Memory to User Space retval = copy_to_user(buf, tmpBuf, i); // free kernel memory kfree(tmpBuf); return 0; }
/* * Get state of the modem control input lines */ static u_int atmel_get_mctrl(struct uart_port *port) { unsigned int status, ret = 0; status = UART_GET_CSR(port); /* * The control signals are active low. */ if (!(status & ATMEL_US_DCD)) ret |= TIOCM_CD; if (!(status & ATMEL_US_CTS)) ret |= TIOCM_CTS; if (!(status & ATMEL_US_DSR)) ret |= TIOCM_DSR; if (!(status & ATMEL_US_RI)) ret |= TIOCM_RI; return ret; }
/* * Interrupts are disabled on entering */ static void atmel_console_write(struct console *co, const char *s, u_int count) { struct uart_port *port = &atmel_ports[co->index].uart; unsigned int status, imr; /* * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); /* get interrupt mask */ UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY); uart_console_write(port, s, count, atmel_console_putchar); /* * Finally, wait for transmitter to become empty * and restore IMR */ do { status = UART_GET_CSR(port); } while (!(status & ATMEL_US_TXRDY)); UART_PUT_IER(port, imr); /* set interrupts back the way they were */ }
/* * Change the port parameters */ static void atmel_set_termios(struct uart_port *port, struct ktermios * termios, struct ktermios * old) { struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; unsigned long flags; unsigned int mode, imr, quot, baud; /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP | ATMEL_US_PAR); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */ quot /= 8; mode |= ATMEL_US_USCLKS_MCK_DIV8; } /* byte size */ switch (termios->c_cflag & CSIZE) { case CS5: mode |= ATMEL_US_CHRL_5; break; case CS6: mode |= ATMEL_US_CHRL_6; break; case CS7: mode |= ATMEL_US_CHRL_7; break; default: mode |= ATMEL_US_CHRL_8; break; } /* stop bits */ if (termios->c_cflag & CSTOPB) mode |= ATMEL_US_NBSTOP_2; /* parity */ if (termios->c_cflag & PARENB) { if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */ if (termios->c_cflag & PARODD) mode |= ATMEL_US_PAR_MARK; else mode |= ATMEL_US_PAR_SPACE; } else if (termios->c_cflag & PARODD) mode |= ATMEL_US_PAR_ODD; else mode |= ATMEL_US_PAR_EVEN; } else mode |= ATMEL_US_PAR_NONE; spin_lock_irqsave(&port->lock, flags); port->read_status_mask = ATMEL_US_OVRE; if (termios->c_iflag & INPCK) port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); if (termios->c_iflag & (BRKINT | PARMRK)) port->read_status_mask |= ATMEL_US_RXBRK; if (atmel_port->use_dma_rx) /* need to enable error interrupts */ UART_PUT_IER(port, port->read_status_mask); /* * Characters to ignore */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); if (termios->c_iflag & IGNBRK) { port->ignore_status_mask |= ATMEL_US_RXBRK; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) port->ignore_status_mask |= ATMEL_US_OVRE; } // TODO: Ignore all characters if CREAD is set. /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); /* disable interrupts and drain transmitter */ imr = UART_GET_IMR(port); /* get interrupt mask */ UART_PUT_IDR(port, -1); /* disable all interrupts */ while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) cpu_relax(); /* disable receiver and transmitter */ UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); /* set the parity, stop bits and data size */ UART_PUT_MR(port, mode); /* set the baud rate */ UART_PUT_BRGR(port, quot); UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); /* restore interrupts */ UART_PUT_IER(port, imr); /* CTS flow-control and modem-status interrupts */ if (UART_ENABLE_MS(port, termios->c_cflag)) port->ops->enable_ms(port); spin_unlock_irqrestore(&port->lock, flags); }
/* * Characters received (called from interrupt handler) */ static void atmel_rx_chars(struct uart_port *port) { struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; struct tty_struct *tty = port->info->tty; unsigned int status, ch, flg; status = UART_GET_CSR(port); while (status & ATMEL_US_RXRDY) { ch = UART_GET_CHAR(port); port->icount.rx++; flg = TTY_NORMAL; /* * note that the error handling code is * out of the main execution path */ if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME | ATMEL_US_OVRE | ATMEL_US_RXBRK) || atmel_port->break_active)) { UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */ if (status & ATMEL_US_RXBRK && !atmel_port->break_active) { status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */ port->icount.brk++; atmel_port->break_active = 1; UART_PUT_IER(port, ATMEL_US_RXBRK); if (uart_handle_break(port)) goto ignore_char; } else { /* * This is either the end-of-break * condition or we've received at * least one character without RXBRK * being set. In both cases, the next * RXBRK will indicate start-of-break. */ UART_PUT_IDR(port, ATMEL_US_RXBRK); status &= ~ATMEL_US_RXBRK; atmel_port->break_active = 0; } if (status & ATMEL_US_PARE) port->icount.parity++; if (status & ATMEL_US_FRAME) port->icount.frame++; if (status & ATMEL_US_OVRE) port->icount.overrun++; status &= port->read_status_mask; if (status & ATMEL_US_RXBRK) flg = TTY_BREAK; else if (status & ATMEL_US_PARE) flg = TTY_PARITY; else if (status & ATMEL_US_FRAME) flg = TTY_FRAME; } if (uart_handle_sysrq_char(port, ch)) goto ignore_char; uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg); ignore_char: status = UART_GET_CSR(port); } tty_flip_buffer_push(tty); }
/* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ static u_int atmel_tx_empty(struct uart_port *port) { return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0; }
static void atmel_console_putchar(struct uart_port *port, int ch) { while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) cpu_relax(); UART_PUT_CHAR(port, ch); }