Merge remote-tracking branches 'spi/topic/spidev', 'spi/topic/spidev-test', 'spi...
authorMark Brown <broonie@kernel.org>
Wed, 26 Apr 2017 14:58:16 +0000 (15:58 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 26 Apr 2017 14:58:16 +0000 (15:58 +0100)
drivers/spi/spi-loopback-test.c
drivers/spi/spi-sun6i.c
drivers/spi/spi-tegra114.c
drivers/spi/spi-tegra20-sflash.c
drivers/spi/spi-tegra20-slink.c
drivers/spi/spi-test.h
drivers/spi/spidev.c
tools/spi/spidev_test.c

index 4e7843ec8aac8af5f486199f18b0c0e7a18b3809..f4875f177df04fe3ae9c8f334b534b3119ba6672 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/delay.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/list.h>
 #include <linux/list_sort.h>
 #include <linux/module.h>
@@ -76,9 +77,9 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_rx_align = ITERATE_ALIGN,
+               .transfer_count = 1,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(0),
                                .rx_buf = RX(0),
                        },
@@ -90,9 +91,9 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_rx_align = ITERATE_ALIGN,
+               .transfer_count = 1,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(PAGE_SIZE - 4),
                                .rx_buf = RX(PAGE_SIZE - 4),
                        },
@@ -103,9 +104,9 @@ static struct spi_test spi_tests[] = {
                .fill_option    = FILL_COUNT_8,
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
+               .transfer_count = 1,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(0),
                        },
                },
@@ -115,9 +116,9 @@ static struct spi_test spi_tests[] = {
                .fill_option    = FILL_COUNT_8,
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_rx_align = ITERATE_ALIGN,
+               .transfer_count = 1,
                .transfers              = {
                        {
-                               .len = 1,
                                .rx_buf = RX(0),
                        },
                },
@@ -128,13 +129,12 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(0) | BIT(1),
+               .transfer_count = 2,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(0),
                        },
                        {
-                               .len = 1,
                                /* this is why we cant use ITERATE_MAX_LEN */
                                .tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
                        },
@@ -145,10 +145,10 @@ static struct spi_test spi_tests[] = {
                .fill_option    = FILL_COUNT_8,
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
-               .iterate_transfer_mask = BIT(1),
+               .iterate_transfer_mask = BIT(0),
+               .transfer_count = 2,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(64),
                        },
                        {
@@ -162,14 +162,14 @@ static struct spi_test spi_tests[] = {
                .fill_option    = FILL_COUNT_8,
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
-               .iterate_transfer_mask = BIT(0),
+               .iterate_transfer_mask = BIT(1),
+               .transfer_count = 2,
                .transfers              = {
                        {
                                .len = 16,
                                .tx_buf = TX(0),
                        },
                        {
-                               .len = 1,
                                .tx_buf = TX(64),
                        },
                },
@@ -180,13 +180,12 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(0) | BIT(1),
+               .transfer_count = 2,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(0),
                        },
                        {
-                               .len = 1,
                                .rx_buf = RX(0),
                        },
                },
