aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/atmel_serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/atmel_serial.c')
-rw-r--r--drivers/tty/serial/atmel_serial.c202
1 files changed, 146 insertions, 56 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 4d848a29e223..4e959c43f680 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -47,6 +47,7 @@
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/irq.h>
+#include <linux/suspend.h>
#include <asm/io.h>
#include <asm/ioctls.h>
@@ -173,6 +174,12 @@ struct atmel_uart_port {
bool ms_irq_enabled;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
+
+ bool suspended;
+ unsigned int pending;
+ unsigned int pending_status;
+ spinlock_t lock_suspended;
+
int (*prepare_rx)(struct uart_port *port);
int (*prepare_tx)(struct uart_port *port);
void (*schedule_rx)(struct uart_port *port);
@@ -341,13 +348,37 @@ static u_int atmel_tx_empty(struct uart_port *port)
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
- unsigned int mode;
+ unsigned int mode = UART_GET_MR(port);
+ unsigned int rts_paused, rts_ready;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ /* override mode to RS485 if needed, otherwise keep the current mode */
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ if ((port->rs485.delay_rts_after_send) > 0)
+ UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
+ mode &= ~ATMEL_US_USMODE;
+ mode |= ATMEL_US_USMODE_RS485;
+ }
+
+ /* set the RTS line state according to the mode */
+ if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+ /* force RTS line to high level */
+ rts_paused = ATMEL_US_RTSEN;
+
+ /* give the control of the RTS line back to the hardware */
+ rts_ready = ATMEL_US_RTSDIS;
+ } else {
+ /* force RTS line to high level */
+ rts_paused = ATMEL_US_RTSDIS;
+
+ /* force RTS line to low level */
+ rts_ready = ATMEL_US_RTSEN;
+ }
+
if (mctrl & TIOCM_RTS)
- control |= ATMEL_US_RTSEN;
+ control |= rts_ready;
else
- control |= ATMEL_US_RTSDIS;
+ control |= rts_paused;
if (mctrl & TIOCM_DTR)
control |= ATMEL_US_DTREN;
@@ -359,23 +390,12 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
mctrl_gpio_set(atmel_port->gpios, mctrl);
/* Local loopback mode? */
- mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
+ mode &= ~ATMEL_US_CHMODE;
if (mctrl & TIOCM_LOOP)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
- /* Resetting serial mode to RS232 (0x0) */
- mode &= ~ATMEL_US_USMODE;
-
- if (port->rs485.flags & SER_RS485_ENABLED) {
- dev_dbg(port->dev, "Setting UART to RS485\n");
- if ((port->rs485.delay_rts_after_send) > 0)
- UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
- mode |= ATMEL_US_USMODE_RS485;
- } else {
- dev_dbg(port->dev, "Setting UART to RS232\n");
- }
UART_PUT_MR(port, mode);
}
@@ -725,7 +745,11 @@ static void atmel_complete_tx_dma(void *arg)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- /* Do we really need this? */
+ /*
+ * xmit is a circular buffer so, if we have just send data from
+ * xmit->tail to the end of xmit->buf, now we have to transmit the
+ * remaining data from the beginning of xmit->buf to xmit->head.
+ */
if (!uart_circ_empty(xmit))
tasklet_schedule(&atmel_port->tasklet);
@@ -784,17 +808,17 @@ static void atmel_tx_dma(struct uart_port *port)
BUG_ON(!sg_dma_len(sg));
desc = dmaengine_prep_slave_sg(chan,
- sg,
- 1,
- DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT |
- DMA_CTRL_ACK);
+ sg,
+ 1,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
if (!desc) {
dev_err(port->dev, "Failed to send via dma!\n");
return;
}
- dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
+ dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
atmel_port->desc_tx = desc;
desc->callback = atmel_complete_tx_dma;
@@ -927,7 +951,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
dma_sync_sg_for_cpu(port->dev,
&atmel_port->sg_rx,
1,
- DMA_DEV_TO_MEM);
+ DMA_FROM_DEVICE);
/*
* ring->head points to the end of data already written by the DMA.
@@ -974,7 +998,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
dma_sync_sg_for_device(port->dev,
&atmel_port->sg_rx,
1,
- DMA_DEV_TO_MEM);
+ DMA_FROM_DEVICE);
/*
* Drop the lock here since it might end up calling
@@ -1012,13 +1036,13 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
/* UART circular rx buffer is an aligned page. */
BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
sg_set_page(&atmel_port->sg_rx,
- virt_to_page(ring->buf),
- ATMEL_SERIAL_RINGSIZE,
- (int)ring->buf & ~PAGE_MASK);
- nent = dma_map_sg(port->dev,
- &atmel_port->sg_rx,
- 1,
- DMA_FROM_DEVICE);
+ virt_to_page(ring->buf),
+ ATMEL_SERIAL_RINGSIZE,
+ (int)ring->buf & ~PAGE_MASK);
+ nent = dma_map_sg(port->dev,
+ &atmel_port->sg_rx,
+ 1,
+ DMA_FROM_DEVICE);
if (!nent) {
dev_dbg(port->dev, "need to release resource of dma\n");
@@ -1047,11 +1071,11 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
* each one is half ring buffer size
*/
desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
- sg_dma_address(&atmel_port->sg_rx),
- sg_dma_len(&atmel_port->sg_rx),
- sg_dma_len(&atmel_port->sg_rx)/2,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
+ sg_dma_address(&atmel_port->sg_rx),
+ sg_dma_len(&atmel_port->sg_rx),
+ sg_dma_len(&atmel_port->sg_rx)/2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
desc->callback = atmel_complete_rx_dma;
desc->callback_param = port;
atmel_port->desc_rx = desc;
@@ -1162,12 +1186,15 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- unsigned int status, pending, pass_counter = 0;
+ unsigned int status, pending, mask, pass_counter = 0;
bool gpio_handled = false;
+ spin_lock(&atmel_port->lock_suspended);
+
do {
status = atmel_get_lines_status(port);
- pending = status & UART_GET_IMR(port);
+ mask = UART_GET_IMR(port);
+ pending = status & mask;
if (!gpio_handled) {
/*
* Dealing with GPIO interrupt
@@ -1189,11 +1216,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
if (!pending)
break;
+ if (atmel_port->suspended) {
+ atmel_port->pending |= pending;
+ atmel_port->pending_status = status;
+ UART_PUT_IDR(port, mask);
+ pm_system_wakeup();
+ break;
+ }
+
atmel_handle_receive(port, pending);
atmel_handle_status(port, pending, status);
atmel_handle_transmit(port, pending);
} while (pass_counter++ < ATMEL_ISR_PASS_LIMIT);
+ spin_unlock(&atmel_port->lock_suspended);
+
return pass_counter ? IRQ_HANDLED : IRQ_NONE;
}
@@ -1725,7 +1762,8 @@ static int atmel_startup(struct uart_port *port)
/*
* Allocate the IRQ
*/
- retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,
+ retval = request_irq(port->irq, atmel_interrupt,
+ IRQF_SHARED | IRQF_COND_SUSPEND,
tty ? tty->name : "atmel_serial", port);
if (retval) {
dev_err(port->dev, "atmel_startup - Can't get irq\n");
@@ -1921,12 +1959,14 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
unsigned long flags;
- unsigned int mode, imr, quot, baud;
+ unsigned int old_mode, mode, imr, quot, baud;
+
+ /* save the current mode register */
+ mode = old_mode = UART_GET_MR(port);
- /* Get current mode register */
- mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
- | ATMEL_US_NBSTOP | ATMEL_US_PAR
- | ATMEL_US_USMODE);
+ /* reset the mode, clock divisor, parity, stop bits and data size */
+ mode &= ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP |
+ ATMEL_US_PAR | ATMEL_US_USMODE);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
quot = uart_get_divisor(port, baud);
@@ -1971,12 +2011,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
} else
mode |= ATMEL_US_PAR_NONE;
- /* hardware handshake (RTS/CTS) */
- if (termios->c_cflag & CRTSCTS)
- mode |= ATMEL_US_USMODE_HWHS;
- else
- mode |= ATMEL_US_USMODE_NORMAL;
-
spin_lock_irqsave(&port->lock, flags);
port->read_status_mask = ATMEL_US_OVRE;
@@ -2020,18 +2054,40 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
- /* Resetting serial mode to RS232 (0x0) */
- mode &= ~ATMEL_US_USMODE;
-
+ /* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
if ((port->rs485.delay_rts_after_send) > 0)
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
+ } else if (termios->c_cflag & CRTSCTS) {
+ /* RS232 with hardware handshake (RTS/CTS) */
+ mode |= ATMEL_US_USMODE_HWHS;
+ } else {
+ /* RS232 without hadware handshake */
+ mode |= ATMEL_US_USMODE_NORMAL;
}
- /* set the parity, stop bits and data size */
+ /* set the mode, clock divisor, parity, stop bits and data size */
UART_PUT_MR(port, mode);
+ /*
+ * when switching the mode, set the RTS line state according to the
+ * new mode, otherwise keep the former state
+ */
+ if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
+ unsigned int rts_state;
+
+ if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+ /* let the hardware control the RTS line */
+ rts_state = ATMEL_US_RTSDIS;
+ } else {
+ /* force RTS line to low level */
+ rts_state = ATMEL_US_RTSEN;
+ }
+
+ UART_PUT_CR(port, rts_state);
+ }
+
/* set the baud rate */
UART_PUT_BRGR(port, quot);
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
@@ -2478,8 +2534,14 @@ static int atmel_serial_suspend(struct platform_device *pdev,
/* we can not wake up if we're running on slow clock */
atmel_port->may_wakeup = device_may_wakeup(&pdev->dev);
- if (atmel_serial_clk_will_stop())
+ if (atmel_serial_clk_will_stop()) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&atmel_port->lock_suspended, flags);
+ atmel_port->suspended = true;
+ spin_unlock_irqrestore(&atmel_port->lock_suspended, flags);
device_set_wakeup_enable(&pdev->dev, 0);
+ }
uart_suspend_port(&atmel_uart, port);
@@ -2490,6 +2552,18 @@ static int atmel_serial_resume(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&atmel_port->lock_suspended, flags);
+ if (atmel_port->pending) {
+ atmel_handle_receive(port, atmel_port->pending);
+ atmel_handle_status(port, atmel_port->pending,
+ atmel_port->pending_status);
+ atmel_handle_transmit(port, atmel_port->pending);
+ atmel_port->pending = 0;
+ }
+ atmel_port->suspended = false;
+ spin_unlock_irqrestore(&atmel_port->lock_suspended, flags);
uart_resume_port(&atmel_uart, port);
device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup);
@@ -2558,6 +2632,8 @@ static int atmel_serial_probe(struct platform_device *pdev)
port->backup_imr = 0;
port->uart.line = ret;
+ spin_lock_init(&port->lock_suspended);
+
ret = atmel_init_gpios(port, &pdev->dev);
if (ret < 0)
dev_err(&pdev->dev, "%s",
@@ -2565,7 +2641,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
ret = atmel_init_port(port, pdev);
if (ret)
- goto err;
+ goto err_clear_bit;
if (!atmel_use_pdc_rx(&port->uart)) {
ret = -ENOMEM;
@@ -2596,6 +2672,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
+ /*
+ * The peripheral clock has been disabled by atmel_init_port():
+ * enable it before accessing I/O registers
+ */
+ clk_prepare_enable(port->clk);
+
if (rs485_enabled) {
UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
@@ -2606,6 +2688,12 @@ static int atmel_serial_probe(struct platform_device *pdev)
*/
atmel_get_ip_name(&port->uart);
+ /*
+ * The peripheral clock can now safely be disabled till the port
+ * is used
+ */
+ clk_disable_unprepare(port->clk);
+
return 0;
err_add_port:
@@ -2616,6 +2704,8 @@ err_alloc_ring:
clk_put(port->clk);
port->clk = NULL;
}
+err_clear_bit:
+ clear_bit(port->uart.line, atmel_ports_in_use);
err:
return ret;
}