diff options
Diffstat (limited to 'drivers/tty/serial/atmel_serial.c')
-rw-r--r-- | drivers/tty/serial/atmel_serial.c | 230 |
1 files changed, 110 insertions, 120 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index edde3eca055d..2cb04137ae78 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -167,7 +167,6 @@ struct atmel_uart_port { struct circ_buf rx_ring; - struct serial_rs485 rs485; /* rs485 settings */ struct mctrl_gpios *gpios; int gpio_irq[UART_GPIO_MAX]; unsigned int tx_done_mask; @@ -290,13 +289,11 @@ static unsigned int atmel_get_lines_status(struct uart_port *port) } /* Enable or disable the rs485 support */ -void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +static int atmel_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485conf) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int mode; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); @@ -306,7 +303,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) /* Resetting serial mode to RS232 (0x0) */ mode &= ~ATMEL_US_USMODE; - atmel_port->rs485 = *rs485conf; + port->rs485 = *rs485conf; if (rs485conf->flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); @@ -327,8 +324,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); - spin_unlock_irqrestore(&port->lock, flags); - + return 0; } /* @@ -372,11 +368,10 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) /* Resetting serial mode to RS232 (0x0) */ mode &= ~ATMEL_US_USMODE; - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); - if ((atmel_port->rs485.delay_rts_after_send) > 0) - UART_PUT_TTGR(port, - atmel_port->rs485.delay_rts_after_send); + 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"); @@ -423,8 +418,8 @@ static void atmel_stop_tx(struct uart_port *port) /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); - if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && - !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) atmel_start_rx(port); } @@ -441,8 +436,8 @@ static void atmel_start_tx(struct uart_port *port) really need this.*/ return; - if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && - !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) atmel_stop_rx(port); /* re-enable PDC transmit */ @@ -807,7 +802,7 @@ static void atmel_tx_dma(struct uart_port *port) atmel_port->cookie_tx = dmaengine_submit(desc); } else { - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + if (port->rs485.flags & SER_RS485_ENABLED) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -862,9 +857,8 @@ static int atmel_prepare_tx_dma(struct uart_port *port) config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; config.dst_addr = port->mapbase + ATMEL_US_THR; - ret = dmaengine_device_control(atmel_port->chan_tx, - DMA_SLAVE_CONFIG, - (unsigned long)&config); + ret = dmaengine_slave_config(atmel_port->chan_tx, + &config); if (ret) { dev_err(port->dev, "DMA tx slave configuration failed\n"); goto chan_err; @@ -880,32 +874,6 @@ chan_err: return -EINVAL; } -static void atmel_flip_buffer_rx_dma(struct uart_port *port, - char *buf, size_t count) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_port *tport = &port->state->port; - - dma_sync_sg_for_cpu(port->dev, - &atmel_port->sg_rx, - 1, - DMA_DEV_TO_MEM); - - tty_insert_flip_string(tport, buf, count); - - dma_sync_sg_for_device(port->dev, - &atmel_port->sg_rx, - 1, - DMA_DEV_TO_MEM); - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); - tty_flip_buffer_push(tport); - spin_lock(&port->lock); -} - static void atmel_complete_rx_dma(void *arg) { struct uart_port *port = arg; @@ -934,11 +902,12 @@ static void atmel_release_rx_dma(struct uart_port *port) static void atmel_rx_from_dma(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_port *tport = &port->state->port; struct circ_buf *ring = &atmel_port->rx_ring; struct dma_chan *chan = atmel_port->chan_rx; struct dma_tx_state state; enum dma_status dmastat; - size_t pending, count; + size_t count; /* Reset the UART timeout early so that we don't miss one */ @@ -953,27 +922,68 @@ static void atmel_rx_from_dma(struct uart_port *port) tasklet_schedule(&atmel_port->tasklet); return; } - /* current transfer size should no larger than dma buffer */ - pending = sg_dma_len(&atmel_port->sg_rx) - state.residue; - BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx)); + + /* CPU claims ownership of RX DMA buffer */ + dma_sync_sg_for_cpu(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); /* - * This will take the chars we have so far, - * ring->head will record the transfer size, only new bytes come - * will insert into the framework. + * ring->head points to the end of data already written by the DMA. + * ring->tail points to the beginning of data to be read by the + * framework. + * The current transfer size should not be larger than the dma buffer + * length. */ - if (pending > ring->head) { - count = pending - ring->head; + ring->head = sg_dma_len(&atmel_port->sg_rx) - state.residue; + BUG_ON(ring->head > sg_dma_len(&atmel_port->sg_rx)); + /* + * At this point ring->head may point to the first byte right after the + * last byte of the dma buffer: + * 0 <= ring->head <= sg_dma_len(&atmel_port->sg_rx) + * + * However ring->tail must always points inside the dma buffer: + * 0 <= ring->tail <= sg_dma_len(&atmel_port->sg_rx) - 1 + * + * Since we use a ring buffer, we have to handle the case + * where head is lower than tail. In such a case, we first read from + * tail to the end of the buffer then reset tail. + */ + if (ring->head < ring->tail) { + count = sg_dma_len(&atmel_port->sg_rx) - ring->tail; + + tty_insert_flip_string(tport, ring->buf + ring->tail, count); + ring->tail = 0; + port->icount.rx += count; + } - atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count); + /* Finally we read data from tail to head */ + if (ring->tail < ring->head) { + count = ring->head - ring->tail; - ring->head += count; - if (ring->head == sg_dma_len(&atmel_port->sg_rx)) + tty_insert_flip_string(tport, ring->buf + ring->tail, count); + /* Wrap ring->head if needed */ + if (ring->head >= sg_dma_len(&atmel_port->sg_rx)) ring->head = 0; - + ring->tail = ring->head; port->icount.rx += count; } + /* USART retreives ownership of RX DMA buffer */ + dma_sync_sg_for_device(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); + UART_PUT_IER(port, ATMEL_US_TIMEOUT); } @@ -1026,9 +1036,8 @@ static int atmel_prepare_rx_dma(struct uart_port *port) config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; config.src_addr = port->mapbase + ATMEL_US_RHR; - ret = dmaengine_device_control(atmel_port->chan_rx, - DMA_SLAVE_CONFIG, - (unsigned long)&config); + ret = dmaengine_slave_config(atmel_port->chan_rx, + &config); if (ret) { dev_err(port->dev, "DMA rx slave configuration failed\n"); goto chan_err; @@ -1240,8 +1249,8 @@ static void atmel_tx_pdc(struct uart_port *port) /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); } else { - if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && - !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) { + if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1552,7 +1561,7 @@ static int atmel_init_property(struct atmel_uart_port *atmel_port, return 0; } -static void atmel_init_rs485(struct atmel_uart_port *atmel_port, +static void atmel_init_rs485(struct uart_port *port, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1563,7 +1572,7 @@ static void atmel_init_rs485(struct atmel_uart_port *atmel_port, /* rs485 properties */ if (of_property_read_u32_array(np, "rs485-rts-delay", rs485_delay, 2) == 0) { - struct serial_rs485 *rs485conf = &atmel_port->rs485; + struct serial_rs485 *rs485conf = &port->rs485; rs485conf->delay_rts_before_send = rs485_delay[0]; rs485conf->delay_rts_after_send = rs485_delay[1]; @@ -1577,7 +1586,7 @@ static void atmel_init_rs485(struct atmel_uart_port *atmel_port, rs485conf->flags |= SER_RS485_ENABLED; } } else { - atmel_port->rs485 = pdata->rs485; + port->rs485 = pdata->rs485; } } @@ -1802,6 +1811,20 @@ free_irq: } /* + * Flush any TX data submitted for DMA. Called when the TX circular + * buffer is reset. + */ +static void atmel_flush_buffer(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_pdc_tx(port)) { + UART_PUT_TCR(port, 0); + atmel_port->pdc_tx.ofs = 0; + } +} + +/* * Disable the port */ static void atmel_shutdown(struct uart_port *port) @@ -1852,20 +1875,8 @@ static void atmel_shutdown(struct uart_port *port) atmel_free_gpio_irq(port); atmel_port->ms_irq_enabled = false; -} -/* - * Flush any TX data submitted for DMA. Called when the TX circular - * buffer is reset. - */ -static void atmel_flush_buffer(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_use_pdc_tx(port)) { - UART_PUT_TCR(port, 0); - atmel_port->pdc_tx.ofs = 0; - } + atmel_flush_buffer(port); } /* @@ -1911,7 +1922,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int mode, imr, quot, baud; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL @@ -2013,10 +2023,9 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* Resetting serial mode to RS232 (0x0) */ mode &= ~ATMEL_US_USMODE; - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { - if ((atmel_port->rs485.delay_rts_after_send) > 0) - UART_PUT_TTGR(port, - atmel_port->rs485.delay_rts_after_send); + 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; } @@ -2040,13 +2049,20 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&port->lock, flags); } -static void atmel_set_ldisc(struct uart_port *port, int new) +static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios) { - if (new == N_PPS) { + if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; + spin_lock_irq(&port->lock); atmel_enable_ms(port); + spin_unlock_irq(&port->lock); } else { port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { + spin_lock_irq(&port->lock); + atmel_disable_ms(port); + spin_unlock_irq(&port->lock); + } } } @@ -2148,35 +2164,6 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) } #endif -static int -atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) -{ - struct serial_rs485 rs485conf; - - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, - sizeof(rs485conf))) - return -EFAULT; - - atmel_config_rs485(port, &rs485conf); - break; - - case TIOCGRS485: - if (copy_to_user((struct serial_rs485 *) arg, - &(to_atmel_uart_port(port)->rs485), - sizeof(rs485conf))) - return -EFAULT; - break; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - - - static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -2197,7 +2184,6 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, - .ioctl = atmel_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = atmel_poll_get_char, .poll_put_char = atmel_poll_put_char, @@ -2217,7 +2203,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, if (!atmel_init_property(atmel_port, pdev)) atmel_set_ops(port); - atmel_init_rs485(atmel_port, pdev); + atmel_init_rs485(port, pdev); port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; @@ -2226,6 +2212,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; + port->rs485_config = atmel_config_rs485; tasklet_init(&atmel_port->tasklet, atmel_tasklet_func, (unsigned long)port); @@ -2260,7 +2247,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, } /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ - if (atmel_port->rs485.flags & SER_RS485_ENABLED) + if (port->rs485.flags & SER_RS485_ENABLED) atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; else if (atmel_use_pdc_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; @@ -2541,6 +2528,7 @@ static int atmel_serial_probe(struct platform_device *pdev) struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; int ret = -ENODEV; + bool rs485_enabled; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); @@ -2588,6 +2576,8 @@ static int atmel_serial_probe(struct platform_device *pdev) port->rx_ring.buf = data; } + rs485_enabled = port->uart.rs485.flags & SER_RS485_ENABLED; + ret = uart_add_one_port(&atmel_uart, &port->uart); if (ret) goto err_add_port; @@ -2606,7 +2596,7 @@ static int atmel_serial_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); - if (port->rs485.flags & SER_RS485_ENABLED) { + if (rs485_enabled) { UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL); UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); } |