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