| /* Copyright 2019 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "common.h" |
| #include "console.h" |
| #include "i2c.h" |
| #include "i2c_bitbang.h" |
| #include "test_util.h" |
| #include "util.h" |
| |
| const struct i2c_port_t i2c_bitbang_ports[] = { { .name = "", |
| .port = 0, |
| .kbps = 100, |
| .scl = GPIO_I2C_SCL, |
| .sda = GPIO_I2C_SDA } }; |
| const unsigned int i2c_bitbang_ports_used = 1; |
| |
| struct pin_state { |
| int scl, sda; |
| } history[64]; |
| |
| int history_count; |
| |
| void reset_state(void) |
| { |
| history[0] = (struct pin_state){ 1, 1 }; |
| history_count = 1; |
| bitbang_set_started(0); |
| } |
| |
| void gpio_set_level(enum gpio_signal signal, int level) |
| { |
| struct pin_state new = history[history_count - 1]; |
| |
| /* reject if stack is full */ |
| if (history_count >= ARRAY_SIZE(history)) |
| return; |
| |
| if (signal == GPIO_I2C_SDA) |
| new.sda = level; |
| else if (signal == GPIO_I2C_SCL) |
| new.scl = level; |
| |
| if (new.scl != history[history_count - 1].scl || |
| new.sda != history[history_count - 1].sda) |
| history[history_count++] = new; |
| } |
| |
| int gpio_get_level(enum gpio_signal signal) |
| { |
| if (signal == GPIO_I2C_SDA) |
| return history[history_count - 1].sda; |
| else if (signal == GPIO_I2C_SCL) |
| return history[history_count - 1].scl; |
| |
| return 0; |
| } |
| |
| static int test_i2c_start_stop(void) |
| { |
| struct pin_state expected[] = { |
| /* start */ |
| { 1, 1 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| /* stop */ |
| { 1, 0 }, |
| { 1, 1 }, |
| }; |
| int i; |
| |
| reset_state(); |
| |
| bitbang_start_cond(&i2c_bitbang_ports[0]); |
| bitbang_stop_cond(&i2c_bitbang_ports[0]); |
| |
| TEST_EQ((int)ARRAY_SIZE(expected), history_count, "%d"); |
| |
| for (i = 0; i < ARRAY_SIZE(expected); i++) { |
| TEST_EQ(expected[i].scl, history[i].scl, "%d"); |
| TEST_EQ(expected[i].sda, history[i].sda, "%d"); |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| static int test_i2c_repeated_start(void) |
| { |
| struct pin_state expected[] = { |
| /* start */ |
| { 1, 1 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| /* repeated start */ |
| { 0, 1 }, |
| { 1, 1 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| }; |
| int i; |
| |
| reset_state(); |
| |
| bitbang_start_cond(&i2c_bitbang_ports[0]); |
| bitbang_start_cond(&i2c_bitbang_ports[0]); |
| |
| TEST_EQ((int)ARRAY_SIZE(expected), history_count, "%d"); |
| |
| for (i = 0; i < ARRAY_SIZE(expected); i++) { |
| TEST_EQ(expected[i].scl, history[i].scl, "%d"); |
| TEST_EQ(expected[i].sda, history[i].sda, "%d"); |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| static int test_i2c_write(void) |
| { |
| struct pin_state expected[] = { |
| /* start */ |
| { 1, 1 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| /* bit 7: 0 */ |
| { 1, 0 }, |
| { 0, 0 }, |
| /* bit 6: 1 */ |
| { 0, 1 }, |
| { 1, 1 }, |
| { 0, 1 }, |
| /* bit 5: 0 */ |
| { 0, 0 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| /* bit 4: 1 */ |
| { 0, 1 }, |
| { 1, 1 }, |
| { 0, 1 }, |
| /* bit 3: 0 */ |
| { 0, 0 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| /* bit 2: 1 */ |
| { 0, 1 }, |
| { 1, 1 }, |
| { 0, 1 }, |
| /* bit 1: 1 */ |
| { 1, 1 }, |
| { 0, 1 }, |
| /* bit 0: 0 */ |
| { 0, 0 }, |
| { 1, 0 }, |
| { 0, 0 }, |
| /* read bit */ |
| { 0, 1 }, |
| { 1, 1 }, |
| { 0, 1 }, |
| /* stop */ |
| { 0, 0 }, |
| { 1, 0 }, |
| { 1, 1 }, |
| }; |
| int i, ret; |
| |
| reset_state(); |
| |
| bitbang_start_cond(&i2c_bitbang_ports[0]); |
| ret = bitbang_write_byte(&i2c_bitbang_ports[0], 0x56); |
| |
| /* expected to fail because no slave answering the nack bit */ |
| TEST_EQ(EC_ERROR_BUSY, ret, "%d"); |
| |
| TEST_EQ((int)ARRAY_SIZE(expected), history_count, "%d"); |
| |
| for (i = 0; i < ARRAY_SIZE(expected); i++) { |
| TEST_EQ(expected[i].scl, history[i].scl, "%d"); |
| TEST_EQ(expected[i].sda, history[i].sda, "%d"); |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| void run_test(int argc, const char **argv) |
| { |
| test_reset(); |
| |
| RUN_TEST(test_i2c_start_stop); |
| RUN_TEST(test_i2c_repeated_start); |
| RUN_TEST(test_i2c_write); |
| |
| test_print_result(); |
| } |