summaryrefslogtreecommitdiff
path: root/tests/device/i2c-test.c
diff options
context:
space:
mode:
authorJan Dabros <jsd@semihalf.com>2020-04-20 14:34:16 +0200
committerPatrick Georgi <pgeorgi@google.com>2020-05-01 06:33:49 +0000
commita67cc5f5e8d11cd1004faf112193cf368af25f4b (patch)
tree336ea8b2cf541125b3ea0b8f8eac794fbaab5504 /tests/device/i2c-test.c
parent2d0ee36913a5e0f6c74beb5cdb9f25ea36ea9290 (diff)
downloadcoreboot-a67cc5f5e8d11cd1004faf112193cf368af25f4b.tar.xz
tests: Add device/i2c-test test case
Add unit test for src/device/i2c.c module. This patch is also used as an example for incorporating Cmocka mocking feature (-wrap linker flag). Signed-off-by: Jan Dabros <jsd@semihalf.com> Change-Id: I2eeb565aacc724ae3b9f5c76ef4b98ef695416d6 Reviewed-on: https://review.coreboot.org/c/coreboot/+/40539 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Julius Werner <jwerner@chromium.org> Reviewed-by: Paul Fagerburg <pfagerburg@chromium.org>
Diffstat (limited to 'tests/device/i2c-test.c')
-rw-r--r--tests/device/i2c-test.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/tests/device/i2c-test.c b/tests/device/i2c-test.c
new file mode 100644
index 0000000000..16e4d0d1ed
--- /dev/null
+++ b/tests/device/i2c-test.c
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <cmocka.h>
+
+#include <device/i2c_simple.h>
+
+/* Simulate two i2c devices, both on bus 0, each with three uint8_t regs
+ implemented. */
+typedef struct {
+ uint8_t reg;
+ uint8_t data;
+} i2c_ex_regs_t;
+
+typedef struct {
+ unsigned int bus;
+ uint8_t slave;
+ i2c_ex_regs_t regs[3];
+} i2c_ex_devs_t;
+
+i2c_ex_devs_t i2c_ex_devs[] = {
+ {.bus = 0, .slave = 0xA, .regs = {
+ {.reg = 0x0, .data = 0xB},
+ {.reg = 0x1, .data = 0x6},
+ {.reg = 0x2, .data = 0xF},
+ } },
+ {.bus = 0, .slave = 0x3, .regs = {
+ {.reg = 0x0, .data = 0xDE},
+ {.reg = 0x1, .data = 0xAD},
+ {.reg = 0x2, .data = 0xBE},
+ } },
+};
+
+int __wrap_platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
+ int count)
+{
+ int i;
+ int reg;
+ struct i2c_msg *tmp = segments;
+ i2c_ex_devs_t *i2c_dev = NULL;
+
+ check_expected(count);
+
+ for (i = 0; i < count; i++, segments++) {
+ check_expected_ptr(segments->buf);
+ check_expected(segments->flags);
+ }
+
+ reg = tmp->buf[0];
+
+ /* Find object for requested device */
+ for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++, i2c_dev++)
+ if (i2c_ex_devs[i].slave == tmp->slave) {
+ i2c_dev = &i2c_ex_devs[i];
+ break;
+ }
+
+ if (i2c_dev == NULL)
+ return -1;
+
+ /* Write commands */
+ if (tmp->len > 1) {
+ i2c_dev->regs[reg].data = tmp->buf[1];
+ };
+
+ /* Read commands */
+ for (i = 0; i < count; i++, tmp++)
+ if (tmp->flags & I2C_M_RD) {
+ *(tmp->buf) = i2c_dev->regs[reg].data;
+ };
+}
+
+static void mock_expect_params_platform_i2c_transfer(void)
+{
+ unsigned long int expected_flags[] = {0, I2C_M_RD, I2C_M_TEN,
+ I2C_M_RECV_LEN, I2C_M_NOSTART};
+
+ /* Flags should always be only within supported range */
+ expect_in_set_count(__wrap_platform_i2c_transfer, segments->flags,
+ expected_flags, -1);
+
+ expect_not_value_count(__wrap_platform_i2c_transfer, segments->buf,
+ NULL, -1);
+
+ expect_in_range_count(__wrap_platform_i2c_transfer, count, 1, INT_MAX,
+ -1);
+}
+
+#define MASK 0x3
+#define SHIFT 0x1
+
+static void i2c_read_field_test(void **state)
+{
+ int bus, slave, reg;
+ int i, j;
+ uint8_t buf;
+
+ mock_expect_params_platform_i2c_transfer();
+
+ /* Read particular bits in all registers in all devices, then compare
+ with expected value. */
+ for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
+ for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
+ i2c_read_field(i2c_ex_devs[i].bus,
+ i2c_ex_devs[i].slave,
+ i2c_ex_devs[i].regs[j].reg,
+ &buf, MASK, SHIFT);
+ assert_int_equal((i2c_ex_devs[i].regs[j].data &
+ (MASK << SHIFT)) >> SHIFT, buf);
+ };
+
+ /* Read whole registers */
+ for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
+ for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
+ i2c_read_field(i2c_ex_devs[i].bus,
+ i2c_ex_devs[i].slave,
+ i2c_ex_devs[i].regs[j].reg,
+ &buf, 0xFF, 0);
+ assert_int_equal(i2c_ex_devs[i].regs[j].data, buf);
+ };
+}
+
+static void i2c_write_field_test(void **state)
+{
+ int bus, slave, reg;
+ int i, j;
+ uint8_t buf, tmp;
+
+ mock_expect_params_platform_i2c_transfer();
+
+ /* Clear particular bits in all registers in all devices, then compare
+ with expected value. */
+ for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
+ for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
+ buf = 0x0;
+ tmp = i2c_ex_devs[i].regs[j].data;
+ i2c_write_field(i2c_ex_devs[i].bus,
+ i2c_ex_devs[i].slave,
+ i2c_ex_devs[i].regs[j].reg,
+ buf, MASK, SHIFT);
+ assert_int_equal(i2c_ex_devs[i].regs[j].data,
+ (tmp & ~(MASK << SHIFT)) | (buf << SHIFT));
+ };
+
+ /* Set all bits in all registers, this time verify using
+ i2c_read_field() accessor. */
+ for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
+ for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
+ i2c_write_field(i2c_ex_devs[i].bus,
+ i2c_ex_devs[i].slave,
+ i2c_ex_devs[i].regs[j].reg,
+ 0xFF, 0xFF, 0);
+ i2c_read_field(i2c_ex_devs[i].bus,
+ i2c_ex_devs[i].slave,
+ i2c_ex_devs[i].regs[j].reg,
+ &buf, 0xFF, 0);
+ assert_int_equal(buf, 0xFF);
+ };
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(i2c_read_field_test),
+ cmocka_unit_test(i2c_write_field_test)
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}