Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[safe/jmp/linux-2.6] / drivers / spi / dw_spi.c
index 31620fa..d256cb0 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/interrupt.h>
 #include <linux/highmem.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 
 #include <linux/spi/dw_spi.h>
 #include <linux/spi/spi.h>
@@ -152,6 +153,7 @@ static void mrst_spi_debugfs_remove(struct dw_spi *dws)
 #else
 static inline int mrst_spi_debugfs_init(struct dw_spi *dws)
 {
+       return 0;
 }
 
 static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
@@ -161,14 +163,14 @@ static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
 
 static void wait_till_not_busy(struct dw_spi *dws)
 {
-       unsigned long end = jiffies + usecs_to_jiffies(1000);
+       unsigned long end = jiffies + 1 + usecs_to_jiffies(1000);
 
        while (time_before(jiffies, end)) {
                if (!(dw_readw(dws, sr) & SR_BUSY))
                        return;
        }
        dev_err(&dws->master->dev,
-               "DW SPI: Stutus keeps busy for 1000us after a read/write!\n");
+               "DW SPI: Status keeps busy for 1000us after a read/write!\n");
 }
 
 static void flush(struct dw_spi *dws)
@@ -358,6 +360,8 @@ static void transfer_complete(struct dw_spi *dws)
 static irqreturn_t interrupt_transfer(struct dw_spi *dws)
 {
        u16 irq_status, irq_mask = 0x3f;
+       u32 int_level = dws->fifo_len / 2;
+       u32 left;
 
        irq_status = dw_readw(dws, isr) & irq_mask;
        /* Error handling */
@@ -369,22 +373,23 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
                return IRQ_HANDLED;
        }
 
-       /* INT comes from tx */
-       if (dws->tx && (irq_status & SPI_INT_TXEI)) {
-               while (dws->tx < dws->tx_end)
+       if (irq_status & SPI_INT_TXEI) {
+               spi_mask_intr(dws, SPI_INT_TXEI);
+
+               left = (dws->tx_end - dws->tx) / dws->n_bytes;
+               left = (left > int_level) ? int_level : left;
+
+               while (left--)
                        dws->write(dws);
+               dws->read(dws);
 
-               if (dws->tx == dws->tx_end) {
-                       spi_mask_intr(dws, SPI_INT_TXEI);
+               /* Re-enable the IRQ if there is still data left to tx */
+               if (dws->tx_end > dws->tx)
+                       spi_umask_intr(dws, SPI_INT_TXEI);
+               else
                        transfer_complete(dws);
-               }
        }
 
-       /* INT comes from rx */
-       if (dws->rx && (irq_status & SPI_INT_RXFI)) {
-               if (dws->read(dws))
-                       transfer_complete(dws);
-       }
        return IRQ_HANDLED;
 }
 
@@ -404,12 +409,9 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
 /* Must be called inside pump_transfers() */
 static void poll_transfer(struct dw_spi *dws)
 {
-       if (dws->tx) {
-               while (dws->write(dws))
-                       dws->read(dws);
-       }
+       while (dws->write(dws))
+               dws->read(dws);
 
-       dws->read(dws);
        transfer_complete(dws);
 }
 
@@ -428,6 +430,7 @@ static void pump_transfers(unsigned long data)
        u8 bits = 0;
        u8 imask = 0;
        u8 cs_change = 0;
+       u16 txint_level = 0;
        u16 clk_div = 0;
        u32 speed = 0;
        u32 cr0 = 0;
@@ -438,6 +441,9 @@ static void pump_transfers(unsigned long data)
        chip = dws->cur_chip;
        spi = message->spi;
 
+       if (unlikely(!chip->clk_div))
+               chip->clk_div = dws->max_freq / chip->speed_hz;
+
        if (message->state == ERROR_STATE) {
                message->status = -EIO;
                goto early_exit;
@@ -492,7 +498,7 @@ static void pump_transfers(unsigned long data)
 
                        /* clk_div doesn't support odd number */
                        clk_div = dws->max_freq / speed;
-                       clk_div = (clk_div >> 1) << 1;
+                       clk_div = (clk_div + 1) & 0xfffe;
 
                        chip->speed_hz = speed;
                        chip->clk_div = clk_div;
@@ -532,14 +538,35 @@ static void pump_transfers(unsigned long data)
        }
        message->state = RUNNING_STATE;
 
