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, ¬ify_callback);
346 zassert_equal(ret, -ENOENT, "Callback remove failed");
347
348 /* Set callback */
349 ret = smbus_host_notify_set_cb(dev, ¬ify_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