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/util.h>
15 
16 #include <zephyr/drivers/i2c.h>
17 #include <zephyr/drivers/i2c/target/eeprom.h>
18 
19 #include <zephyr/ztest.h>
20 
21 #define NODE_EP0 DT_NODELABEL(eeprom0)
22 #define NODE_EP1 DT_NODELABEL(eeprom1)
23 
24 #define TEST_DATA_SIZE	20
25 static const uint8_t eeprom_0_data[TEST_DATA_SIZE] = "0123456789abcdefghij";
26 static const uint8_t eeprom_1_data[TEST_DATA_SIZE] = "jihgfedcba9876543210";
27 static uint8_t i2c_buffer[TEST_DATA_SIZE];
28 
29 /*
30  * We need 5x(buffer size) + 1 to print a comma-separated list of each
31  * byte in hex, plus a null.
32  */
33 uint8_t buffer_print_eeprom[TEST_DATA_SIZE * 5 + 1];
34 uint8_t buffer_print_i2c[TEST_DATA_SIZE * 5 + 1];
35 
to_display_format(const uint8_t * src,size_t size,char * dst)36 static void to_display_format(const uint8_t *src, size_t size, char *dst)
37 {
38 	size_t i;
39 
40 	for (i = 0; i < size; i++) {
41 		sprintf(dst + 5 * i, "0x%02x,", src[i]);
42 	}
43 }
44 
run_full_read(const struct device * i2c,uint8_t addr,const uint8_t * comp_buffer)45 static int run_full_read(const struct device *i2c, uint8_t addr,
46 			 const uint8_t *comp_buffer)
47 {
48 	int ret;
49 
50 	TC_PRINT("Testing full read: Master: %s, address: 0x%x\n",
51 		 i2c->name, addr);
52 
53 	/* Read EEPROM from I2C Master requests, then compare */
54 	ret = i2c_burst_read(i2c, addr, 0, i2c_buffer, TEST_DATA_SIZE);
55 	zassert_equal(ret, 0, "Failed to read EEPROM");
56 
57 	if (memcmp(i2c_buffer, comp_buffer, TEST_DATA_SIZE)) {
58 		to_display_format(i2c_buffer, TEST_DATA_SIZE,
59 				  buffer_print_i2c);
60 		to_display_format(comp_buffer, TEST_DATA_SIZE,
61 				  buffer_print_eeprom);
62 		TC_PRINT("Error: Buffer contents are different: %s\n",
63 			 buffer_print_i2c);
64 		TC_PRINT("                         vs expected: %s\n",
65 			 buffer_print_eeprom);
66 		return -EIO;
67 	}
68 
69 	return 0;
70 }
71 
run_partial_read(const struct device * i2c,uint8_t addr,const uint8_t * comp_buffer,unsigned int offset)72 static int run_partial_read(const struct device *i2c, uint8_t addr,
73 			    const uint8_t *comp_buffer, unsigned int offset)
74 {
75 	int ret;
76 
77 	TC_PRINT("Testing partial read. Master: %s, address: 0x%x, off=%d\n",
78 		 i2c->name, addr, offset);
79 
80 	ret = i2c_burst_read(i2c, addr,
81 			     offset, i2c_buffer, TEST_DATA_SIZE-offset);
82 	zassert_equal(ret, 0, "Failed to read EEPROM");
83 
84 	if (memcmp(i2c_buffer, &comp_buffer[offset], TEST_DATA_SIZE-offset)) {
85 		to_display_format(i2c_buffer, TEST_DATA_SIZE-offset,
86 				  buffer_print_i2c);
87 		to_display_format(&comp_buffer[offset], TEST_DATA_SIZE-offset,
88 				  buffer_print_eeprom);
89 		TC_PRINT("Error: Buffer contents are different: %s\n",
90 			 buffer_print_i2c);
91 		TC_PRINT("                         vs expected: %s\n",
92 			 buffer_print_eeprom);
93 		return -EIO;
94 	}
95 
96 	return 0;
97 }
98 
run_program_read(const struct device * i2c,uint8_t addr,unsigned int offset)99 static int run_program_read(const struct device *i2c, uint8_t addr,
100 			    unsigned int offset)
101 {
102 	int ret, i;
103 
104 	TC_PRINT("Testing program. Master: %s, address: 0x%x, off=%d\n",
105 		i2c->name, addr, offset);
106 
107 	for (i = 0 ; i < TEST_DATA_SIZE-offset ; ++i) {
108 		i2c_buffer[i] = i;
109 	}
110 
111 	ret = i2c_burst_write(i2c, addr,
112 			      offset, i2c_buffer, TEST_DATA_SIZE-offset);
113 	zassert_equal(ret, 0, "Failed to write EEPROM");
114 
115 	(void)memset(i2c_buffer, 0xFF, TEST_DATA_SIZE);
116 
117 	/* Read back EEPROM from I2C Master requests, then compare */
118 	ret = i2c_burst_read(i2c, addr,
119 			     offset, i2c_buffer, TEST_DATA_SIZE-offset);
120 	zassert_equal(ret, 0, "Failed to read EEPROM");
121 
122 	for (i = 0 ; i < TEST_DATA_SIZE-offset ; ++i) {
123 		if (i2c_buffer[i] != i) {
124 			to_display_format(i2c_buffer, TEST_DATA_SIZE-offset,
125 					  buffer_print_i2c);
126 			TC_PRINT("Error: Unexpected buffer content: %s\n",
127 				 buffer_print_i2c);
128 			return -EIO;
129 		}
130 	}
131 
132 	return 0;
133 }
134 
ZTEST(i2c_eeprom_target,test_eeprom_target)135 ZTEST(i2c_eeprom_target, test_eeprom_target)
136 {
137 	const struct device *const eeprom_0 = DEVICE_DT_GET(NODE_EP0);
138 	const struct device *const i2c_0 = DEVICE_DT_GET(DT_BUS(NODE_EP0));
139 	int addr_0 = DT_REG_ADDR(NODE_EP0);
140 	const struct device *const eeprom_1 = DEVICE_DT_GET(NODE_EP1);
141 	const struct device *const i2c_1 = DEVICE_DT_GET(DT_BUS(NODE_EP1));
142 	int addr_1 = DT_REG_ADDR(NODE_EP1);
143 	int ret, offset;
144 
145 	zassert_not_null(i2c_0, "EEPROM 0 - I2C bus not found");
146 	zassert_not_null(eeprom_0, "EEPROM 0 device not found");
147 
148 	zassert_true(device_is_ready(i2c_0), "EEPROM 0 - I2C bus not ready");
149 
150 	TC_PRINT("Found EEPROM 0 on I2C bus device %s at addr %02x\n",
151 		 i2c_0->name, addr_0);
152 
153 	zassert_not_null(i2c_1, "EEPROM 1 - I2C device not found");
154 	zassert_not_null(eeprom_1, "EEPROM 1 device not found");
155 
156 	zassert_true(device_is_ready(i2c_1), "EEPROM 1 - I2C bus not ready");
157 
158 	TC_PRINT("Found EEPROM 1 on I2C bus device %s at addr %02x\n",
159 		 i2c_1->name, addr_1);
160 
161 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
162 		TC_PRINT("Testing dual-role\n");
163 	} else {
164 		TC_PRINT("Testing single-role\n");
165 	}
166 
167 	/* Program differentiable data into the two devices through a back door
168 	 * that doesn't use I2C.
169 	 */
170 	ret = eeprom_target_program(eeprom_0, eeprom_0_data, TEST_DATA_SIZE);
171 	zassert_equal(ret, 0, "Failed to program EEPROM 0");
172 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
173 		ret = eeprom_target_program(eeprom_1, eeprom_1_data,
174 					   TEST_DATA_SIZE);
175 		zassert_equal(ret, 0, "Failed to program EEPROM 1");
176 	}
177 
178 	/* Attach each EEPROM to its owning bus as a target device. */
179 	ret = i2c_target_driver_register(eeprom_0);
180 	zassert_equal(ret, 0, "Failed to register EEPROM 0");
181 
182 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
183 		ret = i2c_target_driver_register(eeprom_1);
184 		zassert_equal(ret, 0, "Failed to register EEPROM 1");
185 	}
186 
187 	/* The simulated EP0 is configured to be accessed as a target device
188 	 * at addr_0 on i2c_0 and should expose eeprom_0_data.  The validation
189 	 * uses i2c_1 as a bus master to access this device, which works because
190 	 * i2c_0 and i2_c have their SDA (SCL) pins shorted (they are on the
191 	 * same physical bus).  Thus in these calls i2c_1 is a master device
192 	 * operating on the target address addr_0.
193 	 *
194 	 * Similarly validation of EP1 uses i2c_0 as a master with addr_1 and
195 	 * eeprom_1_data for validation.
196 	 */
197 	ret = run_full_read(i2c_1, addr_0, eeprom_0_data);
198 	zassert_equal(ret, 0,
199 		     "Full I2C read from EP0 failed");
200 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
201 		ret = run_full_read(i2c_0, addr_1, eeprom_1_data);
202 		zassert_equal(ret, 0,
203 			      "Full I2C read from EP1 failed");
204 	}
205 
206 	for (offset = 0 ; offset < TEST_DATA_SIZE-1 ; ++offset) {
207 		zassert_equal(0, run_partial_read(i2c_1, addr_0,
208 			      eeprom_0_data, offset),
209 			      "Partial I2C read EP0 failed");
210 		if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
211 			zassert_equal(0, run_partial_read(i2c_0, addr_1,
212 							  eeprom_1_data,
213 							  offset),
214 				      "Partial I2C read EP1 failed");
215 		}
216 	}
217 
218 	for (offset = 0 ; offset < TEST_DATA_SIZE-1 ; ++offset) {
219 		zassert_equal(0, run_program_read(i2c_1, addr_0, offset),
220 			      "Program I2C read EP0 failed");
221 		if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
222 			zassert_equal(0, run_program_read(i2c_0, addr_1,
223 							  offset),
224 				      "Program I2C read EP1 failed");
225 		}
226 	}
227 
228 	/* Detach EEPROM */
229 	ret = i2c_target_driver_unregister(eeprom_0);
230 	zassert_equal(ret, 0, "Failed to unregister EEPROM 0");
231 
232 	if (IS_ENABLED(CONFIG_APP_DUAL_ROLE_I2C)) {
233 		ret = i2c_target_driver_unregister(eeprom_1);
234 		zassert_equal(ret, 0, "Failed to unregister EEPROM 1");
235 	}
236 }
237 
238 ZTEST_SUITE(i2c_eeprom_target, NULL, NULL, NULL, NULL, NULL);
239