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