rt2x00: LEDS build failure
[pandora-kernel.git] / drivers / spi / spi_imx.c
index 1b06471..c730d05 100644 (file)
@@ -270,19 +270,26 @@ struct chip_data {
 
 static void pump_messages(struct work_struct *work);
 
-static int flush(struct driver_data *drv_data)
+static void flush(struct driver_data *drv_data)
 {
-       unsigned long limit = loops_per_jiffy << 1;
        void __iomem *regs = drv_data->regs;
-       volatile u32 d;
+       u32 control;
 
        dev_dbg(&drv_data->pdev->dev, "flush\n");
+
+       /* Wait for end of transaction */
        do {
-               while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR)
-                       d = readl(regs + SPI_RXDATA);
-       } while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--);
+               control = readl(regs + SPI_CONTROL);
+       } while (control & SPI_CONTROL_XCH);
+
+       /* Release chip select if requested, transfer delays are
+          handled in pump_transfers */
+       if (drv_data->cs_change)
+               drv_data->cs_control(SPI_CS_DEASSERT);
 
-       return limit;
+       /* Disable SPI to flush FIFOs */
+       writel(control & ~SPI_CONTROL_SPIEN, regs + SPI_CONTROL);
+       writel(control, regs + SPI_CONTROL);
 }
 
 static void restore_state(struct driver_data *drv_data)
@@ -570,6 +577,7 @@ static void giveback(struct spi_message *message, struct driver_data *drv_data)
        writel(0, regs + SPI_INT_STATUS);
        writel(0, regs + SPI_DMA);
 
+       /* Unconditioned deselct */
        drv_data->cs_control(SPI_CS_DEASSERT);
 
        message->state = NULL;
@@ -592,13 +600,10 @@ static void dma_err_handler(int channel, void *data, int errcode)
        /* Disable both rx and tx dma channels */
        imx_dma_disable(drv_data->rx_channel);
        imx_dma_disable(drv_data->tx_channel);
-
-       if (flush(drv_data) == 0)
-               dev_err(&drv_data->pdev->dev,
-                               "dma_err_handler - flush failed\n");
-
        unmap_dma_buffers(drv_data);
 
+       flush(drv_data);
+
        msg->state = ERROR_STATE;
        tasklet_schedule(&drv_data->pump_transfers);
 }
@@ -612,8 +617,7 @@ static void dma_tx_handler(int channel, void *data)
        imx_dma_disable(channel);
 
        /* Now waits for TX FIFO empty */
-       writel(readl(drv_data->regs + SPI_INT_STATUS) | SPI_INTEN_TE,
-                       drv_data->regs + SPI_INT_STATUS);
+       writel(SPI_INTEN_TE, drv_data->regs + SPI_INT_STATUS);
 }
 
 static irqreturn_t dma_transfer(struct driver_data *drv_data)
@@ -621,19 +625,18 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
        u32 status;
        struct spi_message *msg = drv_data->cur_msg;
        void __iomem *regs = drv_data->regs;
-       unsigned long limit;
 
        status = readl(regs + SPI_INT_STATUS);
 
