diff options
author | Feng Tang <feng.tang@intel.com> | 2010-07-27 03:20:32 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 16:47:46 -0400 |
commit | 669b7a0938e759097c150400cd36bd49befaf5bb (patch) | |
tree | b7244699ea1e8bf04c50dde2b9efeaf504f44bc6 /drivers/serial/mfd.c | |
parent | d843fc6e9dc9bee7061b6833594860ea93ad98e1 (diff) |
hsu: add a periodic timer to check dma rx channel
A general problem for uart rx dma channel is you never know when
and how much data will be received, so usually preset it a DMA
descriptor with a big size, and rely on DMA RX timeout IRQ to
know there is some data in rx channel.
For a RX data size of multiple of MOTSR, there will be no timeout
IRQ issued, thus OS will never be notified about that.
This is a work around for that, current timer frequency is 5 times
per second, it should vary according to the baud rate
When future silicon version fix the problem, this workaround need
be removed
Signed-off-by: Feng Tang <feng.tang@intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/serial/mfd.c')
-rw-r--r-- | drivers/serial/mfd.c | 50 |
1 files changed, 47 insertions, 3 deletions
diff --git a/drivers/serial/mfd.c b/drivers/serial/mfd.c index 300dcb134e07..ed2bf6b14a4a 100644 --- a/drivers/serial/mfd.c +++ b/drivers/serial/mfd.c | |||
@@ -64,6 +64,8 @@ | |||
64 | #define mfd_readl(obj, offset) readl(obj->reg + offset) | 64 | #define mfd_readl(obj, offset) readl(obj->reg + offset) |
65 | #define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) | 65 | #define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) |
66 | 66 | ||
67 | #define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10) | ||
68 | |||
67 | struct hsu_dma_buffer { | 69 | struct hsu_dma_buffer { |
68 | u8 *buf; | 70 | u8 *buf; |
69 | dma_addr_t dma_addr; | 71 | dma_addr_t dma_addr; |
@@ -75,7 +77,8 @@ struct hsu_dma_chan { | |||
75 | u32 id; | 77 | u32 id; |
76 | u32 dirt; /* to or from device */ | 78 | u32 dirt; /* to or from device */ |
77 | struct uart_hsu_port *uport; | 79 | struct uart_hsu_port *uport; |
78 | void __iomem *reg; | 80 | void __iomem *reg; |
81 | struct timer_list rx_timer; /* only needed by RX channel */ | ||
79 | }; | 82 | }; |
80 | 83 | ||
81 | struct uart_hsu_port { | 84 | struct uart_hsu_port { |
@@ -377,6 +380,8 @@ void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf | |||
377 | | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ | 380 | | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ |
378 | ); | 381 | ); |
379 | chan_writel(rxc, HSU_CH_CR, 0x3); | 382 | chan_writel(rxc, HSU_CH_CR, 0x3); |
383 | |||
384 | mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); | ||
380 | } | 385 | } |
381 | 386 | ||
382 | /* Protected by spin_lock_irqsave(port->lock) */ | 387 | /* Protected by spin_lock_irqsave(port->lock) */ |
@@ -437,8 +442,13 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) | |||
437 | /* We can use 2 ways to calc the actual transfer len */ | 442 | /* We can use 2 ways to calc the actual transfer len */ |
438 | count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; | 443 | count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; |
439 | 444 | ||
440 | if (!count) | 445 | if (!count) { |
446 | /* restart the channel before we leave */ | ||
447 | chan_writel(chan, HSU_CH_CR, 0x3); | ||
441 | return; | 448 | return; |
449 | } | ||
450 | |||
451 | del_timer(&chan->rx_timer); | ||
442 | 452 | ||
443 | dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, | 453 | dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, |
444 | dbuf->dma_size, DMA_FROM_DEVICE); | 454 | dbuf->dma_size, DMA_FROM_DEVICE); |
@@ -463,9 +473,12 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) | |||
463 | | (0x1 << 16) | 473 | | (0x1 << 16) |
464 | | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ | 474 | | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ |
465 | ); | 475 | ); |
476 | tty_flip_buffer_push(tty); | ||
477 | |||
466 | chan_writel(chan, HSU_CH_CR, 0x3); | 478 | chan_writel(chan, HSU_CH_CR, 0x3); |
479 | chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ; | ||
480 | add_timer(&chan->rx_timer); | ||
467 | 481 | ||
468 | tty_flip_buffer_push(tty); | ||
469 | } | 482 | } |
470 | 483 | ||
471 | static void serial_hsu_stop_rx(struct uart_port *port) | 484 | static void serial_hsu_stop_rx(struct uart_port *port) |
@@ -893,6 +906,8 @@ static void serial_hsu_shutdown(struct uart_port *port) | |||
893 | container_of(port, struct uart_hsu_port, port); | 906 | container_of(port, struct uart_hsu_port, port); |
894 | unsigned long flags; | 907 | unsigned long flags; |
895 | 908 | ||
909 | del_timer_sync(&up->rxc->rx_timer); | ||
910 | |||
896 | /* Disable interrupts from this port */ | 911 | /* Disable interrupts from this port */ |
897 | up->ier = 0; | 912 | up->ier = 0; |
898 | serial_out(up, UART_IER, 0); | 913 | serial_out(up, UART_IER, 0); |
@@ -1348,6 +1363,28 @@ err_disable: | |||
1348 | return ret; | 1363 | return ret; |
1349 | } | 1364 | } |
1350 | 1365 | ||
1366 | static void hsu_dma_rx_timeout(unsigned long data) | ||
1367 | { | ||
1368 | struct hsu_dma_chan *chan = (void *)data; | ||
1369 | struct uart_hsu_port *up = chan->uport; | ||
1370 | struct hsu_dma_buffer *dbuf = &up->rxbuf; | ||
1371 | int count = 0; | ||
1372 | unsigned long flags; | ||
1373 | |||
1374 | spin_lock_irqsave(&up->port.lock, flags); | ||
1375 | |||
1376 | count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; | ||
1377 | |||
1378 | if (!count) { | ||
1379 | mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); | ||
1380 | goto exit; | ||
1381 | } | ||
1382 | |||
1383 | hsu_dma_rx(up, 0); | ||
1384 | exit: | ||
1385 | spin_unlock_irqrestore(&up->port.lock, flags); | ||
1386 | } | ||
1387 | |||
1351 | static void hsu_global_init(void) | 1388 | static void hsu_global_init(void) |
1352 | { | 1389 | { |
1353 | struct hsu_port *hsu; | 1390 | struct hsu_port *hsu; |
@@ -1409,6 +1446,13 @@ static void hsu_global_init(void) | |||
1409 | dchan->uport = &hsu->port[i/2]; | 1446 | dchan->uport = &hsu->port[i/2]; |
1410 | dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + | 1447 | dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + |
1411 | i * HSU_DMA_CHANS_REG_LENGTH; | 1448 | i * HSU_DMA_CHANS_REG_LENGTH; |
1449 | |||
1450 | /* Work around for RX */ | ||
1451 | if (dchan->dirt == DMA_FROM_DEVICE) { | ||
1452 | init_timer(&dchan->rx_timer); | ||
1453 | dchan->rx_timer.function = hsu_dma_rx_timeout; | ||
1454 | dchan->rx_timer.data = (unsigned long)dchan; | ||
1455 | } | ||
1412 | dchan++; | 1456 | dchan++; |
1413 | } | 1457 | } |
1414 | 1458 | ||