+       /*
+        * Adjust transfer mode if necessary. Requires platform dependent
+        * chipselect mechanism.
+        */
+       if (dws->cs_control) {
+               if (dws->rx && dws->tx)
+                       chip->tmode = 0x00;
+               else if (dws->rx)
+                       chip->tmode = 0x02;
+               else
+                       chip->tmode = 0x01;
+
+               cr0 &= ~(0x3 << SPI_MODE_OFFSET);
+               cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
+       }
+
        /* Check if current transfer is a DMA transaction */
        dws->dma_mapped = map_dma_buffers(dws);
 
+       /*
+        * Interrupt mode
+        * we only need set the TXEI IRQ, as TX/RX always happen syncronizely
+        */
        if (!dws->dma_mapped && !chip->poll_mode) {
-               if (dws->rx)
-                       imask |= SPI_INT_RXFI;
-               if (dws->tx)
-                       imask |= SPI_INT_TXEI;
+               int templen = dws->len / dws->n_bytes;
+               txint_level = dws->fifo_len / 2;
+               txint_level = (templen > txint_level) ? txint_level : templen;
+
+               imask |= SPI_INT_TXEI;
                dws->transfer_handler = interrupt_transfer;
        }
 
@@ -549,21 +576,23 @@ static void pump_transfers(unsigned long data)
         *      2. clk_div is changed
         *      3. control value changes
         */
-       if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) {
+       if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div || imask) {
                spi_enable_chip(dws, 0);
 
                if (dw_readw(dws, ctrl0) != cr0)
                        dw_writew(dws, ctrl0, cr0);
 
+               spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
+               spi_chip_sel(dws, spi->chip_select);
+
                /* Set the interrupt mask, for poll mode just diable all int */
                spi_mask_intr(dws, 0xff);
-               if (!chip->poll_mode)
+               if (imask)
                        spi_umask_intr(dws, imask);
+               if (txint_level)
+                       dw_writew(dws, txfltr, txint_level);
 
-               spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
-               spi_chip_sel(dws, spi->chip_select);
                spi_enable_chip(dws, 1);
-
                if (cs_change)
                        dws->prev_chip = chip;
        }
@@ -712,11 +741,11 @@ static int dw_spi_setup(struct spi_device *spi)
        }
        chip->bits_per_word = spi->bits_per_word;
 
+       if (!spi->max_speed_hz) {
+               dev_err(&spi->dev, "No max speed HZ parameter\n");
+               return -EINVAL;
+       }
        chip->speed_hz = spi->max_speed_hz;
-       if (chip->speed_hz)
-               chip->clk_div = 25000000 / chip->speed_hz;
-       else
-               chip->clk_div = 8;      /* default value */
 
        chip->tmode = 0; /* Tx & Rx */
        /* Default SPI mode is SCPOL = 0, SCPH = 0 */
@@ -735,7 +764,7 @@ static void dw_spi_cleanup(struct spi_device *spi)
        kfree(chip);
 }
 
-static int __init init_queue(struct dw_spi *dws)
+static int __devinit init_queue(struct dw_spi *dws)
 {
        INIT_LIST_HEAD(&dws->queue);
        spin_lock_init(&dws->lock);
@@ -817,6 +846,22 @@ static void spi_hw_init(struct dw_spi *dws)
        spi_mask_intr(dws, 0xff);
        spi_enable_chip(dws, 1);
        flush(dws);
+
+       /*
+        * Try to detect the FIFO depth if not set by interface driver,
+        * the depth could be from 2 to 256 from HW spec
+        */
+       if (!dws->fifo_len) {
+               u32 fifo;
+               for (fifo = 2; fifo <= 257; fifo++) {
+                       dw_writew(dws, txfltr, fifo);
+                       if (fifo != dw_readw(dws, txfltr))
+                               break;
+               }
+
+               dws->fifo_len = (fifo == 257) ? 0 : fifo;
+               dw_writew(dws, txfltr, 0);
+       }
 }
 
 int __devinit dw_spi_add_host(struct dw_spi *dws)
@@ -913,6 +958,7 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws)
        /* Disconnect from the SPI framework */
        spi_unregister_master(dws->master);
 }
+EXPORT_SYMBOL(dw_spi_remove_host);
 
 int dw_spi_suspend_host(struct dw_spi *dws)
 {