Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
[pandora-kernel.git] / drivers / spi / spi_bfin5xx.c
index d1ff34f..011c5bd 100644 (file)
@@ -36,8 +36,6 @@ MODULE_AUTHOR(DRV_AUTHOR);
 MODULE_DESCRIPTION(DRV_DESC);
 MODULE_LICENSE("GPL");
 
-#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07) == 0)
-
 #define START_STATE    ((void *)0)
 #define RUNNING_STATE  ((void *)1)
 #define DONE_STATE     ((void *)2)
@@ -45,6 +43,9 @@ MODULE_LICENSE("GPL");
 #define QUEUE_RUNNING  0
 #define QUEUE_STOPPED  1
 
+/* Value to send if no TX value is supplied */
+#define SPI_IDLE_TXVAL 0x0000
+
 struct driver_data {
        /* Driver model hookup */
        struct platform_device *pdev;
@@ -111,6 +112,8 @@ struct chip_data {
        u8 bits_per_word;       /* 8 or 16 */
        u8 cs_change_per_word;
        u16 cs_chg_udelay;      /* Some devices require > 255usec delay */
+       u32 cs_gpio;
+       u16 idle_tx_val;
        void (*write) (struct driver_data *);
        void (*read) (struct driver_data *);
        void (*duplex) (struct driver_data *);
@@ -177,22 +180,30 @@ static int bfin_spi_flush(struct driver_data *drv_data)
 /* Chip select operation functions for cs_change flag */
 static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *chip)
 {
-       u16 flag = read_FLAG(drv_data);
+       if (likely(chip->chip_select_num)) {
+               u16 flag = read_FLAG(drv_data);
 
-       flag |= chip->flag;
-       flag &= ~(chip->flag << 8);
+               flag |= chip->flag;
+               flag &= ~(chip->flag << 8);
 
-       write_FLAG(drv_data, flag);
+               write_FLAG(drv_data, flag);
+       } else {
+               gpio_set_value(chip->cs_gpio, 0);
+       }
 }
 
 static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data *chip)
 {
-       u16 flag = read_FLAG(drv_data);
+       if (likely(chip->chip_select_num)) {
+               u16 flag = read_FLAG(drv_data);
 
-       flag &= ~chip->flag;
-       flag |= (chip->flag << 8);
+               flag &= ~chip->flag;
+               flag |= (chip->flag << 8);
 
-       write_FLAG(drv_data, flag);
+               write_FLAG(drv_data, flag);
+       } else {
+               gpio_set_value(chip->cs_gpio, 1);
+       }
 
        /* Move delay here for consistency */
        if (chip->cs_chg_udelay)
@@ -217,134 +228,126 @@ static void bfin_spi_restore_state(struct driver_data *drv_data)
        bfin_spi_cs_active(drv_data, chip);
 }
 
