On Wed, Apr 21, 2010 at 01:00:56PM -0500, H Hartley Sweeten wrote:
>  
> Same results are your v4 driver.  But, I think your on the right track.
> 
> I think the problem is in the ep93xx_spi_read_write routine.  That function
> returns 0 as long as there is still data left in the current transfer.  The
> only time it doesn't return 0, which will cause the can_continue, is when:
> 
>       if (espi->rx == t->len) {
>               msg->actual_length += t->len;
>               return t->len;
>       }
> 
> At this point the tx and rx fifos will both be empty, which causes the SSP
> peripheral to raise the SFRM signal.

Hi again,

I crafted up next hack. It includes also your priming the FIFO
finding (thanks for that). Following patch is against v4 version
of the driver.

It performs another read/write after given transfer is finished.
I'm hoping that it could make it before SFRMOUT is deasserted.

Could you test this in your setup?

Thanks,
MW

diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c
index 310032d..7a5c89e 100644
--- a/drivers/spi/ep93xx_spi.c
+++ b/drivers/spi/ep93xx_spi.c
@@ -105,6 +105,7 @@ struct ep93xx_spi {
        int                             irq;
        unsigned long                   min_rate;
        unsigned long                   max_rate;
+       bool                            continuous;
        bool                            running;
        struct workqueue_struct         *wq;
        struct work_struct              msg_work;
@@ -380,6 +381,7 @@ static int ep93xx_spi_transfer(struct spi_device *spi, 
struct spi_message *msg)
        struct ep93xx_spi *espi = spi_master_get_devdata(spi->master);
        struct spi_transfer *t;
        unsigned long flags;
+       bool continuous = true;
 
        if (!msg || !msg->complete)
                return -EINVAL;
@@ -389,9 +391,19 @@ static int ep93xx_spi_transfer(struct spi_device *spi, 
struct spi_message *msg)
                if (t->bits_per_word) {
                        if (t->bits_per_word < 4 || t->bits_per_word > 16)
                                return -EINVAL;
+                       continuous = false;
                }
-               if (t->speed_hz && t->speed_hz < espi->min_rate)
+               if (t->speed_hz) {
+                       if (t->speed_hz < espi->min_rate)
                                return -EINVAL;
+                       continuous = false;
+               }
+               if (t->cs_change) {
+                       if (!list_is_last(&t->transfer_list, &msg->transfers))
+                               continuous = false;
+               }
+               if (t->delay_usecs)
+                       continuous = false;
        }
 
        /*
@@ -399,8 +411,11 @@ static int ep93xx_spi_transfer(struct spi_device *spi, 
struct spi_message *msg)
         * suitable for us. We use @msg->status to signal whether there was
         * error in transfer and @msg->state is used to hold pointer to the
         * current transfer (or %NULL if no active current transfer).
+        *
+        * We hold continuation information in @msg->state while it is not
+        * processed.
         */
-       msg->state = NULL;
+       msg->state = (void *)continuous;
        msg->status = 0;
        msg->actual_length = 0;
 
@@ -550,6 +565,91 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi 
*espi,
                ep93xx_spi_chip_setup(espi, chip);
 }
 
