1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/tc_util.h>
9 #include <zephyr/sys/byteorder.h>
10 #include <zephyr/random/random.h>
11 
12 #include <zephyr/drivers/pcie/pcie.h>
13 #include <zephyr/drivers/smbus.h>
14 
15 #include "emul.h"
16 
17 #define CONFIG_SMBUS_LOG_LEVEL LOG_LEVEL_DBG
18 
19 #define PERIPH_ADDR	0x10
20 
mock_sys_in8(io_port_t port)21 static uint8_t mock_sys_in8(io_port_t port)
22 {
23 	return emul_in8(port);
24 }
25 
mock_sys_out8(uint8_t data,io_port_t port)26 static void mock_sys_out8(uint8_t data, io_port_t port)
27 {
28 	emul_out8(data, port);
29 }
30 
mock_conf_read(pcie_bdf_t bdf,unsigned int reg)31 static uint32_t mock_conf_read(pcie_bdf_t bdf, unsigned int reg)
32 {
33 	return emul_pci_read(reg);
34 }
35 
36 #if defined(PCIE_CONF_WRITE)
mock_conf_write(pcie_bdf_t bdf,unsigned int reg,uint32_t data)37 static void mock_conf_write(pcie_bdf_t bdf, unsigned int reg, uint32_t data)
38 {
39 	emul_pci_write(bdf, reg, data);
40 }
41 
42 #define pcie_conf_write(bdf, reg, val)	mock_conf_write(bdf, reg, val)
43 #endif /* PCIE_CONF_WRITE */
44 
45 /* Redefine PCIE access */
46 #define pcie_conf_read(bdf, reg)	mock_conf_read(bdf, reg)
47 
48 /* Redefine sys_in function */
49 #define sys_in8(port)			mock_sys_in8(port)
50 #define sys_out8(data, port)		mock_sys_out8(data, port)
51 
52 #define CONFIG_SMBUS_INTEL_PCH_ACCESS_IO
53 #define device_map(a, b, c, d)
54 #define pcie_probe(bdf, id)	1
55 #define pcie_set_cmd(a, b, c)
56 
57 #define SMBUS_EMUL	"smbus_emul"
58 
59 #ifdef PERIPHERAL_INT
60 #define CONFIG_SMBUS_INTEL_PCH_SMBALERT		1
61 #define CONFIG_SMBUS_INTEL_PCH_HOST_NOTIFY	1
62 #endif
63 
64 #include "intel_pch_smbus.c"
65 
run_isr(enum emul_isr_type type)66 void run_isr(enum emul_isr_type type)
67 {
68 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
69 
70 	switch (type) {
71 	case EMUL_SMBUS_INTR:
72 		emul_set_io(emul_get_io(PCH_SMBUS_HSTS) |
73 			    PCH_SMBUS_HSTS_INTERRUPT, PCH_SMBUS_HSTS);
74 		break;
75 	case EMUL_SMBUS_SMBALERT:
76 		emul_set_io(emul_get_io(PCH_SMBUS_HSTS) |
77 			    PCH_SMBUS_HSTS_SMB_ALERT, PCH_SMBUS_HSTS);
78 		break;
79 	case EMUL_SMBUS_HOST_NOTIFY:
80 		emul_set_io(emul_get_io(PCH_SMBUS_SSTS)|
81 			    PCH_SMBUS_SSTS_HNS, PCH_SMBUS_SSTS);
82 		peripheral_handle_host_notify();
83 		break;
84 	default:
85 		break;
86 	}
87 
88 	smbus_isr(dev);
89 }
90 
config_function(const struct device * dev)91 static void config_function(const struct device *dev)
92 {
93 	TC_PRINT("Emulator device configuration\n");
94 }
95 static struct pch_data smbus_data;
96 /* Zero initialized, dummy device does not care about pcie ids */
97 static struct pcie_dev pcie_params;
98 static struct pch_config pch_config_data = {
99 	.config_func = config_function,
100 	.pcie = &pcie_params,
101 };
102 
103 DEVICE_DEFINE(dummy_driver, SMBUS_EMUL, &pch_smbus_init,
104 	      NULL, &smbus_data, &pch_config_data, POST_KERNEL,
105 	      CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &funcs);
106 
ZTEST(test_smbus_emul,test_byte)107 ZTEST(test_smbus_emul, test_byte)
108 {
109 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
110 	uint8_t snd_byte, rcv_byte;
111 	int ret;
112 
113 	zassert_not_null(dev, "Device not found");
114 
115 	ret = smbus_quick(dev, PERIPH_ADDR, 1);
116 	zassert_ok(ret, "SMBus Quick failed");
117 
118 	snd_byte = (uint8_t)sys_rand32_get();
119 
120 	ret = smbus_byte_write(dev, PERIPH_ADDR, snd_byte);
121 	zassert_ok(ret, "SMBus Byte Write failed");
122 
123 	ret = smbus_byte_read(dev, PERIPH_ADDR, &rcv_byte);
124 	zassert_ok(ret, "SMBus Byte Read failed");
125 
126 	zassert_equal(snd_byte, rcv_byte, "Data mismatch");
127 
128 	ret = smbus_byte_data_write(dev, PERIPH_ADDR, 0, snd_byte);
129 	zassert_ok(ret, "SMBus Byte Data Write failed");
130 
131 	ret = smbus_byte_data_read(dev, PERIPH_ADDR, 0, &rcv_byte);
132 	zassert_ok(ret, "SMBus Byte Data Read failed");
133 
134 	zassert_equal(snd_byte, rcv_byte, "Data mismatch");
135 }
136 
ZTEST(test_smbus_emul,test_word)137 ZTEST(test_smbus_emul, test_word)
138 {
139 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
140 	uint16_t snd_word, rcv_word;
141 	uint8_t snd_byte;
142 	int ret;
143 
144 	zassert_not_null(dev, "Device not found");
145 
146 	snd_word = (uint16_t)sys_rand32_get();
147 
148 	ret = smbus_word_data_write(dev, PERIPH_ADDR, 0, snd_word);
149 	zassert_ok(ret, "SMBus Word Data Write failed");
150 
151 	ret = smbus_word_data_read(dev, PERIPH_ADDR, 0, &rcv_word);
152 	zassert_ok(ret, "SMBus Byte Data Read failed");
153 
154 	zassert_equal(snd_word, rcv_word, "Data mismatch");
155 
156 	/* Test 2 byte writes following word read */
157 
158 	snd_byte = (uint8_t)sys_rand32_get();
159 
160 	ret = smbus_byte_data_write(dev, PERIPH_ADDR, 0, snd_byte);
161 	zassert_ok(ret, "SMBus Byte Data Write failed");
162 	ret = smbus_byte_data_write(dev, PERIPH_ADDR, 1, snd_byte);
163 	zassert_ok(ret, "SMBus Byte Data Write failed");
164 
165 	ret = smbus_word_data_read(dev, PERIPH_ADDR, 0, &rcv_word);
166 	zassert_ok(ret, "SMBus Byte Data Read failed");
167 
168 	zassert_equal(snd_byte << 8 | snd_byte, rcv_word, "Data mismatch");
169 }
170 
ZTEST(test_smbus_emul,test_proc_call)171 ZTEST(test_smbus_emul, test_proc_call)
172 {
173 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
174 	uint16_t snd_word, rcv_word;
175 	int ret;
176 
177 	zassert_not_null(dev, "Device not found");
178 
179 	snd_word = (uint16_t)sys_rand32_get();
180 	zassert_not_equal(snd_word, 0, "Random number generator misconfgured");
181 
182 	ret = smbus_pcall(dev, PERIPH_ADDR, 0x0, snd_word, &rcv_word);
183 	zassert_ok(ret, "SMBus Proc Call failed");
184 
185 	/* Our emulated Proc Call swaps bytes */
186 	zassert_equal(snd_word, __bswap_16(rcv_word), "Data mismatch");
187 }
188 
ZTEST(test_smbus_emul,test_block)189 ZTEST(test_smbus_emul, test_block)
190 {
191 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
192 	uint8_t snd_block[SMBUS_BLOCK_BYTES_MAX];
193 	uint8_t rcv_block[SMBUS_BLOCK_BYTES_MAX];
194 	uint8_t snd_count, rcv_count;
195 	int ret;
196 
197 	zassert_not_null(dev, "Device not found");
198 
199 	for (int i = 0; i < sizeof(snd_block); i++) {
200 		snd_block[i] = (uint8_t)sys_rand32_get();
201 	}
202 
203 	snd_count = sizeof(snd_block);
204 
205 	ret = smbus_block_write(dev, PERIPH_ADDR, 0, snd_count, snd_block);
206 	zassert_ok(ret, "SMBUS write block failed, ret %d", ret);
207 
208 	ret = smbus_block_read(dev, PERIPH_ADDR, 0, &rcv_count, rcv_block);
209 	zassert_ok(ret, "SMBUS read block failed, ret %d", ret);
210 
211 	zassert_equal(snd_count, rcv_count, "Block count differs");
212 	zassert_true(!memcmp(snd_block, rcv_block, rcv_count),
213 		     "Data mismatch");
214 }
215 
ZTEST(test_smbus_emul,test_block_pcall)216 ZTEST(test_smbus_emul, test_block_pcall)
217 {
218 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
219 	uint8_t snd_block[SMBUS_BLOCK_BYTES_MAX];
220 	uint8_t rcv_block[SMBUS_BLOCK_BYTES_MAX];
221 	uint8_t snd_count, rcv_count;
222 	int ret;
223 
224 	zassert_not_null(dev, "Device not found");
225 
226 	for (int i = 0; i < sizeof(snd_block); i++) {
227 		snd_block[i] = (uint8_t)sys_rand32_get();
228 	}
229 
230 	snd_count = SMBUS_BLOCK_BYTES_MAX / 2;
231 	ret = smbus_block_pcall(dev, PERIPH_ADDR, 0, snd_count, snd_block,
232 				&rcv_count, rcv_block);
233 	zassert_ok(ret, "SMBUS block pcall failed, ret %d", ret);
234 	zassert_equal(snd_count, rcv_count, "Block count differs");
235 
236 	/**
237 	 * Verify that our emulated peripheral swapped bytes in the block
238 	 * buffer
239 	 */
240 	for (int i = 0; i < rcv_count; i++) {
241 		zassert_equal(snd_block[i], rcv_block[rcv_count - (i + 1)],
242 			      "Data mismatch, not swapped");
243 
244 	}
245 }
246 
247 /* SMBALERT handling */
248 
249 /* False by default */
250 bool smbalert_handled;
251 
smbalert_cb(const struct device * dev,struct smbus_callback * cb,uint8_t addr)252 static void smbalert_cb(const struct device *dev, struct smbus_callback *cb,
253 			uint8_t addr)
254 {
255 	LOG_DBG("SMBALERT callback");
256 
257 	smbalert_handled = true;
258 }
259 
260 struct smbus_callback smbalert_callback = {
261 	.handler = smbalert_cb,
262 	.addr = PERIPH_ADDR,
263 };
264 
265 /* Host Notify handling */
266 
267 /* False by default */
268 bool notify_handled;
269 
notify_cb(const struct device * dev,struct smbus_callback * cb,uint8_t addr)270 static void notify_cb(const struct device *dev, struct smbus_callback *cb,
271 		      uint8_t addr)
272 {
273 	LOG_DBG("Notify callback");
274 
275 	notify_handled = true;
276 }
277 
278 struct smbus_callback notify_callback = {
279 	.handler = notify_cb,
280 	.addr = PERIPH_ADDR,
281 };
282 
283 /* Setup peripheral SMBus device on a bus */
284 
285 struct smbus_peripheral peripheral = {
286 	.addr = PERIPH_ADDR,
287 	.smbalert = true,
288 	.host_notify = true,
289 };
290 
ZTEST(test_smbus_emul,test_alert)291 ZTEST(test_smbus_emul, test_alert)
292 {
293 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
294 	int ret;
295 
296 	Z_TEST_SKIP_IFNDEF(CONFIG_SMBUS_INTEL_PCH_SMBALERT);
297 
298 	zassert_not_null(dev, "Device not found");
299 
300 	/* Try to remove not existing callback */
301 	ret = smbus_smbalert_remove_cb(dev, &smbalert_callback);
302 	zassert_equal(ret, -ENOENT, "Callback remove failed");
303 
304 	/* Set callback */
305 	ret = smbus_smbalert_set_cb(dev, &smbalert_callback);
306 	zassert_ok(ret, "Callback set failed");
307 
308 	/* Emulate SMBus alert from peripheral device */
309 	peripheral_clear_smbalert(&peripheral);
310 	smbalert_handled = false;
311 
312 	/* Run without configure smbalert */
313 	run_isr(EMUL_SMBUS_SMBALERT);
314 
315 	/* Wait for delayed work handled */
316 	k_sleep(K_MSEC(100));
317 
318 	/* Verify that smbalert is NOT handled */
319 	zassert_false(smbalert_handled, "smbalert is not handled");
320 
321 	/* Now enable smbalert */
322 	ret = smbus_configure(dev, SMBUS_MODE_CONTROLLER | SMBUS_MODE_SMBALERT);
323 	zassert_ok(ret, "Configure failed");
324 
325 	/* Emulate SMBus alert again */
326 	run_isr(EMUL_SMBUS_SMBALERT);
327 
328 	/* Wait for delayed work handled */
329 	k_sleep(K_MSEC(100));
330 
331 	/* Verify that smbalert is not handled */
332 	zassert_true(smbalert_handled, "smbalert is not handled");
333 }
334 
ZTEST(test_smbus_emul,test_host_notify)335 ZTEST(test_smbus_emul, test_host_notify)
336 {
337 	const struct device *const dev = device_get_binding(SMBUS_EMUL);
338 	int ret;
339 
340 	Z_TEST_SKIP_IFNDEF(CONFIG_SMBUS_INTEL_PCH_HOST_NOTIFY);
341 
342 	zassert_not_null(dev, "Device not found");
343 
344 	/* Try to remove not existing callback */
345 	ret = smbus_host_notify_remove_cb(dev, &notify_callback);
346 	zassert_equal(ret, -ENOENT, "Callback remove failed");
347 
348 	/* Set callback */
349 	ret = smbus_host_notify_set_cb(dev, &notify_callback);
350 	zassert_ok(ret, "Callback set failed");
351 
352 	/* Emulate SMBus alert from peripheral device */
353 	notify_handled = false;
354 
355 	/* Run without configuring Host Notify */
356 	run_isr(EMUL_SMBUS_HOST_NOTIFY);
357 
358 	/* Wait for delayed work handled */
359 	k_sleep(K_MSEC(100));
360 
361 	/* Verify that smbalert is NOT handled */
362 	zassert_false(notify_handled, "smbalert is not handled");
363 
364 	/* Now enable smbalert */
365 	ret = smbus_configure(dev,
366 			      SMBUS_MODE_CONTROLLER | SMBUS_MODE_HOST_NOTIFY);
367 	zassert_ok(ret, "Configure failed");
368 
369 	/* Emulate SMBus alert again */
370 	run_isr(EMUL_SMBUS_HOST_NOTIFY);
371 
372 	/* Wait for delayed work handled */
373 	k_sleep(K_MSEC(100));
374 
375 	/* Verify that smbalert is handled */
376 	zassert_true(notify_handled, "smbalert is not handled");
377 }
378 
379 /* Test setup function */
smbus_emul_setup(void)380 static void *smbus_emul_setup(void)
381 {
382 	emul_register_smbus_peripheral(&peripheral);
383 
384 	return NULL;
385 }
386 
387 ZTEST_SUITE(test_smbus_emul, NULL, smbus_emul_setup, NULL, NULL, NULL);
388