back-to-back SPI transfers occur when the 'more' bit is set in either
the outgoing or incoming SPI message header. In this case the
protocol driver should keep MRDY asserted and initiate the next
SPI message immediately minimizing delay between clocking cycles.

this patch address that by implementing the required logic and fixing
the incorrect calculation of the 'more' and 'cts' bits in the SPI header.

Signed-off-by: Russ Gorby <russ.go...@intel.com>
---
 drivers/serial/ifx6x60.c |  163 +++++++++++++++++++++++++---------------------
 drivers/serial/ifx6x60.h |    4 +-
 2 files changed, 91 insertions(+), 76 deletions(-)

diff --git a/drivers/serial/ifx6x60.c b/drivers/serial/ifx6x60.c
index db84fa8..8284132 100644
--- a/drivers/serial/ifx6x60.c
+++ b/drivers/serial/ifx6x60.c
@@ -56,9 +56,10 @@
 
 #include "ifx6x60.h"
 
-#define IFX_SPI_MORE_MASK              0x10
 #define IFX_SPI_MORE_BIT               12      /* bit position in u16 */
-#define IFX_SPI_CTS_BIT                        13      /* bit position in u16 
*/
+#define IFX_SPI_MORE_MASK              (1<<IFX_SPI_MORE_BIT)   /* u16 mask */
+#define IFX_SPI_CTS_BIT                        14      /* bit position in u16 
*/
+#define IFX_SPI_CTS_MASK               (1<<IFX_SPI_CTS_BIT)    /* u16 mask */
 #define IFX_SPI_MODE                   SPI_MODE_1
 #define IFX_SPI_TTY_ID                 0
 #define IFX_SPI_TIMEOUT_SEC            2
@@ -106,6 +107,21 @@ MODULE_PARM_DESC(ignore_srdy_start,
 /* GPIO/GPE settings */
 
 /**
+ *     spi_more                -       back-to-back spi transaction condition
+ *
+ *     This tests the following condition:
+ *     ((master-able-to-receive && slave-has-more)
+ *      || (slave-able-to-receive && master-has-more))
+ *
+ *     FIXME: need to implement RTS logic on master side
+ */
+static inline bool spi_more(struct ifx_spi_device *ifx_dev)
+{
+       return (ifx_dev->spi_more_in && !ifx_dev->spi_master_rts) ||
+               (ifx_dev->spi_more_out && !ifx_dev->spi_slave_cts);
+}
+
+/**
  *     mrdy_set_high           -       set MRDY GPIO
  *
  */
@@ -475,8 +491,8 @@ static int ifx_spi_decode_spi_header(unsigned char *buffer, 
int *length,
        }
 
        *length = h1 & 0xfff;   /* upper bits of byte are flags */
-       *more = (buffer[1] >> IFX_SPI_MORE_BIT) & 1;
-       *received_cts = (buffer[3] >> IFX_SPI_CTS_BIT) & 1;
+       *more = (h1 & IFX_SPI_MORE_MASK) != 0;
+       *received_cts = (h2 & IFX_SPI_CTS_MASK) != 0;
 
        dev_dbg(&saved_ifx_dev->spi_dev->dev,
                "(%x %x %x %x) received length %d, more %d, cts %d",
@@ -499,9 +515,10 @@ static int ifx_spi_decode_spi_header(unsigned char 
*buffer, int *length,
 static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count,
                                        unsigned char more)
 {
-       *(u16 *)(txbuffer) = tx_count;
-       *(u16 *)(txbuffer+2) = IFX_SPI_PAYLOAD_SIZE;
-       txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK;
+       u16 *spi = (u16 *)txbuffer;
+       spi[0] = tx_count;
+       spi[0] |= ((more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK);
+       spi[1] = IFX_SPI_PAYLOAD_SIZE;
 }
 
 /**
@@ -551,12 +568,12 @@ static int ifx_spi_prepare_tx_buffer(struct 
ifx_spi_device *ifx_dev)
        tx_buffer += IFX_SPI_HEADER_OVERHEAD;
        tx_count = IFX_SPI_HEADER_OVERHEAD;
 
-       /* clear to signal no more data if this turns out to be the
-        * last buffer sent in a sequence */
-       ifx_dev->spi_more = 0;
-
        /* if modem cts is set, just send empty buffer */
-       if (!(ifx_dev->spi_slave_cts)) {
+       if (!ifx_dev->spi_slave_cts) {
+               /* clear to signal no more data if this turns out to be the
+                * last buffer sent in a sequence */
+               ifx_dev->spi_more_out = 0;
+
                /* see if there's tx data */
                queue_length = kfifo_len(&port_data->tx_fifo);
                if (queue_length != 0) {
@@ -586,7 +603,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device 
*ifx_dev)
                                ifx_spi_wakeup_serial(port_data);
                        } else {
                                /* more data in port, use next SPI message */
-                               ifx_dev->spi_more = 1;
+                               ifx_dev->spi_more_out = 1;
                        }
                }
        } else {
@@ -597,7 +614,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device 
*ifx_dev)
        /* spi header needs payload size, not entire buffer size */
        ifx_spi_setup_spi_header(ifx_dev->tx_buffer,
                                        tx_count-IFX_SPI_HEADER_OVERHEAD,
-                                       ifx_dev->spi_more);
+                                       ifx_dev->spi_more_out);
        /* swap actual data in the buffer */
        swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count,
                &ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]);
