1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <string.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/linker/devicetree_regions.h>
12 #include <zephyr/ztest.h>
13 
14 #define SPI_MODE (SPI_MODE_CPOL | SPI_WORD_SET(8) | SPI_LINES_SINGLE)
15 #define SPIM_OP	 (SPI_OP_MODE_MASTER | SPI_MODE)
16 #define SPIS_OP	 (SPI_OP_MODE_SLAVE | SPI_MODE)
17 
18 static struct spi_dt_spec spim = SPI_DT_SPEC_GET(DT_NODELABEL(dut_spi_dt), SPIM_OP, 0);
19 static const struct device *spis_dev = DEVICE_DT_GET(DT_NODELABEL(dut_spis));
20 static const struct spi_config spis_config = {
21 	.operation = SPIS_OP
22 };
23 
24 #define MEMORY_SECTION(node)                                                                       \
25 	COND_CODE_1(DT_NODE_HAS_PROP(node, memory_regions),                                        \
26 		    (__attribute__((__section__(                                                   \
27 			    LINKER_DT_NODE_REGION_NAME(DT_PHANDLE(node, memory_regions)))))),      \
28 		    ())
29 
30 static uint8_t spim_buffer[32] MEMORY_SECTION(DT_BUS(DT_NODELABEL(dut_spi_dt)));
31 static uint8_t spis_buffer[32] MEMORY_SECTION(DT_NODELABEL(dut_spis));
32 
33 struct test_data {
34 	int spim_alloc_idx;
35 	int spis_alloc_idx;
36 	struct spi_buf_set sets[4];
37 	struct spi_buf_set *mtx_set;
38 	struct spi_buf_set *mrx_set;
39 	struct spi_buf_set *stx_set;
40 	struct spi_buf_set *srx_set;
41 	struct spi_buf bufs[4];
42 };
43 
44 static struct test_data tdata;
45 
46 /* Allocate buffer from spim or spis space. */
buf_alloc(size_t len,bool spim)47 static uint8_t *buf_alloc(size_t len, bool spim)
48 {
49 	int *idx = spim ? &tdata.spim_alloc_idx : &tdata.spis_alloc_idx;
50 	uint8_t *buf = spim ? spim_buffer : spis_buffer;
51 	size_t total = spim ? sizeof(spim_buffer) : sizeof(spis_buffer);
52 	uint8_t *rv;
53 
54 	if (*idx + len > total) {
55 		zassert_false(true);
56 
57 		return NULL;
58 	}
59 
60 	rv = &buf[*idx];
61 	*idx += len;
62 
63 	return rv;
64 }
65 
ZTEST(spi_error_cases,test_SPI_HALF_DUPLEX_not_supported)66 ZTEST(spi_error_cases, test_SPI_HALF_DUPLEX_not_supported)
67 {
68 	int rv;
69 	int slave_rv;
70 	struct spi_dt_spec spim_invalid = spim;
71 	struct spi_config spis_config_invalid = spis_config;
72 
73 	spim_invalid.config.operation |= SPI_HALF_DUPLEX;
74 	spis_config_invalid.operation |= SPI_HALF_DUPLEX;
75 
76 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
77 	zassert_equal(rv, -ENOTSUP, "Got %d instead", rv);
78 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
79 	zassert_equal(slave_rv, -ENOTSUP, "Got %d instead", slave_rv);
80 }
81 
ZTEST(spi_error_cases,test_SPI_OP_MODE_invalid)82 ZTEST(spi_error_cases, test_SPI_OP_MODE_invalid)
83 {
84 	int rv;
85 	int slave_rv;
86 	struct spi_dt_spec spim_invalid = spim;
87 	struct spi_config spis_config_invalid = spis_config;
88 
89 	spim_invalid.config.operation |= SPI_OP_MODE_SLAVE;
90 	spis_config_invalid.operation &= !SPI_OP_MODE_SLAVE;
91 
92 	/* Check that Operation Mode Slave on spim is not supported */
93 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
94 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
95 	/* Check that Operation Mode Master on spis is not supported */
96 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
97 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
98 }
99 
ZTEST(spi_error_cases,test_SPI_MODE_LOOP_not_supported)100 ZTEST(spi_error_cases, test_SPI_MODE_LOOP_not_supported)
101 {
102 	int rv;
103 	int slave_rv;
104 	struct spi_dt_spec spim_invalid = spim;
105 	struct spi_config spis_config_invalid = spis_config;
106 
107 	spim_invalid.config.operation |= SPI_MODE_LOOP;
108 	spis_config_invalid.operation |= SPI_MODE_LOOP;
109 
110 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
111 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
112 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
113 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
114 }
115 
ZTEST(spi_error_cases,test_only_SPI_LINES_SINGLE_supported)116 ZTEST(spi_error_cases, test_only_SPI_LINES_SINGLE_supported)
117 {
118 	int rv;
119 	int slave_rv;
120 	struct spi_dt_spec spim_invalid = spim;
121 	struct spi_config spis_config_invalid = spis_config;
122 
123 	spim_invalid.config.operation |= SPI_LINES_DUAL;
124 	spis_config_invalid.operation |= SPI_LINES_DUAL;
125 
126 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
127 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
128 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
129 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
130 
131 	spim_invalid = spim;
132 	spis_config_invalid = spis_config;
133 	spim_invalid.config.operation |= SPI_LINES_QUAD;
134 	spis_config_invalid.operation |= SPI_LINES_QUAD;
135 
136 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
137 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
138 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
139 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
140 
141 	spim_invalid = spim;
142 	spis_config_invalid = spis_config;
143 	spim_invalid.config.operation |= SPI_LINES_OCTAL;
144 	spis_config_invalid.operation |= SPI_LINES_OCTAL;
145 
146 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
147 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
148 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
149 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
150 }
151 
ZTEST(spi_error_cases,test_only_8BIT_supported)152 ZTEST(spi_error_cases, test_only_8BIT_supported)
153 {
154 	int rv;
155 	int slave_rv;
156 	struct spi_dt_spec spim_invalid = spim;
157 	struct spi_config spis_config_invalid = spis_config;
158 
159 	spim_invalid.config.operation |= SPI_WORD_SET(16);
160 	spis_config_invalid.operation |= SPI_WORD_SET(16);
161 
162 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
163 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
164 	slave_rv = spi_transceive(spis_dev, &spis_config_invalid, tdata.stx_set, tdata.srx_set);
165 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
166 }
167 
ZTEST(spi_error_cases,test_unsupported_frequency)168 ZTEST(spi_error_cases, test_unsupported_frequency)
169 {
170 	int rv;
171 	struct spi_dt_spec spim_invalid = spim;
172 
173 	spim_invalid.config.frequency = 124999;
174 
175 	rv = spi_transceive_dt(&spim_invalid, tdata.stx_set, tdata.srx_set);
176 	zassert_equal(rv, -EINVAL, "Got %d instead", rv);
177 }
178 
ZTEST(spi_error_cases,test_spis_scattered_tx_buf_not_supported)179 ZTEST(spi_error_cases, test_spis_scattered_tx_buf_not_supported)
180 {
181 	int slave_rv;
182 
183 	tdata.sets[2].count = 2;
184 	slave_rv = spi_transceive(spis_dev, &spis_config, tdata.stx_set, tdata.srx_set);
185 	zassert_equal(slave_rv, -ENOTSUP, "Got %d instead", slave_rv);
186 }
187 
ZTEST(spi_error_cases,test_spis_scattered_rx_buf_not_supported)188 ZTEST(spi_error_cases, test_spis_scattered_rx_buf_not_supported)
189 {
190 	int slave_rv;
191 
192 	tdata.sets[3].count = 2;
193 	slave_rv = spi_transceive(spis_dev, &spis_config, tdata.stx_set, tdata.srx_set);
194 	zassert_equal(slave_rv, -ENOTSUP, "Got %d instead", slave_rv);
195 }
196 
ZTEST(spi_error_cases,test_spis_tx_buf_too_big)197 ZTEST(spi_error_cases, test_spis_tx_buf_too_big)
198 {
199 	int slave_rv;
200 
201 	tdata.bufs[2].len = (size_t)65536;
202 	slave_rv = spi_transceive(spis_dev, &spis_config, tdata.stx_set, tdata.srx_set);
203 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
204 }
205 
ZTEST(spi_error_cases,test_spis_rx_buf_too_big)206 ZTEST(spi_error_cases, test_spis_rx_buf_too_big)
207 {
208 	int slave_rv;
209 
210 	tdata.bufs[3].len = (size_t)65536;
211 	slave_rv = spi_transceive(spis_dev, &spis_config, tdata.stx_set, tdata.srx_set);
212 	zassert_equal(slave_rv, -EINVAL, "Got %d instead", slave_rv);
213 }
214 
ZTEST(spi_error_cases,test_spis_tx_buf_not_in_ram)215 ZTEST(spi_error_cases, test_spis_tx_buf_not_in_ram)
216 {
217 	int slave_rv;
218 
219 	tdata.bufs[2].buf = (void *)0x12345678;
220 	slave_rv = spi_transceive(spis_dev, &spis_config, tdata.stx_set, tdata.srx_set);
221 	zassert_equal(slave_rv, -ENOTSUP, "Got %d instead", slave_rv);
222 }
223 
before(void * not_used)224 static void before(void *not_used)
225 {
226 	ARG_UNUSED(not_used);
227 	size_t len = 16;
228 
229 	memset(&tdata, 0, sizeof(tdata));
230 	for (size_t i = 0; i < sizeof(spim_buffer); i++) {
231 		spim_buffer[i] = (uint8_t)i;
232 	}
233 	for (size_t i = 0; i < sizeof(spis_buffer); i++) {
234 		spis_buffer[i] = (uint8_t)i;
235 	}
236 
237 	for (int i = 0; i < 4; i++) {
238 		tdata.bufs[i].buf = buf_alloc(len, i < 2);
239 		tdata.bufs[i].len = len;
240 		tdata.sets[i].buffers = &tdata.bufs[i];
241 		tdata.sets[i].count = 1;
242 	}
243 
244 	tdata.mtx_set = &tdata.sets[0];
245 	tdata.mrx_set = &tdata.sets[1];
246 	tdata.stx_set = &tdata.sets[2];
247 	tdata.srx_set = &tdata.sets[3];
248 }
249 
suite_setup(void)250 static void *suite_setup(void)
251 {
252 	return NULL;
253 }
254 
255 ZTEST_SUITE(spi_error_cases, NULL, suite_setup, before, NULL, NULL);
256