+/**
+ * bits_per_word() - returns bits per word for current message
+ */
+static inline int bits_per_word(const struct ep93xx_spi *espi)
+{
+       struct spi_message *msg = espi->current_msg;
+       struct spi_transfer *t = msg->state;
+
+       return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word;
+}
+
+static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
+{
+       if (bits_per_word(espi) > 8) {
+               u16 tx_val = 0;
+
+               if (t->tx_buf)
+                       tx_val = ((u16 *)t->tx_buf)[espi->tx];
+               ep93xx_spi_write_u16(espi, SSPDR, tx_val);
+               espi->tx += sizeof(tx_val);
+       } else {
+               u8 tx_val = 0;
+
+               if (t->tx_buf)
+                       tx_val = ((u8 *)t->tx_buf)[espi->tx];
+               ep93xx_spi_write_u8(espi, SSPDR, tx_val);
+               espi->tx += sizeof(tx_val);
+       }
+}
+
+static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
+{
+       if (bits_per_word(espi) > 8) {
+               u16 rx_val;
+
+               rx_val = ep93xx_spi_read_u16(espi, SSPDR);
+               if (t->rx_buf)
+                       ((u16 *)t->rx_buf)[espi->rx] = rx_val;
+               espi->rx += sizeof(rx_val);
+       } else {
+               u8 rx_val;
+
+               rx_val = ep93xx_spi_read_u8(espi, SSPDR);
+               if (t->rx_buf)
+                       ((u8 *)t->rx_buf)[espi->rx] = rx_val;
+               espi->rx += sizeof(rx_val);
+       }
+}
+
+/**
+ * ep93xx_spi_read_write() - perform next RX/TX transfer
+ * @espi: ep93xx SPI controller struct
+ *
+ * This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If
+ * called several times, the whole transfer will be completed. Returns %0 when
+ * current transfer was not yet completed otherwise length of the transfer
+ * (>%0). When this function is finished, RX FIFO should be empty and TX FIFO
+ * should be full.
+ */
+static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
+{
+       struct spi_message *msg = espi->current_msg;
+       struct spi_transfer *t = msg->state;
+
+       /* read as long as RX FIFO has frames in it */
+       while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) {
+               ep93xx_do_read(espi, t);
+               espi->fifo_level--;
+       }
+
+       /* write as long as TX FIFO has room */
+       while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) {
+               ep93xx_do_write(espi, t);
+               espi->fifo_level++;
+       }
+
+       if (espi->rx == t->len) {
+               msg->actual_length += t->len;
+               return t->len;
+       }
+
+       return 0;
+}
+
+
 /*
  * ep93xx_spi_process_message() - process one SPI message
  * @espi: ep93xx SPI controller struct
@@ -569,6 +669,8 @@ static void ep93xx_spi_process_message(struct ep93xx_spi 
*espi,
        struct spi_transfer *t;
        int err;
 
+       espi->continuous = (bool)msg->state;
+
        /*
         * Enable the SPI controller and its clock.
         */
@@ -606,12 +708,43 @@ static void ep93xx_spi_process_message(struct ep93xx_spi 
*espi,
        ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi));
        ep93xx_spi_select_device(espi, msg->spi);
 
