diff options
-rw-r--r-- | driver/ioexpander/it8801.c | 159 | ||||
-rw-r--r-- | driver/ioexpander/it8801.h | 20 |
2 files changed, 153 insertions, 26 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) diff --git a/driver/ioexpander/it8801.h b/driver/ioexpander/it8801.h index ec38bba6af..5cb713c42b 100644 --- a/driver/ioexpander/it8801.h +++ b/driver/ioexpander/it8801.h @@ -36,13 +36,20 @@ #define IT8801_REG_MASK_GPIOPUE BIT(0) #define IT8801_REG_GPIO23SOV BIT(3) #define IT8801_REG_MASK_SELKSO2 0x02 +#define IT8801_REG_GISR 0xF9 +#define IT8801_REG_MASK_GISR_GKSIIS BIT(6) +#define IT8801_REG_MASK_GISR_GGPIOG2IS BIT(2) +#define IT8801_REG_MASK_GISR_GGPIOG1IS BIT(1) +#define IT8801_REG_MASK_GISR_GGPIOG0IS BIT(0) +#define IT8801_REG_MASK_GISR_GGPIOGXIS (IT8801_REG_MASK_GISR_GGPIOG2IS | \ + IT8801_REG_MASK_GISR_GGPIOG1IS | IT8801_REG_MASK_GISR_GGPIOG0IS) #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) + GPIO_OUTPUT | GPIO_LOW | GPIO_HIGH | GPIO_INT_ANY) #define IT8801_REG_MASK_GPIOAFS_FUNC1 (0x00 << 7) @@ -70,14 +77,9 @@ extern const struct ioexpander_drv it8801_ioexpander_drv; #define IT8801_GPIOAFS_SHIFT 6 /* bit 6~7 */ #define IT8801_GPIODIR BIT(5) /* direction, output=1 */ - -#define IT8801_GPIOIOT_SHIFT 3 /* bit 3~4 */ -#define IT8801_GPIOIOT_MASK 0x3 -#define IT8801_GPIOIOT_INT_LEVEL 0 -#define IT8801_GPIOIOT_INT_RISING 1 -#define IT8801_GPIOIOT_INT_FALLING 2 -#define IT8801_GPIOIOT_INT_EDGE 3 /* = RISING + FALLING */ -#define IT8801_GPIOIOT_OPEN_DRAIN 2 +/* input pin */ +#define IT8801_GPIOIOT_INT_RISING BIT(3) +#define IT8801_GPIOIOT_INT_FALLING BIT(4) #define IT8801_GPIODIR BIT(5) #define IT8801_GPIOIOT BIT(4) |