spi-topcliff-pch: Add recovery processing in case FIFO overrun error occurs
[pandora-kernel.git] / drivers / spi / spi-topcliff-pch.c
index 1d23f38..c8efa75 100644 (file)
@@ -58,6 +58,7 @@
 #define PCH_SLEEP_TIME         10
 
 #define SSN_LOW                        0x02U
+#define SSN_HIGH               0x03U
 #define SSN_NO_CONTROL         0x00U
 #define PCH_MAX_CS             0xFF
 #define PCI_DEVICE_ID_GE_SPI   0x8816
@@ -316,16 +317,19 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 
        /* if transfer complete interrupt */
        if (reg_spsr_val & SPSR_FI_BIT) {
-               if (tx_index < bpw_len)
+               if ((tx_index == bpw_len) && (rx_index == tx_index)) {
+                       /* disable interrupts */
+                       pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+
+                       /* transfer is completed;
+                          inform pch_spi_process_messages */
+                       data->transfer_complete = true;
+                       data->transfer_active = false;
+                       wake_up(&data->wait);
+               } else {
                        dev_err(&data->master->dev,
                                "%s : Transfer is not completed", __func__);
-               /* disable interrupts */
-               pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
-
-               /* transfer is completed;inform pch_spi_process_messages */
-               data->transfer_complete = true;
-               data->transfer_active = false;
-               wake_up(&data->wait);
+               }
        }
 }
 
@@ -348,16 +352,26 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
                        "%s returning due to suspend\n", __func__);
                return IRQ_NONE;
        }
-       if (data->use_dma)
-               return IRQ_NONE;
 
        io_remap_addr = data->io_remap_addr;
        spsr = io_remap_addr + PCH_SPSR;
 
        reg_spsr_val = ioread32(spsr);
 
-       if (reg_spsr_val & SPSR_ORF_BIT)
-               dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+       if (reg_spsr_val & SPSR_ORF_BIT) {
+               dev_err(&board_dat->pdev->dev, "%s Over run error\n", __func__);
+               if (data->current_msg->complete != 0) {
+                       data->transfer_complete = true;
+                       data->current_msg->status = -EIO;
+                       data->current_msg->complete(data->current_msg->context);
+                       data->bcurrent_msg_processing = false;
+                       data->current_msg = NULL;
+                       data->cur_trans = NULL;
+               }
+       }
+
+       if (data->use_dma)
+               return IRQ_NONE;
 
        /* Check if the interrupt is for SPI device */
        if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
@@ -756,10 +770,6 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
 
        wait_event_interruptible(data->wait, data->transfer_complete);
 
-       pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
-       dev_dbg(&data->master->dev,
-               "%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
-
        /* clear all interrupts */
        pch_spi_writereg(data->master, PCH_SPSR,
                         pch_spi_readreg(data->master, PCH_SPSR));
@@ -815,10 +825,11 @@ static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
        }
 }
 
-static void pch_spi_start_transfer(struct pch_spi_data *data)
+static int pch_spi_start_transfer(struct pch_spi_data *data)
 {
        struct pch_spi_dma_ctrl *dma;
        unsigned long flags;
+       int rtn;
 
        dma = &data->dma;
 
@@ -833,19 +844,23 @@ static void pch_spi_start_transfer(struct pch_spi_data *data)
                                 initiating the transfer. */
        dev_dbg(&data->master->dev,
                "%s:waiting for transfer to get over\n", __func__);
-       wait_event_interruptible(data->wait, data->transfer_complete);
+       rtn = wait_event_interruptible_timeout(data->wait,
+                                              data->transfer_complete,
+                                              msecs_to_jiffies(2 * HZ));
 
        dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
                            DMA_FROM_DEVICE);
+
+       dma_sync_sg_for_cpu(&data->master->dev, dma->sg_tx_p, dma->nent,
+                           DMA_FROM_DEVICE);
+       memset(data->dma.tx_buf_virt, 0, PAGE_SIZE);
+
        async_tx_ack(dma->desc_rx);
        async_tx_ack(dma->desc_tx);
        kfree(dma->sg_tx_p);
        kfree(dma->sg_rx_p);
 
        spin_lock_irqsave(&data->lock, flags);
-       pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
-       dev_dbg(&data->master->dev,
-               "%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
 
        /* clear fifo threshold, disable interrupts, disable SPI transfer */
        pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
@@ -858,6 +873,8 @@ static void pch_spi_start_transfer(struct pch_spi_data *data)
        pch_spi_clear_fifo(data->master);
 
        spin_unlock_irqrestore(&data->lock, flags);
+
+       return rtn;
 }
 
 static void pch_dma_rx_complete(void *arg)
@@ -1162,6 +1179,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
        if (data->use_dma)
                pch_spi_request_dma(data,
                                    data->current_msg->spi->bits_per_word);
+       pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
        do {
                /* If we are already processing a message get the next
                transfer structure from the message otherwise retrieve
@@ -1184,7 +1202,8 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 
                if (data->use_dma) {
                        pch_spi_handle_dma(data, &bpw);
-                       pch_spi_start_transfer(data);
+                       if (!pch_spi_start_transfer(data))
+                               goto out;
                        pch_spi_copy_rx_data_for_dma(data, bpw);
                } else {
                        pch_spi_set_tx(data, &bpw);
@@ -1222,6 +1241,8 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 
        } while (data->cur_trans != NULL);
 
+out:
+       pch_spi_writereg(data->master, PCH_SSNXCR, SSN_HIGH);
        if (data->use_dma)
                pch_spi_release_dma(data);
 }