diff options
author | tim <tim2.lin@ite.corp-partner.google.com> | 2020-02-14 11:55:48 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-02-17 10:24:39 +0000 |
commit | c2797a55a58678e3118aa0b95431c78f02b1ec3d (patch) | |
tree | 04290d466feeadef28ad620a22da9bca73f92b24 | |
parent | a3cf5101156136e6a17434f57e9e521ed624eca8 (diff) | |
download | chrome-ec-c2797a55a58678e3118aa0b95431c78f02b1ec3d.tar.gz |
driver/ioexpander_it8801: add I/O expander driver for GPIO
IT8801 is an I/O expander with the gpio. Add api compatible with
ioexpander_drv so the main io expander framework can make use
of the pins on the it8801.
BUG=b/138352732, b/146996723
BRANCH=none
TEST=Use gpio.inc to declare the gpio state.
The console command #ioexget can get as follows:
0 O L IT8801_G0_00
1 O H IT8801_G0_03
1 O H IT8801_G0_04
0 O L IT8801_G0_06
0 O L IT8801_G0_07
1 O H ODR IT8801_G1_10
1 O H ODR IT8801_G1_11
1 O H ODR IT8801_G1_12
1 I H IT8801_G1_13
1 I H IT8801_G1_14
1 I H IT8801_G1_15
1 O H ODR IT8801_G2_20
1 O H ODR IT8801_G2_21
0 O L ODR IT8801_G2_22
0 O L ODR IT8801_G2_23
TEST=jacuzzi, juniper and kappa still compile
TEST=keyboard scanning still works
TEST=keyboard scanning now uses fewer i2c packets due to caching of GPIO23
Change-Id: I7ad89058ccd43b073d648e93877b86d6f187b5df
Signed-off-by: tim <tim2.lin@ite.corp-partner.google.com>
Signed-off-by: Alexandru M Stan <amstan@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1712887
Reviewed-by: David Huang <David.Huang@quantatw.com>
Reviewed-by: Ting Shen <phoenixshen@chromium.org>
Tested-by: David Huang <David.Huang@quantatw.com>
-rw-r--r-- | board/damu/board.c | 28 | ||||
-rw-r--r-- | board/damu/board.h | 8 | ||||
-rw-r--r-- | board/damu/ec.tasklist | 1 | ||||
-rw-r--r-- | board/damu/gpio.inc | 3 | ||||
-rw-r--r-- | board/jacuzzi/board.c | 8 | ||||
-rw-r--r-- | board/jacuzzi/board.h | 6 | ||||
-rw-r--r-- | board/kappa/board.c | 8 | ||||
-rw-r--r-- | board/kappa/board.h | 6 | ||||
-rw-r--r-- | driver/ioexpander/it8801.c | 221 | ||||
-rw-r--r-- | driver/ioexpander/it8801.h | 16 |
10 files changed, 283 insertions, 22 deletions
diff --git a/board/damu/board.c b/board/damu/board.c index 4639a23e50..0fd5487f95 100644 --- a/board/damu/board.c +++ b/board/damu/board.c @@ -27,6 +27,8 @@ #include "hooks.h" #include "host_command.h" #include "i2c.h" +#include "it8801.h" +#include "keyboard_scan.h" #include "lid_switch.h" #include "power.h" #include "power_button.h" @@ -79,6 +81,32 @@ const struct power_signal_info power_signal_list[] = { }; BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); +/* Keyboard scan setting */ +struct keyboard_scan_config keyscan_config = { + /* + * TODO(b/133200075): Tune this once we have the final performance + * out of the driver and the i2c bus. + */ + .output_settle_us = 35, + .debounce_down_us = 5 * MSEC, + .debounce_up_us = 40 * MSEC, + .scan_period_us = 3 * MSEC, + .min_post_scan_delay_us = 1000, + .poll_timeout_us = 100 * MSEC, + .actual_key_mask = { + 0x14, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, + 0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca /* full set */ + }, +}; + +struct ioexpander_config_t ioex_config[CONFIG_IO_EXPANDER_PORT_COUNT] = { + [0] = { + .i2c_host_port = I2C_PORT_IO_EXPANDER_IT8801, + .i2c_slave_addr = IT8801_I2C_ADDR, + .drv = &it8801_ioexpander_drv, + }, +}; + /******************************************************************************/ /* SPI devices */ /* TODO: to be added once sensors land via CL:1714436 */ diff --git a/board/damu/board.h b/board/damu/board.h index 8399a19b81..8e45ebc3e6 100644 --- a/board/damu/board.h +++ b/board/damu/board.h @@ -81,6 +81,14 @@ (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) |\ EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON)) +#define CONFIG_IO_EXPANDER +#define CONFIG_IO_EXPANDER_IT8801 +#define CONFIG_IO_EXPANDER_PORT_COUNT 1 +#define CONFIG_KEYBOARD_DEBUG +#define CONFIG_KEYBOARD_NOT_RAW +#define CONFIG_KEYBOARD_BOARD_CONFIG +#define CONFIG_KEYBOARD_COL2_INVERTED + #define PD_OPERATING_POWER_MW 30000 #ifndef __ASSEMBLER__ diff --git a/board/damu/ec.tasklist b/board/damu/ec.tasklist index f4fb8670ea..4b7574652e 100644 --- a/board/damu/ec.tasklist +++ b/board/damu/ec.tasklist @@ -15,6 +15,7 @@ TASK_NOTEST(PDCMD, pd_command_task, NULL, 1024) \ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, 1024) \ TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, LARGER_TASK_STACK_SIZE) \ TASK_ALWAYS(PD_C0, pd_task, NULL, 1280) \ TASK_ALWAYS(PD_INT_C0, pd_interrupt_handler_task, 0, 1024) \ TASK_ALWAYS_RO(EMMC, emmc_task, NULL, LARGER_TASK_STACK_SIZE) diff --git a/board/damu/gpio.inc b/board/damu/gpio.inc index 16daee88ee..90f6edd908 100644 --- a/board/damu/gpio.inc +++ b/board/damu/gpio.inc @@ -33,11 +33,12 @@ GPIO_INT(AC_PRESENT, PIN(A, 6), GPIO_INT_BOTH, extpower_interrupt) /* ACOK_OD */ GPIO_INT(BC12_EC_INT_ODL, PIN(C, 9), GPIO_INT_FALLING, bc12_interrupt) +GPIO_INT(IT8801_SMB_INT, PIN(A, 8), GPIO_INT_FALLING | GPIO_PULL_UP, + io_expander_it8801_interrupt) /* KB_INT_ODL */ GPIO_INT(AP_EC_WATCHDOG_L, PIN(D, 2), GPIO_INT_FALLING, chipset_watchdog_interrupt) /* Unimplemented interrupts */ -GPIO(KB_INT_ODL, PIN(A, 8), GPIO_INPUT) GPIO(ALS_RGB_INT_ODL, PIN(C, 10), GPIO_INPUT) GPIO(TABLET_MODE_L, PIN(B, 11), GPIO_INPUT) diff --git a/board/jacuzzi/board.c b/board/jacuzzi/board.c index 8becdc3328..9029fa764b 100644 --- a/board/jacuzzi/board.c +++ b/board/jacuzzi/board.c @@ -107,6 +107,14 @@ struct keyboard_scan_config keyscan_config = { }, }; +struct ioexpander_config_t ioex_config[CONFIG_IO_EXPANDER_PORT_COUNT] = { + [0] = { + .i2c_host_port = I2C_PORT_IO_EXPANDER_IT8801, + .i2c_slave_addr = IT8801_I2C_ADDR, + .drv = &it8801_ioexpander_drv, + }, +}; + /******************************************************************************/ /* SPI devices */ const struct spi_device_t spi_devices[] = { diff --git a/board/jacuzzi/board.h b/board/jacuzzi/board.h index faab414edd..a3ca10e276 100644 --- a/board/jacuzzi/board.h +++ b/board/jacuzzi/board.h @@ -91,13 +91,15 @@ (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) |\ EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON)) -#define CONFIG_EC_KEYBOARD +#define CONFIG_IO_EXPANDER +#define CONFIG_IO_EXPANDER_IT8801 +#define CONFIG_IO_EXPANDER_PORT_COUNT 1 #define CONFIG_KEYBOARD_DEBUG #define CONFIG_KEYBOARD_NOT_RAW -#define CONFIG_IO_EXPANDER_IT8801 #define CONFIG_KEYBOARD_BOARD_CONFIG #define CONFIG_KEYBOARD_COL2_INVERTED + #define PD_OPERATING_POWER_MW 30000 #define CONFIG_LED_PWM diff --git a/board/kappa/board.c b/board/kappa/board.c index 97644e78d8..fcf1707528 100644 --- a/board/kappa/board.c +++ b/board/kappa/board.c @@ -103,6 +103,14 @@ struct keyboard_scan_config keyscan_config = { }, }; +struct ioexpander_config_t ioex_config[CONFIG_IO_EXPANDER_PORT_COUNT] = { + [0] = { + .i2c_host_port = I2C_PORT_IO_EXPANDER_IT8801, + .i2c_slave_addr = IT8801_I2C_ADDR, + .drv = &it8801_ioexpander_drv, + }, +}; + /******************************************************************************/ /* SPI devices */ /* TODO: to be added once sensors land via CL:1714436 */ diff --git a/board/kappa/board.h b/board/kappa/board.h index 3e594dc7d9..8687c7a89b 100644 --- a/board/kappa/board.h +++ b/board/kappa/board.h @@ -82,13 +82,15 @@ (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) |\ EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON)) -#define CONFIG_EC_KEYBOARD +#define CONFIG_IO_EXPANDER +#define CONFIG_IO_EXPANDER_IT8801 +#define CONFIG_IO_EXPANDER_PORT_COUNT 1 #define CONFIG_KEYBOARD_DEBUG #define CONFIG_KEYBOARD_NOT_RAW -#define CONFIG_IO_EXPANDER_IT8801 #define CONFIG_KEYBOARD_BOARD_CONFIG #define CONFIG_KEYBOARD_COL2_INVERTED + #define PD_OPERATING_POWER_MW 30000 #undef CONFIG_LED_PWM_NEAR_FULL_COLOR diff --git a/driver/ioexpander/it8801.c b/driver/ioexpander/it8801.c index 00e40e12ed..1226378c9a 100644 --- a/driver/ioexpander/it8801.c +++ b/driver/ioexpander/it8801.c @@ -7,6 +7,7 @@ #include "console.h" #include "gpio.h" #include "i2c.h" +#include "ioexpander.h" #include "it8801.h" #include "keyboard_raw.h" #include "keyboard_scan.h" @@ -16,6 +17,8 @@ #define CPRINTS(format, args...) cprints(CC_KEYSCAN, format, ## args) +static int it8801_ioex_set_level(int ioex, int port, int mask, int value); + static int it8801_read(int reg, int *data) { return i2c_read8(I2C_PORT_IO_EXPANDER_IT8801, IT8801_I2C_ADDR, @@ -58,7 +61,7 @@ static int it8801_check_vendor_id(void) void keyboard_raw_init(void) { - int ret, val; + int ret; /* Verify Vendor ID registers. */ ret = it8801_check_vendor_id(); @@ -84,8 +87,7 @@ void keyboard_raw_init(void) it8801_write(IT8801_REG_GPIO23_KSO20, IT8801_REG_MASK_GPIODIR); /* Start with KEYBOARD_COLUMN_ALL, output high(so selected). */ - it8801_read(IT8801_REG_GPIOG2SOVR, &val); - it8801_write(IT8801_REG_GPIOG2SOVR, val | IT8801_REG_GPIO23SOV); + it8801_ioex_set_level(0, 2, IT8801_REG_GPIO23SOV, 1); } /* Keyboard scan in interrupt enable register */ @@ -113,7 +115,7 @@ BUILD_ASSERT(ARRAY_SIZE(kso_mapping) == KEYBOARD_COLS_MAX); test_mockable void keyboard_raw_drive_column(int col) { - int kso_val, val; + int kso_val; /* Tri-state all outputs */ if (col == KEYBOARD_COLUMN_NONE) { @@ -122,9 +124,7 @@ test_mockable void keyboard_raw_drive_column(int col) if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { /* Output low(so not selected). */ - it8801_read(IT8801_REG_GPIOG2SOVR, &val); - it8801_write(IT8801_REG_GPIOG2SOVR, val & - ~IT8801_REG_GPIO23SOV); + it8801_ioex_set_level(0, 2, IT8801_REG_GPIO23SOV, 0); } } /* Assert all outputs */ @@ -134,9 +134,7 @@ test_mockable void keyboard_raw_drive_column(int col) if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { /* Output high(so selected). */ - it8801_read(IT8801_REG_GPIOG2SOVR, &val); - it8801_write(IT8801_REG_GPIOG2SOVR, val | - IT8801_REG_GPIO23SOV); + it8801_ioex_set_level(0, 2, IT8801_REG_GPIO23SOV, 1); } } else { /* To check if column is valid or not. */ @@ -152,14 +150,12 @@ test_mockable void keyboard_raw_drive_column(int col) /* GPIO23 is inverted. */ if (col == IT8801_REG_MASK_SELKSO2) { /* Output high(so selected). */ - it8801_read(IT8801_REG_GPIOG2SOVR, &val); - it8801_write(IT8801_REG_GPIOG2SOVR, val | - IT8801_REG_GPIO23SOV); + it8801_ioex_set_level(0, 2, + IT8801_REG_GPIO23SOV, 1); } else { /* Output low(so not selected). */ - it8801_read(IT8801_REG_GPIOG2SOVR, &val); - it8801_write(IT8801_REG_GPIOG2SOVR, val & - ~IT8801_REG_GPIO23SOV); + it8801_ioex_set_level(0, 2, + IT8801_REG_GPIO23SOV, 0); } } } @@ -199,6 +195,199 @@ void io_expander_it8801_interrupt(enum gpio_signal signal) task_wake(TASK_ID_KEYSCAN); } +static int it8801_ioex_read(int ioex, int reg, int *data) +{ + struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; + + return i2c_read8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr, + reg, data); +} + +static int it8801_ioex_write(int ioex, int reg, int data) +{ + struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; + + return i2c_write8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr, + reg, data); +} + +/* + * Initialize the general purpose I/O port(GPIO) + */ +static int it8801_ioex_init(int ioex) +{ + int ret; + + /* Verify Vendor ID registers. */ + ret = it8801_check_vendor_id(); + if (ret) { + CPRINTS("Failed to read IT8801 vendor id %x", ret); + return ret; + } + + return EC_SUCCESS; +} + +static const int it8801_valid_gpio_group[] = { + IT8801_VALID_GPIO_G0_MASK, + IT8801_VALID_GPIO_G1_MASK, + IT8801_VALID_GPIO_G2_MASK, +}; + +/* Mutexes */ +static struct mutex ioex_mutex; + +static uint8_t it8801_gpio_sov[ARRAY_SIZE(it8801_valid_gpio_group)]; + +static int ioex_check_is_not_valid(int port, int mask) +{ + if (port >= ARRAY_SIZE(it8801_valid_gpio_group)) { + CPRINTS("Port%d is not support in IT8801", port); + return EC_ERROR_INVAL; + } + + if (mask & ~it8801_valid_gpio_group[port]) { + CPRINTS("GPIO%d-%d is not support in IT8801", port, + __fls(mask & ~it8801_valid_gpio_group[port])); + return EC_ERROR_INVAL; + } + + return EC_SUCCESS; +} + +static int it8801_ioex_get_level(int ioex, int port, int mask, int *val) +{ + int rv; + + if (ioex_check_is_not_valid(port, mask)) + return EC_ERROR_INVAL; + + rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_IPSR(port), val); + + *val = !!(*val & mask); + + return rv; +} + +static int it8801_ioex_set_level(int ioex, int port, int mask, int value) +{ + int rv = EC_SUCCESS; + + if (ioex_check_is_not_valid(port, mask)) + return EC_ERROR_INVAL; + + mutex_lock(&ioex_mutex); + /* + * The bit of output value in SOV is different than + * the one we were about to set it to. + */ + if (!!(it8801_gpio_sov[port] & mask) ^ value) { + if (value) + it8801_gpio_sov[port] |= mask; + else + it8801_gpio_sov[port] &= ~mask; + + rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_SOVR(port), + it8801_gpio_sov[port]); + } + mutex_unlock(&ioex_mutex); + + return rv; +} + +static int it8801_ioex_get_flags_by_mask(int ioex, int port, + int mask, int *flags) +{ + int rv, val; + + if (ioex_check_is_not_valid(port, mask)) + return EC_ERROR_INVAL; + + rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_CR(port, mask), &val); + if (rv) + return rv; + + *flags = 0; + + /* Get GPIO direction */ + *flags |= (val & IT8801_GPIODIR) ? GPIO_OUTPUT : GPIO_INPUT; + + /* Get GPIO type, 0:push-pull 1:open-drain */ + if (val & IT8801_GPIOIOT) + *flags |= GPIO_OPEN_DRAIN; + + rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_IPSR(port), &val); + if (rv) + return rv; + + /* Get GPIO output level */ + *flags |= (val & mask) ? GPIO_HIGH : GPIO_LOW; + + return EC_SUCCESS; +} + +static int it8801_ioex_set_flags_by_mask(int ioex, int port, + int mask, int flags) +{ + int rv, val; + + if (ioex_check_is_not_valid(port, mask)) + return EC_ERROR_INVAL; + + if (flags & ~IT8801_SUPPORT_GPIO_FLAGS) { + CPRINTS("Flag 0x%08x is not supported at port %d, mask %d", + flags, port, mask); + return EC_ERROR_INVAL; + } + + /* GPIO alternate function switching(GPIO[00, 12:15, 20:23]). */ + it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), + IT8801_REG_MASK_GPIOAFS_FUNC1); + + mutex_lock(&ioex_mutex); + rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_CR(port, mask), &val); + if (rv) { + mutex_unlock(&ioex_mutex); + return rv; + } + /* Select open drain 0:push-pull 1:open-drain */ + if (flags & GPIO_OPEN_DRAIN) + val |= IT8801_GPIOIOT; + else + val &= ~IT8801_GPIOIOT; + + /* Select GPIO direction */ + if (flags & GPIO_OUTPUT) { + /* Configure the output level */ + if (flags & GPIO_HIGH) + it8801_gpio_sov[port] |= mask; + else + it8801_gpio_sov[port] &= ~mask; + + rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_SOVR(port), + it8801_gpio_sov[port]); + if (rv) { + mutex_unlock(&ioex_mutex); + return rv; + } + val |= IT8801_GPIODIR; + } else + val &= ~IT8801_GPIODIR; + + rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), val); + mutex_unlock(&ioex_mutex); + + return rv; +} + +const struct ioexpander_drv it8801_ioexpander_drv = { + .init = &it8801_ioex_init, + .get_level = &it8801_ioex_get_level, + .set_level = &it8801_ioex_set_level, + .get_flags_by_mask = &it8801_ioex_get_flags_by_mask, + .set_flags_by_mask = &it8801_ioex_set_flags_by_mask, +}; + static void dump_register(int reg) { int rv; diff --git a/driver/ioexpander/it8801.h b/driver/ioexpander/it8801.h index b7e661effa..016412ca22 100644 --- a/driver/ioexpander/it8801.h +++ b/driver/ioexpander/it8801.h @@ -33,13 +33,25 @@ #define IT8801_REG_MASK_GPIOAFS_FUNC2 BIT(6) #define IT8801_REG_MASK_GPIODIR BIT(5) #define IT8801_REG_MASK_GPIOPUE BIT(0) -#define IT8801_REG_GPIOG2SOVR 0x07 #define IT8801_REG_GPIO23SOV BIT(3) #define IT8801_REG_MASK_SELKSO2 0x02 #define IT8801_REG_LBVIDR 0xFE #define IT8801_REG_HBVIDR 0xFF #define IT8801_KSO_COUNT 18 +/* General Purpose I/O Port (GPIO) */ +#define IT8801_SUPPORT_GPIO_FLAGS (GPIO_OPEN_DRAIN | GPIO_INPUT | \ + GPIO_OUTPUT | GPIO_LOW | GPIO_HIGH) + +#define IT8801_REG_MASK_GPIOAFS_FUNC1 (0x00 << 7) + +/* IT8801 only supports GPIO 0/1/2 */ +#define IT8801_VALID_GPIO_G0_MASK 0xD9 +#define IT8801_VALID_GPIO_G1_MASK 0x3F +#define IT8801_VALID_GPIO_G2_MASK 0x0F + +extern const struct ioexpander_drv it8801_ioexpander_drv; + /* GPIO Register map */ /* Input pin status register */ #define IT8801_REG_GPIO_IPSR(port) (0x00 + (port)) @@ -66,6 +78,8 @@ #define IT8801_GPIOIOT_INT_EDGE 3 /* = RISING + FALLING */ #define IT8801_GPIOIOT_OPEN_DRAIN 2 +#define IT8801_GPIODIR BIT(5) +#define IT8801_GPIOIOT BIT(4) #define IT8801_GPIOPOL BIT(2) /* polarity */ #define IT8801_GPIOPDE BIT(1) /* pull-down enable */ #define IT8801_GPIOPUE BIT(0) /* pull-up enable */ |