-       list_for_each_entry(t, &msg->transfers, transfer_list) {
-               ep93xx_spi_process_transfer(espi, msg, t);
-               if (msg->status)
-                       break;
+       if (espi->continuous) {
+               /*
+                * We can transfer all the transfers continuously.
+                */
+               espi->rx = 0;
+               espi->tx = 0;
+               msg->state = list_first_entry(&msg->transfers, struct 
spi_transfer,
+                                       transfer_list);
+
+               /* prime the first transfer */
+               if (ep93xx_spi_read_write(espi)) {
+                       /*
+                        * Transfer was completed. Pick next one and continue
+                        * if necessary.
+                        */
+                       t = msg->state;
+                       if (list_is_last(&t->transfer_list, &msg->transfers))
+                               goto done;
+
+                       espi->rx = 0;
+                       espi->tx = 0;
+                       msg->state = list_entry(t->transfer_list.next,
+                                               struct spi_transfer,
+                                               transfer_list);
+               }
+
+               ep93xx_spi_enable_interrupts(espi);
+               wait_for_completion(&espi->wait);
+       } else {
+               list_for_each_entry(t, &msg->transfers, transfer_list) {
+                       ep93xx_spi_process_transfer(espi, msg, t);
+                       if (msg->status)
+                               break;
+               }
        }
 
+done:
        /*
         * Now the whole message is transferred (or failed for some reason). We
         * deselect the device and disable the SPI controller.
@@ -667,90 +800,6 @@ static void ep93xx_spi_work(struct work_struct *work)
 }
 
 /**
- * bits_per_word() - returns bits per word for current message
- */
-static inline int bits_per_word(const struct ep93xx_spi *espi)
-{
-       struct spi_message *msg = espi->current_msg;
-       struct spi_transfer *t = msg->state;
-
-       return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word;
-}
-
-static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
-{
-       if (bits_per_word(espi) > 8) {
-               u16 tx_val = 0;
-
-               if (t->tx_buf)
-                       tx_val = ((u16 *)t->tx_buf)[espi->tx];
-               ep93xx_spi_write_u16(espi, SSPDR, tx_val);
-               espi->tx += sizeof(tx_val);
-       } else {
-               u8 tx_val = 0;
-
-               if (t->tx_buf)
-                       tx_val = ((u8 *)t->tx_buf)[espi->tx];
-               ep93xx_spi_write_u8(espi, SSPDR, tx_val);
-               espi->tx += sizeof(tx_val);
-       }
-}
-
-static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
-{
-       if (bits_per_word(espi) > 8) {
-               u16 rx_val;
-
-               rx_val = ep93xx_spi_read_u16(espi, SSPDR);
-               if (t->rx_buf)
-                       ((u16 *)t->rx_buf)[espi->rx] = rx_val;
-               espi->rx += sizeof(rx_val);
-       } else {
-               u8 rx_val;
-
-               rx_val = ep93xx_spi_read_u8(espi, SSPDR);
-               if (t->rx_buf)
-                       ((u8 *)t->rx_buf)[espi->rx] = rx_val;
-               espi->rx += sizeof(rx_val);
-       }
-}
-
-/**
- * ep93xx_spi_read_write() - perform next RX/TX transfer
- * @espi: ep93xx SPI controller struct
- *
- * This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If
- * called several times, the whole transfer will be completed. Returns %0 when
- * current transfer was not yet completed otherwise length of the transfer
- * (>%0). When this function is finished, RX FIFO should be empty and TX FIFO
- * should be full.
- */
-static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
-{
-       struct spi_message *msg = espi->current_msg;
-       struct spi_transfer *t = msg->state;
-
-       /* read as long as RX FIFO has frames in it */
-       while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) {
-               ep93xx_do_read(espi, t);
-               espi->fifo_level--;
-       }
-
-       /* write as long as TX FIFO has room */
-       while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) {
-               ep93xx_do_write(espi, t);
-               espi->fifo_level++;
-       }
-
-       if (espi->rx == t->len) {
-               msg->actual_length += t->len;
-               return t->len;
-       }
-
-       return 0;
-}
-
-/**
  * ep93xx_spi_interrupt() - SPI interrupt handler
  * @irq: IRQ number (not used)
  * @dev_id: pointer to EP93xx controller struct
@@ -792,6 +841,27 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void 
*dev_id)
                         * interrupt then.
                         */
                        return IRQ_HANDLED;
+               } else {
+                       struct spi_message *msg = espi->current_msg;
+                       struct spi_transfer *t = msg->state;
+
+                       if (!espi->continuous)
+                               goto done;
+
+                       if (!list_is_last(&t->transfer_list, &msg->transfers)) {
+                               /*
+                                * Pick next transfer and refill the FIFO.
+                                */
+                               msg->state = list_entry(t->transfer_list.next,
+                                                       struct spi_transfer,
+                                                       transfer_list);
+                               espi->rx = 0;
+                               espi->tx = 0;
+
+                               ep93xx_spi_read_write(espi);
+                               return IRQ_HANDLED;
+                       }
+                       /* we are done here */
                }
        }
 
@@ -800,6 +870,7 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void 
*dev_id)
         * any case we disable interrupts and notify the worker to handle
         * any post-processing of the message.
         */
+done:
        ep93xx_spi_disable_interrupts(espi);
        complete(&espi->wait);
        return IRQ_HANDLED;

------------------------------------------------------------------------------
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

Reply via email to