-       if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) {
+       if ((status & (SPI_INTEN_RO | SPI_STATUS_RO))
+                       == (SPI_INTEN_RO | SPI_STATUS_RO)) {
                writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
 
+               imx_dma_disable(drv_data->tx_channel);
                imx_dma_disable(drv_data->rx_channel);
                unmap_dma_buffers(drv_data);
 
-               if (flush(drv_data) == 0)
-                       dev_err(&drv_data->pdev->dev,
-                               "dma_transfer - flush failed\n");
+               flush(drv_data);
 
                dev_warn(&drv_data->pdev->dev,
                                "dma_transfer - fifo overun\n");
@@ -649,20 +652,17 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
 
                if (drv_data->rx) {
                        /* Wait end of transfer before read trailing data */
-                       limit = loops_per_jiffy << 1;
-                       while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) &&
-                                       limit--);
-
-                       if (limit == 0)
-                               dev_err(&drv_data->pdev->dev,
-                                       "dma_transfer - end of tx failed\n");
-                       else
-                               dev_dbg(&drv_data->pdev->dev,
-                                       "dma_transfer - end of tx\n");
+                       while (readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH)
+                               cpu_relax();
 
                        imx_dma_disable(drv_data->rx_channel);
                        unmap_dma_buffers(drv_data);
 
+                       /* Release chip select if requested, transfer delays are
+                          handled in pump_transfers() */
+                       if (drv_data->cs_change)
+                               drv_data->cs_control(SPI_CS_DEASSERT);
+
                        /* Calculate number of trailing data and read them */
                        dev_dbg(&drv_data->pdev->dev,
                                "dma_transfer - test = 0x%08X\n",
@@ -676,19 +676,12 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
                        /* Write only transfer */
                        unmap_dma_buffers(drv_data);
 
-                       if (flush(drv_data) == 0)
-                               dev_err(&drv_data->pdev->dev,
-                                       "dma_transfer - flush failed\n");
+                       flush(drv_data);
                }
 
                /* End of transfer, update total byte transfered */
                msg->actual_length += drv_data->len;
 
-               /* Release chip select if requested, transfer delays are
-                  handled in pump_transfers() */
-               if (drv_data->cs_change)
-                       drv_data->cs_control(SPI_CS_DEASSERT);
-
                /* Move to next transfer */
                msg->state = next_transfer(drv_data);
 
@@ -711,44 +704,43 @@ static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data)
 
        status = readl(regs + SPI_INT_STATUS);
 
-       while (status & SPI_STATUS_TH) {
+       if (status & SPI_INTEN_TE) {
+               /* TXFIFO Empty Interrupt on the last transfered word */
+               writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
                dev_dbg(&drv_data->pdev->dev,
-                       "interrupt_wronly_transfer - status = 0x%08X\n", status);
+                       "interrupt_wronly_transfer - end of tx\n");
 
-               /* Pump data */
-               if (write(drv_data)) {
-                       writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
-                               regs + SPI_INT_STATUS);
+               flush(drv_data);
 
-                       dev_dbg(&drv_data->pdev->dev,
-                               "interrupt_wronly_transfer - end of tx\n");
+               /* Update total byte transfered */
+               msg->actual_length += drv_data->len;
 
-                       if (flush(drv_data) == 0)
-                               dev_err(&drv_data->pdev->dev,
-                                       "interrupt_wronly_transfer - "
-                                       "flush failed\n");
+               /* Move to next transfer */
+               msg->state = next_transfer(drv_data);
 
-                       /* End of transfer, update total byte transfered */
-                       msg->actual_length += drv_data->len;
+               /* Schedule transfer tasklet */
+               tasklet_schedule(&drv_data->pump_transfers);
 
-                       /* Release chip select if requested, transfer delays are
-                          handled in pump_transfers */
-                       if (drv_data->cs_change)
-                               drv_data->cs_control(SPI_CS_DEASSERT);
+               return IRQ_HANDLED;
+       } else {
+               while (status & SPI_STATUS_TH) {
+                       dev_dbg(&drv_data->pdev->dev,
+                               "interrupt_wronly_transfer - status = 0x%08X\n",
+                               status);
 
-                       /* Move to next transfer */
-                       msg->state = next_transfer(drv_data);
+                       /* Pump data */
+                       if (write(drv_data)) {
+                               /* End of TXFIFO writes,
+                                  now wait until TXFIFO is empty */
+                               writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
+                               return IRQ_HANDLED;
+                       }
 
-                       /* Schedule transfer tasklet */
-                       tasklet_schedule(&drv_data->pump_transfers);
+                       status = readl(regs + SPI_INT_STATUS);
 
-                       return IRQ_HANDLED;
+                       /* We did something */
+                       handled = IRQ_HANDLED;
                }
-
-               status = readl(regs + SPI_INT_STATUS);
-
-               /* We did something */
-               handled = IRQ_HANDLED;
        }
 
        return handled;
