diff options
| author | Richard Genoud <richard.genoud@gmail.com> | 2014-05-13 14:20:45 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-28 15:51:18 -0400 |
| commit | ab5e4e4108ca5d8326cb6b4b3a21b096a002f68f (patch) | |
| tree | 4908be40a0d34a8cd5b1c71b938c7f923cff9a06 /drivers/tty | |
| parent | e0b0baadb7a4509bdcd5ba37d0be61e2c4bb0d48 (diff) | |
tty/serial: at91: add interrupts for modem control lines
Handle CTS/DSR/RI/DCD GPIO interrupts in atmel_serial.
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
| -rw-r--r-- | drivers/tty/serial/atmel_serial.c | 125 |
1 files changed, 122 insertions, 3 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 43ca659c1d4b..3fceae099c44 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c | |||
| @@ -45,6 +45,7 @@ | |||
| 45 | #include <linux/gpio.h> | 45 | #include <linux/gpio.h> |
| 46 | #include <linux/gpio/consumer.h> | 46 | #include <linux/gpio/consumer.h> |
| 47 | #include <linux/err.h> | 47 | #include <linux/err.h> |
| 48 | #include <linux/irq.h> | ||
| 48 | 49 | ||
| 49 | #include <asm/io.h> | 50 | #include <asm/io.h> |
| 50 | #include <asm/ioctls.h> | 51 | #include <asm/ioctls.h> |
| @@ -167,7 +168,9 @@ struct atmel_uart_port { | |||
| 167 | 168 | ||
| 168 | struct serial_rs485 rs485; /* rs485 settings */ | 169 | struct serial_rs485 rs485; /* rs485 settings */ |
| 169 | struct mctrl_gpios *gpios; | 170 | struct mctrl_gpios *gpios; |
| 171 | int gpio_irq[UART_GPIO_MAX]; | ||
| 170 | unsigned int tx_done_mask; | 172 | unsigned int tx_done_mask; |
| 173 | bool ms_irq_enabled; | ||
| 171 | bool is_usart; /* usart or uart */ | 174 | bool is_usart; /* usart or uart */ |
| 172 | struct timer_list uart_timer; /* uart timer */ | 175 | struct timer_list uart_timer; /* uart timer */ |
| 173 | int (*prepare_rx)(struct uart_port *port); | 176 | int (*prepare_rx)(struct uart_port *port); |
| @@ -489,8 +492,38 @@ static void atmel_stop_rx(struct uart_port *port) | |||
| 489 | */ | 492 | */ |
| 490 | static void atmel_enable_ms(struct uart_port *port) | 493 | static void atmel_enable_ms(struct uart_port *port) |
| 491 | { | 494 | { |
| 492 | UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC | 495 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); |
| 493 | | ATMEL_US_DCDIC | ATMEL_US_CTSIC); | 496 | uint32_t ier = 0; |
| 497 | |||
| 498 | /* | ||
| 499 | * Interrupt should not be enabled twice | ||
| 500 | */ | ||
| 501 | if (atmel_port->ms_irq_enabled) | ||
| 502 | return; | ||
| 503 | |||
| 504 | atmel_port->ms_irq_enabled = true; | ||
| 505 | |||
| 506 | if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0) | ||
| 507 | enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]); | ||
| 508 | else | ||
| 509 | ier |= ATMEL_US_CTSIC; | ||
| 510 | |||
| 511 | if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0) | ||
| 512 | enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]); | ||
| 513 | else | ||
| 514 | ier |= ATMEL_US_DSRIC; | ||
| 515 | |||
| 516 | if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0) | ||
| 517 | enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]); | ||
| 518 | else | ||
| 519 | ier |= ATMEL_US_RIIC; | ||
| 520 | |||
| 521 | if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0) | ||
| 522 | enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]); | ||
| 523 | else | ||
| 524 | ier |= ATMEL_US_DCDIC; | ||
| 525 | |||
| 526 | UART_PUT_IER(port, ier); | ||
| 494 | } | 527 | } |
| 495 | 528 | ||
| 496 | /* | 529 | /* |
| @@ -1079,11 +1112,31 @@ atmel_handle_status(struct uart_port *port, unsigned int pending, | |||
| 1079 | static irqreturn_t atmel_interrupt(int irq, void *dev_id) | 1112 | static irqreturn_t atmel_interrupt(int irq, void *dev_id) |
| 1080 | { | 1113 | { |
| 1081 | struct uart_port *port = dev_id; | 1114 | struct uart_port *port = dev_id; |
| 1115 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | ||
| 1082 | unsigned int status, pending, pass_counter = 0; | 1116 | unsigned int status, pending, pass_counter = 0; |
| 1117 | bool gpio_handled = false; | ||
| 1083 | 1118 | ||
| 1084 | do { | 1119 | do { |
| 1085 | status = atmel_get_lines_status(port); | 1120 | status = atmel_get_lines_status(port); |
| 1086 | pending = status & UART_GET_IMR(port); | 1121 | pending = status & UART_GET_IMR(port); |
| 1122 | if (!gpio_handled) { | ||
| 1123 | /* | ||
| 1124 | * Dealing with GPIO interrupt | ||
| 1125 | */ | ||
| 1126 | if (irq == atmel_port->gpio_irq[UART_GPIO_CTS]) | ||
| 1127 | pending |= ATMEL_US_CTSIC; | ||
| 1128 | |||
| 1129 | if (irq == atmel_port->gpio_irq[UART_GPIO_DSR]) | ||
| 1130 | pending |= ATMEL_US_DSRIC; | ||
| 1131 | |||
| 1132 | if (irq == atmel_port->gpio_irq[UART_GPIO_RI]) | ||
| 1133 | pending |= ATMEL_US_RIIC; | ||
| 1134 | |||
| 1135 | if (irq == atmel_port->gpio_irq[UART_GPIO_DCD]) | ||
| 1136 | pending |= ATMEL_US_DCDIC; | ||
| 1137 | |||
| 1138 | gpio_handled = true; | ||
| 1139 | } | ||
| 1087 | if (!pending) | 1140 | if (!pending) |
| 1088 | break; | 1141 | break; |
| 1089 | 1142 | ||
| @@ -1563,6 +1616,45 @@ static void atmel_get_ip_name(struct uart_port *port) | |||
| 1563 | } | 1616 | } |
| 1564 | } | 1617 | } |
| 1565 | 1618 | ||
| 1619 | static void atmel_free_gpio_irq(struct uart_port *port) | ||
| 1620 | { | ||
| 1621 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | ||
| 1622 | enum mctrl_gpio_idx i; | ||
| 1623 | |||
| 1624 | for (i = 0; i < UART_GPIO_MAX; i++) | ||
| 1625 | if (atmel_port->gpio_irq[i] >= 0) | ||
| 1626 | free_irq(atmel_port->gpio_irq[i], port); | ||
| 1627 | } | ||
| 1628 | |||
| 1629 | static int atmel_request_gpio_irq(struct uart_port *port) | ||
| 1630 | { | ||
| 1631 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | ||
| 1632 | int *irq = atmel_port->gpio_irq; | ||
| 1633 | enum mctrl_gpio_idx i; | ||
| 1634 | int err = 0; | ||
| 1635 | |||
| 1636 | for (i = 0; (i < UART_GPIO_MAX) && !err; i++) { | ||
| 1637 | if (irq[i] < 0) | ||
| 1638 | continue; | ||
| 1639 | |||
| 1640 | irq_set_status_flags(irq[i], IRQ_NOAUTOEN); | ||
| 1641 | err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH, | ||
| 1642 | "atmel_serial", port); | ||
| 1643 | if (err) | ||
| 1644 | dev_err(port->dev, "atmel_startup - Can't get %d irq\n", | ||
| 1645 | irq[i]); | ||
| 1646 | } | ||
| 1647 | |||
| 1648 | /* | ||
| 1649 | * If something went wrong, rollback. | ||
| 1650 | */ | ||
| 1651 | while (err && (--i >= 0)) | ||
| 1652 | if (irq[i] >= 0) | ||
| 1653 | free_irq(irq[i], port); | ||
| 1654 | |||
| 1655 | return err; | ||
| 1656 | } | ||
| 1657 | |||
| 1566 | /* | 1658 | /* |
| 1567 | * Perform initialization and enable port for reception | 1659 | * Perform initialization and enable port for reception |
| 1568 | */ | 1660 | */ |
| @@ -1579,6 +1671,7 @@ static int atmel_startup(struct uart_port *port) | |||
| 1579 | * handle an unexpected interrupt | 1671 | * handle an unexpected interrupt |
| 1580 | */ | 1672 | */ |
| 1581 | UART_PUT_IDR(port, -1); | 1673 | UART_PUT_IDR(port, -1); |
| 1674 | atmel_port->ms_irq_enabled = false; | ||
| 1582 | 1675 | ||
| 1583 | /* | 1676 | /* |
| 1584 | * Allocate the IRQ | 1677 | * Allocate the IRQ |
| @@ -1591,6 +1684,13 @@ static int atmel_startup(struct uart_port *port) | |||
| 1591 | } | 1684 | } |
| 1592 | 1685 | ||
| 1593 | /* | 1686 | /* |
| 1687 | * Get the GPIO lines IRQ | ||
| 1688 | */ | ||
| 1689 | retval = atmel_request_gpio_irq(port); | ||
| 1690 | if (retval) | ||
| 1691 | goto free_irq; | ||
| 1692 | |||
| 1693 | /* | ||
| 1594 | * Initialize DMA (if necessary) | 1694 | * Initialize DMA (if necessary) |
| 1595 | */ | 1695 | */ |
| 1596 | atmel_init_property(atmel_port, pdev); | 1696 | atmel_init_property(atmel_port, pdev); |
| @@ -1654,6 +1754,11 @@ static int atmel_startup(struct uart_port *port) | |||
| 1654 | } | 1754 | } |
| 1655 | 1755 | ||
| 1656 | return 0; | 1756 | return 0; |
| 1757 | |||
| 1758 | free_irq: | ||
| 1759 | free_irq(port->irq, port); | ||
| 1760 | |||
| 1761 | return retval; | ||
| 1657 | } | 1762 | } |
| 1658 | 1763 | ||
| 1659 | /* | 1764 | /* |
| @@ -1701,9 +1806,12 @@ static void atmel_shutdown(struct uart_port *port) | |||
| 1701 | atmel_port->rx_ring.tail = 0; | 1806 | atmel_port->rx_ring.tail = 0; |
| 1702 | 1807 | ||
| 1703 | /* | 1808 | /* |
| 1704 | * Free the interrupt | 1809 | * Free the interrupts |
| 1705 | */ | 1810 | */ |
| 1706 | free_irq(port->irq, port); | 1811 | free_irq(port->irq, port); |
| 1812 | atmel_free_gpio_irq(port); | ||
| 1813 | |||
| 1814 | atmel_port->ms_irq_enabled = false; | ||
| 1707 | } | 1815 | } |
| 1708 | 1816 | ||
| 1709 | /* | 1817 | /* |
| @@ -2366,10 +2474,21 @@ static int atmel_serial_resume(struct platform_device *pdev) | |||
| 2366 | 2474 | ||
| 2367 | static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) | 2475 | static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) |
| 2368 | { | 2476 | { |
| 2477 | enum mctrl_gpio_idx i; | ||
| 2478 | struct gpio_desc *gpiod; | ||
| 2479 | |||
| 2369 | p->gpios = mctrl_gpio_init(dev, 0); | 2480 | p->gpios = mctrl_gpio_init(dev, 0); |
| 2370 | if (IS_ERR_OR_NULL(p->gpios)) | 2481 | if (IS_ERR_OR_NULL(p->gpios)) |
| 2371 | return -1; | 2482 | return -1; |
| 2372 | 2483 | ||
| 2484 | for (i = 0; i < UART_GPIO_MAX; i++) { | ||
| 2485 | gpiod = mctrl_gpio_to_gpiod(p->gpios, i); | ||
| 2486 | if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN)) | ||
| 2487 | p->gpio_irq[i] = gpiod_to_irq(gpiod); | ||
| 2488 | else | ||
| 2489 | p->gpio_irq[i] = -EINVAL; | ||
| 2490 | } | ||
| 2491 | |||
| 2373 | return 0; | 2492 | return 0; |
| 2374 | } | 2493 | } |
| 2375 | 2494 | ||
