From 913f536c6c18a2e19e32f06971101c1d0ae3739c Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 6 Mar 2017 20:14:43 +0800 Subject: [PATCH 01/13] spi: sun6i: Allow transfers larger than FIFO size The spi-sun6i driver have the same problem that spi-sun4i used to have -- SPI transfers are limited to one FIFO depth. This commit fixes this problem in the same way it's fixed in spi-sun4i. See commit 196737912da5 ("spi: sun4i: Allow transfers larger than FIFO size") for more information. The sun6i SPI controllers features changeable interrupt trigger level, but I set it to 3/4 of fifo depth, as same as the the sun4i SPI controllers. Signed-off-by: Icenowy Zheng Acked-by: Maxime Ripard Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 90 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index e3114832c485..6e9ca93db9bf 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -46,13 +46,19 @@ #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 @@ -68,14 +74,16 @@ #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; @@ -170,12 +206,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 +226,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 +321,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 +344,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 +358,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; } From 91b4634632e0138b655a63fdb1704edbbcd6cdda Mon Sep 17 00:00:00 2001 From: Ben Whitten Date: Thu, 16 Mar 2017 11:00:30 +0000 Subject: [PATCH 02/13] spi: spidev: Add sx1301 to device tree compatibility list Add entry for the semtech sx1301 baseband processor implementing a LoRa concentrator IP. At this time this chip requires an spidev userspace driver. Signed-off-by: Ben Whitten Signed-off-by: Steve deRosier Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 9e2e099baf8c..9a2a79a871ba 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -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); From c4e121aeb754f1ac5f2bcc791aa4177dcfb3b401 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 18 Mar 2017 03:17:26 +0900 Subject: [PATCH 03/13] spi: loopback-test: correct mismatched test description and configuration The test "two tx-transfers - alter first" actually alters the second not the first transfer. Similarly the test "two tx-transfers - alter second" actually alters the first not the second transfer. The mismatches for the two symmetrical tests cancel each other's mistakes. But it's better to fix the mismatches to avoid confusion. Signed-off-by: Akinobu Mita Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 50e620f4e8fe..99422f3a15e0 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -132,7 +132,7 @@ 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), .transfers = { { .len = 1, @@ -149,7 +149,7 @@ 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), .transfers = { { .len = 16, From 8494801db1fc21867f587dae465236100bf228cc Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 18 Mar 2017 03:17:27 +0900 Subject: [PATCH 04/13] spi: loopback-test: don't skip comparing the first byte of rx_buf When the loopback parameter is set, rx_buf are compared with tx_buf after the spi_message is executed. But the first byte of buffer is not checked. Signed-off-by: Akinobu Mita Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 99422f3a15e0..85c0c4e391d9 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -507,7 +507,7 @@ static int spi_test_check_loopback_result(struct spi_device *spi, 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) From 8916671e93a38c509e2ffb02c2ce3f37efe8af40 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 18 Mar 2017 03:17:28 +0900 Subject: [PATCH 05/13] spi: loopback-test: add ability to test zero-length transfer The spi-loopback-test module currently cannot test the spi_message including a zero-length transfer. Because the zero-length transfer is treated as a special value in several meanings. 1. The number of spi_transfer to execute in one test case is described by spi_test.transfer_count. It is normally computed by counting number of transfers with len > 0 in spi_test.transfers array. This change stops the detection for the number of spi_transfer. Each spi_test.transfer_count needs to be filled by hand now. 2. The spi_test.iterate_len is a list of transfer length to iterate on. This list is terminated by zero, so zero-length transfer cannot be included. This changes the terminal value from 0 to -1. 3. The length for the spi_transfer masked by spi_test.iterate_transfer_mask is iterated. Before starting the iteration, the default value which is statically initialized is applied. In order to specify the default value, zero-length is reserved. Currently, the default values are always '1'. So this removes this trick and add '1' to iterate_len list. By applying all these changes, the spi-loopback-test can execute spi messages with zero-length transfer. Signed-off-by: Akinobu Mita Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 55 ++++++++++----------------------- drivers/spi/spi-test.h | 19 ++++++------ 2 files changed, 26 insertions(+), 48 deletions(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 85c0c4e391d9..6df6dddc2890 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -63,9 +63,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), }, @@ -77,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(PAGE_SIZE - 4), .rx_buf = RX(PAGE_SIZE - 4), }, @@ -90,9 +90,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), }, }, @@ -102,9 +102,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), }, }, @@ -115,13 +115,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), }, @@ -133,9 +132,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(64), }, { @@ -150,13 +149,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 = 16, .tx_buf = TX(0), }, { - .len = 1, .tx_buf = TX(64), }, }, @@ -167,13 +166,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), }, }, @@ -184,9 +182,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), }, { @@ -201,13 +199,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), }, }, @@ -218,14 +216,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 */ @@ -240,9 +237,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), @@ -261,6 +258,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, @@ -268,7 +266,6 @@ 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), @@ -503,7 +500,7 @@ 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) { @@ -745,15 +742,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 */ @@ -799,8 +787,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) @@ -910,15 +897,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 ? \ @@ -928,7 +906,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 */ diff --git a/drivers/spi/spi-test.h b/drivers/spi/spi-test.h index 922c52833239..2dd569138880 100644 --- a/drivers/spi/spi-test.h +++ b/drivers/spi/spi-test.h @@ -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: @@ -89,7 +87,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; @@ -126,11 +124,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 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) From 0bd7fda56b94002d9985040c02bb701956fa657e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 18 Mar 2017 03:17:29 +0900 Subject: [PATCH 06/13] spi: loopback-test: test zero-length transfer In order to test various spi_messages including zero-length transfer, this adds zero length into the iterate_len list. Signed-off-by: Akinobu Mita Signed-off-by: Mark Brown --- drivers/spi/spi-test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-test.h b/drivers/spi/spi-test.h index 2dd569138880..82fff4a0fd94 100644 --- a/drivers/spi/spi-test.h +++ b/drivers/spi/spi-test.h @@ -124,7 +124,7 @@ int spi_test_execute_msg(struct spi_device *spi, int spi_test_run_tests(struct spi_device *spi, struct spi_test *tests); -#define ITERATE_LEN_LIST 1, 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 /* some of the default @spi_transfer.len to test, terminated by a -1 */ #define ITERATE_LEN ITERATE_LEN_LIST, -1 From ea9936f32435699907807aaac87f993482208578 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 18 Mar 2017 03:17:30 +0900 Subject: [PATCH 07/13] spi: loopback-test: add elapsed time check This adds checks whether the elapsed time is longer than the minimam estimated time. The estimated time is calculated with the total transfer length per clock rate and optional spi_transfer.delay_usecs. Signed-off-by: Akinobu Mita Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 38 +++++++++++++++++++++++++++++++++ drivers/spi/spi-test.h | 2 ++ 2 files changed, 40 insertions(+) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 6df6dddc2890..66e8cfd04395 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -479,6 +480,35 @@ 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 = 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 minimam 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) @@ -817,12 +847,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"); @@ -848,6 +882,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) */ diff --git a/drivers/spi/spi-test.h b/drivers/spi/spi-test.h index 82fff4a0fd94..6ed7b899da8a 100644 --- a/drivers/spi/spi-test.h +++ b/drivers/spi/spi-test.h @@ -75,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 { @@ -108,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 */ From 8687113e1515f4c9104a6eaedc384ec762c6550f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 18 Mar 2017 03:17:31 +0900 Subject: [PATCH 08/13] spi: loopback-test: add test spi_message with delay after transfers This adds a new test to check whether the spi_transfer.delay_usecs setting has properly taken effect. Signed-off-by: Akinobu Mita Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 66e8cfd04395..fcae3377ec26 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -273,6 +273,25 @@ static struct spi_test spi_tests[] = { }, }, }, + { + .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 */ } }; From 3288d5cb40c09213e86d67b5cab09de1deb65a5c Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 20 Mar 2017 22:38:49 +0800 Subject: [PATCH 09/13] spi: sun6i: update max transfer size reported The spi-sun6i driver have already got the ability to do large transfers. However, the max transfer size reported is still fifo depth - 1. Update the max transfer size reported to the max value possible. Reported-by: Martin Ayotte Signed-off-by: Icenowy Zheng Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 6e9ca93db9bf..03a773a9531a 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -194,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, From d2c14c64d678713fced6f2261ce7d398b4351de5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 20 Mar 2017 13:58:05 +0000 Subject: [PATCH 10/13] spi: loopback-test: fix potential integer overflow on multiple A multiplication of 8U * xfer-len with the type of a 32 bit unsigned int is evaluated using 32 bit arithmetic and then used in a context that expects an expression of type unsigned long long (64 bits). Avoid any potential overflow by casting BITS_PER_BYTE to unsigned long long. Detected by CoverityScan, CID#1419691 ("Unintentional integer overflow") Fixes: ea9936f324356 ("spi: loopback-test: add elapsed time check") Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index fcae3377ec26..6888d5c34ac4 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -508,7 +508,8 @@ static int spi_test_check_elapsed_time(struct spi_device *spi, for (i = 0; i < test->transfer_count; i++) { struct spi_transfer *xfer = test->transfers + i; - unsigned long long nbits = BITS_PER_BYTE * xfer->len; + unsigned long long nbits = (unsigned long long)BITS_PER_BYTE * + xfer->len; delay_usecs += xfer->delay_usecs; if (!xfer->speed_hz) From 9006a7b3220e7293ef8bc1ac9bba6c54411051c1 Mon Sep 17 00:00:00 2001 From: Frode Isaksen Date: Tue, 21 Mar 2017 16:48:46 +0100 Subject: [PATCH 11/13] spi: spidev_test: add option to continuously transfer data Add option to send+recv bytes with iterations and show the transfer rate every 5 seconds. Example: rate: tx 4235.2kbps, rx 4235.2kbps total: tx 4882.8KB, rx 4882.8KB Signed-off-by: Frode Isaksen Signed-off-by: Mark Brown --- tools/spi/spidev_test.c | 93 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 816f119c9b7b..8c590cd1171a 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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, ¤t); + 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); From 905e0b5ef9690c5433b642f46f1d756bb374da17 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 30 Mar 2017 11:01:25 +0100 Subject: [PATCH 12/13] spi: loopback-test: fix spelling mistake: "minimam" -> "minimum" trivial fix to spelling mistake in dev_err error message Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 6888d5c34ac4..e6041539afed 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -520,7 +520,7 @@ static int spi_test_check_elapsed_time(struct spi_device *spi, estimated_time += delay_usecs * NSEC_PER_USEC; if (test->elapsed_time < estimated_time) { dev_err(&spi->dev, - "elapsed time %lld ns is shorter than minimam estimated time %lld ns\n", + "elapsed time %lld ns is shorter than minimum estimated time %lld ns\n", test->elapsed_time, estimated_time); return -EINVAL; From bfca76185d9bd5b3de562bf5023e5d3dddaa9dce Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 23 Apr 2017 18:14:36 +0100 Subject: [PATCH 13/13] spi: tegra: fix spelling mistake: "trasfer" -> "transfer" trivial fix to spelling mistake in dbg_err messages Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 2 +- drivers/spi/spi-tegra20-sflash.c | 2 +- drivers/spi/spi-tegra20-slink.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 73779cecc3bb..08012ae5aa66 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -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; } diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index b6558bb6f9df..2c797ee2664d 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -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; } diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 85c91f58b42f..0c06ce424210 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -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; }