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 | ||