diff options
author | Vijay Hiremath <vijay.p.hiremath@intel.com> | 2021-08-05 16:45:54 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-09-10 19:07:11 +0000 |
commit | d398a53a858d6f946e6a35cd06736033bce19094 (patch) | |
tree | 1a72fd20c6818b774a10dc8619290c1e9392509b /driver/ioexpander/it8801.c | |
parent | 431323a240bd98bf97012126d136dc2106febbf3 (diff) | |
download | chrome-ec-d398a53a858d6f946e6a35cd06736033bce19094.tar.gz |
IOEX: it8801: Enable GPIO based interrupts
Added code to enable GPIO based interrupts on it8801
I/O Expander.
BUG=b:197659347
BRANCH=none
TEST=Tested on ADL-RVP able to get ISR triggered
Change-Id: I7f5f460f48fc21e51bb93bede5a05da89b7dc807
Signed-off-by: Vijay Hiremath <vijay.p.hiremath@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3076469
Reviewed-by: Keith Short <keithshort@chromium.org>
Reviewed-by: Dino Li <Dino.Li@ite.com.tw>
Commit-Queue: Keith Short <keithshort@chromium.org>
Diffstat (limited to 'driver/ioexpander/it8801.c')
-rw-r--r-- | driver/ioexpander/it8801.c | 159 |
1 files changed, 142 insertions, 17 deletions
diff --git a/driver/ioexpander/it8801.c b/driver/ioexpander/it8801.c index ea59aaa85c..075f53df96 100644 --- a/driver/ioexpander/it8801.c +++ b/driver/ioexpander/it8801.c @@ -6,6 +6,7 @@ #include "common.h" #include "console.h" #include "gpio.h" +#include "hooks.h" #include "i2c.h" #include "ioexpander.h" #include "it8801.h" @@ -19,6 +20,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 void it8801_ioex_event_handler(void); +DECLARE_DEFERRED(it8801_ioex_event_handler); static int it8801_read(int reg, int *data) { @@ -60,6 +63,27 @@ static int it8801_check_vendor_id(void) return EC_SUCCESS; } +/* + * Keyboard and GPIO interrupts are muxed inside the IT8801 chip. + * Interrupt enable register controls the individual pins from + * triggering this global interrupt hence it is okay that this + * pin is enabled all the time. + */ +static void it8801_muxed_kbd_gpio_intr_enable(void) +{ + static bool intr_enabled; + + /* + * Allow enabling this pin either by Keyboard enable code or + * IOEX init code whichever gets called first. + */ + if (!intr_enabled) { + gpio_clear_pending_interrupt(GPIO_IT8801_SMB_INT); + gpio_enable_interrupt(GPIO_IT8801_SMB_INT); + intr_enabled = true; + } +} + #ifdef CONFIG_KEYBOARD_NOT_RAW void keyboard_raw_init(void) { @@ -183,20 +207,21 @@ test_mockable int keyboard_raw_read_rows(void) void keyboard_raw_enable_interrupt(int enable) { if (enable) { + /* Clear pending iterrupts */ it8801_write(IT8801_REG_KSIEER, 0xff); - gpio_clear_pending_interrupt(GPIO_IT8801_SMB_INT); - gpio_enable_interrupt(GPIO_IT8801_SMB_INT); - } else { - gpio_disable_interrupt(GPIO_IT8801_SMB_INT); + + /* Enable muxed Keyboard & GPIO interrupt */ + it8801_muxed_kbd_gpio_intr_enable(); } + + it8801_write(IT8801_REG_KSIIER, enable ? 0xff : 0x00); } +#endif /* CONFIG_KEYBOARD_NOT_RAW */ void io_expander_it8801_interrupt(enum gpio_signal signal) { - /* Wake the scan task */ - task_wake(TASK_ID_KEYSCAN); + hook_call_deferred(&it8801_ioex_event_handler_data, 0); } -#endif /* CONFIG_KEYBOARD_NOT_RAW */ static int it8801_ioex_read(int ioex, int reg, int *data) { @@ -214,6 +239,15 @@ static int it8801_ioex_write(int ioex, int reg, int data) reg, data); } +static int it8801_ioex_update(int ioex, int reg, int data, + enum mask_update_action action) +{ + struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; + + return i2c_update8(ioex_p->i2c_host_port, ioex_p->i2c_addr_flags, + reg, data, action); +} + static const int it8801_valid_gpio_group[] = { IT8801_VALID_GPIO_G0_MASK, IT8801_VALID_GPIO_G1_MASK, @@ -249,6 +283,9 @@ static int it8801_ioex_init(int ioex) it8801_gpio_sov[port] = val; } + /* Enable muxed Keyboard & GPIO interrupt */ + it8801_muxed_kbd_gpio_intr_enable(); + return EC_SUCCESS; } @@ -354,15 +391,16 @@ static int it8801_ioex_set_flags_by_mask(int ioex, int port, } /* GPIO alternate function switching(GPIO[00, 12:15, 20:23]). */ - it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), + rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), IT8801_REG_MASK_GPIOAFS_FUNC1); + if (rv) + return rv; mutex_lock(&ioex_mutex); rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_CR(port, mask), &val); - if (rv) { - mutex_unlock(&ioex_mutex); - return rv; - } + if (rv) + goto unlock_mutex; + /* Select open drain 0:push-pull 1:open-drain */ if (flags & GPIO_OPEN_DRAIN) val |= IT8801_GPIOIOT; @@ -379,26 +417,113 @@ static int it8801_ioex_set_flags_by_mask(int ioex, int port, rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_SOVR(port), it8801_gpio_sov[port]); - if (rv) { - mutex_unlock(&ioex_mutex); - return rv; - } + if (rv) + goto unlock_mutex; + val |= IT8801_GPIODIR; - } else + } else { val &= ~IT8801_GPIODIR; + } + + /* Set Interrupt Type */ + if (flags & GPIO_INT_RISING) + val |= IT8801_GPIOIOT_INT_RISING; + if (flags & GPIO_INT_FALLING) + val |= IT8801_GPIOIOT_INT_FALLING; rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), val); + +unlock_mutex: mutex_unlock(&ioex_mutex); return rv; } +/* Enable the individual GPIO interrupt pins based on the board requirement. */ +static int it8801_ioex_enable_interrupt(int ioex, int port, int mask, + int enable) +{ + int rv; + + if (ioex_check_is_not_valid(port, mask)) + return EC_ERROR_INVAL; + + /* Clear pending interrupt */ + rv = it8801_ioex_update(ioex, IT8801_REG_GPIO_ISR(port), + mask, MASK_SET); + if (rv) + return rv; + + return it8801_ioex_update(ioex, IT8801_REG_GPIO_IER(port), + mask, enable ? MASK_SET : MASK_CLR); +} + +static void it8801_ioex_irq(int ioex, int port) +{ + int rv, data, i; + const struct ioex_info *g; + + rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_ISR(port), &data); + if (rv || !data) + return; + + /* Trigger the intended interrupt from the IOEX IRQ pins */ + for (i = 0, g = ioex_list; i < ioex_ih_count; i++, g++) { + if (ioex == g->ioex && port == g->port && data & g->mask) { + ioex_irq_handlers[i](i + IOEX_SIGNAL_START); + data &= ~g->mask; + + /* Clear pending interrupt */ + it8801_ioex_update(ioex, IT8801_REG_GPIO_ISR(port), + g->mask, MASK_SET); + + if (!data) + break; + } + } +} + +static void it8801_ioex_event_handler(void) +{ + int data, i; + + /* Gather KSI interrupt status register */ + if (it8801_read(IT8801_REG_GISR, &data)) + return; + + /* Wake the keyboard scan task if KSI interrupts are triggered */ + if (IS_ENABLED(CONFIG_KEYBOARD_NOT_RAW) && + data & IT8801_REG_MASK_GISR_GKSIIS) + task_wake(TASK_ID_KEYSCAN); + + /* + * Trigger the GPIO callback functions if the GPIO interrupts are + * triggered. + */ + if (data & (IT8801_REG_MASK_GISR_GGPIOGXIS)) { + for (i = 0; i < CONFIG_IO_EXPANDER_PORT_COUNT; i++) { + if (ioex_config[i].drv == &it8801_ioexpander_drv) { + /* Interrupt from GPIO port 0 is triggered */ + if (data & IT8801_REG_MASK_GISR_GGPIOG0IS) + it8801_ioex_irq(i, 0); + /* Interrupt from GPIO port 1 is triggered */ + if (data & IT8801_REG_MASK_GISR_GGPIOG1IS) + it8801_ioex_irq(i, 1); + /* Interrupt from GPIO port 2 is triggered */ + if (data & IT8801_REG_MASK_GISR_GGPIOG2IS) + it8801_ioex_irq(i, 2); + } + } + } +} + 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, + .enable_interrupt = &it8801_ioex_enable_interrupt, }; static void dump_register(int reg) |