/* * Copyright (c) 2022 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include "emul.h" #define CONFIG_SMBUS_LOG_LEVEL LOG_LEVEL_DBG #define PERIPH_ADDR 0x10 static uint8_t mock_sys_in8(io_port_t port) { return emul_in8(port); } static void mock_sys_out8(uint8_t data, io_port_t port) { emul_out8(data, port); } static uint32_t mock_conf_read(pcie_bdf_t bdf, unsigned int reg) { return emul_pci_read(reg); } #if defined(PCIE_CONF_WRITE) static void mock_conf_write(pcie_bdf_t bdf, unsigned int reg, uint32_t data) { emul_pci_write(bdf, reg, data); } #define pcie_conf_write(bdf, reg, val) mock_conf_write(bdf, reg, val) #endif /* PCIE_CONF_WRITE */ /* Redefine PCIE access */ #define pcie_conf_read(bdf, reg) mock_conf_read(bdf, reg) /* Redefine sys_in function */ #define sys_in8(port) mock_sys_in8(port) #define sys_out8(data, port) mock_sys_out8(data, port) #define CONFIG_SMBUS_INTEL_PCH_ACCESS_IO #define device_map(a, b, c, d) #define pcie_probe(bdf, id) 1 #define pcie_set_cmd(a, b, c) #define SMBUS_EMUL "smbus_emul" #ifdef PERIPHERAL_INT #define CONFIG_SMBUS_INTEL_PCH_SMBALERT 1 #define CONFIG_SMBUS_INTEL_PCH_HOST_NOTIFY 1 #endif #include "intel_pch_smbus.c" void run_isr(enum emul_isr_type type) { const struct device *const dev = device_get_binding(SMBUS_EMUL); switch (type) { case EMUL_SMBUS_INTR: emul_set_io(emul_get_io(PCH_SMBUS_HSTS) | PCH_SMBUS_HSTS_INTERRUPT, PCH_SMBUS_HSTS); break; case EMUL_SMBUS_SMBALERT: emul_set_io(emul_get_io(PCH_SMBUS_HSTS) | PCH_SMBUS_HSTS_SMB_ALERT, PCH_SMBUS_HSTS); break; case EMUL_SMBUS_HOST_NOTIFY: emul_set_io(emul_get_io(PCH_SMBUS_SSTS)| PCH_SMBUS_SSTS_HNS, PCH_SMBUS_SSTS); peripheral_handle_host_notify(); break; default: break; } smbus_isr(dev); } static void config_function(const struct device *dev) { TC_PRINT("Emulator device configuration\n"); } static struct pch_data smbus_data; /* Zero initialized, dummy device does not care about pcie ids */ static struct pcie_dev pcie_params; static struct pch_config pch_config_data = { .config_func = config_function, .pcie = &pcie_params, }; DEVICE_DEFINE(dummy_driver, SMBUS_EMUL, &pch_smbus_init, NULL, &smbus_data, &pch_config_data, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &funcs); ZTEST(test_smbus_emul, test_byte) { const struct device *const dev = device_get_binding(SMBUS_EMUL); uint8_t snd_byte, rcv_byte; int ret; zassert_not_null(dev, "Device not found"); ret = smbus_quick(dev, PERIPH_ADDR, 1); zassert_ok(ret, "SMBus Quick failed"); snd_byte = (uint8_t)sys_rand32_get(); ret = smbus_byte_write(dev, PERIPH_ADDR, snd_byte); zassert_ok(ret, "SMBus Byte Write failed"); ret = smbus_byte_read(dev, PERIPH_ADDR, &rcv_byte); zassert_ok(ret, "SMBus Byte Read failed"); zassert_equal(snd_byte, rcv_byte, "Data mismatch"); ret = smbus_byte_data_write(dev, PERIPH_ADDR, 0, snd_byte); zassert_ok(ret, "SMBus Byte Data Write failed"); ret = smbus_byte_data_read(dev, PERIPH_ADDR, 0, &rcv_byte); zassert_ok(ret, "SMBus Byte Data Read failed"); zassert_equal(snd_byte, rcv_byte, "Data mismatch"); } ZTEST(test_smbus_emul, test_word) { const struct device *const dev = device_get_binding(SMBUS_EMUL); uint16_t snd_word, rcv_word; uint8_t snd_byte; int ret; zassert_not_null(dev, "Device not found"); snd_word = (uint16_t)sys_rand32_get(); ret = smbus_word_data_write(dev, PERIPH_ADDR, 0, snd_word); zassert_ok(ret, "SMBus Word Data Write failed"); ret = smbus_word_data_read(dev, PERIPH_ADDR, 0, &rcv_word); zassert_ok(ret, "SMBus Byte Data Read failed"); zassert_equal(snd_word, rcv_word, "Data mismatch"); /* Test 2 byte writes following word read */ snd_byte = (uint8_t)sys_rand32_get(); ret = smbus_byte_data_write(dev, PERIPH_ADDR, 0, snd_byte); zassert_ok(ret, "SMBus Byte Data Write failed"); ret = smbus_byte_data_write(dev, PERIPH_ADDR, 1, snd_byte); zassert_ok(ret, "SMBus Byte Data Write failed"); ret = smbus_word_data_read(dev, PERIPH_ADDR, 0, &rcv_word); zassert_ok(ret, "SMBus Byte Data Read failed"); zassert_equal(snd_byte << 8 | snd_byte, rcv_word, "Data mismatch"); } ZTEST(test_smbus_emul, test_proc_call) { const struct device *const dev = device_get_binding(SMBUS_EMUL); uint16_t snd_word, rcv_word; int ret; zassert_not_null(dev, "Device not found"); snd_word = (uint16_t)sys_rand32_get(); zassert_not_equal(snd_word, 0, "Random number generator misconfgured"); ret = smbus_pcall(dev, PERIPH_ADDR, 0x0, snd_word, &rcv_word); zassert_ok(ret, "SMBus Proc Call failed"); /* Our emulated Proc Call swaps bytes */ zassert_equal(snd_word, __bswap_16(rcv_word), "Data mismatch"); } ZTEST(test_smbus_emul, test_block) { const struct device *const dev = device_get_binding(SMBUS_EMUL); uint8_t snd_block[SMBUS_BLOCK_BYTES_MAX]; uint8_t rcv_block[SMBUS_BLOCK_BYTES_MAX]; uint8_t snd_count, rcv_count; int ret; zassert_not_null(dev, "Device not found"); for (int i = 0; i < sizeof(snd_block); i++) { snd_block[i] = (uint8_t)sys_rand32_get(); } snd_count = sizeof(snd_block); ret = smbus_block_write(dev, PERIPH_ADDR, 0, snd_count, snd_block); zassert_ok(ret, "SMBUS write block failed, ret %d", ret); ret = smbus_block_read(dev, PERIPH_ADDR, 0, &rcv_count, rcv_block); zassert_ok(ret, "SMBUS read block failed, ret %d", ret); zassert_equal(snd_count, rcv_count, "Block count differs"); zassert_true(!memcmp(snd_block, rcv_block, rcv_count), "Data mismatch"); } ZTEST(test_smbus_emul, test_block_pcall) { const struct device *const dev = device_get_binding(SMBUS_EMUL); uint8_t snd_block[SMBUS_BLOCK_BYTES_MAX]; uint8_t rcv_block[SMBUS_BLOCK_BYTES_MAX]; uint8_t snd_count, rcv_count; int ret; zassert_not_null(dev, "Device not found"); for (int i = 0; i < sizeof(snd_block); i++) { snd_block[i] = (uint8_t)sys_rand32_get(); } snd_count = SMBUS_BLOCK_BYTES_MAX / 2; ret = smbus_block_pcall(dev, PERIPH_ADDR, 0, snd_count, snd_block, &rcv_count, rcv_block); zassert_ok(ret, "SMBUS block pcall failed, ret %d", ret); zassert_equal(snd_count, rcv_count, "Block count differs"); /** * Verify that our emulated peripheral swapped bytes in the block * buffer */ for (int i = 0; i < rcv_count; i++) { zassert_equal(snd_block[i], rcv_block[rcv_count - (i + 1)], "Data mismatch, not swapped"); } } /* SMBALERT handling */ /* False by default */ bool smbalert_handled; static void smbalert_cb(const struct device *dev, struct smbus_callback *cb, uint8_t addr) { LOG_DBG("SMBALERT callback"); smbalert_handled = true; } struct smbus_callback smbalert_callback = { .handler = smbalert_cb, .addr = PERIPH_ADDR, }; /* Host Notify handling */ /* False by default */ bool notify_handled; static void notify_cb(const struct device *dev, struct smbus_callback *cb, uint8_t addr) { LOG_DBG("Notify callback"); notify_handled = true; } struct smbus_callback notify_callback = { .handler = notify_cb, .addr = PERIPH_ADDR, }; /* Setup peripheral SMBus device on a bus */ struct smbus_peripheral peripheral = { .addr = PERIPH_ADDR, .smbalert = true, .host_notify = true, }; ZTEST(test_smbus_emul, test_alert) { const struct device *const dev = device_get_binding(SMBUS_EMUL); int ret; Z_TEST_SKIP_IFNDEF(CONFIG_SMBUS_INTEL_PCH_SMBALERT); zassert_not_null(dev, "Device not found"); /* Try to remove not existing callback */ ret = smbus_smbalert_remove_cb(dev, &smbalert_callback); zassert_equal(ret, -ENOENT, "Callback remove failed"); /* Set callback */ ret = smbus_smbalert_set_cb(dev, &smbalert_callback); zassert_ok(ret, "Callback set failed"); /* Emulate SMBus alert from peripheral device */ peripheral_clear_smbalert(&peripheral); smbalert_handled = false; /* Run without configure smbalert */ run_isr(EMUL_SMBUS_SMBALERT); /* Wait for delayed work handled */ k_sleep(K_MSEC(100)); /* Verify that smbalert is NOT handled */ zassert_false(smbalert_handled, "smbalert is not handled"); /* Now enable smbalert */ ret = smbus_configure(dev, SMBUS_MODE_CONTROLLER | SMBUS_MODE_SMBALERT); zassert_ok(ret, "Configure failed"); /* Emulate SMBus alert again */ run_isr(EMUL_SMBUS_SMBALERT); /* Wait for delayed work handled */ k_sleep(K_MSEC(100)); /* Verify that smbalert is not handled */ zassert_true(smbalert_handled, "smbalert is not handled"); } ZTEST(test_smbus_emul, test_host_notify) { const struct device *const dev = device_get_binding(SMBUS_EMUL); int ret; Z_TEST_SKIP_IFNDEF(CONFIG_SMBUS_INTEL_PCH_HOST_NOTIFY); zassert_not_null(dev, "Device not found"); /* Try to remove not existing callback */ ret = smbus_host_notify_remove_cb(dev, ¬ify_callback); zassert_equal(ret, -ENOENT, "Callback remove failed"); /* Set callback */ ret = smbus_host_notify_set_cb(dev, ¬ify_callback); zassert_ok(ret, "Callback set failed"); /* Emulate SMBus alert from peripheral device */ notify_handled = false; /* Run without configuring Host Notify */ run_isr(EMUL_SMBUS_HOST_NOTIFY); /* Wait for delayed work handled */ k_sleep(K_MSEC(100)); /* Verify that smbalert is NOT handled */ zassert_false(notify_handled, "smbalert is not handled"); /* Now enable smbalert */ ret = smbus_configure(dev, SMBUS_MODE_CONTROLLER | SMBUS_MODE_HOST_NOTIFY); zassert_ok(ret, "Configure failed"); /* Emulate SMBus alert again */ run_isr(EMUL_SMBUS_HOST_NOTIFY); /* Wait for delayed work handled */ k_sleep(K_MSEC(100)); /* Verify that smbalert is handled */ zassert_true(notify_handled, "smbalert is not handled"); } /* Test setup function */ static void *smbus_emul_setup(void) { emul_register_smbus_peripheral(&peripheral); return NULL; } ZTEST_SUITE(test_smbus_emul, NULL, smbus_emul_setup, NULL, NULL, NULL);