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, ¬ify_callback);
339 zassert_equal(ret, -ENOENT, "Callback remove failed");
340
341 /* Set callback */
342 ret = smbus_host_notify_set_cb(dev, ¬ify_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