Blackfin SPI Driver: add timeout while waiting for SPIF in dma irq handler
[safe/jmp/linux-2.6] / drivers / spi / spi_bfin5xx.c
index 0bbe19e..e706de1 100644 (file)
@@ -198,8 +198,6 @@ static void cs_deactive(struct driver_data *drv_data, struct chip_data *chip)
                udelay(chip->cs_chg_udelay);
 }
 
-#define MAX_SPI_SSEL   7
-
 /* stop controller and re-config current chip*/
 static void restore_state(struct driver_data *drv_data)
 {
@@ -559,9 +557,14 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
        struct driver_data *drv_data = dev_id;
        struct chip_data *chip = drv_data->cur_chip;
        struct spi_message *msg = drv_data->cur_msg;
+       unsigned long timeout;
+       unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel);
        u16 spistat = read_STAT(drv_data);
 
-       dev_dbg(&drv_data->pdev->dev, "in dma_irq_handler\n");
+       dev_dbg(&drv_data->pdev->dev,
+               "in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
+               dmastat, spistat);
+
        clear_dma_irqstat(drv_data->dma_channel);
 
        /* Wait for DMA to complete */
@@ -580,10 +583,19 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
                        cpu_relax();
        }
 
+       dev_dbg(&drv_data->pdev->dev,
+               "in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
+               dmastat, read_STAT(drv_data));
+
+       timeout = jiffies + HZ;
        while (!(read_STAT(drv_data) & SPIF))
-               cpu_relax();
+               if (!time_before(jiffies, timeout)) {
+                       dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF");
+                       break;
+               } else
+                       cpu_relax();
 
-       if (spistat & RBSY) {
+       if ((dmastat & DMA_ERR) && (spistat & RBSY)) {
                msg->state = ERROR_STATE;
                dev_err(&drv_data->pdev->dev, "dma receive: fifo/buffer overflow\n");
        } else {
@@ -631,6 +643,7 @@ static void pump_transfers(unsigned long data)
 
         /* Handle for abort */
        if (message->state == ERROR_STATE) {
+               dev_dbg(&drv_data->pdev->dev, "transfer: we've hit an error\n");
                message->status = -EIO;
                giveback(drv_data);
                return;
@@ -638,6 +651,7 @@ static void pump_transfers(unsigned long data)
 
        /* Handle end of message */
        if (message->state == DONE_STATE) {
+               dev_dbg(&drv_data->pdev->dev, "transfer: all done!\n");
                message->status = 0;
                giveback(drv_data);
                return;
@@ -645,6 +659,7 @@ static void pump_transfers(unsigned long data)
 
        /* Delay if requested at end of transfer */
        if (message->state == RUNNING_STATE) {
+               dev_dbg(&drv_data->pdev->dev, "transfer: still running ...\n");
                previous = list_entry(transfer->transfer_list.prev,
                                      struct spi_transfer, transfer_list);
                if (previous->delay_usecs)
@@ -756,11 +771,10 @@ static void pump_transfers(unsigned long data)
        if (!full_duplex && drv_data->cur_chip->enable_dma
                                && drv_data->len > 6) {
 
-               unsigned long dma_start_addr;
+               unsigned long dma_start_addr, flags;
 
                disable_dma(drv_data->dma_channel);
                clear_dma_irqstat(drv_data->dma_channel);
-               bfin_spi_disable(drv_data);
 
                /* config dma channel */
                dev_dbg(&drv_data->pdev->dev, "doing dma transfer\n");
@@ -791,8 +805,7 @@ static void pump_transfers(unsigned long data)
                        enable_dma(drv_data->dma_channel);
 
                        /* start SPI transfer */
-                       write_CTRL(drv_data,
-                               (cr | CFG_SPI_DMAWRITE | BIT_CTL_ENABLE));
+                       write_CTRL(drv_data, cr | BIT_CTL_TIMOD_DMA_TX);
 
                        /* just return here, there can only be one transfer
                         * in this mode
@@ -806,7 +819,8 @@ static void pump_transfers(unsigned long data)
                dma_config = (RESTART | dma_width | DI_EN);
                if (drv_data->rx != NULL) {
                        /* set transfer mode, and enable SPI */
-                       dev_dbg(&drv_data->pdev->dev, "doing DMA in.\n");
+                       dev_dbg(&drv_data->pdev->dev, "doing DMA in to %p (size %zx)\n",
+                               drv_data->rx, drv_data->len_in_bytes);
 
                        /* invalidate caches, if needed */
                        if (bfin_addr_dcachable((unsigned long) drv_data->rx))
@@ -819,7 +833,7 @@ static void pump_transfers(unsigned long data)
 
                        dma_config |= WNR;
                        dma_start_addr = (unsigned long)drv_data->rx;
-                       cr |= CFG_SPI_DMAREAD;
+                       cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT;
 
                } else if (drv_data->tx != NULL) {
                        dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n");
@@ -831,19 +845,27 @@ static void pump_transfers(unsigned long data)
                                                drv_data->len_in_bytes));
 
                        dma_start_addr = (unsigned long)drv_data->tx;
-                       cr |= CFG_SPI_DMAWRITE;
+                       cr |= BIT_CTL_TIMOD_DMA_TX;
 
                } else
                        BUG();
 
-               /* start dma */
-               dma_enable_irq(drv_data->dma_channel);
-               set_dma_config(drv_data->dma_channel, dma_config);
+               /* oh man, here there be monsters ... and i dont mean the
+                * fluffy cute ones from pixar, i mean the kind that'll eat
+                * your data, kick your dog, and love it all.  do *not* try
+                * and change these lines unless you (1) heavily test DMA
+                * with SPI flashes on a loaded system (e.g. ping floods),
+                * (2) know just how broken the DMA engine interaction with
+                * the SPI peripheral is, and (3) have someone else to blame
+                * when you screw it all up anyways.
+                */
                set_dma_start_addr(drv_data->dma_channel, dma_start_addr);
+               set_dma_config(drv_data->dma_channel, dma_config);
+               local_irq_save(flags);
                enable_dma(drv_data->dma_channel);
-
-               /* start SPI transfer */
-               write_CTRL(drv_data, (cr | BIT_CTL_ENABLE));
+               write_CTRL(drv_data, cr);
+               dma_enable_irq(drv_data->dma_channel);
+               local_irq_restore(flags);
 
        } else {
                /* IO mode write then read */
@@ -993,7 +1015,7 @@ static int transfer(struct spi_device *spi, struct spi_message *msg)
 
 #define MAX_SPI_SSEL   7
 
-static u16 ssel[3][MAX_SPI_SSEL] = {
+static u16 ssel[][MAX_SPI_SSEL] = {
        {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
        P_SPI0_SSEL4, P_SPI0_SSEL5,
        P_SPI0_SSEL6, P_SPI0_SSEL7},