1 /*
2  * Copyright (c) 2017 BayLibre, SAS
3  * Copyright (c) 2020 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <errno.h>
9 #include <string.h>
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <stdio.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/util.h>
16 
17 #include <zephyr/drivers/i2c.h>
18 #include <zephyr/drivers/i2c/target/eeprom.h>
19 #include <zephyr/drivers/gpio.h>
20 
21 #include <zephyr/ztest.h>
22 
23 #define NODE_EP0 DT_NODELABEL(eeprom0)
24 #define NODE_EP1 DT_NODELABEL(eeprom1)
25 
26 #define TEST_DATA_SIZE	MIN(CONFIG_I2C_TEST_DATA_MAX_SIZE, \
27 			    MIN(DT_PROP(NODE_EP0, size), DT_PROP(NODE_EP1, size)))
28 
29 static uint8_t eeprom_0_data[TEST_DATA_SIZE];
30 static uint8_t eeprom_1_data[TEST_DATA_SIZE];
31 static uint8_t i2c_buffer[TEST_DATA_SIZE];
32 
33 /*
34  * We need 5x(buffer size) + 1 to print a comma-separated list of each
35  * byte in hex, plus a null.
36  */
37 uint8_t buffer_print_eeprom[TEST_DATA_SIZE * 5 + 1];
38 uint8_t buffer_print_i2c[TEST_DATA_SIZE * 5 + 1];
39 
init_eeprom_test_data(void)40 static void init_eeprom_test_data(void)
41 {
42 	size_t n;
43 
44 	/*
45 	 * Initialize EEPROM data with printable ASCII value (range [32 126]).
46 	 * Make sure content differs between eeprom_0_data[] and eeprom_1_data[].
47 	 */
48 	for (n = 0; n < sizeof(eeprom_0_data); n++) {
49 		eeprom_0_data[n] = 32 + (n % (126 - 32));
50 	}
51 
52 	for (n = 0; n < sizeof(eeprom_1_data); n++) {
53 		eeprom_1_data[n] = 32 + (((n + 10) * 3) % (126 - 32));
54 	}
55 }
56 
to_display_format(const uint8_t * src,size_t size,char * dst)57 static void to_display_format(const uint8_t *src, size_t size, char *dst)
58 {
59 	size_t i;
60 
61 	for (i = 0; i < size; i++) {
62 		sprintf(dst + 5 * i, "0x%02x,", src[i]);
63 	}
64 }
65 
run_full_read(const struct device * i2c,uint8_t addr,uint8_t addr_width,const uint8_t * comp_buffer)66 static int run_full_read(const struct device *i2c, uint8_t addr,
67 			 uint8_t addr_width, const uint8_t *comp_buffer)
68 {
69 	int ret;
70 	uint8_t start_addr[2];
71 
72 	TC_PRINT("Testing full read: Master: %s, address: 0x%x\n",
73 		 i2c->name, addr);
74 
75 	/* Read EEPROM from I2C Master requests, then compare */
76 	memset(start_addr, 0, sizeof(start_addr));
77 	ret = i2c_write_read(i2c, addr, start_addr, (addr_width >> 3), i2c_buffer, TEST_DATA_SIZE);
78 	zassert_equal(ret, 0, "Failed to read EEPROM");
79 
80 	if (memcmp(i2c_buffer, comp_buffer, TEST_DATA_SIZE)) {
81 		to_display_format(i2c_buffer, TEST_DATA_SIZE,
82 				  buffer_print_i2c);
83 		to_display_format(comp_buffer, TEST_DATA_SIZE,
84 				  buffer_print_eeprom);
85 		TC_PRINT("Error: Buffer contents are different: %s\n",
86 			 buffer_print_i2c);
87 		TC_PRINT("                         vs expected: %s\n",
88 			 buffer_print_eeprom);
89 		return -EIO;
90 	}
91 
92 	return 0;
93 }
94 
run_partial_read(const struct device * i2c,uint8_t addr,uint8_t addr_width,const uint8_t * comp_buffer,unsigned int offset)95 static int run_partial_read(const struct device *i2c, uint8_t addr,
96 			    uint8_t addr_width, const uint8_t *comp_buffer, unsigned int offset)
97 {
98 	int ret;
99 	uint8_t start_addr[2];
100 
101 	TC_PRINT("Testing partial read. Master: %s, address: 0x%x, off=%d\n",
102 		 i2c->name, addr, offset);
103 
104 	switch (addr_width) {
105 	case 8:
106 		start_addr[0] = (uint8_t) (offset & 0xFF);
107 	break;
108 	case 16:
109 		sys_put_be16((uint16_t)(offset & 0xFFFF), start_addr);
110 	break;
111 	default:
112 		return -EINVAL;
113 	}
114 
115 	ret = i2c_write_read(i2c, addr,
116 			     start_addr, (addr_width >> 3), i2c_buffer, TEST_DATA_SIZE-offset);
117 	zassert_equal(ret, 0, "Failed to read EEPROM");
118 
119 	if (memcmp(i2c_buffer, &comp_buffer[offset], TEST_DATA_SIZE-offset)) {
120 		to_display_format(i2c_buffer, TEST_DATA_SIZE-offset,
121 				  buffer_print_i2c);
122 		to_display_format(&comp_buffer[offset], TEST_DATA_SIZE-offset,
123 				  buffer_print_eeprom);
124 		TC_PRINT("Error: Buffer contents are different: %s\n",
125 			 buffer_print_i2c);
126 		TC_PRINT("                         vs expected: %s\n",
127 			 buffer_print_eeprom);
128 		return -EIO;
129 	}
130 
131 	return 0;
132 }
133 
run_program_read(const struct device * i2c,uint8_t addr,uint8_t addr_width,unsigned int offset)134 static int run_program_read(const struct device *i2c, uint8_t addr,
135 			    uint8_t addr_width, unsigned int offset)
136 {
137 	int ret, i;
138 	uint8_t buf[TEST_DATA_SIZE + 2];
139 	uint8_t addr_size;
140 
141 	TC_PRINT("Testing program. Master: %s, address: 0x%x, off=%d\n",
142 		i2c->name, addr, offset);
143 
144 	switch (addr_width) {
145 	case 8:
146 		buf[0] = (uint8_t) (offset & 0xFF);
147 		addr_size = 1;
148 	break;
149 	case 16:
150 		sys_put_be16((uint16_t)(offset & 0xFFFF), buf);
151 		addr_size = 2;
152 	break;
153 	default:
154 		return -EINVAL;
155 	}
156 
157 	for (i = 0; i < TEST_DATA_SIZE - offset; ++i) {
158 		buf[i + addr_size] = i & 0xFF;
159 	}
160 
161 	ret = i2c_write(i2c, &buf[0], TEST_DATA_SIZE - offset + addr_size, addr);
162 	zassert_equal(ret, 0, "Failed to write EEPROM");
163 
164 	/* Read back EEPROM from I2C Master requests, then compare */
165 	ret = i2c_write_read(i2c, addr, buf, addr_size, i2c_buffer, TEST_DATA_SIZE - offset);
166 	zassert_equal(ret, 0, "Failed to read EEPROM");
167 
168 	for (i = 0 ; i < TEST_DATA_SIZE-offset ; ++i) {
169 		if (i2c_buffer[i] != (i & 0xFF)) {
170 			to_display_format(i2c_buffer, TEST_DATA_SIZE-offset,
171 					  buffer_print_i2c);
172 			TC_PRINT("Error: Unexpected %u (%02x) buffer content: %s\n",
173 				 i, i2c_buffer[i], buffer_print_i2c);
174 			return -EIO;
175 		}
176 	}
177 
178 	return 0;
179 }
180 
ZTEST(i2c_eeprom_target,test_deinit)181 ZTEST(i2c_eeprom_target, test_deinit)
182 {
183 	const struct device *const i2c_0 = DEVICE_DT_GET(DT_BUS(NODE_EP0));
184 	const struct device *const i2c_1 = DEVICE_DT_GET(DT_BUS(NODE_EP1));
185 	const struct gpio_dt_spec sda_pin_0 =
186 		GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), sda0_gpios, {});
187 	const struct gpio_dt_spec scl_pin_0 =
188 		GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), scl0_gpios, {});
189 	const struct gpio_dt_spec sda_pin_1 =
190 		GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), sda1_gpios, {});
191 	const struct gpio_dt_spec scl_pin_1 =
192 		GPIO_DT_SPEC_GET_OR(DT_PATH(zephyr_user), scl1_gpios, {});
193 	int ret;
194 
195 	if (i2c_0 == i2c_1) {
196 		TC_PRINT("  gpio loopback required for test\n");
197 		ztest_test_skip();
198 	}
199 
200 	if (scl_pin_0.port == NULL || sda_pin_0.port == NULL ||
201 	    scl_pin_1.port == NULL || sda_pin_1.port == NULL) {
202 		TC_PRINT("  bus gpios not specified in zephyr,path\n");
203 		ztest_test_skip();
204 	}
205 
206 	ret = device_deinit(i2c_0);
207 	if (ret == -ENOTSUP) {
208 		TC_PRINT("  device deinit not supported\n");
209 		ztest_test_skip();
210 	}
211 
212 	zassert_ok(ret);
213 
214 	ret = device_deinit(i2c_1);
215 	if (ret == -ENOTSUP) {
216 		TC_PRINT("  device deinit not supported\n");
217 		zassert_ok(device_init(i2c_0));
218 		ztest_test_skip();
219 	}
220 
221 	zassert_ok(gpio_pin_configure_dt(&sda_pin_0, GPIO_INPUT));
222 	zassert_ok(gpio_pin_configure_dt(&sda_pin_1, GPIO_OUTPUT_INACTIVE));
223 	zassert_ok(gpio_pin_configure_dt(&scl_pin_0, GPIO_INPUT));
224 	zassert_ok(gpio_pin_configure_dt(&scl_pin_1, GPIO_OUTPUT_INACTIVE));
225 	zassert_equal(gpio_pin_get_dt(&sda_pin_0), 0);
226 	zassert_equal(gpio_pin_get_dt(&scl_pin_0), 0);
227 	zassert_ok(gpio_pin_set_dt(&sda_pin_1, 1));
228 	zassert_ok(gpio_pin_set_dt(&scl_pin_1, 1));
229 	zassert_equal(gpio_pin_get_dt(&sda_pin_0), 1);
230 	zassert_equal(gpio_pin_get_dt(&scl_pin_0), 1);
231 	zassert_ok(gpio_pin_configure_dt(&sda_pin_1, GPIO_INPUT));
232 	zassert_ok(gpio_pin_configure_dt(&scl_pin_1, GPIO_INPUT));
233 	zassert_ok(device_init(i2c_0));
234 	zassert_ok(device_init(i2c_1));
235 }
236 
ZTEST(i2c_eeprom_target,test_eeprom_target)237 ZTEST(i2c_eeprom_target, test_eeprom_target)
238 {
239 	const struct device *const eeprom_0 = DEVICE_DT_GET(NODE_EP0);
240 	const struct device *const i2c_0 = DEVICE_DT_GET(DT_BUS(NODE_EP0));
241 	int addr_0 = DT_REG_ADDR(NODE_EP0);
242 	uint8_t addr_0_width = DT_PROP_OR(NODE_EP0, address_width, 8);
243 	const struct device *const eeprom_1 = DEVICE_DT_GET(NODE_EP1);
244 	const struct device *const i2c_1 = DEVICE_DT_GET(DT_BUS(NODE_EP1));
245 	int addr_1 = DT_REG_ADDR(NODE_EP1);
246 	uint8_t addr_1_width = DT_PROP_OR(NODE_EP1, address_width, 8);
247 	int ret, offset;
248 
249 	init_eeprom_test_data();
250 
251 	zassert_not_null(i2c_0, "EEPROM 0 - I2C bus not found");
252 	zassert_not_null(eeprom_0, "EEPROM 0 device not found");
253 
254 	zassert_true(device_is_ready(i2c_0), "EEPROM 0 - I2C bus not ready");
255 
256 	TC_PRINT("Found EEPROM 0 on I2C bus device %s at addr %02x\n",
257 		 i2c_0->name, addr_0);
258 
259 	zassert_not_null(i2c_1, "EEPROM 1 - I2C device not found");
260 	zassert_not_null(eeprom_1, "EEPROM 1 device not found");
261 
262 	zassert_true(device_is_ready(i2c_1), "EEPROM 1 - I2C bus not ready");
263 
264 	TC_PRINT("Found EEPROM 1 on I2C bus device %s at addr %02x\n",
265 		 i2c_1->name, addr_1);
266 
267 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
268 		TC_PRINT("Testing dual-role\n");
269 	} else {
270 		TC_PRINT("Testing single-role\n");
271 	}
272 
273 	/* Program differentiable data into the two devices through a back door
274 	 * that doesn't use I2C.
275 	 */
276 	ret = eeprom_target_program(eeprom_0, eeprom_0_data, TEST_DATA_SIZE);
277 	zassert_equal(ret, 0, "Failed to program EEPROM 0");
278 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
279 		ret = eeprom_target_program(eeprom_1, eeprom_1_data,
280 					   TEST_DATA_SIZE);
281 		zassert_equal(ret, 0, "Failed to program EEPROM 1");
282 	}
283 
284 	/* Attach each EEPROM to its owning bus as a target device. */
285 	ret = i2c_target_driver_register(eeprom_0);
286 	zassert_equal(ret, 0, "Failed to register EEPROM 0");
287 
288 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
289 		ret = i2c_target_driver_register(eeprom_1);
290 		zassert_equal(ret, 0, "Failed to register EEPROM 1");
291 	}
292 
293 	/* The simulated EP0 is configured to be accessed as a target device
294 	 * at addr_0 on i2c_0 and should expose eeprom_0_data.  The validation
295 	 * uses i2c_1 as a bus master to access this device, which works because
296 	 * i2c_0 and i2_c have their SDA (SCL) pins shorted (they are on the
297 	 * same physical bus).  Thus in these calls i2c_1 is a master device
298 	 * operating on the target address addr_0.
299 	 *
300 	 * Similarly validation of EP1 uses i2c_0 as a master with addr_1 and
301 	 * eeprom_1_data for validation.
302 	 */
303 	ret = run_full_read(i2c_1, addr_0, addr_0_width, eeprom_0_data);
304 	zassert_equal(ret, 0,
305 		     "Full I2C read from EP0 failed");
306 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
307 		ret = run_full_read(i2c_0, addr_1, addr_1_width, eeprom_1_data);
308 		zassert_equal(ret, 0,
309 			      "Full I2C read from EP1 failed");
310 	}
311 
312 	for (offset = 0 ; offset < TEST_DATA_SIZE-1 ; ++offset) {
313 		zassert_equal(0, run_partial_read(i2c_1, addr_0,
314 			      addr_0_width, eeprom_0_data, offset),
315 			      "Partial I2C read EP0 failed");
316 		if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
317 			zassert_equal(0, run_partial_read(i2c_0, addr_1,
318 							  addr_1_width,
319 							  eeprom_1_data,
320 							  offset),
321 				      "Partial I2C read EP1 failed");
322 		}
323 	}
324 
325 	for (offset = 0 ; offset < TEST_DATA_SIZE-1 ; ++offset) {
326 		zassert_equal(0, run_program_read(i2c_1, addr_0,
327 							  addr_0_width, offset),
328 			      "Program I2C read EP0 failed");
329 		if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
330 			zassert_equal(0, run_program_read(i2c_0, addr_1,
331 							  addr_1_width, offset),
332 				      "Program I2C read EP1 failed");
333 		}
334 	}
335 
336 	/* Detach EEPROM */
337 	ret = i2c_target_driver_unregister(eeprom_0);
338 	zassert_equal(ret, 0, "Failed to unregister EEPROM 0");
339 
340 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
341 		ret = i2c_target_driver_unregister(eeprom_1);
342 		zassert_equal(ret, 0, "Failed to unregister EEPROM 1");
343 	}
344 }
345 
346 ZTEST_SUITE(i2c_eeprom_target, NULL, NULL, NULL, NULL, NULL);
347