diff options
Diffstat (limited to 'drivers/tty/serial/atmel_serial.c')
-rw-r--r-- | drivers/tty/serial/atmel_serial.c | 202 |
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; } |