162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/drivers/spi/spi-loopback-test.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  (c) Martin Sperl <kernel@martin.sperl.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Loopback test driver to test several typical spi_message conditions
862306a36Sopenharmony_ci *  that a spi_master driver may encounter
962306a36Sopenharmony_ci *  this can also get used for regression testing
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/ktime.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/list_sort.h>
1762306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/printk.h>
2062306a36Sopenharmony_ci#include <linux/vmalloc.h>
2162306a36Sopenharmony_ci#include <linux/spi/spi.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "spi-test.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* flag to only simulate transfers */
2662306a36Sopenharmony_cistatic int simulate_only;
2762306a36Sopenharmony_cimodule_param(simulate_only, int, 0);
2862306a36Sopenharmony_ciMODULE_PARM_DESC(simulate_only, "if not 0 do not execute the spi message");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* dump spi messages */
3162306a36Sopenharmony_cistatic int dump_messages;
3262306a36Sopenharmony_cimodule_param(dump_messages, int, 0);
3362306a36Sopenharmony_ciMODULE_PARM_DESC(dump_messages,
3462306a36Sopenharmony_ci		 "=1 dump the basic spi_message_structure, " \
3562306a36Sopenharmony_ci		 "=2 dump the spi_message_structure including data, " \
3662306a36Sopenharmony_ci		 "=3 dump the spi_message structure before and after execution");
3762306a36Sopenharmony_ci/* the device is jumpered for loopback - enabling some rx_buf tests */
3862306a36Sopenharmony_cistatic int loopback;
3962306a36Sopenharmony_cimodule_param(loopback, int, 0);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(loopback,
4162306a36Sopenharmony_ci		 "if set enable loopback mode, where the rx_buf "	\
4262306a36Sopenharmony_ci		 "is checked to match tx_buf after the spi_message "	\
4362306a36Sopenharmony_ci		 "is executed");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int loop_req;
4662306a36Sopenharmony_cimodule_param(loop_req, int, 0);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(loop_req,
4862306a36Sopenharmony_ci		 "if set controller will be asked to enable test loop mode. " \
4962306a36Sopenharmony_ci		 "If controller supported it, MISO and MOSI will be connected");
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int no_cs;
5262306a36Sopenharmony_cimodule_param(no_cs, int, 0);
5362306a36Sopenharmony_ciMODULE_PARM_DESC(no_cs,
5462306a36Sopenharmony_ci		 "if set Chip Select (CS) will not be used");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* run tests only for a specific length */
5762306a36Sopenharmony_cistatic int run_only_iter_len = -1;
5862306a36Sopenharmony_cimodule_param(run_only_iter_len, int, 0);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(run_only_iter_len,
6062306a36Sopenharmony_ci		 "only run tests for a length of this number in iterate_len list");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* run only a specific test */
6362306a36Sopenharmony_cistatic int run_only_test = -1;
6462306a36Sopenharmony_cimodule_param(run_only_test, int, 0);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(run_only_test,
6662306a36Sopenharmony_ci		 "only run the test with this number (0-based !)");
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* use vmalloc'ed buffers */
6962306a36Sopenharmony_cistatic int use_vmalloc;
7062306a36Sopenharmony_cimodule_param(use_vmalloc, int, 0644);
7162306a36Sopenharmony_ciMODULE_PARM_DESC(use_vmalloc,
7262306a36Sopenharmony_ci		 "use vmalloc'ed buffers instead of kmalloc'ed");
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* check rx ranges */
7562306a36Sopenharmony_cistatic int check_ranges = 1;
7662306a36Sopenharmony_cimodule_param(check_ranges, int, 0644);
7762306a36Sopenharmony_ciMODULE_PARM_DESC(check_ranges,
7862306a36Sopenharmony_ci		 "checks rx_buffer pattern are valid");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic unsigned int delay_ms = 100;
8162306a36Sopenharmony_cimodule_param(delay_ms, uint, 0644);
8262306a36Sopenharmony_ciMODULE_PARM_DESC(delay_ms,
8362306a36Sopenharmony_ci		 "delay between tests, in milliseconds (default: 100)");
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* the actual tests to execute */
8662306a36Sopenharmony_cistatic struct spi_test spi_tests[] = {
8762306a36Sopenharmony_ci	{
8862306a36Sopenharmony_ci		.description	= "tx/rx-transfer - start of page",
8962306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
9062306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
9162306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
9262306a36Sopenharmony_ci		.iterate_rx_align = ITERATE_ALIGN,
9362306a36Sopenharmony_ci		.transfer_count = 1,
9462306a36Sopenharmony_ci		.transfers		= {
9562306a36Sopenharmony_ci			{
9662306a36Sopenharmony_ci				.tx_buf = TX(0),
9762306a36Sopenharmony_ci				.rx_buf = RX(0),
9862306a36Sopenharmony_ci			},
9962306a36Sopenharmony_ci		},
10062306a36Sopenharmony_ci	},
10162306a36Sopenharmony_ci	{
10262306a36Sopenharmony_ci		.description	= "tx/rx-transfer - crossing PAGE_SIZE",
10362306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
10462306a36Sopenharmony_ci		.iterate_len    = { ITERATE_LEN },
10562306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
10662306a36Sopenharmony_ci		.iterate_rx_align = ITERATE_ALIGN,
10762306a36Sopenharmony_ci		.transfer_count = 1,
10862306a36Sopenharmony_ci		.transfers		= {
10962306a36Sopenharmony_ci			{
11062306a36Sopenharmony_ci				.tx_buf = TX(PAGE_SIZE - 4),
11162306a36Sopenharmony_ci				.rx_buf = RX(PAGE_SIZE - 4),
11262306a36Sopenharmony_ci			},
11362306a36Sopenharmony_ci		},
11462306a36Sopenharmony_ci	},
11562306a36Sopenharmony_ci	{
11662306a36Sopenharmony_ci		.description	= "tx-transfer - only",
11762306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
11862306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
11962306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
12062306a36Sopenharmony_ci		.transfer_count = 1,
12162306a36Sopenharmony_ci		.transfers		= {
12262306a36Sopenharmony_ci			{
12362306a36Sopenharmony_ci				.tx_buf = TX(0),
12462306a36Sopenharmony_ci			},
12562306a36Sopenharmony_ci		},
12662306a36Sopenharmony_ci	},
12762306a36Sopenharmony_ci	{
12862306a36Sopenharmony_ci		.description	= "rx-transfer - only",
12962306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
13062306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
13162306a36Sopenharmony_ci		.iterate_rx_align = ITERATE_ALIGN,
13262306a36Sopenharmony_ci		.transfer_count = 1,
13362306a36Sopenharmony_ci		.transfers		= {
13462306a36Sopenharmony_ci			{
13562306a36Sopenharmony_ci				.rx_buf = RX(0),
13662306a36Sopenharmony_ci			},
13762306a36Sopenharmony_ci		},
13862306a36Sopenharmony_ci	},
13962306a36Sopenharmony_ci	{
14062306a36Sopenharmony_ci		.description	= "two tx-transfers - alter both",
14162306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
14262306a36Sopenharmony_ci		.iterate_len    = { ITERATE_LEN },
14362306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
14462306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0) | BIT(1),
14562306a36Sopenharmony_ci		.transfer_count = 2,
14662306a36Sopenharmony_ci		.transfers		= {
14762306a36Sopenharmony_ci			{
14862306a36Sopenharmony_ci				.tx_buf = TX(0),
14962306a36Sopenharmony_ci			},
15062306a36Sopenharmony_ci			{
15162306a36Sopenharmony_ci				/* this is why we cant use ITERATE_MAX_LEN */
15262306a36Sopenharmony_ci				.tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
15362306a36Sopenharmony_ci			},
15462306a36Sopenharmony_ci		},
15562306a36Sopenharmony_ci	},
15662306a36Sopenharmony_ci	{
15762306a36Sopenharmony_ci		.description	= "two tx-transfers - alter first",
15862306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
15962306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
16062306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
16162306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0),
16262306a36Sopenharmony_ci		.transfer_count = 2,
16362306a36Sopenharmony_ci		.transfers		= {
16462306a36Sopenharmony_ci			{
16562306a36Sopenharmony_ci				.tx_buf = TX(64),
16662306a36Sopenharmony_ci			},
16762306a36Sopenharmony_ci			{
16862306a36Sopenharmony_ci				.len = 1,
16962306a36Sopenharmony_ci				.tx_buf = TX(0),
17062306a36Sopenharmony_ci			},
17162306a36Sopenharmony_ci		},
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci	{
17462306a36Sopenharmony_ci		.description	= "two tx-transfers - alter second",
17562306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
17662306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
17762306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
17862306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(1),
17962306a36Sopenharmony_ci		.transfer_count = 2,
18062306a36Sopenharmony_ci		.transfers		= {
18162306a36Sopenharmony_ci			{
18262306a36Sopenharmony_ci				.len = 16,
18362306a36Sopenharmony_ci				.tx_buf = TX(0),
18462306a36Sopenharmony_ci			},
18562306a36Sopenharmony_ci			{
18662306a36Sopenharmony_ci				.tx_buf = TX(64),
18762306a36Sopenharmony_ci			},
18862306a36Sopenharmony_ci		},
18962306a36Sopenharmony_ci	},
19062306a36Sopenharmony_ci	{
19162306a36Sopenharmony_ci		.description	= "two transfers tx then rx - alter both",
19262306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
19362306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
19462306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
19562306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0) | BIT(1),
19662306a36Sopenharmony_ci		.transfer_count = 2,
19762306a36Sopenharmony_ci		.transfers		= {
19862306a36Sopenharmony_ci			{
19962306a36Sopenharmony_ci				.tx_buf = TX(0),
20062306a36Sopenharmony_ci			},
20162306a36Sopenharmony_ci			{
20262306a36Sopenharmony_ci				.rx_buf = RX(0),
20362306a36Sopenharmony_ci			},
20462306a36Sopenharmony_ci		},
20562306a36Sopenharmony_ci	},
20662306a36Sopenharmony_ci	{
20762306a36Sopenharmony_ci		.description	= "two transfers tx then rx - alter tx",
20862306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
20962306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
21062306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
21162306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0),
21262306a36Sopenharmony_ci		.transfer_count = 2,
21362306a36Sopenharmony_ci		.transfers		= {
21462306a36Sopenharmony_ci			{
21562306a36Sopenharmony_ci				.tx_buf = TX(0),
21662306a36Sopenharmony_ci			},
21762306a36Sopenharmony_ci			{
21862306a36Sopenharmony_ci				.len = 1,
21962306a36Sopenharmony_ci				.rx_buf = RX(0),
22062306a36Sopenharmony_ci			},
22162306a36Sopenharmony_ci		},
22262306a36Sopenharmony_ci	},
22362306a36Sopenharmony_ci	{
22462306a36Sopenharmony_ci		.description	= "two transfers tx then rx - alter rx",
22562306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
22662306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
22762306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
22862306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(1),
22962306a36Sopenharmony_ci		.transfer_count = 2,
23062306a36Sopenharmony_ci		.transfers		= {
23162306a36Sopenharmony_ci			{
23262306a36Sopenharmony_ci				.len = 1,
23362306a36Sopenharmony_ci				.tx_buf = TX(0),
23462306a36Sopenharmony_ci			},
23562306a36Sopenharmony_ci			{
23662306a36Sopenharmony_ci				.rx_buf = RX(0),
23762306a36Sopenharmony_ci			},
23862306a36Sopenharmony_ci		},
23962306a36Sopenharmony_ci	},
24062306a36Sopenharmony_ci	{
24162306a36Sopenharmony_ci		.description	= "two tx+rx transfers - alter both",
24262306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
24362306a36Sopenharmony_ci		.iterate_len    = { ITERATE_LEN },
24462306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
24562306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0) | BIT(1),
24662306a36Sopenharmony_ci		.transfer_count = 2,
24762306a36Sopenharmony_ci		.transfers		= {
24862306a36Sopenharmony_ci			{
24962306a36Sopenharmony_ci				.tx_buf = TX(0),
25062306a36Sopenharmony_ci				.rx_buf = RX(0),
25162306a36Sopenharmony_ci			},
25262306a36Sopenharmony_ci			{
25362306a36Sopenharmony_ci				/* making sure we align without overwrite
25462306a36Sopenharmony_ci				 * the reason we can not use ITERATE_MAX_LEN
25562306a36Sopenharmony_ci				 */
25662306a36Sopenharmony_ci				.tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
25762306a36Sopenharmony_ci				.rx_buf = RX(SPI_TEST_MAX_SIZE_HALF),
25862306a36Sopenharmony_ci			},
25962306a36Sopenharmony_ci		},
26062306a36Sopenharmony_ci	},
26162306a36Sopenharmony_ci	{
26262306a36Sopenharmony_ci		.description	= "two tx+rx transfers - alter first",
26362306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
26462306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
26562306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
26662306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0),
26762306a36Sopenharmony_ci		.transfer_count = 2,
26862306a36Sopenharmony_ci		.transfers		= {
26962306a36Sopenharmony_ci			{
27062306a36Sopenharmony_ci				/* making sure we align without overwrite */
27162306a36Sopenharmony_ci				.tx_buf = TX(1024),
27262306a36Sopenharmony_ci				.rx_buf = RX(1024),
27362306a36Sopenharmony_ci			},
27462306a36Sopenharmony_ci			{
27562306a36Sopenharmony_ci				.len = 1,
27662306a36Sopenharmony_ci				/* making sure we align without overwrite */
27762306a36Sopenharmony_ci				.tx_buf = TX(0),
27862306a36Sopenharmony_ci				.rx_buf = RX(0),
27962306a36Sopenharmony_ci			},
28062306a36Sopenharmony_ci		},
28162306a36Sopenharmony_ci	},
28262306a36Sopenharmony_ci	{
28362306a36Sopenharmony_ci		.description	= "two tx+rx transfers - alter second",
28462306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
28562306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
28662306a36Sopenharmony_ci		.iterate_tx_align = ITERATE_ALIGN,
28762306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(1),
28862306a36Sopenharmony_ci		.transfer_count = 2,
28962306a36Sopenharmony_ci		.transfers		= {
29062306a36Sopenharmony_ci			{
29162306a36Sopenharmony_ci				.len = 1,
29262306a36Sopenharmony_ci				.tx_buf = TX(0),
29362306a36Sopenharmony_ci				.rx_buf = RX(0),
29462306a36Sopenharmony_ci			},
29562306a36Sopenharmony_ci			{
29662306a36Sopenharmony_ci				/* making sure we align without overwrite */
29762306a36Sopenharmony_ci				.tx_buf = TX(1024),
29862306a36Sopenharmony_ci				.rx_buf = RX(1024),
29962306a36Sopenharmony_ci			},
30062306a36Sopenharmony_ci		},
30162306a36Sopenharmony_ci	},
30262306a36Sopenharmony_ci	{
30362306a36Sopenharmony_ci		.description	= "two tx+rx transfers - delay after transfer",
30462306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
30562306a36Sopenharmony_ci		.iterate_len    = { ITERATE_MAX_LEN },
30662306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(0) | BIT(1),
30762306a36Sopenharmony_ci		.transfer_count = 2,
30862306a36Sopenharmony_ci		.transfers		= {
30962306a36Sopenharmony_ci			{
31062306a36Sopenharmony_ci				.tx_buf = TX(0),
31162306a36Sopenharmony_ci				.rx_buf = RX(0),
31262306a36Sopenharmony_ci				.delay = {
31362306a36Sopenharmony_ci					.value = 1000,
31462306a36Sopenharmony_ci					.unit = SPI_DELAY_UNIT_USECS,
31562306a36Sopenharmony_ci				},
31662306a36Sopenharmony_ci			},
31762306a36Sopenharmony_ci			{
31862306a36Sopenharmony_ci				.tx_buf = TX(0),
31962306a36Sopenharmony_ci				.rx_buf = RX(0),
32062306a36Sopenharmony_ci				.delay = {
32162306a36Sopenharmony_ci					.value = 1000,
32262306a36Sopenharmony_ci					.unit = SPI_DELAY_UNIT_USECS,
32362306a36Sopenharmony_ci				},
32462306a36Sopenharmony_ci			},
32562306a36Sopenharmony_ci		},
32662306a36Sopenharmony_ci	},
32762306a36Sopenharmony_ci	{
32862306a36Sopenharmony_ci		.description	= "three tx+rx transfers with overlapping cache lines",
32962306a36Sopenharmony_ci		.fill_option	= FILL_COUNT_8,
33062306a36Sopenharmony_ci		/*
33162306a36Sopenharmony_ci		 * This should be large enough for the controller driver to
33262306a36Sopenharmony_ci		 * choose to transfer it with DMA.
33362306a36Sopenharmony_ci		 */
33462306a36Sopenharmony_ci		.iterate_len    = { 512, -1 },
33562306a36Sopenharmony_ci		.iterate_transfer_mask = BIT(1),
33662306a36Sopenharmony_ci		.transfer_count = 3,
33762306a36Sopenharmony_ci		.transfers		= {
33862306a36Sopenharmony_ci			{
33962306a36Sopenharmony_ci				.len = 1,
34062306a36Sopenharmony_ci				.tx_buf = TX(0),
34162306a36Sopenharmony_ci				.rx_buf = RX(0),
34262306a36Sopenharmony_ci			},
34362306a36Sopenharmony_ci			{
34462306a36Sopenharmony_ci				.tx_buf = TX(1),
34562306a36Sopenharmony_ci				.rx_buf = RX(1),
34662306a36Sopenharmony_ci			},
34762306a36Sopenharmony_ci			{
34862306a36Sopenharmony_ci				.len = 1,
34962306a36Sopenharmony_ci				.tx_buf = TX(513),
35062306a36Sopenharmony_ci				.rx_buf = RX(513),
35162306a36Sopenharmony_ci			},
35262306a36Sopenharmony_ci		},
35362306a36Sopenharmony_ci	},
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	{ /* end of tests sequence */ }
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int spi_loopback_test_probe(struct spi_device *spi)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	int ret;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (loop_req || no_cs) {
36362306a36Sopenharmony_ci		spi->mode |= loop_req ? SPI_LOOP : 0;
36462306a36Sopenharmony_ci		spi->mode |= no_cs ? SPI_NO_CS : 0;
36562306a36Sopenharmony_ci		ret = spi_setup(spi);
36662306a36Sopenharmony_ci		if (ret) {
36762306a36Sopenharmony_ci			dev_err(&spi->dev, "SPI setup with SPI_LOOP or SPI_NO_CS failed (%d)\n",
36862306a36Sopenharmony_ci				ret);
36962306a36Sopenharmony_ci			return ret;
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	dev_info(&spi->dev, "Executing spi-loopback-tests\n");
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = spi_test_run_tests(spi, spi_tests);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	dev_info(&spi->dev, "Finished spi-loopback-tests with return: %i\n",
37862306a36Sopenharmony_ci		 ret);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return ret;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/* non const match table to permit to change via a module parameter */
38462306a36Sopenharmony_cistatic struct of_device_id spi_loopback_test_of_match[] = {
38562306a36Sopenharmony_ci	{ .compatible	= "linux,spi-loopback-test", },
38662306a36Sopenharmony_ci	{ }
38762306a36Sopenharmony_ci};
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/* allow to override the compatible string via a module_parameter */
39062306a36Sopenharmony_cimodule_param_string(compatible, spi_loopback_test_of_match[0].compatible,
39162306a36Sopenharmony_ci		    sizeof(spi_loopback_test_of_match[0].compatible),
39262306a36Sopenharmony_ci		    0000);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spi_loopback_test_of_match);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic struct spi_driver spi_loopback_test_driver = {
39762306a36Sopenharmony_ci	.driver = {
39862306a36Sopenharmony_ci		.name = "spi-loopback-test",
39962306a36Sopenharmony_ci		.owner = THIS_MODULE,
40062306a36Sopenharmony_ci		.of_match_table = spi_loopback_test_of_match,
40162306a36Sopenharmony_ci	},
40262306a36Sopenharmony_ci	.probe = spi_loopback_test_probe,
40362306a36Sopenharmony_ci};
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cimodule_spi_driver(spi_loopback_test_driver);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ciMODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
40862306a36Sopenharmony_ciMODULE_DESCRIPTION("test spi_driver to check core functionality");
40962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/* spi_test implementation */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci#define RANGE_CHECK(ptr, plen, start, slen) \
41662306a36Sopenharmony_ci	((ptr >= start) && (ptr + plen <= start + slen))
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/* we allocate one page more, to allow for offsets */
41962306a36Sopenharmony_ci#define SPI_TEST_MAX_SIZE_PLUS (SPI_TEST_MAX_SIZE + PAGE_SIZE)
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void spi_test_print_hex_dump(char *pre, const void *ptr, size_t len)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	/* limit the hex_dump */
42462306a36Sopenharmony_ci	if (len < 1024) {
42562306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, pre,
42662306a36Sopenharmony_ci			       DUMP_PREFIX_OFFSET, 16, 1,
42762306a36Sopenharmony_ci			       ptr, len, 0);
42862306a36Sopenharmony_ci		return;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci	/* print head */
43162306a36Sopenharmony_ci	print_hex_dump(KERN_INFO, pre,
43262306a36Sopenharmony_ci		       DUMP_PREFIX_OFFSET, 16, 1,
43362306a36Sopenharmony_ci		       ptr, 512, 0);
43462306a36Sopenharmony_ci	/* print tail */
43562306a36Sopenharmony_ci	pr_info("%s truncated - continuing at offset %04zx\n",
43662306a36Sopenharmony_ci		pre, len - 512);
43762306a36Sopenharmony_ci	print_hex_dump(KERN_INFO, pre,
43862306a36Sopenharmony_ci		       DUMP_PREFIX_OFFSET, 16, 1,
43962306a36Sopenharmony_ci		       ptr + (len - 512), 512, 0);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void spi_test_dump_message(struct spi_device *spi,
44362306a36Sopenharmony_ci				  struct spi_message *msg,
44462306a36Sopenharmony_ci				  bool dump_data)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct spi_transfer *xfer;
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci	u8 b;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	dev_info(&spi->dev, "  spi_msg@%pK\n", msg);
45162306a36Sopenharmony_ci	if (msg->status)
45262306a36Sopenharmony_ci		dev_info(&spi->dev, "    status:        %i\n",
45362306a36Sopenharmony_ci			 msg->status);
45462306a36Sopenharmony_ci	dev_info(&spi->dev, "    frame_length:  %i\n",
45562306a36Sopenharmony_ci		 msg->frame_length);
45662306a36Sopenharmony_ci	dev_info(&spi->dev, "    actual_length: %i\n",
45762306a36Sopenharmony_ci		 msg->actual_length);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
46062306a36Sopenharmony_ci		dev_info(&spi->dev, "    spi_transfer@%pK\n", xfer);
46162306a36Sopenharmony_ci		dev_info(&spi->dev, "      len:    %i\n", xfer->len);
46262306a36Sopenharmony_ci		dev_info(&spi->dev, "      tx_buf: %pK\n", xfer->tx_buf);
46362306a36Sopenharmony_ci		if (dump_data && xfer->tx_buf)
46462306a36Sopenharmony_ci			spi_test_print_hex_dump("          TX: ",
46562306a36Sopenharmony_ci						xfer->tx_buf,
46662306a36Sopenharmony_ci						xfer->len);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		dev_info(&spi->dev, "      rx_buf: %pK\n", xfer->rx_buf);
46962306a36Sopenharmony_ci		if (dump_data && xfer->rx_buf)
47062306a36Sopenharmony_ci			spi_test_print_hex_dump("          RX: ",
47162306a36Sopenharmony_ci						xfer->rx_buf,
47262306a36Sopenharmony_ci						xfer->len);
47362306a36Sopenharmony_ci		/* check for unwritten test pattern on rx_buf */
47462306a36Sopenharmony_ci		if (xfer->rx_buf) {
47562306a36Sopenharmony_ci			for (i = 0 ; i < xfer->len ; i++) {
47662306a36Sopenharmony_ci				b = ((u8 *)xfer->rx_buf)[xfer->len - 1 - i];
47762306a36Sopenharmony_ci				if (b != SPI_TEST_PATTERN_UNWRITTEN)
47862306a36Sopenharmony_ci					break;
47962306a36Sopenharmony_ci			}
48062306a36Sopenharmony_ci			if (i)
48162306a36Sopenharmony_ci				dev_info(&spi->dev,
48262306a36Sopenharmony_ci					 "      rx_buf filled with %02x starts at offset: %i\n",
48362306a36Sopenharmony_ci					 SPI_TEST_PATTERN_UNWRITTEN,
48462306a36Sopenharmony_ci					 xfer->len - i);
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistruct rx_ranges {
49062306a36Sopenharmony_ci	struct list_head list;
49162306a36Sopenharmony_ci	u8 *start;
49262306a36Sopenharmony_ci	u8 *end;
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int rx_ranges_cmp(void *priv, const struct list_head *a,
49662306a36Sopenharmony_ci			 const struct list_head *b)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list);
49962306a36Sopenharmony_ci	struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (rx_a->start > rx_b->start)
50262306a36Sopenharmony_ci		return 1;
50362306a36Sopenharmony_ci	if (rx_a->start < rx_b->start)
50462306a36Sopenharmony_ci		return -1;
50562306a36Sopenharmony_ci	return 0;
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic int spi_check_rx_ranges(struct spi_device *spi,
50962306a36Sopenharmony_ci			       struct spi_message *msg,
51062306a36Sopenharmony_ci			       void *rx)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct spi_transfer *xfer;
51362306a36Sopenharmony_ci	struct rx_ranges ranges[SPI_TEST_MAX_TRANSFERS], *r;
51462306a36Sopenharmony_ci	int i = 0;
51562306a36Sopenharmony_ci	LIST_HEAD(ranges_list);
51662306a36Sopenharmony_ci	u8 *addr;
51762306a36Sopenharmony_ci	int ret = 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* loop over all transfers to fill in the rx_ranges */
52062306a36Sopenharmony_ci	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
52162306a36Sopenharmony_ci		/* if there is no rx, then no check is needed */
52262306a36Sopenharmony_ci		if (!xfer->rx_buf)
52362306a36Sopenharmony_ci			continue;
52462306a36Sopenharmony_ci		/* fill in the rx_range */
52562306a36Sopenharmony_ci		if (RANGE_CHECK(xfer->rx_buf, xfer->len,
52662306a36Sopenharmony_ci				rx, SPI_TEST_MAX_SIZE_PLUS)) {
52762306a36Sopenharmony_ci			ranges[i].start = xfer->rx_buf;
52862306a36Sopenharmony_ci			ranges[i].end = xfer->rx_buf + xfer->len;
52962306a36Sopenharmony_ci			list_add(&ranges[i].list, &ranges_list);
53062306a36Sopenharmony_ci			i++;
53162306a36Sopenharmony_ci		}
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* if no ranges, then we can return and avoid the checks...*/
53562306a36Sopenharmony_ci	if (!i)
53662306a36Sopenharmony_ci		return 0;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* sort the list */
53962306a36Sopenharmony_ci	list_sort(NULL, &ranges_list, rx_ranges_cmp);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* and iterate over all the rx addresses */
54262306a36Sopenharmony_ci	for (addr = rx; addr < (u8 *)rx + SPI_TEST_MAX_SIZE_PLUS; addr++) {
54362306a36Sopenharmony_ci		/* if we are the DO not write pattern,
54462306a36Sopenharmony_ci		 * then continue with the loop...
54562306a36Sopenharmony_ci		 */
54662306a36Sopenharmony_ci		if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE)
54762306a36Sopenharmony_ci			continue;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		/* check if we are inside a range */
55062306a36Sopenharmony_ci		list_for_each_entry(r, &ranges_list, list) {
55162306a36Sopenharmony_ci			/* if so then set to end... */
55262306a36Sopenharmony_ci			if ((addr >= r->start) && (addr < r->end))
55362306a36Sopenharmony_ci				addr = r->end;
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci		/* second test after a (hopefull) translation */
55662306a36Sopenharmony_ci		if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE)
55762306a36Sopenharmony_ci			continue;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		/* if still not found then something has modified too much */
56062306a36Sopenharmony_ci		/* we could list the "closest" transfer here... */
56162306a36Sopenharmony_ci		dev_err(&spi->dev,
56262306a36Sopenharmony_ci			"loopback strangeness - rx changed outside of allowed range at: %pK\n",
56362306a36Sopenharmony_ci			addr);
56462306a36Sopenharmony_ci		/* do not return, only set ret,
56562306a36Sopenharmony_ci		 * so that we list all addresses
56662306a36Sopenharmony_ci		 */
56762306a36Sopenharmony_ci		ret = -ERANGE;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return ret;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int spi_test_check_elapsed_time(struct spi_device *spi,
57462306a36Sopenharmony_ci				       struct spi_test *test)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	int i;
57762306a36Sopenharmony_ci	unsigned long long estimated_time = 0;
57862306a36Sopenharmony_ci	unsigned long long delay_usecs = 0;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	for (i = 0; i < test->transfer_count; i++) {
58162306a36Sopenharmony_ci		struct spi_transfer *xfer = test->transfers + i;
58262306a36Sopenharmony_ci		unsigned long long nbits = (unsigned long long)BITS_PER_BYTE *
58362306a36Sopenharmony_ci					   xfer->len;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		delay_usecs += xfer->delay.value;
58662306a36Sopenharmony_ci		if (!xfer->speed_hz)
58762306a36Sopenharmony_ci			continue;
58862306a36Sopenharmony_ci		estimated_time += div_u64(nbits * NSEC_PER_SEC, xfer->speed_hz);
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	estimated_time += delay_usecs * NSEC_PER_USEC;
59262306a36Sopenharmony_ci	if (test->elapsed_time < estimated_time) {
59362306a36Sopenharmony_ci		dev_err(&spi->dev,
59462306a36Sopenharmony_ci			"elapsed time %lld ns is shorter than minimum estimated time %lld ns\n",
59562306a36Sopenharmony_ci			test->elapsed_time, estimated_time);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		return -EINVAL;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return 0;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic int spi_test_check_loopback_result(struct spi_device *spi,
60462306a36Sopenharmony_ci					  struct spi_message *msg,
60562306a36Sopenharmony_ci					  void *tx, void *rx)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct spi_transfer *xfer;
60862306a36Sopenharmony_ci	u8 rxb, txb;
60962306a36Sopenharmony_ci	size_t i;
61062306a36Sopenharmony_ci	int ret;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/* checks rx_buffer pattern are valid with loopback or without */
61362306a36Sopenharmony_ci	if (check_ranges) {
61462306a36Sopenharmony_ci		ret = spi_check_rx_ranges(spi, msg, rx);
61562306a36Sopenharmony_ci		if (ret)
61662306a36Sopenharmony_ci			return ret;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* if we run without loopback, then return now */
62062306a36Sopenharmony_ci	if (!loopback)
62162306a36Sopenharmony_ci		return 0;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* if applicable to transfer check that rx_buf is equal to tx_buf */
62462306a36Sopenharmony_ci	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
62562306a36Sopenharmony_ci		/* if there is no rx, then no check is needed */
62662306a36Sopenharmony_ci		if (!xfer->len || !xfer->rx_buf)
62762306a36Sopenharmony_ci			continue;
62862306a36Sopenharmony_ci		/* so depending on tx_buf we need to handle things */
62962306a36Sopenharmony_ci		if (xfer->tx_buf) {
63062306a36Sopenharmony_ci			for (i = 0; i < xfer->len; i++) {
63162306a36Sopenharmony_ci				txb = ((u8 *)xfer->tx_buf)[i];
63262306a36Sopenharmony_ci				rxb = ((u8 *)xfer->rx_buf)[i];
63362306a36Sopenharmony_ci				if (txb != rxb)
63462306a36Sopenharmony_ci					goto mismatch_error;
63562306a36Sopenharmony_ci			}
63662306a36Sopenharmony_ci		} else {
63762306a36Sopenharmony_ci			/* first byte received */
63862306a36Sopenharmony_ci			txb = ((u8 *)xfer->rx_buf)[0];
63962306a36Sopenharmony_ci			/* first byte may be 0 or xff */
64062306a36Sopenharmony_ci			if (!((txb == 0) || (txb == 0xff))) {
64162306a36Sopenharmony_ci				dev_err(&spi->dev,
64262306a36Sopenharmony_ci					"loopback strangeness - we expect 0x00 or 0xff, but not 0x%02x\n",
64362306a36Sopenharmony_ci					txb);
64462306a36Sopenharmony_ci				return -EINVAL;
64562306a36Sopenharmony_ci			}
64662306a36Sopenharmony_ci			/* check that all bytes are identical */
64762306a36Sopenharmony_ci			for (i = 1; i < xfer->len; i++) {
64862306a36Sopenharmony_ci				rxb = ((u8 *)xfer->rx_buf)[i];
64962306a36Sopenharmony_ci				if (rxb != txb)
65062306a36Sopenharmony_ci					goto mismatch_error;
65162306a36Sopenharmony_ci			}
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cimismatch_error:
65862306a36Sopenharmony_ci	dev_err(&spi->dev,
65962306a36Sopenharmony_ci		"loopback strangeness - transfer mismatch on byte %04zx - expected 0x%02x, but got 0x%02x\n",
66062306a36Sopenharmony_ci		i, txb, rxb);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return -EINVAL;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic int spi_test_translate(struct spi_device *spi,
66662306a36Sopenharmony_ci			      void **ptr, size_t len,
66762306a36Sopenharmony_ci			      void *tx, void *rx)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	size_t off;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* return on null */
67262306a36Sopenharmony_ci	if (!*ptr)
67362306a36Sopenharmony_ci		return 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* in the MAX_SIZE_HALF case modify the pointer */
67662306a36Sopenharmony_ci	if (((size_t)*ptr) & SPI_TEST_MAX_SIZE_HALF)
67762306a36Sopenharmony_ci		/* move the pointer to the correct range */
67862306a36Sopenharmony_ci		*ptr += (SPI_TEST_MAX_SIZE_PLUS / 2) -
67962306a36Sopenharmony_ci			SPI_TEST_MAX_SIZE_HALF;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* RX range
68262306a36Sopenharmony_ci	 * - we check against MAX_SIZE_PLUS to allow for automated alignment
68362306a36Sopenharmony_ci	 */
68462306a36Sopenharmony_ci	if (RANGE_CHECK(*ptr, len,  RX(0), SPI_TEST_MAX_SIZE_PLUS)) {
68562306a36Sopenharmony_ci		off = *ptr - RX(0);
68662306a36Sopenharmony_ci		*ptr = rx + off;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		return 0;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* TX range */
69262306a36Sopenharmony_ci	if (RANGE_CHECK(*ptr, len,  TX(0), SPI_TEST_MAX_SIZE_PLUS)) {
69362306a36Sopenharmony_ci		off = *ptr - TX(0);
69462306a36Sopenharmony_ci		*ptr = tx + off;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci		return 0;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	dev_err(&spi->dev,
70062306a36Sopenharmony_ci		"PointerRange [%pK:%pK[ not in range [%pK:%pK[ or [%pK:%pK[\n",
70162306a36Sopenharmony_ci		*ptr, *ptr + len,
70262306a36Sopenharmony_ci		RX(0), RX(SPI_TEST_MAX_SIZE),
70362306a36Sopenharmony_ci		TX(0), TX(SPI_TEST_MAX_SIZE));
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return -EINVAL;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic int spi_test_fill_pattern(struct spi_device *spi,
70962306a36Sopenharmony_ci				 struct spi_test *test)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct spi_transfer *xfers = test->transfers;
71262306a36Sopenharmony_ci	u8 *tx_buf;
71362306a36Sopenharmony_ci	size_t count = 0;
71462306a36Sopenharmony_ci	int i, j;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
71762306a36Sopenharmony_ci#define GET_VALUE_BYTE(value, index, bytes) \
71862306a36Sopenharmony_ci	(value >> (8 * (bytes - 1 - count % bytes)))
71962306a36Sopenharmony_ci#else
72062306a36Sopenharmony_ci#define GET_VALUE_BYTE(value, index, bytes) \
72162306a36Sopenharmony_ci	(value >> (8 * (count % bytes)))
72262306a36Sopenharmony_ci#endif
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* fill all transfers with the pattern requested */
72562306a36Sopenharmony_ci	for (i = 0; i < test->transfer_count; i++) {
72662306a36Sopenharmony_ci		/* fill rx_buf with SPI_TEST_PATTERN_UNWRITTEN */
72762306a36Sopenharmony_ci		if (xfers[i].rx_buf)
72862306a36Sopenharmony_ci			memset(xfers[i].rx_buf, SPI_TEST_PATTERN_UNWRITTEN,
72962306a36Sopenharmony_ci			       xfers[i].len);
73062306a36Sopenharmony_ci		/* if tx_buf is NULL then skip */
73162306a36Sopenharmony_ci		tx_buf = (u8 *)xfers[i].tx_buf;
73262306a36Sopenharmony_ci		if (!tx_buf)
73362306a36Sopenharmony_ci			continue;
73462306a36Sopenharmony_ci		/* modify all the transfers */
73562306a36Sopenharmony_ci		for (j = 0; j < xfers[i].len; j++, tx_buf++, count++) {
73662306a36Sopenharmony_ci			/* fill tx */
73762306a36Sopenharmony_ci			switch (test->fill_option) {
73862306a36Sopenharmony_ci			case FILL_MEMSET_8:
73962306a36Sopenharmony_ci				*tx_buf = test->fill_pattern;
74062306a36Sopenharmony_ci				break;
74162306a36Sopenharmony_ci			case FILL_MEMSET_16:
74262306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(test->fill_pattern,
74362306a36Sopenharmony_ci							 count, 2);
74462306a36Sopenharmony_ci				break;
74562306a36Sopenharmony_ci			case FILL_MEMSET_24:
74662306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(test->fill_pattern,
74762306a36Sopenharmony_ci							 count, 3);
74862306a36Sopenharmony_ci				break;
74962306a36Sopenharmony_ci			case FILL_MEMSET_32:
75062306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(test->fill_pattern,
75162306a36Sopenharmony_ci							 count, 4);
75262306a36Sopenharmony_ci				break;
75362306a36Sopenharmony_ci			case FILL_COUNT_8:
75462306a36Sopenharmony_ci				*tx_buf = count;
75562306a36Sopenharmony_ci				break;
75662306a36Sopenharmony_ci			case FILL_COUNT_16:
75762306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(count, count, 2);
75862306a36Sopenharmony_ci				break;
75962306a36Sopenharmony_ci			case FILL_COUNT_24:
76062306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(count, count, 3);
76162306a36Sopenharmony_ci				break;
76262306a36Sopenharmony_ci			case FILL_COUNT_32:
76362306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(count, count, 4);
76462306a36Sopenharmony_ci				break;
76562306a36Sopenharmony_ci			case FILL_TRANSFER_BYTE_8:
76662306a36Sopenharmony_ci				*tx_buf = j;
76762306a36Sopenharmony_ci				break;
76862306a36Sopenharmony_ci			case FILL_TRANSFER_BYTE_16:
76962306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(j, j, 2);
77062306a36Sopenharmony_ci				break;
77162306a36Sopenharmony_ci			case FILL_TRANSFER_BYTE_24:
77262306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(j, j, 3);
77362306a36Sopenharmony_ci				break;
77462306a36Sopenharmony_ci			case FILL_TRANSFER_BYTE_32:
77562306a36Sopenharmony_ci				*tx_buf = GET_VALUE_BYTE(j, j, 4);
77662306a36Sopenharmony_ci				break;
77762306a36Sopenharmony_ci			case FILL_TRANSFER_NUM:
77862306a36Sopenharmony_ci				*tx_buf = i;
77962306a36Sopenharmony_ci				break;
78062306a36Sopenharmony_ci			default:
78162306a36Sopenharmony_ci				dev_err(&spi->dev,
78262306a36Sopenharmony_ci					"unsupported fill_option: %i\n",
78362306a36Sopenharmony_ci					test->fill_option);
78462306a36Sopenharmony_ci				return -EINVAL;
78562306a36Sopenharmony_ci			}
78662306a36Sopenharmony_ci		}
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic int _spi_test_run_iter(struct spi_device *spi,
79362306a36Sopenharmony_ci			      struct spi_test *test,
79462306a36Sopenharmony_ci			      void *tx, void *rx)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct spi_message *msg = &test->msg;
79762306a36Sopenharmony_ci	struct spi_transfer *x;
79862306a36Sopenharmony_ci	int i, ret;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	/* initialize message - zero-filled via static initialization */
80162306a36Sopenharmony_ci	spi_message_init_no_memset(msg);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* fill rx with the DO_NOT_WRITE pattern */
80462306a36Sopenharmony_ci	memset(rx, SPI_TEST_PATTERN_DO_NOT_WRITE, SPI_TEST_MAX_SIZE_PLUS);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	/* add the individual transfers */
80762306a36Sopenharmony_ci	for (i = 0; i < test->transfer_count; i++) {
80862306a36Sopenharmony_ci		x = &test->transfers[i];
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		/* patch the values of tx_buf */
81162306a36Sopenharmony_ci		ret = spi_test_translate(spi, (void **)&x->tx_buf, x->len,
81262306a36Sopenharmony_ci					 (void *)tx, rx);
81362306a36Sopenharmony_ci		if (ret)
81462306a36Sopenharmony_ci			return ret;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		/* patch the values of rx_buf */
81762306a36Sopenharmony_ci		ret = spi_test_translate(spi, &x->rx_buf, x->len,
81862306a36Sopenharmony_ci					 (void *)tx, rx);
81962306a36Sopenharmony_ci		if (ret)
82062306a36Sopenharmony_ci			return ret;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		/* and add it to the list */
82362306a36Sopenharmony_ci		spi_message_add_tail(x, msg);
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* fill in the transfer buffers with pattern */
82762306a36Sopenharmony_ci	ret = spi_test_fill_pattern(spi, test);
82862306a36Sopenharmony_ci	if (ret)
82962306a36Sopenharmony_ci		return ret;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* and execute */
83262306a36Sopenharmony_ci	if (test->execute_msg)
83362306a36Sopenharmony_ci		ret = test->execute_msg(spi, test, tx, rx);
83462306a36Sopenharmony_ci	else
83562306a36Sopenharmony_ci		ret = spi_test_execute_msg(spi, test, tx, rx);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	/* handle result */
83862306a36Sopenharmony_ci	if (ret == test->expected_return)
83962306a36Sopenharmony_ci		return 0;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	dev_err(&spi->dev,
84262306a36Sopenharmony_ci		"test failed - test returned %i, but we expect %i\n",
84362306a36Sopenharmony_ci		ret, test->expected_return);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (ret)
84662306a36Sopenharmony_ci		return ret;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* if it is 0, as we expected something else,
84962306a36Sopenharmony_ci	 * then return something special
85062306a36Sopenharmony_ci	 */
85162306a36Sopenharmony_ci	return -EFAULT;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic int spi_test_run_iter(struct spi_device *spi,
85562306a36Sopenharmony_ci			     const struct spi_test *testtemplate,
85662306a36Sopenharmony_ci			     void *tx, void *rx,
85762306a36Sopenharmony_ci			     size_t len,
85862306a36Sopenharmony_ci			     size_t tx_off,
85962306a36Sopenharmony_ci			     size_t rx_off
86062306a36Sopenharmony_ci	)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct spi_test test;
86362306a36Sopenharmony_ci	int i, tx_count, rx_count;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* copy the test template to test */
86662306a36Sopenharmony_ci	memcpy(&test, testtemplate, sizeof(test));
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	/* if iterate_transfer_mask is not set,
86962306a36Sopenharmony_ci	 * then set it to first transfer only
87062306a36Sopenharmony_ci	 */
87162306a36Sopenharmony_ci	if (!(test.iterate_transfer_mask & (BIT(test.transfer_count) - 1)))
87262306a36Sopenharmony_ci		test.iterate_transfer_mask = 1;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* count number of transfers with tx/rx_buf != NULL */
87562306a36Sopenharmony_ci	rx_count = tx_count = 0;
87662306a36Sopenharmony_ci	for (i = 0; i < test.transfer_count; i++) {
87762306a36Sopenharmony_ci		if (test.transfers[i].tx_buf)
87862306a36Sopenharmony_ci			tx_count++;
87962306a36Sopenharmony_ci		if (test.transfers[i].rx_buf)
88062306a36Sopenharmony_ci			rx_count++;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* in some iteration cases warn and exit early,
88462306a36Sopenharmony_ci	 * as there is nothing to do, that has not been tested already...
88562306a36Sopenharmony_ci	 */
88662306a36Sopenharmony_ci	if (tx_off && (!tx_count)) {
88762306a36Sopenharmony_ci		dev_warn_once(&spi->dev,
88862306a36Sopenharmony_ci			      "%s: iterate_tx_off configured with tx_buf==NULL - ignoring\n",
88962306a36Sopenharmony_ci			      test.description);
89062306a36Sopenharmony_ci		return 0;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci	if (rx_off && (!rx_count)) {
89362306a36Sopenharmony_ci		dev_warn_once(&spi->dev,
89462306a36Sopenharmony_ci			      "%s: iterate_rx_off configured with rx_buf==NULL - ignoring\n",
89562306a36Sopenharmony_ci			      test.description);
89662306a36Sopenharmony_ci		return 0;
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/* write out info */
90062306a36Sopenharmony_ci	if (!(len || tx_off || rx_off)) {
90162306a36Sopenharmony_ci		dev_info(&spi->dev, "Running test %s\n", test.description);
90262306a36Sopenharmony_ci	} else {
90362306a36Sopenharmony_ci		dev_info(&spi->dev,
90462306a36Sopenharmony_ci			 "  with iteration values: len = %zu, tx_off = %zu, rx_off = %zu\n",
90562306a36Sopenharmony_ci			 len, tx_off, rx_off);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* update in the values from iteration values */
90962306a36Sopenharmony_ci	for (i = 0; i < test.transfer_count; i++) {
91062306a36Sopenharmony_ci		/* only when bit in transfer mask is set */
91162306a36Sopenharmony_ci		if (!(test.iterate_transfer_mask & BIT(i)))
91262306a36Sopenharmony_ci			continue;
91362306a36Sopenharmony_ci		test.transfers[i].len = len;
91462306a36Sopenharmony_ci		if (test.transfers[i].tx_buf)
91562306a36Sopenharmony_ci			test.transfers[i].tx_buf += tx_off;
91662306a36Sopenharmony_ci		if (test.transfers[i].rx_buf)
91762306a36Sopenharmony_ci			test.transfers[i].rx_buf += rx_off;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* and execute */
92162306a36Sopenharmony_ci	return _spi_test_run_iter(spi, &test, tx, rx);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/**
92562306a36Sopenharmony_ci * spi_test_execute_msg - default implementation to run a test
92662306a36Sopenharmony_ci *
92762306a36Sopenharmony_ci * @spi: @spi_device on which to run the @spi_message
92862306a36Sopenharmony_ci * @test: the test to execute, which already contains @msg
92962306a36Sopenharmony_ci * @tx:   the tx buffer allocated for the test sequence
93062306a36Sopenharmony_ci * @rx:   the rx buffer allocated for the test sequence
93162306a36Sopenharmony_ci *
93262306a36Sopenharmony_ci * Returns: error code of spi_sync as well as basic error checking
93362306a36Sopenharmony_ci */
93462306a36Sopenharmony_ciint spi_test_execute_msg(struct spi_device *spi, struct spi_test *test,
93562306a36Sopenharmony_ci			 void *tx, void *rx)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct spi_message *msg = &test->msg;
93862306a36Sopenharmony_ci	int ret = 0;
93962306a36Sopenharmony_ci	int i;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* only if we do not simulate */
94262306a36Sopenharmony_ci	if (!simulate_only) {
94362306a36Sopenharmony_ci		ktime_t start;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		/* dump the complete message before and after the transfer */
94662306a36Sopenharmony_ci		if (dump_messages == 3)
94762306a36Sopenharmony_ci			spi_test_dump_message(spi, msg, true);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		start = ktime_get();
95062306a36Sopenharmony_ci		/* run spi message */
95162306a36Sopenharmony_ci		ret = spi_sync(spi, msg);
95262306a36Sopenharmony_ci		test->elapsed_time = ktime_to_ns(ktime_sub(ktime_get(), start));
95362306a36Sopenharmony_ci		if (ret == -ETIMEDOUT) {
95462306a36Sopenharmony_ci			dev_info(&spi->dev,
95562306a36Sopenharmony_ci				 "spi-message timed out - rerunning...\n");
95662306a36Sopenharmony_ci			/* rerun after a few explicit schedules */
95762306a36Sopenharmony_ci			for (i = 0; i < 16; i++)
95862306a36Sopenharmony_ci				schedule();
95962306a36Sopenharmony_ci			ret = spi_sync(spi, msg);
96062306a36Sopenharmony_ci		}
96162306a36Sopenharmony_ci		if (ret) {
96262306a36Sopenharmony_ci			dev_err(&spi->dev,
96362306a36Sopenharmony_ci				"Failed to execute spi_message: %i\n",
96462306a36Sopenharmony_ci				ret);
96562306a36Sopenharmony_ci			goto exit;
96662306a36Sopenharmony_ci		}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		/* do some extra error checks */
96962306a36Sopenharmony_ci		if (msg->frame_length != msg->actual_length) {
97062306a36Sopenharmony_ci			dev_err(&spi->dev,
97162306a36Sopenharmony_ci				"actual length differs from expected\n");
97262306a36Sopenharmony_ci			ret = -EIO;
97362306a36Sopenharmony_ci			goto exit;
97462306a36Sopenharmony_ci		}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		/* run rx-buffer tests */
97762306a36Sopenharmony_ci		ret = spi_test_check_loopback_result(spi, msg, tx, rx);
97862306a36Sopenharmony_ci		if (ret)
97962306a36Sopenharmony_ci			goto exit;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		ret = spi_test_check_elapsed_time(spi, test);
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* if requested or on error dump message (including data) */
98562306a36Sopenharmony_ciexit:
98662306a36Sopenharmony_ci	if (dump_messages || ret)
98762306a36Sopenharmony_ci		spi_test_dump_message(spi, msg,
98862306a36Sopenharmony_ci				      (dump_messages >= 2) || (ret));
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	return ret;
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_test_execute_msg);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci/**
99562306a36Sopenharmony_ci * spi_test_run_test - run an individual spi_test
99662306a36Sopenharmony_ci *                     including all the relevant iterations on:
99762306a36Sopenharmony_ci *                     length and buffer alignment
99862306a36Sopenharmony_ci *
99962306a36Sopenharmony_ci * @spi:  the spi_device to send the messages to
100062306a36Sopenharmony_ci * @test: the test which we need to execute
100162306a36Sopenharmony_ci * @tx:   the tx buffer allocated for the test sequence
100262306a36Sopenharmony_ci * @rx:   the rx buffer allocated for the test sequence
100362306a36Sopenharmony_ci *
100462306a36Sopenharmony_ci * Returns: status code of spi_sync or other failures
100562306a36Sopenharmony_ci */
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ciint spi_test_run_test(struct spi_device *spi, const struct spi_test *test,
100862306a36Sopenharmony_ci		      void *tx, void *rx)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	int idx_len;
101162306a36Sopenharmony_ci	size_t len;
101262306a36Sopenharmony_ci	size_t tx_align, rx_align;
101362306a36Sopenharmony_ci	int ret;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* test for transfer limits */
101662306a36Sopenharmony_ci	if (test->transfer_count >= SPI_TEST_MAX_TRANSFERS) {
101762306a36Sopenharmony_ci		dev_err(&spi->dev,
101862306a36Sopenharmony_ci			"%s: Exceeded max number of transfers with %i\n",
101962306a36Sopenharmony_ci			test->description, test->transfer_count);
102062306a36Sopenharmony_ci		return -E2BIG;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* setting up some values in spi_message
102462306a36Sopenharmony_ci	 * based on some settings in spi_master
102562306a36Sopenharmony_ci	 * some of this can also get done in the run() method
102662306a36Sopenharmony_ci	 */
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* iterate over all the iterable values using macros
102962306a36Sopenharmony_ci	 * (to make it a bit more readable...
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci#define FOR_EACH_ALIGNMENT(var)						\
103262306a36Sopenharmony_ci	for (var = 0;							\
103362306a36Sopenharmony_ci	    var < (test->iterate_##var ?				\
103462306a36Sopenharmony_ci			(spi->master->dma_alignment ?			\
103562306a36Sopenharmony_ci			 spi->master->dma_alignment :			\
103662306a36Sopenharmony_ci			 test->iterate_##var) :				\
103762306a36Sopenharmony_ci			1);						\
103862306a36Sopenharmony_ci	    var++)
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	for (idx_len = 0; idx_len < SPI_TEST_MAX_ITERATE &&
104162306a36Sopenharmony_ci	     (len = test->iterate_len[idx_len]) != -1; idx_len++) {
104262306a36Sopenharmony_ci		if ((run_only_iter_len > -1) && len != run_only_iter_len)
104362306a36Sopenharmony_ci			continue;
104462306a36Sopenharmony_ci		FOR_EACH_ALIGNMENT(tx_align) {
104562306a36Sopenharmony_ci			FOR_EACH_ALIGNMENT(rx_align) {
104662306a36Sopenharmony_ci				/* and run the iteration */
104762306a36Sopenharmony_ci				ret = spi_test_run_iter(spi, test,
104862306a36Sopenharmony_ci							tx, rx,
104962306a36Sopenharmony_ci							len,
105062306a36Sopenharmony_ci							tx_align,
105162306a36Sopenharmony_ci							rx_align);
105262306a36Sopenharmony_ci				if (ret)
105362306a36Sopenharmony_ci					return ret;
105462306a36Sopenharmony_ci			}
105562306a36Sopenharmony_ci		}
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	return 0;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_test_run_test);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci/**
106362306a36Sopenharmony_ci * spi_test_run_tests - run an array of spi_messages tests
106462306a36Sopenharmony_ci * @spi: the spi device on which to run the tests
106562306a36Sopenharmony_ci * @tests: NULL-terminated array of @spi_test
106662306a36Sopenharmony_ci *
106762306a36Sopenharmony_ci * Returns: status errors as per @spi_test_run_test()
106862306a36Sopenharmony_ci */
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ciint spi_test_run_tests(struct spi_device *spi,
107162306a36Sopenharmony_ci		       struct spi_test *tests)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	char *rx = NULL, *tx = NULL;
107462306a36Sopenharmony_ci	int ret = 0, count = 0;
107562306a36Sopenharmony_ci	struct spi_test *test;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* allocate rx/tx buffers of 128kB size without devm
107862306a36Sopenharmony_ci	 * in the hope that is on a page boundary
107962306a36Sopenharmony_ci	 */
108062306a36Sopenharmony_ci	if (use_vmalloc)
108162306a36Sopenharmony_ci		rx = vmalloc(SPI_TEST_MAX_SIZE_PLUS);
108262306a36Sopenharmony_ci	else
108362306a36Sopenharmony_ci		rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
108462306a36Sopenharmony_ci	if (!rx)
108562306a36Sopenharmony_ci		return -ENOMEM;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (use_vmalloc)
108962306a36Sopenharmony_ci		tx = vmalloc(SPI_TEST_MAX_SIZE_PLUS);
109062306a36Sopenharmony_ci	else
109162306a36Sopenharmony_ci		tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
109262306a36Sopenharmony_ci	if (!tx) {
109362306a36Sopenharmony_ci		ret = -ENOMEM;
109462306a36Sopenharmony_ci		goto err_tx;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* now run the individual tests in the table */
109862306a36Sopenharmony_ci	for (test = tests, count = 0; test->description[0];
109962306a36Sopenharmony_ci	     test++, count++) {
110062306a36Sopenharmony_ci		/* only run test if requested */
110162306a36Sopenharmony_ci		if ((run_only_test > -1) && (count != run_only_test))
110262306a36Sopenharmony_ci			continue;
110362306a36Sopenharmony_ci		/* run custom implementation */
110462306a36Sopenharmony_ci		if (test->run_test)
110562306a36Sopenharmony_ci			ret = test->run_test(spi, test, tx, rx);
110662306a36Sopenharmony_ci		else
110762306a36Sopenharmony_ci			ret = spi_test_run_test(spi, test, tx, rx);
110862306a36Sopenharmony_ci		if (ret)
110962306a36Sopenharmony_ci			goto out;
111062306a36Sopenharmony_ci		/* add some delays so that we can easily
111162306a36Sopenharmony_ci		 * detect the individual tests when using a logic analyzer
111262306a36Sopenharmony_ci		 * we also add scheduling to avoid potential spi_timeouts...
111362306a36Sopenharmony_ci		 */
111462306a36Sopenharmony_ci		if (delay_ms)
111562306a36Sopenharmony_ci			mdelay(delay_ms);
111662306a36Sopenharmony_ci		schedule();
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ciout:
112062306a36Sopenharmony_ci	kvfree(tx);
112162306a36Sopenharmony_cierr_tx:
112262306a36Sopenharmony_ci	kvfree(rx);
112362306a36Sopenharmony_ci	return ret;
112462306a36Sopenharmony_ci}
112562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spi_test_run_tests);
1126