summaryrefslogtreecommitdiff
path: root/driver/ioexpander/it8801.c
diff options
context:
space:
mode:
authorVijay Hiremath <vijay.p.hiremath@intel.com>2021-08-05 16:45:54 -0700
committerCommit Bot <commit-bot@chromium.org>2021-09-10 19:07:11 +0000
commitd398a53a858d6f946e6a35cd06736033bce19094 (patch)
tree1a72fd20c6818b774a10dc8619290c1e9392509b /driver/ioexpander/it8801.c
parent431323a240bd98bf97012126d136dc2106febbf3 (diff)
downloadchrome-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.c159
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)