#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>
#include <linux/of_device.h>
#include <linux/printk.h>
+++++#include <linux/vmalloc.h>
#include <linux/spi/spi.h>
#include "spi-test.h"
MODULE_PARM_DESC(run_only_test,
"only run the test with this number (0-based !)");
+++++/* use vmalloc'ed buffers */
+++++int use_vmalloc;
+++++module_param(use_vmalloc, int, 0644);
+++++MODULE_PARM_DESC(use_vmalloc,
+++++ "use vmalloc'ed buffers instead of kmalloc'ed");
+++++
+++++/* check rx ranges */
+++++int check_ranges = 1;
+++++module_param(check_ranges, int, 0644);
+++++MODULE_PARM_DESC(check_ranges,
+++++ "checks rx_buffer pattern are valid");
+++++
/* the actual tests to execute */
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),
},
.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),
},
.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),
},
},
.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),
},
},
.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),
},
.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),
},
{
.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),
},
},
.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),
},
},
.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),
},
{
.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),
},
},
.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
*/
.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),
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(1),
+++++ .transfer_count = 2,
.transfers = {
{
.len = 1,
.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 */ }
};
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)
int ret;
/* checks rx_buffer pattern are valid with loopback or without */
----- ret = spi_check_rx_ranges(spi, msg, rx);
----- if (ret)
----- return ret;
+++++ if (check_ranges) {
+++++ ret = spi_check_rx_ranges(spi, msg, rx);
+++++ if (ret)
+++++ return ret;
+++++ }
/* if we run without loopback, then return now */
if (!loopback)
/* 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)
/* 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
*/
/* 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)
/* 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");
/* 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) */
/* 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 ? \
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 */
/* allocate rx/tx buffers of 128kB size without devm
* in the hope that is on a page boundary
*/
----- rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
+++++ if (use_vmalloc)
+++++ rx = vmalloc(SPI_TEST_MAX_SIZE_PLUS);
+++++ else
+++++ rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
if (!rx) {
ret = -ENOMEM;
goto out;
}
----- tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
+++++ if (use_vmalloc)
+++++ tx = vmalloc(SPI_TEST_MAX_SIZE_PLUS);
+++++ else
+++++ tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
if (!tx) {
ret = -ENOMEM;
goto out;
}
out:
----- kfree(rx);
----- kfree(tx);
+++++ kvfree(rx);
+++++ kvfree(tx);
return ret;
}
EXPORT_SYMBOL_GPL(spi_test_run_tests);