@@ -758,45 +750,31 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
 {
        struct spi_message *msg = drv_data->cur_msg;
        void __iomem *regs = drv_data->regs;
-       u32 status;
+       u32 status, control;
        irqreturn_t handled = IRQ_NONE;
        unsigned long limit;
 
        status = readl(regs + SPI_INT_STATUS);
 
-       while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
+       if (status & SPI_INTEN_TE) {
+               /* TXFIFO Empty Interrupt on the last transfered word */
+               writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
                dev_dbg(&drv_data->pdev->dev,
-                       "interrupt_transfer - status = 0x%08X\n", status);
-
-               if (status & SPI_STATUS_RO) {
-                       writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
-                               regs + SPI_INT_STATUS);
+                       "interrupt_transfer - end of tx\n");
 
-                       dev_warn(&drv_data->pdev->dev,
-                               "interrupt_transfer - fifo overun\n"
-                               "    data not yet written = %d\n"
-                               "    data not yet read    = %d\n",
-                               data_to_write(drv_data),
-                               data_to_read(drv_data));
-
-                       if (flush(drv_data) == 0)
-                               dev_err(&drv_data->pdev->dev,
-                                       "interrupt_transfer - flush failed\n");
-
-                       msg->state = ERROR_STATE;
-                       tasklet_schedule(&drv_data->pump_transfers);
-
-                       return IRQ_HANDLED;
-               }
-
-               /* Pump data */
-               read(drv_data);
-               if (write(drv_data)) {
-                       writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
-                               regs + SPI_INT_STATUS);
+               if (msg->state == ERROR_STATE) {
+                       /* RXFIFO overrun was detected and message aborted */
+                       flush(drv_data);
+               } else {
+                       /* Wait for end of transaction */
+                       do {
+                               control = readl(regs + SPI_CONTROL);
+                       } while (control & SPI_CONTROL_XCH);
 
-                       dev_dbg(&drv_data->pdev->dev,
-                               "interrupt_transfer - end of tx\n");
+                       /* Release chip select if requested, transfer delays are
+                          handled in pump_transfers */
+                       if (drv_data->cs_change)
+                               drv_data->cs_control(SPI_CS_DEASSERT);
 
                        /* Read trailing bytes */
                        limit = loops_per_jiffy << 1;
@@ -810,27 +788,54 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
                                dev_dbg(&drv_data->pdev->dev,
                                        "interrupt_transfer - end of rx\n");
 
-                       /* End of transfer, update total byte transfered */
+                       /* Update total byte transfered */
                        msg->actual_length += drv_data->len;
 
-                       /* Release chip select if requested, transfer delays are
-                          handled in pump_transfers */
-                       if (drv_data->cs_change)
-                               drv_data->cs_control(SPI_CS_DEASSERT);
-
                        /* Move to next transfer */
                        msg->state = next_transfer(drv_data);
+               }
 
-                       /* Schedule transfer tasklet */
-                       tasklet_schedule(&drv_data->pump_transfers);
+               /* Schedule transfer tasklet */
+               tasklet_schedule(&drv_data->pump_transfers);
 
-                       return IRQ_HANDLED;
-               }
+               return IRQ_HANDLED;
+       } else {
+               while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
+                       dev_dbg(&drv_data->pdev->dev,
+                               "interrupt_transfer - status = 0x%08X\n",
+                               status);
 
-               status = readl(regs + SPI_INT_STATUS);
+                       if (status & SPI_STATUS_RO) {
+                               /* RXFIFO overrun, abort message end wait
+                                  until TXFIFO is empty */
+                               writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
 
-               /* We did something */
-               handled = IRQ_HANDLED;
+                               dev_warn(&drv_data->pdev->dev,
+                                       "interrupt_transfer - fifo overun\n"
+                                       "    data not yet written = %d\n"
+                                       "    data not yet read    = %d\n",
+                                       data_to_write(drv_data),
+                                       data_to_read(drv_data));
+
+                               msg->state = ERROR_STATE;
+
+                               return IRQ_HANDLED;
+                       }
+
+                       /* Pump data */
+                       read(drv_data);
+                       if (write(drv_data)) {
+                               /* End of TXFIFO writes,
+                                  now wait until TXFIFO is empty */
+                               writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
+                               return IRQ_HANDLED;
+                       }
+
+                       status = readl(regs + SPI_INT_STATUS);
+
+                       /* We did something */
+                       handled = IRQ_HANDLED;
+               }
        }
 
        return handled;
@@ -1722,10 +1727,12 @@ static int spi_imx_resume(struct platform_device *pdev)
 #define spi_imx_resume NULL
 #endif /* CONFIG_PM */
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:spi_imx");
+
 static struct platform_driver driver = {
        .driver = {
                .name = "spi_imx",
-               .bus = &platform_bus_type,
                .owner = THIS_MODULE,
        },
        .remove = __exit_p(spi_imx_remove),