diff options
-rw-r--r-- | drivers/tty/serial/sc16is7xx.c | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 8247bce5f4ad..f218e24463c9 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c | |||
@@ -301,10 +301,17 @@ struct sc16is7xx_devtype { | |||
301 | int nr_uart; | 301 | int nr_uart; |
302 | }; | 302 | }; |
303 | 303 | ||
304 | #define SC16IS7XX_RECONF_MD (1 << 0) | ||
305 | |||
306 | struct sc16is7xx_one_config { | ||
307 | unsigned int flags; | ||
308 | }; | ||
309 | |||
304 | struct sc16is7xx_one { | 310 | struct sc16is7xx_one { |
305 | struct uart_port port; | 311 | struct uart_port port; |
306 | struct kthread_work tx_work; | 312 | struct kthread_work tx_work; |
307 | struct work_struct md_work; | 313 | struct kthread_work reg_work; |
314 | struct sc16is7xx_one_config config; | ||
308 | }; | 315 | }; |
309 | 316 | ||
310 | struct sc16is7xx_port { | 317 | struct sc16is7xx_port { |
@@ -659,6 +666,24 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) | |||
659 | sc16is7xx_handle_tx(port); | 666 | sc16is7xx_handle_tx(port); |
660 | } | 667 | } |
661 | 668 | ||
669 | static void sc16is7xx_reg_proc(struct kthread_work *ws) | ||
670 | { | ||
671 | struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work); | ||
672 | struct sc16is7xx_one_config config; | ||
673 | unsigned long irqflags; | ||
674 | |||
675 | spin_lock_irqsave(&one->port.lock, irqflags); | ||
676 | config = one->config; | ||
677 | memset(&one->config, 0, sizeof(one->config)); | ||
678 | spin_unlock_irqrestore(&one->port.lock, irqflags); | ||
679 | |||
680 | if (config.flags & SC16IS7XX_RECONF_MD) | ||
681 | sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, | ||
682 | SC16IS7XX_MCR_LOOP_BIT, | ||
683 | (one->port.mctrl & TIOCM_LOOP) ? | ||
684 | SC16IS7XX_MCR_LOOP_BIT : 0); | ||
685 | } | ||
686 | |||
662 | static void sc16is7xx_stop_tx(struct uart_port* port) | 687 | static void sc16is7xx_stop_tx(struct uart_port* port) |
663 | { | 688 | { |
664 | sc16is7xx_port_update(port, SC16IS7XX_IER_REG, | 689 | sc16is7xx_port_update(port, SC16IS7XX_IER_REG, |
@@ -701,21 +726,13 @@ static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) | |||
701 | return TIOCM_DSR | TIOCM_CAR; | 726 | return TIOCM_DSR | TIOCM_CAR; |
702 | } | 727 | } |
703 | 728 | ||
704 | static void sc16is7xx_md_proc(struct work_struct *ws) | ||
705 | { | ||
706 | struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work); | ||
707 | |||
708 | sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, | ||
709 | SC16IS7XX_MCR_LOOP_BIT, | ||
710 | (one->port.mctrl & TIOCM_LOOP) ? | ||
711 | SC16IS7XX_MCR_LOOP_BIT : 0); | ||
712 | } | ||
713 | |||
714 | static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) | 729 | static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) |
715 | { | 730 | { |
731 | struct sc16is7xx_port *s = dev_get_drvdata(port->dev); | ||
716 | struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); | 732 | struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); |
717 | 733 | ||
718 | schedule_work(&one->md_work); | 734 | one->config.flags |= SC16IS7XX_RECONF_MD; |
735 | queue_kthread_work(&s->kworker, &one->reg_work); | ||
719 | } | 736 | } |
720 | 737 | ||
721 | static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) | 738 | static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) |
@@ -1132,10 +1149,9 @@ static int sc16is7xx_probe(struct device *dev, | |||
1132 | sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, | 1149 | sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, |
1133 | SC16IS7XX_EFCR_RXDISABLE_BIT | | 1150 | SC16IS7XX_EFCR_RXDISABLE_BIT | |
1134 | SC16IS7XX_EFCR_TXDISABLE_BIT); | 1151 | SC16IS7XX_EFCR_TXDISABLE_BIT); |
1135 | /* Initialize queue for start TX */ | 1152 | /* Initialize kthread work structs */ |
1136 | init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc); | 1153 | init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc); |
1137 | /* Initialize queue for changing mode */ | 1154 | init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc); |
1138 | INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc); | ||
1139 | /* Register port */ | 1155 | /* Register port */ |
1140 | uart_add_one_port(&s->uart, &s->p[i].port); | 1156 | uart_add_one_port(&s->uart, &s->p[i].port); |
1141 | /* Go to suspend mode */ | 1157 | /* Go to suspend mode */ |
@@ -1180,7 +1196,6 @@ static int sc16is7xx_remove(struct device *dev) | |||
1180 | #endif | 1196 | #endif |
1181 | 1197 | ||
1182 | for (i = 0; i < s->uart.nr; i++) { | 1198 | for (i = 0; i < s->uart.nr; i++) { |
1183 | cancel_work_sync(&s->p[i].md_work); | ||
1184 | uart_remove_one_port(&s->uart, &s->p[i].port); | 1199 | uart_remove_one_port(&s->uart, &s->p[i].port); |
1185 | sc16is7xx_power(&s->p[i].port, 0); | 1200 | sc16is7xx_power(&s->p[i].port, 0); |
1186 | } | 1201 | } |