@@ -963,19 +980,13 @@ static void ifx_spi_complete(void *ctx)
        struct tty_ldisc *ldisc = NULL;
        int length = 0;
        int actual_length = 0;
-       unsigned char more = 0;
-       unsigned char cts = 0;
-       int local_write_pending = 0;
        int queue_length = 0;
        int srdy = 0;
        int decode_result;
 
        dev_dbg(&ifx_dev->spi_dev->dev, "SPI completion");
-
-
-       dev_dbg(&ifx_dev->spi_dev->dev, "mrdy set low");
-       mrdy_set_low(ifx_dev);
-
+       ifx_dev->spi_more_in = 0;
+       ifx_dev->spi_slave_cts = 0;
        if (!ifx_dev->spi_msg.status) {
                /* check header validity, get comm flags */
                swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
@@ -988,11 +999,11 @@ static void ifx_spi_complete(void *ctx)
                ifx_dev->rx_buffer[4]);
                port_data = ifx_dev->port_data;
                decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer,
-                               &length, &more, &cts);
+                         &length, &ifx_dev->spi_more_in,
+                         &ifx_dev->spi_slave_cts);
                if (decode_result == IFX_SPI_HEADER_0) {
                        dev_dbg(&ifx_dev->spi_dev->dev,
                                "ignore input: invalid header 0");
-                       ifx_dev->spi_slave_cts = 0;
                        goto complete_exit;
                } else if (decode_result == IFX_SPI_HEADER_F) {
                        dev_dbg(&ifx_dev->spi_dev->dev,
@@ -1000,8 +1011,8 @@ static void ifx_spi_complete(void *ctx)
                        goto complete_exit;
                }
 
-               ifx_dev->spi_slave_cts = cts;
-               dev_dbg(&ifx_dev->spi_dev->dev, "set cts %d", cts);
+               dev_dbg(&ifx_dev->spi_dev->dev, "set cts %d",
+                       ifx_dev->spi_slave_cts);
                actual_length = min((unsigned int)length,
                                        ifx_dev->spi_msg.actual_length);
                swap_buf((u16 *)(ifx_dev->rx_buffer+IFX_SPI_HEADER_OVERHEAD),
@@ -1018,9 +1029,11 @@ static void ifx_spi_complete(void *ctx)
        }
 
 complete_exit:
-       if (ifx_dev->write_pending) {
-               ifx_dev->write_pending = 0;
-               local_write_pending = 1;
+       /* we only keep MRDY high if the more bit is indicated in the
+          outbound or inbound SPI message */
+       if (!spi_more(ifx_dev)) {
+               dev_dbg(&ifx_dev->spi_dev->dev, "mrdy set low");
+               mrdy_set_low(ifx_dev);
        }
 
        clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags));
@@ -1037,55 +1050,60 @@ complete_exit:
        dev_dbg(&ifx_dev->spi_dev->dev, "ifx_spi_complete(): rx_more = %d, "
                "tx_more = %d, cts = %d, "
                "tx_queue_length = %d, srdy = %x, write_pending = %d",
-               more, ifx_dev->spi_more, ifx_dev->spi_slave_cts,
-               queue_length, srdy, local_write_pending);
+               ifx_dev->spi_more_in, ifx_dev->spi_more_out,
+               ifx_dev->spi_slave_cts, queue_length, srdy,
+               ifx_dev->write_pending);
 
