aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Jiang <djiang@mvista.com>2007-05-06 17:48:50 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:12:50 -0400
commit1733310bb762cb926669f2c10f6f8719bb20ed91 (patch)
treecd3568ef8b75edbe9a21cd3b1a12cbdf0c044d02
parentabb4a2390737867353ebafc012d45f2b03f3f944 (diff)
MPSC serial driver tx locking
The MPSC serial driver assumes that interrupt is always on to pick up the DMA transmit ops that aren't submitted while the DMA engine is active. However when irqs are off for a period of time such as operations under kernel crash dump console messages do not show up due to additional DMA ops are being dropped. This makes console writes to process through all the tx DMAs queued up before submitting a new request. Also, the current locking mechanism does not protect the hardware registers and ring buffer when a printk is done during the serial write operations. The additional per port transmit lock provides a finer granular locking and protects registers being clobbered while printks are nested within UART writes. Signed-off-by: Dave Jiang <djiang@mvista.com> Signed-off-by: Mark A. Greer <mgreer@mvista.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/serial/mpsc.c25
1 files changed, 24 insertions, 1 deletions
diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c
index 3d2fcc57b1ce..d09f2097d5b0 100644
--- a/drivers/serial/mpsc.c
+++ b/drivers/serial/mpsc.c
@@ -183,6 +183,7 @@ struct mpsc_port_info {
183 u8 *txb_p; /* Phys addr of txb */ 183 u8 *txb_p; /* Phys addr of txb */
184 int txr_head; /* Where new data goes */ 184 int txr_head; /* Where new data goes */
185 int txr_tail; /* Where sent data comes off */ 185 int txr_tail; /* Where sent data comes off */
186 spinlock_t tx_lock; /* transmit lock */
186 187
187 /* Mirrored values of regs we can't read (if 'mirror_regs' set) */ 188 /* Mirrored values of regs we can't read (if 'mirror_regs' set) */
188 u32 MPSC_MPCR_m; 189 u32 MPSC_MPCR_m;
@@ -1212,6 +1213,9 @@ mpsc_tx_intr(struct mpsc_port_info *pi)
1212{ 1213{
1213 struct mpsc_tx_desc *txre; 1214 struct mpsc_tx_desc *txre;
1214 int rc = 0; 1215 int rc = 0;
1216 unsigned long iflags;
1217
1218 spin_lock_irqsave(&pi->tx_lock, iflags);
1215 1219
1216 if (!mpsc_sdma_tx_active(pi)) { 1220 if (!mpsc_sdma_tx_active(pi)) {
1217 txre = (struct mpsc_tx_desc *)(pi->txr + 1221 txre = (struct mpsc_tx_desc *)(pi->txr +
@@ -1248,6 +1252,7 @@ mpsc_tx_intr(struct mpsc_port_info *pi)
1248 mpsc_sdma_start_tx(pi); /* start next desc if ready */ 1252 mpsc_sdma_start_tx(pi); /* start next desc if ready */
1249 } 1253 }
1250 1254
1255 spin_unlock_irqrestore(&pi->tx_lock, iflags);
1251 return rc; 1256 return rc;
1252} 1257}
1253 1258
@@ -1338,11 +1343,16 @@ static void
1338mpsc_start_tx(struct uart_port *port) 1343mpsc_start_tx(struct uart_port *port)
1339{ 1344{
1340 struct mpsc_port_info *pi = (struct mpsc_port_info *)port; 1345 struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
1346 unsigned long iflags;
1347
1348 spin_lock_irqsave(&pi->tx_lock, iflags);
1341 1349
1342 mpsc_unfreeze(pi); 1350 mpsc_unfreeze(pi);
1343 mpsc_copy_tx_data(pi); 1351 mpsc_copy_tx_data(pi);
1344 mpsc_sdma_start_tx(pi); 1352 mpsc_sdma_start_tx(pi);
1345 1353
1354 spin_unlock_irqrestore(&pi->tx_lock, iflags);
1355
1346 pr_debug("mpsc_start_tx[%d]\n", port->line); 1356 pr_debug("mpsc_start_tx[%d]\n", port->line);
1347 return; 1357 return;
1348} 1358}
@@ -1625,6 +1635,16 @@ mpsc_console_write(struct console *co, const char *s, uint count)
1625 struct mpsc_port_info *pi = &mpsc_ports[co->index]; 1635 struct mpsc_port_info *pi = &mpsc_ports[co->index];
1626 u8 *bp, *dp, add_cr = 0; 1636 u8 *bp, *dp, add_cr = 0;
1627 int i; 1637 int i;
1638 unsigned long iflags;
1639
1640 spin_lock_irqsave(&pi->tx_lock, iflags);
1641
1642 while (pi->txr_head != pi->txr_tail) {
1643 while (mpsc_sdma_tx_active(pi))
1644 udelay(100);
1645 mpsc_sdma_intr_ack(pi);
1646 mpsc_tx_intr(pi);
1647 }
1628 1648
1629 while (mpsc_sdma_tx_active(pi)) 1649 while (mpsc_sdma_tx_active(pi))
1630 udelay(100); 1650 udelay(100);
@@ -1668,6 +1688,7 @@ mpsc_console_write(struct console *co, const char *s, uint count)
1668 pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1); 1688 pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1);
1669 } 1689 }
1670 1690
1691 spin_unlock_irqrestore(&pi->tx_lock, iflags);
1671 return; 1692 return;
1672} 1693}
1673 1694
@@ -2005,7 +2026,8 @@ mpsc_drv_probe(struct platform_device *dev)
2005 if (!(rc = mpsc_drv_map_regs(pi, dev))) { 2026 if (!(rc = mpsc_drv_map_regs(pi, dev))) {
2006 mpsc_drv_get_platform_data(pi, dev, dev->id); 2027 mpsc_drv_get_platform_data(pi, dev, dev->id);
2007 2028
2008 if (!(rc = mpsc_make_ready(pi))) 2029 if (!(rc = mpsc_make_ready(pi))) {
2030 spin_lock_init(&pi->tx_lock);
2009 if (!(rc = uart_add_one_port(&mpsc_reg, 2031 if (!(rc = uart_add_one_port(&mpsc_reg,
2010 &pi->port))) 2032 &pi->port)))
2011 rc = 0; 2033 rc = 0;
@@ -2014,6 +2036,7 @@ mpsc_drv_probe(struct platform_device *dev)
2014 (struct uart_port *)pi); 2036 (struct uart_port *)pi);
2015 mpsc_drv_unmap_regs(pi); 2037 mpsc_drv_unmap_regs(pi);
2016 } 2038 }
2039 }
2017 else 2040 else
2018 mpsc_drv_unmap_regs(pi); 2041 mpsc_drv_unmap_regs(pi);
2019 } 2042 }