mfd: Avoid access the hsu reg in !active state
authorliu chuansheng <chuansheng.liu@intel.com>
Tue, 22 May 2012 15:50:50 +0000 (23:50 +0800)
committerbuildbot <buildbot@intel.com>
Wed, 23 May 2012 13:23:42 +0000 (06:23 -0700)
BZ: 37758

There is a rare case in function serial_hsu_stop_tx(),
which will call pm_runtime_get() without sync, then access
the hsu reg, it will cause fabric error.

To fix it, like serial_hsu_stop_rx(), when in case of non-active
runtime state, queue the stop tx command in qwork worker to do it.

Change-Id: Id6aa540db20db532a2f4b56d770111b985b26756
Signed-off-by: liu chuansheng <chuansheng.liu@intel.com>
Reviewed-on: http://android.intel.com:8080/49633
Reviewed-by: Yang, Bin <bin.yang@intel.com>
Reviewed-by: Du, Alek <alek.du@intel.com>
Tested-by: Tang, HaifengX <haifengx.tang@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
drivers/tty/serial/mfd.c

index 0dbc9bc..509917a 100644 (file)
@@ -80,6 +80,7 @@ struct hsu_dma_chan {
 #define CMD_WB 2
 #define CMD_TX 3
 #define CMD_RX_STOP    4
+#define CMD_TX_STOP    5
 
 /* to record the pin wakeup states */
 #define PM_WAKEUP      1
@@ -561,10 +562,18 @@ static void serial_hsu_stop_tx(struct uart_port *port)
        struct uart_hsu_port *up =
                container_of(port, struct uart_hsu_port, port);
        struct hsu_dma_chan *txc = up->txc;
+       unsigned long flags;
 
        pm_runtime_get(up->dev);
        if (up->use_dma) {
-               chan_writel(txc, HSU_CH_CR, 0x0);
+               if (!hsu_port_is_active(up)) {
+                       spin_lock_irqsave(&up->qlock, flags);
+                       insert_q(up, CMD_TX_STOP, 0, 0);
+                       spin_unlock_irqrestore(&up->qlock, flags);
+                       schedule_work(&up->qwork);
+               } else {
+                       chan_writel(txc, HSU_CH_CR, 0x0);
+               }
                up->dma_tx_on = 0;
        } else if (up->ier & UART_IER_THRI) {
                up->ier &= ~UART_IER_THRI;
@@ -1762,6 +1771,9 @@ static void qwork(struct work_struct *work)
                case CMD_RX_STOP:
                        chan_writel(up->rxc, HSU_CH_CR, 0x2);
                        break;
+               case CMD_TX_STOP:
+                       chan_writel(up->txc, HSU_CH_CR, 0x0);
+                       break;
                default:
                        dev_err(up->dev, "wrong queue cmd type!\n");
                }