-/* used to kick off transfer in rx mode */
-static unsigned short bfin_spi_dummy_read(struct driver_data *drv_data)
+/* used to kick off transfer in rx mode and read unwanted RX data */
+static inline void bfin_spi_dummy_read(struct driver_data *drv_data)
 {
-       unsigned short tmp;
-       tmp = read_RDBR(drv_data);
-       return tmp;
+       (void) read_RDBR(drv_data);
 }
 
 static void bfin_spi_null_writer(struct driver_data *drv_data)
 {
        u8 n_bytes = drv_data->n_bytes;
+       u16 tx_val = drv_data->cur_chip->idle_tx_val;
+
+       /* clear RXS (we check for RXS inside the loop) */
+       bfin_spi_dummy_read(drv_data);
 
        while (drv_data->tx < drv_data->tx_end) {
-               write_TDBR(drv_data, 0);
-               while ((read_STAT(drv_data) & BIT_STAT_TXS))
-                       cpu_relax();
+               write_TDBR(drv_data, tx_val);
                drv_data->tx += n_bytes;
+               /* wait until transfer finished.
+                  checking SPIF or TXS may not guarantee transfer completion */
+               while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+                       cpu_relax();
+               /* discard RX data and clear RXS */
+               bfin_spi_dummy_read(drv_data);
        }
 }
 
 static void bfin_spi_null_reader(struct driver_data *drv_data)
 {
        u8 n_bytes = drv_data->n_bytes;
+       u16 tx_val = drv_data->cur_chip->idle_tx_val;
+
+       /* discard old RX data and clear RXS */
        bfin_spi_dummy_read(drv_data);
 
        while (drv_data->rx < drv_data->rx_end) {
+               write_TDBR(drv_data, tx_val);
+               drv_data->rx += n_bytes;
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
                bfin_spi_dummy_read(drv_data);
-               drv_data->rx += n_bytes;
        }
 }
 
 static void bfin_spi_u8_writer(struct driver_data *drv_data)
 {
-       dev_dbg(&drv_data->pdev->dev,
-               "cr8-s is 0x%x\n", read_STAT(drv_data));
+       /* clear RXS (we check for RXS inside the loop) */
+       bfin_spi_dummy_read(drv_data);
 
        while (drv_data->tx < drv_data->tx_end) {
-               write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
-               while (read_STAT(drv_data) & BIT_STAT_TXS)
+               write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
+               /* wait until transfer finished.
+                  checking SPIF or TXS may not guarantee transfer completion */
+               while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-               ++drv_data->tx;
+               /* discard RX data and clear RXS */
+               bfin_spi_dummy_read(drv_data);
        }
-
-       /* poll for SPI completion before return */
-       while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-               cpu_relax();
 }
 
 static void bfin_spi_u8_cs_chg_writer(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
 
+       /* clear RXS (we check for RXS inside the loop) */
+       bfin_spi_dummy_read(drv_data);
+
        while (drv_data->tx < drv_data->tx_end) {
                bfin_spi_cs_active(drv_data, chip);
-
-               write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
-               while (read_STAT(drv_data) & BIT_STAT_TXS)
-                       cpu_relax();
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
+               write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
+               /* make sure transfer finished before deactiving CS */
+               while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-
+               bfin_spi_dummy_read(drv_data);
                bfin_spi_cs_deactive(drv_data, chip);
-
-               ++drv_data->tx;
        }
 }
 
 static void bfin_spi_u8_reader(struct driver_data *drv_data)
 {
-       dev_dbg(&drv_data->pdev->dev,
-               "cr-8 is 0x%x\n", read_STAT(drv_data));
-
-       /* poll for SPI completion before start */
-       while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-               cpu_relax();
-
-       /* clear TDBR buffer before read(else it will be shifted out) */
-       write_TDBR(drv_data, 0xFFFF);
+       u16 tx_val = drv_data->cur_chip->idle_tx_val;
 
+       /* discard old RX data and clear RXS */
        bfin_spi_dummy_read(drv_data);
 
-       while (drv_data->rx < drv_data->rx_end - 1) {
+       while (drv_data->rx < drv_data->rx_end) {
+               write_TDBR(drv_data, tx_val);
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-               *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
-               ++drv_data->rx;
+               *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
        }
-
-       while (!(read_STAT(drv_data) & BIT_STAT_RXS))
-               cpu_relax();
-       *(u8 *) (drv_data->rx) = read_SHAW(drv_data);
-       ++drv_data->rx;
 }
 
 static void bfin_spi_u8_cs_chg_reader(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
+       u16 tx_val = chip->idle_tx_val;
+
+       /* discard old RX data and clear RXS */
+       bfin_spi_dummy_read(drv_data);
 
        while (drv_data->rx < drv_data->rx_end) {
                bfin_spi_cs_active(drv_data, chip);
-               read_RDBR(drv_data);    /* kick off */
-
+               write_TDBR(drv_data, tx_val);
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-                       cpu_relax();
-
-               *(u8 *) (drv_data->rx) = read_SHAW(drv_data);
+               *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
                bfin_spi_cs_deactive(drv_data, chip);
-
-               ++drv_data->rx;
        }
 }
 
 static void bfin_spi_u8_duplex(struct driver_data *drv_data)
 {
-       /* in duplex mode, clk is triggered by writing of TDBR */
+       /* discard old RX data and clear RXS */
+       bfin_spi_dummy_read(drv_data);
+
        while (drv_data->rx < drv_data->rx_end) {
-               write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-                       cpu_relax();
+               write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-               *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
-               ++drv_data->rx;
-               ++drv_data->tx;
+               *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
        }
 }
 
@@ -352,130 +355,102 @@ static void bfin_spi_u8_cs_chg_duplex(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
 
+       /* discard old RX data and clear RXS */
+       bfin_spi_dummy_read(drv_data);
+
        while (drv_data->rx < drv_data->rx_end) {
                bfin_spi_cs_active(drv_data, chip);
-
-               write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
-
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-                       cpu_relax();
+               write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-               *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
-
+               *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
                bfin_spi_cs_deactive(drv_data, chip);
-
-               ++drv_data->rx;
-               ++drv_data->tx;
        }
 }
 
 static void bfin_spi_u16_writer(struct driver_data *drv_data)
 {
-       dev_dbg(&drv_data->pdev->dev,
-               "cr16 is 0x%x\n", read_STAT(drv_data));
+       /* clear RXS (we check for RXS inside the loop) */
+       bfin_spi_dummy_read(drv_data);
 
        while (drv_data->tx < drv_data->tx_end) {
                write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
-               while ((read_STAT(drv_data) & BIT_STAT_TXS))
-                       cpu_relax();
                drv_data->tx += 2;
+               /* wait until transfer finished.
+                  checking SPIF or TXS may not guarantee transfer completion */
+               while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+                       cpu_relax();
+               /* discard RX data and clear RXS */
+               bfin_spi_dummy_read(drv_data);
        }
-
-       /* poll for SPI completion before return */
-       while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-               cpu_relax();
 }
 
 static void bfin_spi_u16_cs_chg_writer(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
 
+       /* clear RXS (we check for RXS inside the loop) */
+       bfin_spi_dummy_read(drv_data);
+
        while (drv_data->tx < drv_data->tx_end) {
                bfin_spi_cs_active(drv_data, chip);
-
                write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
-               while ((read_STAT(drv_data) & BIT_STAT_TXS))
-                       cpu_relax();
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
+               drv_data->tx += 2;
+               /* make sure transfer finished before deactiving CS */
+               while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-
+               bfin_spi_dummy_read(drv_data);
                bfin_spi_cs_deactive(drv_data, chip);
-
-               drv_data->tx += 2;
        }
 }
 
 static void bfin_spi_u16_reader(struct driver_data *drv_data)
 {
-       dev_dbg(&drv_data->pdev->dev,
-               "cr-16 is 0x%x\n", read_STAT(drv_data));
-
-       /* poll for SPI completion before start */
-       while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-               cpu_relax();
-
-       /* clear TDBR buffer before read(else it will be shifted out) */
-       write_TDBR(drv_data, 0xFFFF);
+       u16 tx_val = drv_data->cur_chip->idle_tx_val;
 
+       /* discard old RX data and clear RXS */
        bfin_spi_dummy_read(drv_data);
 
-       while (drv_data->rx < (drv_data->rx_end - 2)) {
+       while (drv_data->rx < drv_data->rx_end) {
+               write_TDBR(drv_data, tx_val);
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
                *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
                drv_data->rx += 2;
        }
-
-       while (!(read_STAT(drv_data) & BIT_STAT_RXS))
-               cpu_relax();
-       *(u16 *) (drv_data->rx) = read_SHAW(drv_data);
-       drv_data->rx += 2;
 }
 
 static void bfin_spi_u16_cs_chg_reader(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
+       u16 tx_val = chip->idle_tx_val;
 
-       /* poll for SPI completion before start */
-       while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-               cpu_relax();
-
-       /* clear TDBR buffer before read(else it will be shifted out) */
-       write_TDBR(drv_data, 0xFFFF);
-
-       bfin_spi_cs_active(drv_data, chip);
+       /* discard old RX data and clear RXS */
        bfin_spi_dummy_read(drv_data);
 
-       while (drv_data->rx < drv_data->rx_end - 2) {
-               bfin_spi_cs_deactive(drv_data, chip);
-
+       while (drv_data->rx < drv_data->rx_end) {
+               bfin_spi_cs_active(drv_data, chip);
+               write_TDBR(drv_data, tx_val);
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
-               bfin_spi_cs_active(drv_data, chip);
                *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
                drv_data->rx += 2;
+               bfin_spi_cs_deactive(drv_data, chip);
        }
-       bfin_spi_cs_deactive(drv_data, chip);
-
-       while (!(read_STAT(drv_data) & BIT_STAT_RXS))
-               cpu_relax();
-       *(u16 *) (drv_data->rx) = read_SHAW(drv_data);
-       drv_data->rx += 2;
 }
 
 static void bfin_spi_u16_duplex(struct driver_data *drv_data)
 {
-       /* in duplex mode, clk is triggered by writing of TDBR */
-       while (drv_data->tx < drv_data->tx_end) {
+       /* discard old RX data and clear RXS */
+       bfin_spi_dummy_read(drv_data);
+
+       while (drv_data->rx < drv_data->rx_end) {
                write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-                       cpu_relax();
+               drv_data->tx += 2;
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
                *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
                drv_data->rx += 2;
-               drv_data->tx += 2;
        }
 }
 
@@ -483,20 +458,18 @@ static void bfin_spi_u16_cs_chg_duplex(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
 
-       while (drv_data->tx < drv_data->tx_end) {
-               bfin_spi_cs_active(drv_data, chip);
+       /* discard old RX data and clear RXS */
+       bfin_spi_dummy_read(drv_data);
 
+       while (drv_data->rx < drv_data->rx_end) {
+               bfin_spi_cs_active(drv_data, chip);
                write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
-               while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
-                       cpu_relax();
+               drv_data->tx += 2;
                while (!(read_STAT(drv_data) & BIT_STAT_RXS))
                        cpu_relax();
                *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
-
-               bfin_spi_cs_deactive(drv_data, chip);
-
                drv_data->rx += 2;
-               drv_data->tx += 2;
+               bfin_spi_cs_deactive(drv_data, chip);
        }
 }
 
@@ -673,6 +646,13 @@ static void bfin_spi_pump_transfers(unsigned long data)
                return;
        }
 
+       if (transfer->len == 0) {
+               /* Move to next transfer of this msg */
+               message->state = bfin_spi_next_transfer(drv_data);
+               /* Schedule next transfer tasklet */
+               tasklet_schedule(&drv_data->pump_transfers);
+       }
+
        if (transfer->tx_buf != NULL) {
                drv_data->tx = (void *)transfer->tx_buf;
                drv_data->tx_end = drv_data->tx + transfer->len;
@@ -823,14 +803,11 @@ static void bfin_spi_pump_transfers(unsigned long data)
                                drv_data->rx, drv_data->len_in_bytes);
 
                        /* invalidate caches, if needed */
-                       if (bfin_addr_dcachable((unsigned long) drv_data->rx))
+                       if (bfin_addr_dcacheable((unsigned long) drv_data->rx))
                                invalidate_dcache_range((unsigned long) drv_data->rx,
                                                        (unsigned long) (drv_data->rx +
                                                        drv_data->len_in_bytes));
 
-                       /* clear tx reg soformer data is not shifted out */
-                       write_TDBR(drv_data, 0xFFFF);
-
                        dma_config |= WNR;
                        dma_start_addr = (unsigned long)drv_data->rx;
                        cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT;
@@ -839,7 +816,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
                        dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n");
 
                        /* flush caches, if needed */
-                       if (bfin_addr_dcachable((unsigned long) drv_data->tx))
+                       if (bfin_addr_dcacheable((unsigned long) drv_data->tx))
                                flush_dcache_range((unsigned long) drv_data->tx,
                                                (unsigned long) (drv_data->tx +
                                                drv_data->len_in_bytes));
@@ -872,6 +849,11 @@ static void bfin_spi_pump_transfers(unsigned long data)
                /* IO mode write then read */
                dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
 
+               /* we always use SPI_WRITE mode. SPI_READ mode
+                  seems to have problems with setting up the
+                  output value in TDBR prior to the transfer. */
+               write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
+
                if (full_duplex) {
                        /* full duplex mode */
                        BUG_ON((drv_data->tx_end - drv_data->tx) !=
@@ -879,9 +861,6 @@ static void bfin_spi_pump_transfers(unsigned long data)
                        dev_dbg(&drv_data->pdev->dev,
                                "IO duplex: cr is 0x%x\n", cr);
 
-                       /* set SPI transfer mode */
-                       write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
-
                        drv_data->duplex(drv_data);
 
                        if (drv_data->tx != drv_data->tx_end)
@@ -891,9 +870,6 @@ static void bfin_spi_pump_transfers(unsigned long data)
                        dev_dbg(&drv_data->pdev->dev,
                                "IO write: cr is 0x%x\n", cr);
 
-                       /* set SPI transfer mode */
-                       write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
-
                        drv_data->write(drv_data);
 
                        if (drv_data->tx != drv_data->tx_end)
@@ -903,9 +879,6 @@ static void bfin_spi_pump_transfers(unsigned long data)
                        dev_dbg(&drv_data->pdev->dev,
                                "IO read: cr is 0x%x\n", cr);
 
-                       /* set SPI transfer mode */
-                       write_CTRL(drv_data, (cr | CFG_SPI_READ));
-
                        drv_data->read(drv_data);
                        if (drv_data->rx != drv_data->rx_end)
                                tranf_success = 0;
@@ -925,7 +898,6 @@ static void bfin_spi_pump_transfers(unsigned long data)
                }
                /* Schedule next transfer tasklet */
                tasklet_schedule(&drv_data->pump_transfers);
-
        }
 }
 
@@ -1036,6 +1008,7 @@ static int bfin_spi_setup(struct spi_device *spi)
        struct bfin5xx_spi_chip *chip_info = NULL;
        struct chip_data *chip;
        struct driver_data *drv_data = spi_master_get_devdata(spi->master);
+       int ret;
 
        /* Abort device setup if requested features are not supported */
        if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) {
@@ -1081,6 +1054,8 @@ static int bfin_spi_setup(struct spi_device *spi)
                chip->bits_per_word = chip_info->bits_per_word;
                chip->cs_change_per_word = chip_info->cs_change_per_word;
                chip->cs_chg_udelay = chip_info->cs_chg_udelay;
+               chip->cs_gpio = chip_info->cs_gpio;
+               chip->idle_tx_val = chip_info->idle_tx_val;
        }
 
        /* translate common spi framework into our register */
@@ -1121,6 +1096,16 @@ static int bfin_spi_setup(struct spi_device *spi)
        chip->flag = 1 << (spi->chip_select);
        chip->chip_select_num = spi->chip_select;
 
+       if (chip->chip_select_num == 0) {
+               ret = gpio_request(chip->cs_gpio, spi->modalias);
+               if (ret) {
+                       if (drv_data->dma_requested)
+                               free_dma(drv_data->dma_channel);
+                       return ret;
+               }
+               gpio_direction_output(chip->cs_gpio, 1);
+       }
+
        switch (chip->bits_per_word) {
        case 8:
                chip->n_bytes = 1;
@@ -1178,11 +1163,17 @@ static void bfin_spi_cleanup(struct spi_device *spi)
 {
        struct chip_data *chip = spi_get_ctldata(spi);
 
+       if (!chip)
+               return;
+
        if ((chip->chip_select_num > 0)
                && (chip->chip_select_num <= spi->master->num_chipselect))
                peripheral_free(ssel[spi->master->bus_num]
                                        [chip->chip_select_num-1]);
 
+       if (chip->chip_select_num == 0)
+               gpio_free(chip->cs_gpio);
+
        kfree(chip);
 }