From nobody Mon Sep 17 00:00:00 2001 From: HÃ¥vard Skinnemoen Date: Fri Nov 18 16:31:34 2005 +0100 Subject: [PATCH] AVR32: USART3 Serial Driver --- drivers/serial/Kconfig | 21 drivers/serial/Makefile | 1 drivers/serial/atmel_usart.c | 1086 +++++++++++++++++++++++++++++++++++++++++++ drivers/serial/atmel_usart.h | 290 +++++++++++ 4 files changed, 1398 insertions(+) Index: linux-2.6.18-avr32/drivers/serial/Kconfig =================================================================== --- linux-2.6.18-avr32.orig/drivers/serial/Kconfig 2006-11-02 14:16:07.000000000 +0100 +++ linux-2.6.18-avr32/drivers/serial/Kconfig 2006-11-02 15:54:18.000000000 +0100 @@ -246,6 +246,27 @@ config SERIAL_8250_AU1X00 comment "Non-8250 serial port support" +config SERIAL_ATMEL + tristate "Atmel USART3 serial port support" + depends on AVR32 + select SERIAL_CORE + default y + help + Support for the Atmel USART3 on-chip USART found in most + AT32 and AT91 parts from Atmel. + + If unsure, say Y. + +config SERIAL_ATMEL_CONSOLE + bool "Support for console on Atmel USART3 serial port" + depends on SERIAL_ATMEL=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use an Atmel USART3 serial port as + the system console (the system console is the device which + receives all kernel messages and warnings and which allows + logins in single user mode). + config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE) Index: linux-2.6.18-avr32/drivers/serial/Makefile =================================================================== --- linux-2.6.18-avr32.orig/drivers/serial/Makefile 2006-11-02 14:16:07.000000000 +0100 +++ linux-2.6.18-avr32/drivers/serial/Makefile 2006-11-02 14:17:29.000000000 +0100 @@ -20,6 +20,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_b obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o obj-$(CONFIG_SERIAL_8250_AU1X00) += 8250_au1x00.o +obj-$(CONFIG_SERIAL_ATMEL) += atmel_usart.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o Index: linux-2.6.18-avr32/drivers/serial/atmel_usart.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-avr32/drivers/serial/atmel_usart.c 2006-11-02 15:54:18.000000000 +0100 @@ -0,0 +1,1086 @@ +/* + * Driver for Atmel USART3 Serial ports + * + * Based on AT91RM9200 serial driver by Rick Bronson + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/sa1100.c by Deep Blue Solutions Ltd. + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#include +#endif + +#include + +#include "atmel_usart.h" + +/* + * TODO: Move this definition into linux/serial_core.h + */ +#define PORT_USART3 60 + +/* + * Use the same major/minor numbers as the AT91 USART, which is + * actually the same chip + */ +#define SERIAL_USART3_MAJOR TTY_MAJOR +#define MINOR_START 64 +#define NR_PORTS 4 + +#define ERROR_FLAGS (USART3_BIT(CSR_PARE) \ + | USART3_BIT(CSR_FRAME) \ + | USART3_BIT(CSR_OVRE)) + +/* Must be a power of two, or everything will break */ +#define RX_BUFFER_SIZE 32 +struct usart3_port { + void __iomem *regs; + int break_active; + unsigned int tx_dma_head; + int rx_tail; + char *rx_buffer; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + struct clk *mck; + unsigned long mapsize; + struct uart_port uart; +}; +#define to_usart3_port(port) container_of(port, struct usart3_port, uart) + +static void tx_dma_sync(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + struct circ_buf *xmit = &port->info->xmit; + + if (xmit->head < up->tx_dma_head) { + dma_sync_single_for_device(port->dev, + up->tx_dma + up->tx_dma_head, + UART_XMIT_SIZE - up->tx_dma_head, + DMA_TO_DEVICE); + dma_sync_single_for_device(port->dev, up->tx_dma, + xmit->head, DMA_TO_DEVICE); + } else { + dma_sync_single_for_device(port->dev, + up->tx_dma + up->tx_dma_head, + xmit->head - up->tx_dma_head, + DMA_TO_DEVICE); + } +} + +static void tx_dma_update_tail(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + struct circ_buf *xmit = &port->info->xmit; + unsigned long status, remaining; + unsigned int new_tail; + + /* Account for the characters DMA'ed since last update */ + remaining = usart3_readl(up, TCR); + + if (up->tx_dma_head < xmit->tail) { + /* + * We have wrapped around, and there's a chunk at the + * beginning of the buffer that has been submitted for + * DMA. If the ENDTX bit is set, it means that the + * DMA controller also has wrapped around and copied + * TNPR/TNCR into TPR/TCR. + */ + status = usart3_readl(up, CSR); + BUG_ON((up->tx_dma_head != 0) + && (usart3_readl(up, TNCR) == 0) + && !(status & USART3_BIT(ENDTX))); + if (status & USART3_BIT(ENDTX)) { + BUG_ON(usart3_readl(up, TNCR) != 0); + + /* The ENDTX bit might be set after we read TCR */ + remaining = usart3_readl(up, TCR); + + usart3_writel(up, TNCR, 0); + port->icount.tx += UART_XMIT_SIZE - xmit->tail; + xmit->tail = 0; + + BUG_ON(remaining > up->tx_dma_head); + new_tail = up->tx_dma_head - remaining; + } else { + /* + * The DMA controller hasn't switched yet, so + * TCR indicates the number of bytes left + * until this happens. + */ + new_tail = UART_XMIT_SIZE - remaining; + } + } else { + /* No wraparound, move the tail closer to dma_head. */ + BUG_ON(remaining > up->tx_dma_head); + new_tail = up->tx_dma_head - remaining; + } + + BUG_ON(new_tail < xmit->tail); + port->icount.tx += new_tail - xmit->tail; + xmit->tail = new_tail; +} + +static inline void tx_dma_start(struct usart3_port *up) +{ + /* Start the PDC and enable interrupts */ + usart3_writel(up, PTCR, USART3_BIT(TXTEN)); + usart3_writel(up, IER, USART3_BIT(ENDTX)); +} + +static inline void tx_dma_stop(struct usart3_port *up) +{ + usart3_writel(up, PTCR, USART3_BIT(TXTDIS)); + usart3_writel(up, IDR, USART3_BIT(ENDTX)); +} + +static inline unsigned int rx_dma_get_head(struct usart3_port *up) +{ + unsigned int head; + u32 status; + + head = RX_BUFFER_SIZE - usart3_readl(up, RCR); + status = usart3_readl(up, CSR); + if (status & USART3_BIT(ENDRX)) + head = RX_BUFFER_SIZE; + + return head; +} + +static inline int rx_dma_update_tail(struct usart3_port *up, + unsigned int tail) +{ + int again = 0; + + if (!(tail & (RX_BUFFER_SIZE - 1))) { + u32 rnpr = up->rx_dma; + + tail &= RX_BUFFER_SIZE; + if (!tail) + rnpr += RX_BUFFER_SIZE; + + usart3_writel(up, RNPR, rnpr); + usart3_writel(up, RNCR, RX_BUFFER_SIZE); + + again = 1; + } else + BUG_ON(usart3_readl(up, CSR) & USART3_BIT(ENDRX)); + + up->rx_tail = tail; + + return again; +} + +static void usart3_stop_tx(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + + tx_dma_stop(up); + tx_dma_update_tail(port); +} + +static void usart3_start_tx(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + struct circ_buf *xmit = &port->info->xmit; + + BUG_ON(!irqs_disabled()); + + /* + * Stop the DMA engine so that we don't have to worry about race + * conditions when updating the various pointers and counters. + */ + tx_dma_stop(up); + + tx_dma_sync(port); + tx_dma_update_tail(port); + + if (uart_circ_empty(xmit)) + return; + + usart3_writel(up, TPR, up->tx_dma + xmit->tail); + + if (xmit->head > xmit->tail) { + usart3_writel(up, TCR, xmit->head - xmit->tail); + } else { + usart3_writel(up, TCR, UART_XMIT_SIZE - xmit->tail); + usart3_writel(up, TNPR, up->tx_dma); + usart3_writel(up, TNCR, xmit->head); + } + + /* Keep track of what we've submitted for DMA */ + up->tx_dma_head = xmit->head; + + /* Resume operation of DMA engine. */ + tx_dma_start(up); +} + +static void usart3_stop_rx(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + + pr_debug("usart3: stop_rx\n"); + + usart3_writel(up, PTCR, USART3_BIT(RXTDIS)); + usart3_writel(up, IDR, (USART3_BIT(TIMEOUT) + | USART3_BIT(ENDRX) + | USART3_BIT(RXBRK) + | USART3_BIT(OVRE) + | USART3_BIT(FRAME) + | USART3_BIT(PARE))); +} + +static void usart3_flush_buffer(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + + /* + * Reset TX DMA state. Note that we must _always_ update TNCR + * before TCR, since the value in TNCR will automatically move + * to TCR when TCR is 0. + */ + usart3_writel(up, TNCR, 0); + usart3_writel(up, TCR, 0); + up->tx_dma_head = port->info->xmit.tail; +} + +/* + * Enable modem status interrupts + */ +static void usart3_enable_ms(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + + pr_debug("usart3: enable_ms\n"); + usart3_writel(up, IER, (USART3_BIT(RIIC) + | USART3_BIT(DSRIC) + | USART3_BIT(DCDIC) + | USART3_BIT(CTSIC))); +} + +static inline void handle_rx_error(struct uart_port *port, u32 status) +{ + /* + * FIXME: Errors should affect the flag buffer, but due to the + * PDC, we don't really know which char they belong to... + */ + if (status & USART3_BIT(PARE)) { + printk(KERN_NOTICE "usart%u: Parity error\n", port->line); + port->icount.parity++; + } else if (status & USART3_BIT(FRAME)) { + printk(KERN_NOTICE "usart%u: Frame error\n", port->line); + port->icount.frame++; + } + if (status & USART3_BIT(OVRE)) { + printk(KERN_NOTICE "usart%u: Overrun\n", port->line); + port->icount.overrun++; + } + +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif +} + +static inline void handle_pdc_endtx(struct uart_port *port, unsigned long status) +{ + struct usart3_port *up = to_usart3_port(port); + struct circ_buf *xmit = &port->info->xmit; + + tx_dma_update_tail(port); + + if (uart_tx_stopped(port)) { + usart3_stop_tx(port); + printk("usart3: stopped\n"); + return; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + usart3_stop_tx(port); + + /* + * It could be that xmit is being updated right now. If so, + * start_tx() will be called shortly. + */ + if (status & USART3_BIT(TXBUFE)) + usart3_writel(up, IDR, USART3_BIT(ENDTX)); +} + +static void consume_rx_buffer(struct uart_port *port, struct pt_regs *regs) +{ + struct usart3_port *up = to_usart3_port(port); + struct tty_struct *tty = port->info->tty; + unsigned long head, tail; + int len; + int again; + + do { + /* + * Re-arm the timeout before we decide how many + * characters to read. + */ + usart3_writel(up, CR, USART3_BIT(STTTO)); + + head = rx_dma_get_head(up); + + tail = up->rx_tail; + if (tail & RX_BUFFER_SIZE) + head += RX_BUFFER_SIZE; + + if (head == tail) + break; + + dma_sync_single_for_cpu(port->dev, up->rx_dma + tail, + head - tail, DMA_FROM_DEVICE); + + if (uart_handle_sysrq_char(port, up->rx_buffer[tail], + regs)) { + tail++; + if (head == tail) + goto update_tail; + } + + len = tty_insert_flip_string(tty, up->rx_buffer + tail, + head - tail); + port->icount.rx += len; + tail += len; + if (!(head & (RX_BUFFER_SIZE - 1)) && tail != head) { + /* + * head has wrapped, but there isn't enough + * room in the buffer to handle all the + * characters. We must recycle this buffer in + * order to clear the interrupt. + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tail = head; + } + + update_tail: + again = rx_dma_update_tail(up, tail); + } while (again); + + tty_flip_buffer_push(tty); +} + +/* + * This is the serial driver's interrupt routine + */ +static irqreturn_t usart3_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct usart3_port *up = to_usart3_port(port); + u32 status, mask, pending; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&port->lock); + + status = usart3_readl(up, CSR); + mask = usart3_readl(up, IMR); + pending = status & mask; + if (unlikely(!pending)) + goto out; + + ret = IRQ_HANDLED; + + do { + /* + * Consume the buffer and flip buffers if necessary. + */ + consume_rx_buffer(port, regs); + + /* Clear any break and error flags */ + usart3_writel(up, CR, USART3_BIT(RSTSTA)); + + if (pending & (USART3_BIT(OVRE) + | USART3_BIT(FRAME) + | USART3_BIT(PARE))) + handle_rx_error(port, status); + + if (pending & USART3_BIT(RXBRK)) { + if (up->break_active) { + up->break_active = 0; + } else { + up->break_active = 1; + port->icount.brk++; + uart_handle_break(port); + } + } + + if (pending & USART3_BIT(RIIC)) + port->icount.rng++; + if (pending & USART3_BIT(DSRIC)) + port->icount.dsr++; + if (pending & USART3_BIT(DCDIC)) { + port->icount.dcd++; + uart_handle_dcd_change + (port, status & USART3_BIT(DCD)); + } + if (pending & USART3_BIT(CTSIC)) { + port->icount.cts++; + uart_handle_cts_change + (port, status & USART3_BIT(CTS)); + } + if (pending & (USART3_BIT(RIIC) + | USART3_BIT(DSRIC) + | USART3_BIT(DCDIC) + | USART3_BIT(CTSIC))) + wake_up_interruptible(&port->info->delta_msr_wait); + + if (pending & USART3_BIT(ENDTX)) + handle_pdc_endtx(port, status); + + status = usart3_readl(up, CSR); + pending = status & usart3_readl(up, IMR); + } while (pending); + +out: + spin_unlock(&port->lock); + return ret; +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy + */ +static unsigned int usart3_tx_empty(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + unsigned int ret = 0; + + if (usart3_readl(up, CSR) & USART3_BIT(TXEMPTY)) + ret = TIOCSER_TEMT; + + pr_debug("usart3: tx_empty returned %x\n", ret); + + return ret; +} + +static unsigned int usart3_get_mctrl(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + unsigned int ret = 0; + unsigned int status; + + status = usart3_readl(up, CSR); + if (status & USART3_BIT(DCD)) + ret |= TIOCM_CD; + if (status & USART3_BIT(CTS)) + ret |= TIOCM_CTS; + if (status & USART3_BIT(DSR)) + ret |= TIOCM_DSR; + if (status & USART3_BIT(RI)) + ret |= TIOCM_RI; + + pr_debug("usart3: get_mctrl returned %x\n", ret); + + return ret; +} + +static void usart3_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct usart3_port *up = to_usart3_port(port); + unsigned int control = 0; + + pr_debug("usart3: set_mctrl %x\n", mctrl); + + if (mctrl & TIOCM_RTS) + control |= USART3_BIT(RTSEN); + else + control |= USART3_BIT(RTSDIS); + + if (mctrl & TIOCM_DTR) + control |= USART3_BIT(DTREN); + else + control |= USART3_BIT(DTRDIS); + + usart3_writel(up, CR, control); +} + +static void usart3_break_ctl(struct uart_port *port, int break_state) +{ + struct usart3_port *up = to_usart3_port(port); + + pr_debug("usart3: break_ctl %u\n", break_state); + if (break_state != 0) + usart3_writel(up, CR, USART3_BIT(STTBRK)); + else + usart3_writel(up, CR, USART3_BIT(STPBRK)); +} + +static int usart3_startup(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + int ret; + + pr_debug("usart3: startup\n"); + + up->break_active = 0; + + /* Set up interrupt handler */ + ret = request_irq(port->irq, usart3_interrupt, 0, + port->info->tty->name, port); + if (ret) { + printk(KERN_ERR "usart3: Unable to request irq %d\n", + port->irq); + return ret; + } + + up->rx_dma = dma_map_single(port->dev, up->rx_buffer, + 2 * RX_BUFFER_SIZE, DMA_FROM_DEVICE); + up->tx_dma = dma_map_single(port->dev, port->info->xmit.buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + /* Initialize the PDC for RX (TX is done in start_tx) */ + up->rx_tail = 0; + usart3_writel(up, RPR, up->rx_dma); + usart3_writel(up, RCR, RX_BUFFER_SIZE); + usart3_writel(up, RNPR, up->rx_dma + RX_BUFFER_SIZE); + usart3_writel(up, RNCR, RX_BUFFER_SIZE); + usart3_writel(up, PTCR, USART3_BIT(RXTEN)); + + /* Reset DMA state */ + usart3_writel(up, TNCR, 0); + usart3_writel(up, TCR, 0); + up->tx_dma_head = port->info->xmit.tail; + + /* + * Set a suitable timeout. 2000 bit periods corresponds to + * about 17 ms at 115200 bps + */ + usart3_writel(up, RTOR, 2000); + + /* Reset and enable receiver and transmitter */ + usart3_writel(up, CR, (USART3_BIT(RSTRX) + | USART3_BIT(RSTTX) + | USART3_BIT(RSTSTA))); + usart3_writel(up, CR, (USART3_BIT(RXEN) + | USART3_BIT(TXEN))); + + /* Enable timeout, end of rx, break and error interrupts */ + usart3_writel(up, IER, (USART3_BIT(TIMEOUT) + | USART3_BIT(ENDRX) + | USART3_BIT(RXBRK) + | USART3_BIT(OVRE) + | USART3_BIT(FRAME) + | USART3_BIT(PARE))); + + /* Arm the timeout counter */ + usart3_writel(up, CR, USART3_BIT(STTTO)); + + return 0; +} + +static void usart3_shutdown(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + + pr_debug("usart3: shutdown\n"); + + /* Disable all interrupts and reset any error flags */ + usart3_writel(up, IDR, -1L); + usart3_writel(up, CR, USART3_BIT(RSTSTA)); + + dma_unmap_single(port->dev, up->rx_dma, 2 * RX_BUFFER_SIZE, + DMA_FROM_DEVICE); + dma_unmap_single(port->dev, up->tx_dma, UART_XMIT_SIZE, + DMA_TO_DEVICE); + + free_irq(port->irq, port); +} + +static void usart3_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + struct usart3_port *up = to_usart3_port(port); + unsigned int baud, quot, mode = 0; + unsigned int imr, flags; + + pr_debug("usart3: set_termios\n"); + + switch (termios->c_cflag & CSIZE) { + case CS5: + mode |= USART3_BF(CHRL, USART3_CHRL_5); + break; + case CS6: + mode |= USART3_BF(CHRL, USART3_CHRL_6); + break; + case CS7: + mode |= USART3_BF(CHRL, USART3_CHRL_7); + break; + default: + mode |= USART3_BF(CHRL, USART3_CHRL_8); + break; + } + + if (termios->c_cflag & CSTOPB) + mode |= USART3_BF(NBSTOP, USART3_NBSTOP_2); + + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + mode |= USART3_BF(PAR, USART3_PAR_ODD); + else + mode |= USART3_BF(PAR, USART3_PAR_EVEN); + } else { + mode |= USART3_BF(PAR, USART3_PAR_NONE); + } + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + + /* Bits to ignore, timeout, etc. TBD */ + + /* + * Save and disable interrupts + */ + spin_lock_irqsave(&port->lock, flags); + imr = usart3_readl(up, IMR); + usart3_writel(up, IDR, -1L); + spin_unlock_irqrestore(&port->lock, flags); + + /* + * Make sure transmitter is empty. If BRGR == 0, it is safest + * to do a reset, since waiting for the transmitter to be + * empty will take forever. + */ + if (usart3_readl(up, BRGR) != 0) { + while (!(usart3_readl(up, CSR) & USART3_BIT(TXRDY))) + barrier(); + } else { + usart3_writel(up, CR, (USART3_BIT(RSTTX) + | USART3_BIT(RSTRX))); + } + + pr_debug("usart3: Setting BRGR to %u (baud rate %u)...\n", quot, baud); + + /* Disable receiver and transmitter */ + usart3_writel(up, CR, (USART3_BIT(TXDIS) + | USART3_BIT(RXDIS))); + + /* Set the parity, stop bits and data size */ + usart3_writel(up, MR, mode); + + /* Set the baud rate and enable receiver and transmitter */ + usart3_writel(up, BRGR, quot); + usart3_writel(up, CR, (USART3_BIT(TXEN) + | USART3_BIT(RXEN))); + + /* Restore interrupts */ + usart3_writel(up, IER, imr); +} + +static const char *usart3_type(struct uart_port *port) +{ + return "USART3"; +} + +static void usart3_release_port(struct uart_port *port) +{ + pr_debug("usart3: release_port\n"); + iounmap(port->membase); + port->flags |= UPF_IOREMAP; +} + +static int usart3_request_port(struct uart_port *port) +{ + struct usart3_port *up = to_usart3_port(port); + + /* TODO: remove this */ + pr_debug("usart3: request_port\n"); + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, up->mapsize); + up->regs = port->membase; + port->flags &= ~UPF_IOREMAP; + } + return 0; +} + +static void usart3_config_port(struct uart_port *port, int flags) +{ + pr_debug("usart3: config_port\n"); + if (flags & UART_CONFIG_TYPE) { + if (usart3_request_port(port) == 0) + port->type = PORT_USART3; + } +} + +static int usart3_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_USART3) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)port->mapbase != ser->iomem_base) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + + pr_debug("usart3_verify_port returned %d\n", ret); + + return ret; +} + +static struct uart_ops usart3_pops = { + .tx_empty = usart3_tx_empty, + .set_mctrl = usart3_set_mctrl, + .get_mctrl = usart3_get_mctrl, + .stop_tx = usart3_stop_tx, + .start_tx = usart3_start_tx, + .stop_rx = usart3_stop_rx, + .enable_ms = usart3_enable_ms, + .break_ctl = usart3_break_ctl, + .startup = usart3_startup, + .shutdown = usart3_shutdown, + .flush_buffer = usart3_flush_buffer, + .set_termios = usart3_set_termios, + .type = usart3_type, + .release_port = usart3_release_port, + .request_port = usart3_request_port, + .config_port = usart3_config_port, + .verify_port = usart3_verify_port, +}; + +static int __devinit initialize_port(struct usart3_port *up, + struct platform_device *pdev) +{ + struct uart_port *port = &up->uart; + struct resource *regs; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + spin_lock_init(&port->lock); + + up->mck = clk_get(&pdev->dev, "usart"); + if (IS_ERR(up->mck)) + return PTR_ERR(up->mck); + clk_enable(up->mck); + + port->mapbase = regs->start; + up->mapsize = regs->end - regs->start + 1; + port->irq = platform_get_irq(pdev, 0); + + port->uartclk = clk_get_rate(up->mck); + port->iotype = SERIAL_IO_MEM; + port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + port->ops = &usart3_pops; + port->line = pdev->id; + port->dev = &pdev->dev; + + return 0; +} + +static struct usart3_port usart3_ports[NR_PORTS]; + +#ifdef CONFIG_SERIAL_ATMEL_CONSOLE + +static void usart3_console_write(struct console *console, const char *string, + unsigned int len) +{ + struct usart3_port *up = &usart3_ports[console->index]; + unsigned int imr, i; + unsigned long flags, ptsr; + + /* + * Save IMR, then disable interrupts + */ + local_irq_save(flags); + imr = usart3_readl(up, IMR); + usart3_writel(up, IDR, ~0UL); + local_irq_restore(flags); + + /* + * Save PDC state and disable PDC transmission + */ + ptsr = usart3_readl(up, PTSR); + usart3_writel(up, PTCR, USART3_BIT(TXTDIS)); + + /* + * Now, do each character + */ + for (i = 0; i < len; i++, string++) { + char c = *string; + + /* + * If we're sending LF, send CR first... + */ + if (c == '\n') { + while (!(usart3_readl(up, CSR) + & USART3_BIT(TXRDY))) + ; + usart3_writel(up, THR, '\r'); + } + while (!(usart3_readl(up, CSR) & USART3_BIT(TXRDY))) + ; + usart3_writel(up, THR, c); + } + + /* + * Wait for transmitter to become empty and restore the IMR + * and PDC state. + */ + while (!(usart3_readl(up, CSR) & USART3_BIT(TXRDY))) + ; + + usart3_writel(up, PTCR, ptsr & USART3_BIT(TXTEN)); + usart3_writel(up, IER, imr); +} + +static int __init usart3_console_setup(struct console *console, + char *options) +{ + struct platform_device *pdev; + struct usart3_port *up; + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (console->index >= NR_PORTS) { + printk(KERN_ERR + "Can't use USART%u for console: index >= NR_PORTS\n", + console->index); + return -ENODEV; + } + + pdev = at91_default_console_device; + if (!pdev) + return -ENXIO; + + up = &usart3_ports[console->index]; + port = &up->uart; + + ret = initialize_port(up, pdev); + if (ret) + return ret; + + port->membase = ioremap(port->mapbase, up->mapsize); + ret = -ENOMEM; + if (!port->membase) + goto out_disable_clk; + + up->regs = port->membase; + + /* Set a fixed baud rate for now */ + usart3_writel(up, BRGR, 2); + + /* Make sure all interrupts are disabled and reset/enable the USART */ + usart3_writel(up, IDR, -1L); + usart3_writel(up, CR, (USART3_BIT(RSTRX) + | USART3_BIT(RSTTX) + | USART3_BIT(RSTSTA))); + usart3_writel(up, CR, (USART3_BIT(RXEN) + | USART3_BIT(TXEN))); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, console, baud, parity, bits, flow); + +out_disable_clk: + clk_disable(up->mck); + clk_put(up->mck); + return ret; +} + +static struct uart_driver usart3_reg; +static struct console usart3_console = { + .name = "ttyUS", + .write = usart3_console_write, + .device = uart_console_device, + .setup = usart3_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &usart3_reg, +}; + +static int __init usart3_console_init(void) +{ + register_console(&usart3_console); + return 0; +} +console_initcall(usart3_console_init); + +#define USART3_CONSOLE &usart3_console + +#else +#define USART3_CONSOLE NULL +#endif + +static struct uart_driver usart3_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyUS", + .major = SERIAL_USART3_MAJOR, + .minor = MINOR_START, + .nr = NR_PORTS, + .cons = USART3_CONSOLE, +}; + +static int usart3_serial_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct usart3_port *port = platform_get_drvdata(pdev); + int retval = 0; + + if (port) + retval = uart_suspend_port(&usart3_reg, &port->uart); + + return retval; +} + +static int usart3_serial_resume(struct platform_device *pdev) +{ + struct usart3_port *port = platform_get_drvdata(pdev); + int retval = 0; + + if (port) + retval = uart_resume_port(&usart3_reg, &port->uart); + + return retval; +} + +static int __devinit usart3_serial_probe(struct platform_device *pdev) +{ + struct usart3_port *up; + int ret; + + if (pdev->id >= NR_PORTS) { + printk(KERN_WARNING + "Ignoring USART%u, as NR_PORTS is only %u\n", + pdev->id, NR_PORTS); + return -ENOMEM; + } + + up = &usart3_ports[pdev->id]; + + /* + * If the port has already been set up as a console, we + * shouldn't enable it again. + */ + if (!up->uart.uartclk) { + ret = initialize_port(up, pdev); + if (ret) + goto out; + } + + /* + * The RX buffer must be cacheline aligned. If it's not, + * invalidating the cache could be disastrous... + * + * Fortunately, kmalloc() always returns cache-aligned memory. + */ + ret = -ENOMEM; + up->rx_buffer = kmalloc(2 * RX_BUFFER_SIZE, GFP_KERNEL); + if (!up->rx_buffer) + goto out_disable_clk; + + ret = uart_add_one_port(&usart3_reg, &up->uart); + if (ret) + goto out_free_rx_buffer; + + platform_set_drvdata(pdev, up); + + return 0; + +out_free_rx_buffer: + kfree(up->rx_buffer); +out_disable_clk: + clk_disable(up->mck); + clk_put(up->mck); +out: + return ret; +} + +static int __devexit usart3_serial_remove(struct platform_device *pdev) +{ + struct usart3_port *port = platform_get_drvdata(pdev); + int retval = 0; + + platform_set_drvdata(pdev, NULL); + + if (port) { + retval = uart_remove_one_port(&usart3_reg, &port->uart); + clk_disable(port->mck); + clk_put(port->mck); + kfree(port->rx_buffer); + kfree(port); + } + + return retval; +} + +static struct platform_driver usart3_serial_driver = { + .probe = usart3_serial_probe, + .remove = __devexit_p(usart3_serial_remove), + .suspend = usart3_serial_suspend, + .resume = usart3_serial_resume, + .driver = { + .name = "usart", + }, +}; + +static int __init usart3_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: Atmel USART3 driver\n"); + + ret = uart_register_driver(&usart3_reg); + if (ret) + return ret; + + ret = platform_driver_register(&usart3_serial_driver); + if (ret) + uart_unregister_driver(&usart3_reg); + + return ret; +} + +static void __exit usart3_exit(void) +{ + platform_driver_unregister(&usart3_serial_driver); + uart_unregister_driver(&usart3_reg); +} + +module_init(usart3_init); +module_exit(usart3_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel USART3 serial driver"); +MODULE_AUTHOR("Haavard Skinnemoen "); Index: linux-2.6.18-avr32/drivers/serial/atmel_usart.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-avr32/drivers/serial/atmel_usart.h 2006-11-02 16:37:02.000000000 +0100 @@ -0,0 +1,290 @@ +/* + * Register definitions for USART3 + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_AVR32_USART3_H__ +#define __ASM_AVR32_USART3_H__ + +/* USART3 register offsets */ +#define USART3_CR 0x0000 +#define USART3_MR 0x0004 +#define USART3_IER 0x0008 +#define USART3_IDR 0x000c +#define USART3_IMR 0x0010 +#define USART3_CSR 0x0014 +#define USART3_RHR 0x0018 +#define USART3_THR 0x001c +#define USART3_BRGR 0x0020 +#define USART3_RTOR 0x0024 +#define USART3_TTGR 0x0028 +#define USART3_FIDI 0x0040 +#define USART3_NER 0x0044 +#define USART3_XXR 0x0048 +#define USART3_IFR 0x004c + +/* PDC register offsets */ +#define USART3_RPR 0x100 +#define USART3_RCR 0x104 +#define USART3_TPR 0x108 +#define USART3_TCR 0x10c +#define USART3_RNPR 0x110 +#define USART3_RNCR 0x114 +#define USART3_TNPR 0x118 +#define USART3_TNCR 0x11c +#define USART3_PTCR 0x120 +#define USART3_PTSR 0x124 + +/* Bitfields in CR */ +#define USART3_RSTRX_OFFSET 2 +#define USART3_RSTRX_SIZE 1 +#define USART3_RSTTX_OFFSET 3 +#define USART3_RSTTX_SIZE 1 +#define USART3_RXEN_OFFSET 4 +#define USART3_RXEN_SIZE 1 +#define USART3_RXDIS_OFFSET 5 +#define USART3_RXDIS_SIZE 1 +#define USART3_TXEN_OFFSET 6 +#define USART3_TXEN_SIZE 1 +#define USART3_TXDIS_OFFSET 7 +#define USART3_TXDIS_SIZE 1 +#define USART3_RSTSTA_OFFSET 8 +#define USART3_RSTSTA_SIZE 1 +#define USART3_STTBRK_OFFSET 9 +#define USART3_STTBRK_SIZE 1 +#define USART3_STPBRK_OFFSET 10 +#define USART3_STPBRK_SIZE 1 +#define USART3_STTTO_OFFSET 11 +#define USART3_STTTO_SIZE 1 +#define USART3_SENDA_OFFSET 12 +#define USART3_SENDA_SIZE 1 +#define USART3_RSTIT_OFFSET 13 +#define USART3_RSTIT_SIZE 1 +#define USART3_RSTNACK_OFFSET 14 +#define USART3_RSTNACK_SIZE 1 +#define USART3_RETTO_OFFSET 15 +#define USART3_RETTO_SIZE 1 +#define USART3_DTREN_OFFSET 16 +#define USART3_DTREN_SIZE 1 +#define USART3_DTRDIS_OFFSET 17 +#define USART3_DTRDIS_SIZE 1 +#define USART3_RTSEN_OFFSET 18 +#define USART3_RTSEN_SIZE 1 +#define USART3_RTSDIS_OFFSET 19 +#define USART3_RTSDIS_SIZE 1 +#define USART3_COMM_TX_OFFSET 30 +#define USART3_COMM_TX_SIZE 1 +#define USART3_COMM_RX_OFFSET 31 +#define USART3_COMM_RX_SIZE 1 + +/* Bitfields in MR */ +#define USART3_USART_MODE_OFFSET 0 +#define USART3_USART_MODE_SIZE 4 +#define USART3_USCLKS_OFFSET 4 +#define USART3_USCLKS_SIZE 2 +#define USART3_CHRL_OFFSET 6 +#define USART3_CHRL_SIZE 2 +#define USART3_SYNC_OFFSET 8 +#define USART3_SYNC_SIZE 1 +#define USART3_PAR_OFFSET 9 +#define USART3_PAR_SIZE 3 +#define USART3_NBSTOP_OFFSET 12 +#define USART3_NBSTOP_SIZE 2 +#define USART3_CHMODE_OFFSET 14 +#define USART3_CHMODE_SIZE 2 +#define USART3_MSBF_OFFSET 16 +#define USART3_MSBF_SIZE 1 +#define USART3_MODE9_OFFSET 17 +#define USART3_MODE9_SIZE 1 +#define USART3_CLKO_OFFSET 18 +#define USART3_CLKO_SIZE 1 +#define USART3_OVER_OFFSET 19 +#define USART3_OVER_SIZE 1 +#define USART3_INACK_OFFSET 20 +#define USART3_INACK_SIZE 1 +#define USART3_DSNACK_OFFSET 21 +#define USART3_DSNACK_SIZE 1 +#define USART3_MAX_ITERATION_OFFSET 24 +#define USART3_MAX_ITERATION_SIZE 3 +#define USART3_FILTER_OFFSET 28 +#define USART3_FILTER_SIZE 1 + +/* Bitfields in CSR */ +#define USART3_RXRDY_OFFSET 0 +#define USART3_RXRDY_SIZE 1 +#define USART3_TXRDY_OFFSET 1 +#define USART3_TXRDY_SIZE 1 +#define USART3_RXBRK_OFFSET 2 +#define USART3_RXBRK_SIZE 1 +#define USART3_ENDRX_OFFSET 3 +#define USART3_ENDRX_SIZE 1 +#define USART3_ENDTX_OFFSET 4 +#define USART3_ENDTX_SIZE 1 +#define USART3_OVRE_OFFSET 5 +#define USART3_OVRE_SIZE 1 +#define USART3_FRAME_OFFSET 6 +#define USART3_FRAME_SIZE 1 +#define USART3_PARE_OFFSET 7 +#define USART3_PARE_SIZE 1 +#define USART3_TIMEOUT_OFFSET 8 +#define USART3_TIMEOUT_SIZE 1 +#define USART3_TXEMPTY_OFFSET 9 +#define USART3_TXEMPTY_SIZE 1 +#define USART3_ITERATION_OFFSET 10 +#define USART3_ITERATION_SIZE 1 +#define USART3_TXBUFE_OFFSET 11 +#define USART3_TXBUFE_SIZE 1 +#define USART3_RXBUFF_OFFSET 12 +#define USART3_RXBUFF_SIZE 1 +#define USART3_NACK_OFFSET 13 +#define USART3_NACK_SIZE 1 +#define USART3_RIIC_OFFSET 16 +#define USART3_RIIC_SIZE 1 +#define USART3_DSRIC_OFFSET 17 +#define USART3_DSRIC_SIZE 1 +#define USART3_DCDIC_OFFSET 18 +#define USART3_DCDIC_SIZE 1 +#define USART3_CTSIC_OFFSET 19 +#define USART3_CTSIC_SIZE 1 +#define USART3_RI_OFFSET 20 +#define USART3_RI_SIZE 1 +#define USART3_DSR_OFFSET 21 +#define USART3_DSR_SIZE 1 +#define USART3_DCD_OFFSET 22 +#define USART3_DCD_SIZE 1 +#define USART3_CTS_OFFSET 23 +#define USART3_CTS_SIZE 1 + +/* Bitfields in RHR */ +#define USART3_RXCHR_OFFSET 0 +#define USART3_RXCHR_SIZE 9 + +/* Bitfields in THR */ +#define USART3_TXCHR_OFFSET 0 +#define USART3_TXCHR_SIZE 9 + +/* Bitfields in BRGR */ +#define USART3_CD_OFFSET 0 +#define USART3_CD_SIZE 16 + +/* Bitfields in RTOR */ +#define USART3_TO_OFFSET 0 +#define USART3_TO_SIZE 16 + +/* Bitfields in TTGR */ +#define USART3_TG_OFFSET 0 +#define USART3_TG_SIZE 8 + +/* Bitfields in FIDI */ +#define USART3_FI_DI_RATIO_OFFSET 0 +#define USART3_FI_DI_RATIO_SIZE 11 + +/* Bitfields in NER */ +#define USART3_NB_ERRORS_OFFSET 0 +#define USART3_NB_ERRORS_SIZE 8 + +/* Bitfields in XXR */ +#define USART3_XOFF_OFFSET 0 +#define USART3_XOFF_SIZE 8 +#define USART3_XON_OFFSET 8 +#define USART3_XON_SIZE 8 + +/* Bitfields in IFR */ +#define USART3_IRDA_FILTER_OFFSET 0 +#define USART3_IRDA_FILTER_SIZE 8 + +/* Bitfields in PTCR/PTSR (PDC) */ +#define USART3_RXTEN_OFFSET 0 +#define USART3_RXTEN_SIZE 1 +#define USART3_RXTDIS_OFFSET 1 +#define USART3_RXTDIS_SIZE 1 +#define USART3_TXTEN_OFFSET 8 +#define USART3_TXTEN_SIZE 1 +#define USART3_TXTDIS_OFFSET 9 +#define USART3_TXTDIS_SIZE 1 + +/* Constants for USART_MODE */ +#define USART3_USART_MODE_NORMAL 0 +#define USART3_USART_MODE_RS485 1 +#define USART3_USART_MODE_HARDWARE 2 +#define USART3_USART_MODE_MODEM 3 +#define USART3_USART_MODE_ISO7816_T0 4 +#define USART3_USART_MODE_ISO7816_T1 6 +#define USART3_USART_MODE_IRDA 8 + +/* Constants for USCLKS */ +#define USART3_USCLKS_MCK 0 +#define USART3_USCLKS_MCK_DIV 1 +#define USART3_USCLKS_SCK 3 + +/* Constants for CHRL */ +#define USART3_CHRL_5 0 +#define USART3_CHRL_6 1 +#define USART3_CHRL_7 2 +#define USART3_CHRL_8 3 + +/* Constants for PAR */ +#define USART3_PAR_EVEN 0 +#define USART3_PAR_ODD 1 +#define USART3_PAR_SPACE 2 +#define USART3_PAR_MARK 3 +#define USART3_PAR_NONE 4 +#define USART3_PAR_MULTI 6 + +/* Constants for NBSTOP */ +#define USART3_NBSTOP_1 0 +#define USART3_NBSTOP_1_5 1 +#define USART3_NBSTOP_2 2 + +/* Constants for CHMODE */ +#define USART3_CHMODE_NORMAL 0 +#define USART3_CHMODE_ECHO 1 +#define USART3_CHMODE_LOCAL_LOOP 2 +#define USART3_CHMODE_REMOTE_LOOP 3 + +/* Constants for MSBF */ +#define USART3_MSBF_LSBF 0 +#define USART3_MSBF_MSBF 1 + +/* Constants for OVER */ +#define USART3_OVER_X16 0 +#define USART3_OVER_X8 1 + +/* Constants for CD */ +#define USART3_CD_DISABLE 0 +#define USART3_CD_BYPASS 1 + +/* Constants for TO */ +#define USART3_TO_DISABLE 0 + +/* Constants for TG */ +#define USART3_TG_DISABLE 0 + +/* Constants for FI_DI_RATIO */ +#define USART3_FI_DI_RATIO_DISABLE 0 + +/* Bit manipulation macros */ +#define USART3_BIT(name) \ + (1 << USART3_##name##_OFFSET) +#define USART3_BF(name,value) \ + (((value) & ((1 << USART3_##name##_SIZE) - 1)) \ + << USART3_##name##_OFFSET) +#define USART3_BFEXT(name,value) \ + (((value) >> USART3_##name##_OFFSET) \ + & ((1 << USART3_##name##_SIZE) - 1)) +#define USART3_BFINS(name,value,old) \ + (((old) & ~(((1 << USART3_##name##_SIZE) - 1) \ + << USART3_##name##_OFFSET)) \ + | USART3_BF(name,value)) + +/* Register access macros */ +#define usart3_readl(port,reg) \ + __raw_readl((port)->regs + USART3_##reg) +#define usart3_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + USART3_##reg) + +#endif /* __ASM_AVR32_USART3_H__ */