diff options
| author | Johannes Thumshirn <johannes.thumshirn@men.de> | 2015-01-19 01:44:41 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-02-02 13:11:27 -0500 |
| commit | 01ba8d6af4b76aee99a8ae3a76748c8c36db49f3 (patch) | |
| tree | 320426c9174014d2d23e9bef3c6fcb5bfd2020c3 /drivers/tty | |
| parent | b164c9721e3ea4c08a4738cd4d0538bbb0c24419 (diff) | |
tty: serial: men_z135_uart: Fix driver for changes in hardware
16z135 IP Core has changed so the driver needs to be updated to respect
these changes. The following changes have been made:
* Don't invert the 16z135 modem status register when reading.
* Add module parameter to configure the (baud rate dependent) RX timeout.
Character timeout in seconds = (timeout_reg * baud_reg * 4)/freq_reg.
* Enable the handling of UART core's automatic flow control feature.
When AFE is active disable generation of modem status IRQs.
* Rework the handling of IRQs to be conform with newer FPGA versions and
take precautions not to miss an interrupt because of the destructive read
of the IIR register.
* Correct men_z135_handle_modem_status(), MSR is stat_reg[15:8] not
stat_reg[7:0]
* Correct calling of uart_handle_{dcd,cts}_change()
* Reset CLOCAL when CRTSCTS is set
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@men.de>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
| -rw-r--r-- | drivers/tty/serial/men_z135_uart.c | 155 |
1 files changed, 109 insertions, 46 deletions
diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 517cd073dc08..35c55505b3eb 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c | |||
| @@ -23,7 +23,6 @@ | |||
| 23 | #define MEN_Z135_MAX_PORTS 12 | 23 | #define MEN_Z135_MAX_PORTS 12 |
| 24 | #define MEN_Z135_BASECLK 29491200 | 24 | #define MEN_Z135_BASECLK 29491200 |
| 25 | #define MEN_Z135_FIFO_SIZE 1024 | 25 | #define MEN_Z135_FIFO_SIZE 1024 |
| 26 | #define MEN_Z135_NUM_MSI_VECTORS 2 | ||
| 27 | #define MEN_Z135_FIFO_WATERMARK 1020 | 26 | #define MEN_Z135_FIFO_WATERMARK 1020 |
| 28 | 27 | ||
| 29 | #define MEN_Z135_STAT_REG 0x0 | 28 | #define MEN_Z135_STAT_REG 0x0 |
| @@ -34,12 +33,11 @@ | |||
| 34 | #define MEN_Z135_CONF_REG 0x808 | 33 | #define MEN_Z135_CONF_REG 0x808 |
| 35 | #define MEN_Z135_UART_FREQ 0x80c | 34 | #define MEN_Z135_UART_FREQ 0x80c |
| 36 | #define MEN_Z135_BAUD_REG 0x810 | 35 | #define MEN_Z135_BAUD_REG 0x810 |
| 37 | #define MENZ135_TIMEOUT 0x814 | 36 | #define MEN_Z135_TIMEOUT 0x814 |
| 38 | 37 | ||
| 39 | #define MEN_Z135_MEM_SIZE 0x818 | 38 | #define MEN_Z135_MEM_SIZE 0x818 |
| 40 | 39 | ||
| 41 | #define IS_IRQ(x) ((x) & 1) | 40 | #define IRQ_ID(x) ((x) & 0x1f) |
| 42 | #define IRQ_ID(x) (((x) >> 1) & 7) | ||
| 43 | 41 | ||
| 44 | #define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */ | 42 | #define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */ |
| 45 | #define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */ | 43 | #define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */ |
| @@ -94,11 +92,11 @@ | |||
| 94 | #define MEN_Z135_LSR_TEXP BIT(6) | 92 | #define MEN_Z135_LSR_TEXP BIT(6) |
| 95 | #define MEN_Z135_LSR_RXFIFOERR BIT(7) | 93 | #define MEN_Z135_LSR_RXFIFOERR BIT(7) |
| 96 | 94 | ||
| 97 | #define MEN_Z135_IRQ_ID_MST 0 | 95 | #define MEN_Z135_IRQ_ID_RLS BIT(0) |
| 98 | #define MEN_Z135_IRQ_ID_TSA 1 | 96 | #define MEN_Z135_IRQ_ID_RDA BIT(1) |
| 99 | #define MEN_Z135_IRQ_ID_RDA 2 | 97 | #define MEN_Z135_IRQ_ID_CTI BIT(2) |
| 100 | #define MEN_Z135_IRQ_ID_RLS 3 | 98 | #define MEN_Z135_IRQ_ID_TSA BIT(3) |
| 101 | #define MEN_Z135_IRQ_ID_CTI 6 | 99 | #define MEN_Z135_IRQ_ID_MST BIT(4) |
| 102 | 100 | ||
| 103 | #define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff) | 101 | #define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff) |
| 104 | 102 | ||
| @@ -118,12 +116,18 @@ static int align; | |||
| 118 | module_param(align, int, S_IRUGO); | 116 | module_param(align, int, S_IRUGO); |
| 119 | MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0"); | 117 | MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0"); |
| 120 | 118 | ||
| 119 | static uint rx_timeout; | ||
| 120 | module_param(rx_timeout, uint, S_IRUGO); | ||
| 121 | MODULE_PARM_DESC(rx_timeout, "RX timeout. " | ||
| 122 | "Timeout in seconds = (timeout_reg * baud_reg * 4) / freq_reg"); | ||
| 123 | |||
| 121 | struct men_z135_port { | 124 | struct men_z135_port { |
| 122 | struct uart_port port; | 125 | struct uart_port port; |
| 123 | struct mcb_device *mdev; | 126 | struct mcb_device *mdev; |
| 124 | unsigned char *rxbuf; | 127 | unsigned char *rxbuf; |
| 125 | u32 stat_reg; | 128 | u32 stat_reg; |
| 126 | spinlock_t lock; | 129 | spinlock_t lock; |
| 130 | bool automode; | ||
| 127 | }; | 131 | }; |
| 128 | #define to_men_z135(port) container_of((port), struct men_z135_port, port) | 132 | #define to_men_z135(port) container_of((port), struct men_z135_port, port) |
| 129 | 133 | ||
| @@ -180,12 +184,16 @@ static inline void men_z135_reg_clr(struct men_z135_port *uart, | |||
| 180 | */ | 184 | */ |
| 181 | static void men_z135_handle_modem_status(struct men_z135_port *uart) | 185 | static void men_z135_handle_modem_status(struct men_z135_port *uart) |
| 182 | { | 186 | { |
| 183 | if (uart->stat_reg & MEN_Z135_MSR_DDCD) | 187 | u8 msr; |
| 188 | |||
| 189 | msr = (uart->stat_reg >> 8) & 0xff; | ||
| 190 | |||
| 191 | if (msr & MEN_Z135_MSR_DDCD) | ||
| 184 | uart_handle_dcd_change(&uart->port, | 192 | uart_handle_dcd_change(&uart->port, |
| 185 | uart->stat_reg & ~MEN_Z135_MSR_DCD); | 193 | msr & MEN_Z135_MSR_DCD); |
| 186 | if (uart->stat_reg & MEN_Z135_MSR_DCTS) | 194 | if (msr & MEN_Z135_MSR_DCTS) |
| 187 | uart_handle_cts_change(&uart->port, | 195 | uart_handle_cts_change(&uart->port, |
| 188 | uart->stat_reg & ~MEN_Z135_MSR_CTS); | 196 | msr & MEN_Z135_MSR_CTS); |
| 189 | } | 197 | } |
| 190 | 198 | ||
| 191 | static void men_z135_handle_lsr(struct men_z135_port *uart) | 199 | static void men_z135_handle_lsr(struct men_z135_port *uart) |
| @@ -322,7 +330,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart) | |||
| 322 | 330 | ||
| 323 | txfree = MEN_Z135_FIFO_WATERMARK - txc; | 331 | txfree = MEN_Z135_FIFO_WATERMARK - txc; |
| 324 | if (txfree <= 0) { | 332 | if (txfree <= 0) { |
| 325 | pr_err("Not enough room in TX FIFO have %d, need %d\n", | 333 | dev_err(&uart->mdev->dev, |
| 334 | "Not enough room in TX FIFO have %d, need %d\n", | ||
| 326 | txfree, qlen); | 335 | txfree, qlen); |
| 327 | goto irq_en; | 336 | goto irq_en; |
| 328 | } | 337 | } |
| @@ -373,43 +382,54 @@ out: | |||
| 373 | * @irq: The IRQ number | 382 | * @irq: The IRQ number |
| 374 | * @data: Pointer to UART port | 383 | * @data: Pointer to UART port |
| 375 | * | 384 | * |
| 376 | * Check IIR register to see which tasklet to start. | 385 | * Check IIR register to find the cause of the interrupt and handle it. |
| 386 | * It is possible that multiple interrupts reason bits are set and reading | ||
| 387 | * the IIR is a destructive read, so we always need to check for all possible | ||
| 388 | * interrupts and handle them. | ||
| 377 | */ | 389 | */ |
| 378 | static irqreturn_t men_z135_intr(int irq, void *data) | 390 | static irqreturn_t men_z135_intr(int irq, void *data) |
| 379 | { | 391 | { |
| 380 | struct men_z135_port *uart = (struct men_z135_port *)data; | 392 | struct men_z135_port *uart = (struct men_z135_port *)data; |
| 381 | struct uart_port *port = &uart->port; | 393 | struct uart_port *port = &uart->port; |
| 394 | bool handled = false; | ||
| 395 | unsigned long flags; | ||
| 382 | int irq_id; | 396 | int irq_id; |
| 383 | 397 | ||
| 384 | uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); | 398 | uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); |
| 385 | /* IRQ pending is low active */ | ||
| 386 | if (IS_IRQ(uart->stat_reg)) | ||
| 387 | return IRQ_NONE; | ||
| 388 | |||
| 389 | irq_id = IRQ_ID(uart->stat_reg); | 399 | irq_id = IRQ_ID(uart->stat_reg); |
| 390 | switch (irq_id) { | 400 | |
| 391 | case MEN_Z135_IRQ_ID_MST: | 401 | if (!irq_id) |
| 392 | men_z135_handle_modem_status(uart); | 402 | goto out; |
| 393 | break; | 403 | |
| 394 | case MEN_Z135_IRQ_ID_TSA: | 404 | spin_lock_irqsave(&port->lock, flags); |
| 395 | men_z135_handle_tx(uart); | 405 | /* It's save to write to IIR[7:6] RXC[9:8] */ |
| 396 | break; | 406 | iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG); |
| 397 | case MEN_Z135_IRQ_ID_CTI: | 407 | |
| 398 | dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n"); | 408 | if (irq_id & MEN_Z135_IRQ_ID_RLS) { |
| 399 | /* Fallthrough */ | ||
| 400 | case MEN_Z135_IRQ_ID_RDA: | ||
| 401 | /* Reading data clears RX IRQ */ | ||
| 402 | men_z135_handle_rx(uart); | ||
| 403 | break; | ||
| 404 | case MEN_Z135_IRQ_ID_RLS: | ||
| 405 | men_z135_handle_lsr(uart); | 409 | men_z135_handle_lsr(uart); |
| 406 | break; | 410 | handled = true; |
| 407 | default: | 411 | } |
| 408 | dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id); | 412 | |
| 409 | return IRQ_NONE; | 413 | if (irq_id & (MEN_Z135_IRQ_ID_RDA | MEN_Z135_IRQ_ID_CTI)) { |
| 414 | if (irq_id & MEN_Z135_IRQ_ID_CTI) | ||
| 415 | dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n"); | ||
| 416 | men_z135_handle_rx(uart); | ||
| 417 | handled = true; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (irq_id & MEN_Z135_IRQ_ID_TSA) { | ||
| 421 | men_z135_handle_tx(uart); | ||
| 422 | handled = true; | ||
| 410 | } | 423 | } |
| 411 | 424 | ||
| 412 | return IRQ_HANDLED; | 425 | if (irq_id & MEN_Z135_IRQ_ID_MST) { |