-       /* schedule output if there is more to do */
+       /* check if we had an SRDY interrupt */
        if (test_and_clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags)) {
                dev_dbg(&ifx_dev->spi_dev->dev,
                        "io_ready set, reschedule tasklet for more");
                tasklet_schedule(&ifx_dev->io_work_tasklet);
-       } else {
-               if (more || ifx_dev->spi_more || queue_length > 0 ||
-                       local_write_pending) {
-                       if (ifx_dev->spi_slave_cts) {
-                               if (more) {
-                                       dev_dbg(&ifx_dev->spi_dev->dev,
-                                               "cts 1, more 1");
-                                       mrdy_assert(ifx_dev);
-                               } else {
-                                       dev_dbg(&ifx_dev->spi_dev->dev,
-                                               "cts 1, more 0");
-                               }
-                       } else {
+               return;
+       }
+       /* check more conditions */
+       if (spi_more(ifx_dev)) {
+               /*
+                * if spi_more() conditions are met, MRDY remains
+                * high but modem will toggle SRDY between SPI frames
+                * which will reschedule the tasklet
+                */
+               dev_dbg(&ifx_dev->spi_dev->dev,
+                       "spi_more, await srdy interrupt");
+               return;
+       }
+       /* check edge cases */
+       if ((queue_length > 0 && !ifx_dev->spi_slave_cts) ||
+           (ifx_dev->write_pending && !ifx_dev->spi_master_rts)) {
+               dev_dbg(&ifx_dev->spi_dev->dev,
+                       "mrdy set high ql:%d wp:%d", queue_length,
+                       ifx_dev->write_pending);
+               ifx_dev->write_pending = 0;
+               mrdy_assert(ifx_dev);
+               return;
+       }
+       /*
+        * either we have no data to send/receive or we're stopped
+        *
+        * If we could send, poke line discipline driver if any for more data
+        * which we may or may not get
+        */
+       ifx_spi_power_state_clear(ifx_dev,
+                                 IFX_SPI_POWER_DATA_PENDING);
+       if (port_data && !ifx_dev->spi_slave_cts) {
+               tty = tty_port_tty_get(
+                       &port_data->serial.tty_port);
+               if (tty) {
+                       ldisc = tty_ldisc_ref(tty);
+                       if (ldisc) {
                                dev_dbg(&ifx_dev->spi_dev->dev,
-                                       "mrdy set high (spi comp)");
-                               mrdy_assert(ifx_dev);
-                       }
-               } else {
-                       /*
-                        * poke line discipline driver if any for more data
-                        * may or may not get more data to write
-                        * for now, say not busy
-                        */
-                       ifx_spi_power_state_clear(ifx_dev,
-                                                 IFX_SPI_POWER_DATA_PENDING);
-                       if (port_data) {
-                               tty = tty_port_tty_get(
-                                       &port_data->serial.tty_port);
-                               if (tty) {
-                                       ldisc = tty_ldisc_ref(tty);
-                                       if (ldisc) {
-                                               dev_dbg(&ifx_dev->spi_dev->dev,
-                                                     "call line disc wakeup");
-                                               ldisc->ops->write_wakeup(tty);
-                                               tty_ldisc_deref(ldisc);
-                                       } else {
+                                       "call line disc wakeup");
+                               ldisc->ops->write_wakeup(tty);
+                               tty_ldisc_deref(ldisc);
+                       } else {
                                schedule_work(&ifx_dev->write_wakeup_work);
-                                       }
-                                       tty_kref_put(tty);
-                               }
                        }
+                       tty_kref_put(tty);
                }
        }
 }
@@ -1495,7 +1513,6 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
        clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags);
        spin_lock_init(&ifx_dev->write_lock);
        spin_lock_init(&ifx_dev->power_lock);
-       ifx_dev->power_status = 0;
        init_timer(&ifx_dev->spi_timer);
        init_timer(&ifx_dev->spi_retry_timer);
        ifx_dev->spi_timer.function = ifx_spi_timeout;
@@ -1505,10 +1522,6 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
        INIT_WORK(&(ifx_dev->write_wakeup_work),
                        (work_func_t)ifx_spi_write_wakeup_work);
 
-       /* ensure SPI protocol flags are initialized to enable transfer */
-       ifx_dev->spi_more = 0;
-       ifx_dev->spi_slave_cts = 0;
-
        /*initialize transfer and dma buffers */
        ifx_dev->tx_buffer = kzalloc(IFX_SPI_TRANSFER_SIZE,
                GFP_KERNEL | GFP_DMA);
diff --git a/drivers/serial/ifx6x60.h b/drivers/serial/ifx6x60.h
index da1efc6..4cc5934 100644
--- a/drivers/serial/ifx6x60.h
+++ b/drivers/serial/ifx6x60.h
@@ -101,8 +101,10 @@ struct ifx_spi_device {
 
        unsigned char *rx_buffer;
        unsigned char *tx_buffer;
-       unsigned char spi_more;
+       unsigned char spi_more_out;
+       unsigned char spi_more_in;
        unsigned char spi_slave_cts;
+       unsigned char spi_master_rts;
 
        struct timer_list spi_timer;
        struct timer_list spi_retry_timer;
-- 
1.6.0.6

_______________________________________________
MeeGo-kernel mailing list
MeeGo-kernel@lists.meego.com
http://lists.meego.com/listinfo/meego-kernel

Reply via email to