@@ -197,9 +196,9 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(0),
+               .transfer_count = 2,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(0),
                        },
                        {
@@ -214,13 +213,13 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(1),
+               .transfer_count = 2,
                .transfers              = {
                        {
                                .len = 1,
                                .tx_buf = TX(0),
                        },
                        {
-                               .len = 1,
                                .rx_buf = RX(0),
                        },
                },
@@ -231,14 +230,13 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(0) | BIT(1),
+               .transfer_count = 2,
                .transfers              = {
                        {
-                               .len = 1,
                                .tx_buf = TX(0),
                                .rx_buf = RX(0),
                        },
                        {
-                               .len = 1,
                                /* making sure we align without overwrite
                                 * the reason we can not use ITERATE_MAX_LEN
                                 */
@@ -253,9 +251,9 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(0),
+               .transfer_count = 2,
                .transfers              = {
                        {
-                               .len = 1,
                                /* making sure we align without overwrite */
                                .tx_buf = TX(1024),
                                .rx_buf = RX(1024),
@@ -274,6 +272,7 @@ static struct spi_test spi_tests[] = {
                .iterate_len    = { ITERATE_MAX_LEN },
                .iterate_tx_align = ITERATE_ALIGN,
                .iterate_transfer_mask = BIT(1),
+               .transfer_count = 2,
                .transfers              = {
                        {
                                .len = 1,
@@ -281,13 +280,31 @@ static struct spi_test spi_tests[] = {
                                .rx_buf = RX(0),
                        },
                        {
-                               .len = 1,
                                /* making sure we align without overwrite */
                                .tx_buf = TX(1024),
                                .rx_buf = RX(1024),
                        },
                },
        },
+       {
+               .description    = "two tx+rx transfers - delay after transfer",
+               .fill_option    = FILL_COUNT_8,
+               .iterate_len    = { ITERATE_MAX_LEN },
+               .iterate_transfer_mask = BIT(0) | BIT(1),
+               .transfer_count = 2,
+               .transfers              = {
+                       {
+                               .tx_buf = TX(0),
+                               .rx_buf = RX(0),
+                               .delay_usecs = 1000,
+                       },
+                       {
+                               .tx_buf = TX(0),
+                               .rx_buf = RX(0),
+                               .delay_usecs = 1000,
+                       },
+               },
+       },
 
        { /* end of tests sequence */ }
 };
@@ -495,6 +512,36 @@ static int spi_check_rx_ranges(struct spi_device *spi,
        return ret;
 }
 
+static int spi_test_check_elapsed_time(struct spi_device *spi,
+                                      struct spi_test *test)
+{
+       int i;
+       unsigned long long estimated_time = 0;
+       unsigned long long delay_usecs = 0;
+
+       for (i = 0; i < test->transfer_count; i++) {
+               struct spi_transfer *xfer = test->transfers + i;
+               unsigned long long nbits = (unsigned long long)BITS_PER_BYTE *
+                                          xfer->len;
+
+               delay_usecs += xfer->delay_usecs;
+               if (!xfer->speed_hz)
+                       continue;
+               estimated_time += div_u64(nbits * NSEC_PER_SEC, xfer->speed_hz);
+       }
+
+       estimated_time += delay_usecs * NSEC_PER_USEC;
+       if (test->elapsed_time < estimated_time) {
+               dev_err(&spi->dev,
+                       "elapsed time %lld ns is shorter than minimum estimated time %lld ns\n",
+                       test->elapsed_time, estimated_time);
+
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int spi_test_check_loopback_result(struct spi_device *spi,
                                          struct spi_message *msg,
                                          void *tx, void *rx)
@@ -518,11 +565,11 @@ static int spi_test_check_loopback_result(struct spi_device *spi,
        /* if applicable to transfer check that rx_buf is equal to tx_buf */
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                /* if there is no rx, then no check is needed */
-               if (!xfer->rx_buf)
+               if (!xfer->len || !xfer->rx_buf)
                        continue;
                /* so depending on tx_buf we need to handle things */
                if (xfer->tx_buf) {
-                       for (i = 1; i < xfer->len; i++) {
+                       for (i = 0; i < xfer->len; i++) {
                                txb = ((u8 *)xfer->tx_buf)[i];
                                rxb = ((u8 *)xfer->rx_buf)[i];
                                if (txb != rxb)
@@ -760,15 +807,6 @@ static int spi_test_run_iter(struct spi_device *spi,
        /* copy the test template to test */
        memcpy(&test, testtemplate, sizeof(test));
 
-       /* set up test->transfers to the correct count */
-       if (!test.transfer_count) {
-               for (i = 0;
-                   (i < SPI_TEST_MAX_TRANSFERS) && test.transfers[i].len;
-                   i++) {
-                       test.transfer_count++;
-               }
-       }
-
        /* if iterate_transfer_mask is not set,
         * then set it to first transfer only
         */
@@ -814,8 +852,7 @@ static int spi_test_run_iter(struct spi_device *spi,
                /* only when bit in transfer mask is set */
                if (!(test.iterate_transfer_mask & BIT(i)))
                        continue;
-               if (len)
-                       test.transfers[i].len = len;
+               test.transfers[i].len = len;
                if (test.transfers[i].tx_buf)
                        test.transfers[i].tx_buf += tx_off;
                if (test.transfers[i].tx_buf)
@@ -845,12 +882,16 @@ int spi_test_execute_msg(struct spi_device *spi, struct spi_test *test,
 
        /* only if we do not simulate */
        if (!simulate_only) {
+               ktime_t start;
+
                /* dump the complete message before and after the transfer */
                if (dump_messages == 3)
                        spi_test_dump_message(spi, msg, true);
 
+               start = ktime_get();
                /* run spi message */
                ret = spi_sync(spi, msg);
+               test->elapsed_time = ktime_to_ns(ktime_sub(ktime_get(), start));
                if (ret == -ETIMEDOUT) {
                        dev_info(&spi->dev,
                                 "spi-message timed out - reruning...\n");
@@ -876,6 +917,10 @@ int spi_test_execute_msg(struct spi_device *spi, struct spi_test *test,
 
                /* run rx-buffer tests */
                ret = spi_test_check_loopback_result(spi, msg, tx, rx);
+               if (ret)
+                       goto exit;
+
+               ret = spi_test_check_elapsed_time(spi, test);
        }
 
        /* if requested or on error dump message (including data) */
@@ -925,15 +970,6 @@ int spi_test_run_test(struct spi_device *spi, const struct spi_test *test,
        /* iterate over all the iterable values using macros
         * (to make it a bit more readable...
         */
-#define FOR_EACH_ITERATE(var, defaultvalue)                            \
-       for (idx_##var = -1, var = defaultvalue;                        \
-            ((idx_##var < 0) ||                                        \
-                    (                                                  \
-                            (idx_##var < SPI_TEST_MAX_ITERATE) &&      \
-                            (var = test->iterate_##var[idx_##var])     \
-                    )                                                  \
-            );                                                         \
-            idx_##var++)
 #define FOR_EACH_ALIGNMENT(var)                                                \
        for (var = 0;                                                   \
            var < (test->iterate_##var ?                                \
@@ -943,7 +979,8 @@ int spi_test_run_test(struct spi_device *spi, const struct spi_test *test,
                        1);                                             \
            var++)
 
-       FOR_EACH_ITERATE(len, 0) {
+       for (idx_len = 0; idx_len < SPI_TEST_MAX_ITERATE &&
+            (len = test->iterate_len[idx_len]) != -1; idx_len++) {
                FOR_EACH_ALIGNMENT(tx_align) {
                        FOR_EACH_ALIGNMENT(rx_align) {
                                /* and run the iteration */
index e3114832c485cdac7621662ed1d7291b493a76d8..03a773a9531a9d08944c4e5142f1668a3d3f2b6c 100644 (file)
 #define SUN6I_TFR_CTL_XCH                      BIT(31)
 
 #define SUN6I_INT_CTL_REG              0x10
+#define SUN6I_INT_CTL_RF_RDY                   BIT(0)
+#define SUN6I_INT_CTL_TF_ERQ                   BIT(4)
 #define SUN6I_INT_CTL_RF_OVF                   BIT(8)
 #define SUN6I_INT_CTL_TC                       BIT(12)
 
 #define SUN6I_INT_STA_REG              0x14
 
 #define SUN6I_FIFO_CTL_REG             0x18
+#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK  0xff
+#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS  0
 #define SUN6I_FIFO_CTL_RF_RST                  BIT(15)
+#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK  0xff
+#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS  16
 #define SUN6I_FIFO_CTL_TF_RST                  BIT(31)
 
 #define SUN6I_FIFO_STA_REG             0x1c
 #define SUN6I_CLK_CTL_CDR1(div)                        (((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8)
 #define SUN6I_CLK_CTL_DRS                      BIT(12)
 
+#define SUN6I_MAX_XFER_SIZE            0xffffff
+
 #define SUN6I_BURST_CNT_REG            0x30
-#define SUN6I_BURST_CNT(cnt)                   ((cnt) & 0xffffff)
+#define SUN6I_BURST_CNT(cnt)                   ((cnt) & SUN6I_MAX_XFER_SIZE)
 
 #define SUN6I_XMIT_CNT_REG             0x34
-#define SUN6I_XMIT_CNT(cnt)                    ((cnt) & 0xffffff)
+#define SUN6I_XMIT_CNT(cnt)                    ((cnt) & SUN6I_MAX_XFER_SIZE)
 
 #define SUN6I_BURST_CTL_CNT_REG                0x38
-#define SUN6I_BURST_CTL_CNT_STC(cnt)           ((cnt) & 0xffffff)
+#define SUN6I_BURST_CTL_CNT_STC(cnt)           ((cnt) & SUN6I_MAX_XFER_SIZE)
 
 #define SUN6I_TXDATA_REG               0x200
 #define SUN6I_RXDATA_REG               0x300
@@ -105,6 +113,31 @@ static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
        writel(value, sspi->base_addr + reg);
 }
 
+static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi)
+{
+       u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
+
+       reg >>= SUN6I_FIFO_STA_TF_CNT_BITS;
+
+       return reg & SUN6I_FIFO_STA_TF_CNT_MASK;
+}
+
+static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask)
+{
+       u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG);
+
+       reg |= mask;
+       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
+}
+
+static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask)
+{
+       u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG);
+
+       reg &= ~mask;
+       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
+}
+
 static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
 {
        u32 reg, cnt;
@@ -127,10 +160,13 @@ static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
 
 static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len)
 {
+       u32 cnt;
        u8 byte;
 
-       if (len > sspi->len)
-               len = sspi->len;
+       /* See how much data we can fit */
+       cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
+
+       len = min3(len, (int)cnt, sspi->len);
 
        while (len--) {
                byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
@@ -158,9 +194,7 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable)
 
 static size_t sun6i_spi_max_transfer_size(struct spi_device *spi)
 {
-       struct sun6i_spi *sspi = spi_master_get_devdata(spi->master);
-
-       return sspi->fifo_depth - 1;
+       return SUN6I_MAX_XFER_SIZE - 1;
 }
 
 static int sun6i_spi_transfer_one(struct spi_master *master,
@@ -170,12 +204,12 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
        struct sun6i_spi *sspi = spi_master_get_devdata(master);
        unsigned int mclk_rate, div, timeout;
        unsigned int start, end, tx_time;
+       unsigned int trig_level;
        unsigned int tx_len = 0;
        int ret = 0;
        u32 reg;
 
-       /* We don't support transfer larger than the FIFO */
-       if (tfr->len > sspi->fifo_depth)
+       if (tfr->len > SUN6I_MAX_XFER_SIZE)
                return -EINVAL;
 
        reinit_completion(&sspi->done);
@@ -190,6 +224,17 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
        sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
                        SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST);
 
+       /*
+        * Setup FIFO interrupt trigger level
+        * Here we choose 3/4 of the full fifo depth, as it's the hardcoded
+        * value used in old generation of Allwinner SPI controller.
+        * (See spi-sun4i.c)
+        */
+       trig_level = sspi->fifo_depth / 4 * 3;
+       sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
+                       (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) |
+                       (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS));
+
        /*
         * Setup the transfer control register: Chip Select,
         * polarities, etc.
@@ -274,6 +319,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
 
        /* Enable the interrupts */
        sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC);
+       sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC |
+                                        SUN6I_INT_CTL_RF_RDY);
+       if (tx_len > sspi->fifo_depth)
+               sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ);
 
        /* Start the transfer */
        reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
@@ -293,8 +342,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
                goto out;
        }
 
-       sun6i_spi_drain_fifo(sspi, sspi->fifo_depth);
-
 out:
        sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
 
@@ -309,10 +356,33 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
        /* Transfer complete */
        if (status & SUN6I_INT_CTL_TC) {
                sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
+               sun6i_spi_drain_fifo(sspi, sspi->fifo_depth);
                complete(&sspi->done);
                return IRQ_HANDLED;
        }
 
+       /* Receive FIFO 3/4 full */
+       if (status & SUN6I_INT_CTL_RF_RDY) {
+               sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
+               /* Only clear the interrupt _after_ draining the FIFO */
+               sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY);
+               return IRQ_HANDLED;
+       }
+
+       /* Transmit FIFO 3/4 empty */
+       if (status & SUN6I_INT_CTL_TF_ERQ) {
+               sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH);
+
+               if (!sspi->len)
+                       /* nothing left to transmit */
+                       sun6i_spi_disable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ);
+
+               /* Only clear the interrupt _after_ re-seeding the FIFO */
+               sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TF_ERQ);
+
+               return IRQ_HANDLED;
+       }
+
        return IRQ_NONE;
 }
 
index 73779cecc3bbcf544ffb74f55048dcc6d06db952..08012ae5aa66eb3c03396523e5d25a3dc2101c64 100644 (file)
@@ -837,7 +837,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
                                                SPI_DMA_TIMEOUT);
                if (WARN_ON(ret == 0)) {
                        dev_err(tspi->dev,
-                               "spi trasfer timeout, err %d\n", ret);
+                               "spi transfer timeout, err %d\n", ret);
                        ret = -EIO;
                        goto complete_xfer;
                }
index b6558bb6f9dfc1efb0084a28ad95280ab8d527fe..2c797ee2664dee454b308e23f4b46169e7b54cc0 100644 (file)
@@ -341,7 +341,7 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master,
                                                SPI_DMA_TIMEOUT);
                if (WARN_ON(ret == 0)) {
                        dev_err(tsd->dev,
-                               "spi trasfer timeout, err %d\n", ret);
+                               "spi transfer timeout, err %d\n", ret);
                        ret = -EIO;
                        goto exit;
                }
index 85c91f58b42f3f8213f5a66749f0d4683d9b0a7a..0c06ce424210a692f80305f2eadef1b2e6a179af 100644 (file)
@@ -824,7 +824,7 @@ static int tegra_slink_transfer_one(struct spi_master *master,
                                          SLINK_DMA_TIMEOUT);
        if (WARN_ON(ret == 0)) {
                dev_err(tspi->dev,
-                       "spi trasfer timeout, err %d\n", ret);
+                       "spi transfer timeout, err %d\n", ret);
                return -EIO;
        }
 
index 922c528332394569fe3756f8c2434339e857fbd1..6ed7b899da8a7e75914b375cffbd455a9bb0ad13 100644 (file)
@@ -48,9 +48,8 @@
  *
  * @msg:              a template @spi_message usedfor the default settings
  * @transfers:        array of @spi_transfers that are part of the
- *                    resulting spi_message. The first transfer with len == 0
- *                    signifies the end of the list
- * @transfer_count:   normally computed number of transfers with len > 0
+ *                    resulting spi_message.
+ * @transfer_count:   number of transfers
  *
  * @run_test:         run a specific spi_test - this allows to override
  *                    the default implementation of @spi_test_run_transfer
@@ -62,8 +61,7 @@
  * @expected_return:  the expected return code - in some cases we want to
  *                    test also for error conditions
  *
- * @iterate_len:      list of length to iterate on (in addition to the
- *                    explicitly set @spi_transfer.len)
+ * @iterate_len:      list of length to iterate on
  * @iterate_tx_align: change the alignment of @spi_transfer.tx_buf
  *                    for all values in the below range if set.
  *                    the ranges are:
@@ -77,6 +75,7 @@
  * @fill_option:      define the way how tx_buf is filled
  * @fill_pattern:     fill pattern to apply to the tx_buf
  *                    (used in some of the @fill_options)
+ * @elapsed_time:     elapsed time in nanoseconds
  */
 
 struct spi_test {
@@ -89,7 +88,7 @@ struct spi_test {
        int (*execute_msg)(struct spi_device *spi, struct spi_test *test,
                           void *tx, void *rx);
        int expected_return;
-       /* iterate over all the non-zero values */
+       /* iterate over all values, terminated by a -1 */
        int iterate_len[SPI_TEST_MAX_ITERATE];
        int iterate_tx_align;
        int iterate_rx_align;
@@ -110,6 +109,7 @@ struct spi_test {
 #define FILL_TRANSFER_BYTE_32 11 /* fill with the transfer byte - 32 bit */
 #define FILL_TRANSFER_NUM     16 /* fill with the transfer number */
        u32 fill_pattern;
+       unsigned long long elapsed_time;
 };
 
 /* default implementation for @spi_test.run_test */
@@ -126,11 +126,12 @@ int spi_test_execute_msg(struct spi_device *spi,
 int spi_test_run_tests(struct spi_device *spi,
                       struct spi_test *tests);
 
-/* some of the default @spi_transfer.len to test */
-#define ITERATE_LEN 2, 3, 7, 11, 16, 31, 32, 64, 97, 128, 251, 256, \
+#define ITERATE_LEN_LIST 0, 1, 2, 3, 7, 11, 16, 31, 32, 64, 97, 128, 251, 256, \
                1021, 1024, 1031, 4093, PAGE_SIZE, 4099, 65536, 65537
-
-#define ITERATE_MAX_LEN ITERATE_LEN, SPI_TEST_MAX_SIZE - 1, SPI_TEST_MAX_SIZE
+/* some of the default @spi_transfer.len to test, terminated by a -1 */
+#define ITERATE_LEN ITERATE_LEN_LIST, -1
+#define ITERATE_MAX_LEN ITERATE_LEN_LIST, (SPI_TEST_MAX_SIZE - 1), \
+               SPI_TEST_MAX_SIZE, -1
 
 /* the default alignment to test */
 #define ITERATE_ALIGN sizeof(int)
index 9e2e099baf8ca5cc6510912a36d4ca03daeb8273..9a2a79a871ba009fcfa8b7e2b52002c8845d94ce 100644 (file)
@@ -697,6 +697,7 @@ static const struct of_device_id spidev_dt_ids[] = {
        { .compatible = "rohm,dh2228fv" },
        { .compatible = "lineartechnology,ltc2488" },
        { .compatible = "ge,achc" },
+       { .compatible = "semtech,sx1301" },
        {},
 };
 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
index 816f119c9b7b1584a587077a55e28691418ff706..8c590cd1171a99c2ff7d38fb1cf7879b052acc3a 100644 (file)
@@ -18,6 +18,7 @@
 #include <string.h>
 #include <getopt.h>
 #include <fcntl.h>
+#include <time.h>
 #include <sys/ioctl.h>
 #include <linux/ioctl.h>
 #include <sys/stat.h>
@@ -40,6 +41,9 @@ static char *output_file;
 static uint32_t speed = 500000;
 static uint16_t delay;
 static int verbose;
+static int transfer_size;
+static int iterations;
+static int interval = 5; /* interval in seconds for showing transfer rate */
 
 uint8_t default_tx[] = {
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -156,13 +160,13 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
                close(out_fd);
        }
 
-       if (verbose || !output_file)
+       if (verbose)
                hex_dump(rx, len, 32, "RX");
 }
 
 static void print_usage(const char *prog)
 {
-       printf("Usage: %s [-DsbdlHOLC3]\n", prog);
+       printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
        puts("  -D --device   device to use (default /dev/spidev1.1)\n"
             "  -s --speed    max speed (Hz)\n"
             "  -d --delay    delay (usec)\n"
@@ -180,7 +184,9 @@ static void print_usage(const char *prog)
             "  -N --no-cs    no chip select\n"
             "  -R --ready    slave pulls low to pause\n"
             "  -2 --dual     dual transfer\n"
-            "  -4 --quad     quad transfer\n");
+            "  -4 --quad     quad transfer\n"
+            "  -S --size     transfer size\n"
+            "  -I --iter     iterations\n");
        exit(1);
 }
 
@@ -205,11 +211,13 @@ static void parse_opts(int argc, char *argv[])
                        { "dual",    0, 0, '2' },
                        { "verbose", 0, 0, 'v' },
                        { "quad",    0, 0, '4' },
+                       { "size",    1, 0, 'S' },
+                       { "iter",    1, 0, 'I' },
                        { NULL, 0, 0, 0 },
                };
                int c;
 
-               c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
+               c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:vS:I:",
                                lopts, NULL);
 
                if (c == -1)
@@ -270,6 +278,12 @@ static void parse_opts(int argc, char *argv[])
                case '4':
                        mode |= SPI_TX_QUAD;
                        break;
+               case 'S':
+                       transfer_size = atoi(optarg);
+                       break;
+               case 'I':
+                       iterations = atoi(optarg);
+                       break;
                default:
                        print_usage(argv[0]);
                        break;
@@ -336,6 +350,57 @@ static void transfer_file(int fd, char *filename)
        close(tx_fd);
 }
 
+static uint64_t _read_count;
+static uint64_t _write_count;
+
+static void show_transfer_rate(void)
+{
+       static uint64_t prev_read_count, prev_write_count;
+       double rx_rate, tx_rate;
+
+       rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
+       tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
+
+       printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
+
+       prev_read_count = _read_count;
+       prev_write_count = _write_count;
+}
+
+static void transfer_buf(int fd, int len)
+{
+       uint8_t *tx;
+       uint8_t *rx;
+       int i;
+
+       tx = malloc(len);
+       if (!tx)
+               pabort("can't allocate tx buffer");
+       for (i = 0; i < len; i++)
+               tx[i] = random();
+
+       rx = malloc(len);
+       if (!rx)
+               pabort("can't allocate rx buffer");
+
+       transfer(fd, tx, rx, len);
+
+       _write_count += len;
+       _read_count += len;
+
+       if (mode & SPI_LOOP) {
+               if (memcmp(tx, rx, len)) {
+                       fprintf(stderr, "transfer error !\n");
+                       hex_dump(tx, len, 32, "TX");
+                       hex_dump(rx, len, 32, "RX");
+                       exit(1);
+               }
+       }
+
+       free(rx);
+       free(tx);
+}
+
 int main(int argc, char *argv[])
 {
        int ret = 0;
@@ -391,7 +456,25 @@ int main(int argc, char *argv[])
                transfer_escaped_string(fd, input_tx);
        else if (input_file)
                transfer_file(fd, input_file);
-       else
+       else if (transfer_size) {
+               struct timespec last_stat;
+
+               clock_gettime(CLOCK_MONOTONIC, &last_stat);
+
+               while (iterations-- > 0) {
+                       struct timespec current;
+
+                       transfer_buf(fd, transfer_size);
+
+                       clock_gettime(CLOCK_MONOTONIC, &current);
+                       if (current.tv_sec - last_stat.tv_sec > interval) {
+                               show_transfer_rate();
+                               last_stat = current;
+                       }
+               }
+               printf("total: tx %.1fKB, rx %.1fKB\n",
+                      _write_count/1024.0, _read_count/1024.0);
+       } else
                transfer(fd, default_tx, default_rx, sizeof(default_tx));
 
        close(fd);