summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--baseboard/zork/baseboard.c10
-rw-r--r--driver/ioexpander_nct38xx.c344
-rw-r--r--driver/ioexpander_nct38xx.h16
-rw-r--r--driver/tcpm/nct38xx.c39
-rw-r--r--driver/tcpm/nct38xx.h39
-rw-r--r--driver/tcpm/tcpci.h1
-rw-r--r--include/ioexpander.h5
7 files changed, 389 insertions, 65 deletions
diff --git a/baseboard/zork/baseboard.c b/baseboard/zork/baseboard.c
index e160c7b38d..1505405071 100644
--- a/baseboard/zork/baseboard.c
+++ b/baseboard/zork/baseboard.c
@@ -315,7 +315,7 @@ const struct tcpc_config_t tcpc_config[] = {
.bus_type = EC_BUS_TYPE_I2C,
.i2c_info = {
.port = I2C_PORT_TCPC0,
- .addr_flags = NCT38xx_I2C_ADDR1_1_FLAGS,
+ .addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS,
},
.drv = &nct38xx_tcpm_drv,
},
@@ -323,7 +323,7 @@ const struct tcpc_config_t tcpc_config[] = {
.bus_type = EC_BUS_TYPE_I2C,
.i2c_info = {
.port = I2C_PORT_TCPC1,
- .addr_flags = NCT38xx_I2C_ADDR1_1_FLAGS,
+ .addr_flags = NCT38XX_I2C_ADDR1_1_FLAGS,
},
.drv = &nct38xx_tcpm_drv,
},
@@ -453,14 +453,12 @@ BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT);
struct ioexpander_config_t ioex_config[] = {
[USBC_PORT_C0] = {
.i2c_host_port = I2C_PORT_TCPC0,
- .i2c_slave_addr = NCT38xx_I2C_ADDR1_1_FLAGS,
- .chip_info = -1,
+ .i2c_slave_addr = NCT38XX_I2C_ADDR1_1_FLAGS,
.drv = &nct38xx_ioexpander_drv,
},
[USBC_PORT_C1] = {
.i2c_host_port = I2C_PORT_TCPC1,
- .i2c_slave_addr = NCT38xx_I2C_ADDR1_1_FLAGS,
- .chip_info = -1,
+ .i2c_slave_addr = NCT38XX_I2C_ADDR1_1_FLAGS,
.drv = &nct38xx_ioexpander_drv,
},
};
diff --git a/driver/ioexpander_nct38xx.c b/driver/ioexpander_nct38xx.c
index 3baca77ddf..e7fe3d9453 100644
--- a/driver/ioexpander_nct38xx.c
+++ b/driver/ioexpander_nct38xx.c
@@ -16,9 +16,22 @@
#define CPRINTF(format, args...) cprintf(CC_GPIO, format, ## args)
#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args)
-static int nct38xx_ioex_check_is_valid(int chip_info, int port, int mask)
+/*
+ * Store the GPIO_ALERT_MASK_0/1 and chip ID registers locally. In this way,
+ * we don't have to read it via I2C transaction everytime.
+ */
+struct nct38xx_chip_data {
+ uint8_t int_mask[2];
+ int chip_id;
+};
+
+static struct nct38xx_chip_data chip_data[CONFIG_IO_EXPANDER_PORT_COUNT] = {
+ [0 ... (CONFIG_IO_EXPANDER_PORT_COUNT - 1)] = { {0, 0}, -1 }
+};
+
+static int nct38xx_ioex_check_is_valid(int ioex, int port, int mask)
{
- if (chip_info == NCT38XX_VARIANT_3808) {
+ if (chip_data[ioex].chip_id == NCT38XX_VARIANT_3808) {
if (port == 1) {
CPRINTF("Port 1 is not support in NCT3808\n");
return EC_ERROR_INVAL;
@@ -32,7 +45,6 @@ static int nct38xx_ioex_check_is_valid(int chip_info, int port, int mask)
}
return EC_SUCCESS;
-
}
static int nct38xx_ioex_init(int ioex)
@@ -48,54 +60,73 @@ static int nct38xx_ioex_init(int ioex)
rv = i2c_read8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
TCPC_REG_BCD_DEV, &val);
- if (rv)
+ if (rv != EC_SUCCESS) {
CPRINTF("Failed to read NCT38XX DEV ID for IOexpander %d\n",
ioex);
- else
- ioex_p->chip_info =
- ((uint8_t)val & NCT38XX_VARIANT_MASK) >> 2;
+ return rv;
+ }
- return rv;
+ chip_data[ioex].chip_id = ((uint8_t)val & NCT38XX_VARIANT_MASK) >> 2;
+
+ /*
+ * NCT38XX uses the Vendor Define bit in the ALERT event to indicate
+ * that an IOEX IO's interrupt is triggered.
+ * Normally, The ALERT MASK for Vendor Define event should be set by
+ * the NCT38XX TCPCI driver's init function.
+ * However, it should be also set here if we want to test the interrupt
+ * function of IOEX when the NCT38XX TCPCI driver is not included.
+ */
+ if (!IS_ENABLED(CONFIG_USB_PD_TCPM_NCT38XX)) {
+ rv = i2c_write16(ioex_p->i2c_host_port,
+ ioex_p->i2c_slave_addr, TCPC_REG_ALERT_MASK,
+ TCPC_REG_ALERT_VENDOR_DEF);
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+ return EC_SUCCESS;
}
static int nct38xx_ioex_get_level(int ioex, int port, int mask, int *val)
{
int rv, reg;
- struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
- rv = nct38xx_ioex_check_is_valid(ioex_p->chip_info, port, mask);
+ rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
if (rv != EC_SUCCESS)
return rv;
- reg = NCT38XXX_REG_GPIO_DATA_IN(port);
+ reg = NCT38XX_REG_GPIO_DATA_IN(port);
rv = i2c_read8(ioex_config[ioex].i2c_host_port,
ioex_config[ioex].i2c_slave_addr, reg, val);
+ if (rv != EC_SUCCESS)
+ return rv;
*val = !!(*val & mask);
- return rv;
+
+ return EC_SUCCESS;
}
static int nct38xx_ioex_set_level(int ioex, int port, int mask, int value)
{
int rv, reg, val;
- struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
- rv = nct38xx_ioex_check_is_valid(ioex_p->chip_info, port, mask);
+ rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
if (rv != EC_SUCCESS)
return rv;
- reg = NCT38XXX_REG_GPIO_DATA_OUT(port);
+ reg = NCT38XX_REG_GPIO_DATA_OUT(port);
rv = i2c_read8(ioex_config[ioex].i2c_host_port,
ioex_config[ioex].i2c_slave_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
if (value)
val |= mask;
else
val &= ~mask;
- rv |= i2c_write8(ioex_config[ioex].i2c_host_port,
+
+ return i2c_write8(ioex_config[ioex].i2c_host_port,
ioex_config[ioex].i2c_slave_addr, reg, val);
- return rv;
}
static int nct38xx_ioex_get_flags(int ioex, int port, int mask, int *flags)
@@ -106,30 +137,104 @@ static int nct38xx_ioex_get_flags(int ioex, int port, int mask, int *flags)
i2c_port = ioex_p->i2c_host_port;
i2c_addr = ioex_p->i2c_slave_addr;
- rv = nct38xx_ioex_check_is_valid(ioex_p->chip_info, port, mask);
+ rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
if (rv != EC_SUCCESS)
return rv;
- reg = NCT38XXX_REG_GPIO_DIR(port);
+ reg = NCT38XX_REG_GPIO_DIR(port);
rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
if (val & mask)
*flags |= GPIO_OUTPUT;
else
*flags |= GPIO_INPUT;
- reg = NCT38XXX_REG_GPIO_DATA_IN(port);
- rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ reg = NCT38XX_REG_GPIO_DATA_IN(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
if (val & mask)
*flags |= GPIO_HIGH;
else
*flags |= GPIO_LOW;
- reg = NCT38XXX_REG_GPIO_OD_SEL(port);
- rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ reg = NCT38XX_REG_GPIO_OD_SEL(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
if (val & mask)
*flags |= GPIO_OPEN_DRAIN;
- return rv;
+ return EC_SUCCESS;
+}
+
+static int nct38xx_ioex_sel_int_type(int i2c_port, int i2c_addr, int port,
+ int mask, int flags)
+{
+ int rv;
+ int reg_rising, reg_falling;
+ int rising, falling;
+
+ reg_rising = NCT38XX_REG_GPIO_ALERT_RISE(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg_rising, &rising);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ reg_falling = NCT38XX_REG_GPIO_ALERT_FALL(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg_falling, &falling);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Handle interrupt for level trigger */
+ if ((flags & GPIO_INT_F_HIGH) || (flags & GPIO_INT_F_LOW)) {
+ int reg_level, level;
+
+ reg_level = NCT38XX_REG_GPIO_ALERT_LEVEL(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg_level, &level);
+ if (rv != EC_SUCCESS)
+ return rv;
+ /*
+ * For "level" triggered interrupt, the related bit in
+ * ALERT_RISE and ALERT_FALL registers must be 0
+ */
+ rising &= ~mask;
+ falling &= ~mask;
+ if (flags & GPIO_INT_F_HIGH)
+ level |= mask;
+ else
+ level &= ~mask;
+
+ rv = i2c_write8(i2c_port, i2c_addr, reg_rising, rising);
+ if (rv != EC_SUCCESS)
+ return rv;
+ rv = i2c_write8(i2c_port, i2c_addr, reg_falling, falling);
+ if (rv != EC_SUCCESS)
+ return rv;
+ rv = i2c_write8(i2c_port, i2c_addr, reg_level, level);
+ if (rv != EC_SUCCESS)
+ return rv;
+ } else if ((flags & GPIO_INT_F_RISING) ||
+ (flags & GPIO_INT_F_FALLING)) {
+ if (flags & GPIO_INT_F_RISING)
+ rising |= mask;
+ else
+ rising &= ~mask;
+ if (flags & GPIO_INT_F_FALLING)
+ falling |= mask;
+ else
+ falling &= ~mask;
+ rv = i2c_write8(i2c_port, i2c_addr, reg_rising, rising);
+ if (rv != EC_SUCCESS)
+ return rv;
+ rv = i2c_write8(i2c_port, i2c_addr, reg_falling, falling);
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+ return EC_SUCCESS;
}
static int nct38xx_ioex_set_flags_by_mask(int ioex, int port, int mask,
@@ -141,7 +246,7 @@ static int nct38xx_ioex_set_flags_by_mask(int ioex, int port, int mask,
i2c_port = ioex_p->i2c_host_port;
i2c_addr = ioex_p->i2c_slave_addr;
- rv = nct38xx_ioex_check_is_valid(ioex_p->chip_info, port, mask);
+ rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
if (rv != EC_SUCCESS)
return rv;
@@ -151,12 +256,17 @@ static int nct38xx_ioex_set_flags_by_mask(int ioex, int port, int mask,
*/
if (port == 0) {
/* GPIO03 in NCT3807 is not muxed with other function. */
- if (!(ioex_p->chip_info ==
+ if (!(chip_data[ioex].chip_id ==
NCT38XX_VARIANT_3807 && mask & 0x08)) {
- reg = NCT38XXX_REG_MUX_CONTROL;
- rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ reg = NCT38XX_REG_MUX_CONTROL;
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
val = (val | mask);
- rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
+ rv = i2c_write8(i2c_port, i2c_addr, reg, val);
+ if (rv != EC_SUCCESS)
+ return rv;
}
}
@@ -167,32 +277,187 @@ static int nct38xx_ioex_set_flags_by_mask(int ioex, int port, int mask,
}
/* Select open drain 0:push-pull 1:open-drain */
- reg = NCT38XXX_REG_GPIO_OD_SEL(port);
- rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ reg = NCT38XX_REG_GPIO_OD_SEL(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
if (flags & GPIO_OPEN_DRAIN)
val |= mask;
else
val &= ~mask;
- rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
+ rv = i2c_write8(i2c_port, i2c_addr, reg, val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ nct38xx_ioex_sel_int_type(i2c_port, i2c_addr, port, mask, flags);
/* Configure the output level */
- reg = NCT38XXX_REG_GPIO_DATA_OUT(port);
- rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ reg = NCT38XX_REG_GPIO_DATA_OUT(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
if (flags & GPIO_HIGH)
val |= mask;
else if (flags & GPIO_LOW)
val &= ~mask;
- rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
+ rv = i2c_write8(i2c_port, i2c_addr, reg, val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ reg = NCT38XX_REG_GPIO_DIR(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
- reg = NCT38XXX_REG_GPIO_DIR(port);
- rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
if (flags & GPIO_OUTPUT)
val |= mask;
else
val &= ~mask;
- rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
- return rv;
+ return i2c_write8(i2c_port, i2c_addr, reg, val);
+}
+
+/*
+ * The following functions are used for IO's interrupt support.
+ *
+ * please note that if the system needs to use an IO on NCT38XX to support
+ * the interrupt, the following two consideration should be taken into account.
+ * 1. Interrupt latency:
+ * Because it requires to access the registers of NCT38XX via I2C
+ * transaction to know the interrupt event, there is some added latency
+ * for the interrupt handling. If the interrupt requires short latency,
+ * we do not recommend to connect such a signal to the NCT38XX.
+ *
+ * 2. Shared ALERT pin:
+ * Because the ALERT pin is shared also with the TCPC ALERT, we do not
+ * recommend to connect any signal that may generate a high rate of
+ * interrupts so it will not interfere with the normal work of the
+ * TCPC.
+ */
+static int nct38xx_ioex_enable_interrupt(int ioex, int port, int mask,
+ int enable)
+{
+ int rv, reg, val;
+ struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
+
+ rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Clear the pending bit */
+ reg = NCT38XX_REG_GPIO_ALERT_STAT(port);
+ rv = i2c_read8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ reg, &val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ val |= mask;
+ rv = i2c_write8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ reg, val);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ reg = NCT38XX_REG_GPIO_ALERT_MASK(port);
+ if (enable) {
+ /* Enable the alert mask */
+ chip_data[ioex].int_mask[port] |= mask;
+ val = chip_data[ioex].int_mask[port];
+ } else {
+ /* Disable the alert mask */
+ chip_data[ioex].int_mask[port] &= ~mask;
+ val = chip_data[ioex].int_mask[port];
+ }
+
+ return i2c_write8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ reg, val);
+}
+
+int nct38xx_ioex_event_handler(int ioex)
+{
+ int reg, int_status, int_mask;
+ int i, j, total_port;
+ const struct ioex_info *g;
+ struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
+ int rv = 0;
+
+ int_mask = chip_data[ioex].int_mask[0] | (
+ chip_data[ioex].int_mask[1] << 8);
+ reg = NCT38XX_REG_GPIO_ALERT_STAT(0);
+ /*
+ * Read ALERT_STAT_0 and ALERT_STAT_1 register in a single I2C
+ * transaction to increase efficiency
+ */
+ rv = i2c_read16(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ reg, &int_status);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ int_status = int_status & int_mask;
+ /*
+ * Clear the changed status bits in ALERT_STAT_0 and ALERT_STAT_1
+ * register in a single I2C transaction to increase efficiency
+ */
+ rv = i2c_write16(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ reg, int_status);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* For NCT3808, only check one port */
+ total_port = (chip_data[ioex].chip_id == NCT38XX_VARIANT_3808) ?
+ NCT38XX_NCT3808_MAX_IO_PORT :
+ NCT38XX_NCT3807_MAX_IO_PORT;
+ for (i = 0; i < total_port; i++) {
+ uint8_t pending;
+
+ pending = int_status >> (i * 8);
+
+ if (!pending)
+ continue;
+
+ for (j = 0, g = ioex_list; j < ioex_ih_count; j++, g++) {
+
+ if (ioex == g->ioex && i == g->port &&
+ (pending & g->mask)) {
+ ioex_irq_handlers[j](j);
+ pending &= ~g->mask;
+ if (!pending)
+ break;
+ }
+
+ }
+ }
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Normally, the ALERT MASK for Vendor Define event should be checked by
+ * the NCT38XX TCPCI driver's tcpc_alert function.
+ * However, it should be checked here if we want to test the interrupt
+ * function of IOEX when the NCT38XX TCPCI driver is not included.
+ */
+void nct38xx_ioex_handle_alert(int ioex)
+{
+ int rv, status;
+ struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
+
+ rv = i2c_read16(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ TCPC_REG_ALERT, &status);
+ if (rv != EC_SUCCESS)
+ CPRINTF("fail to read ALERT register\n");
+
+ if (status & TCPC_REG_ALERT_VENDOR_DEF) {
+ rv = i2c_write16(ioex_p->i2c_host_port,
+ ioex_p->i2c_slave_addr, TCPC_REG_ALERT,
+ TCPC_REG_ALERT_VENDOR_DEF);
+ if (rv != EC_SUCCESS) {
+ CPRINTF("Fail to clear Vendor Define mask\n");
+ return;
+ }
+ nct38xx_ioex_event_handler(ioex);
+ }
}
const struct ioexpander_drv nct38xx_ioexpander_drv = {
@@ -201,4 +466,5 @@ const struct ioexpander_drv nct38xx_ioexpander_drv = {
.set_level = &nct38xx_ioex_set_level,
.get_flags_by_mask = &nct38xx_ioex_get_flags,
.set_flags_by_mask = &nct38xx_ioex_set_flags_by_mask,
+ .enable_interrupt = &nct38xx_ioex_enable_interrupt,
};
diff --git a/driver/ioexpander_nct38xx.h b/driver/ioexpander_nct38xx.h
index bbd5d89ac0..56dfe76e04 100644
--- a/driver/ioexpander_nct38xx.h
+++ b/driver/ioexpander_nct38xx.h
@@ -12,6 +12,22 @@
*/
#include "nct38xx.h"
+/*
+ * The interrupt handler to handle Vendor Define ALERT event from IOEX chip.
+ *
+ * Normally, the Vendor Define event should be checked by the NCT38XX TCPCI
+ * driver's tcpc_alert function.
+ * This function is only included when NCT38XX TCPC driver is not included.
+ * (i.e. CONFIG_USB_PD_TCPM_NCT38XX is not defined)
+ */
+void nct38xx_ioex_handle_alert(int ioex);
+
+/*
+ * Check which IO's interrupt event is triggered. If any, call its
+ * registered interrupt handler.
+ */
+int nct38xx_ioex_event_handler(int ioex);
+
extern const struct ioexpander_drv nct38xx_ioexpander_drv;
#endif /* defined(__CROS_EC_IOEXPANDER_NCT38XX_H) */
diff --git a/driver/tcpm/nct38xx.c b/driver/tcpm/nct38xx.c
index ee8aeb58e6..8061f7190c 100644
--- a/driver/tcpm/nct38xx.c
+++ b/driver/tcpm/nct38xx.c
@@ -8,6 +8,7 @@
#include "common.h"
#include "console.h"
+#include "ioexpander_nct38xx.h"
#include "nct38xx.h"
#include "tcpci.h"
@@ -75,6 +76,18 @@ static int nct38xx_tcpm_init(int port)
/* Start VBus monitor */
rv = tcpc_write(port, TCPC_REG_COMMAND,
TCPC_REG_COMMAND_ENABLE_VBUS_DETECT);
+
+ /*
+ * Enable the Vendor Define alert event only when the IO expander
+ * feature is defined
+ */
+ if (IS_ENABLED(CONFIG_IO_EXPANDER_NCT38XX)) {
+ int mask;
+
+ rv |= tcpc_read16(port, TCPC_REG_ALERT_MASK, &mask);
+ mask |= TCPC_REG_ALERT_VENDOR_DEF;
+ rv |= tcpc_write16(port, TCPC_REG_ALERT_MASK, mask);
+ }
return rv;
}
@@ -297,6 +310,30 @@ clear:
return rv;
}
+static void nct38xx_tcpc_alert(int port)
+{
+ int alert, rv;
+
+ /*
+ * If IO expander feature is defined, read the ALERT register first to
+ * keep the status of Vendor Define bit. Otherwise, the status of ALERT
+ * register will be cleared after tcpci_tcpc_alert() is executed.
+ */
+ if (IS_ENABLED(CONFIG_IO_EXPANDER_NCT38XX))
+ rv = tcpc_read16(port, TCPC_REG_ALERT, &alert);
+
+ /* Process normal TCPC ALERT event and clear status */
+ tcpci_tcpc_alert(port);
+
+ /*
+ * If IO expander feature is defined, check the Vendor Define bit to
+ * handle the IOEX IO's interrupt event
+ */
+ if (IS_ENABLED(CONFIG_IO_EXPANDER_NCT38XX))
+ if (!rv && (alert & TCPC_REG_ALERT_VENDOR_DEF))
+ nct38xx_ioex_event_handler(port);
+
+}
const struct tcpm_drv nct38xx_tcpm_drv = {
.init = &nct38xx_tcpm_init,
.release = &tcpci_tcpm_release,
@@ -312,7 +349,7 @@ const struct tcpm_drv nct38xx_tcpm_drv = {
.set_rx_enable = &tcpci_tcpm_set_rx_enable,
.get_message_raw = &tcpci_nct38xx_get_message_raw,
.transmit = &tcpci_nct38xx_transmit,
- .tcpc_alert = &tcpci_tcpc_alert,
+ .tcpc_alert = &nct38xx_tcpc_alert,
#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
.tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus,
#endif
diff --git a/driver/tcpm/nct38xx.h b/driver/tcpm/nct38xx.h
index 5d80606018..970c1c8c85 100644
--- a/driver/tcpm/nct38xx.h
+++ b/driver/tcpm/nct38xx.h
@@ -14,19 +14,25 @@
#define NCT38XX_VARIANT_3807 0x0
#define NCT38XX_VARIANT_3808 0x2
+/* There are two IO ports in NCT3807 */
+#define NCT38XX_NCT3807_MAX_IO_PORT 2
+/* There is only one IO port in NCT3808 */
+#define NCT38XX_NCT3808_MAX_IO_PORT 1
+
#define NCT38XX_SUPPORT_GPIO_FLAGS (GPIO_OPEN_DRAIN | GPIO_INPUT | \
- GPIO_OUTPUT | GPIO_LOW | GPIO_HIGH)
+ GPIO_OUTPUT | GPIO_LOW | GPIO_HIGH | GPIO_INT_F_RISING | \
+ GPIO_INT_F_FALLING | GPIO_INT_F_HIGH | GPIO_INT_F_LOW)
/* I2C interface */
-#define NCT38xx_I2C_ADDR1_1_FLAGS 0x70
-#define NCT38xx_I2C_ADDR1_2_FLAGS 0x71
-#define NCT38xx_I2C_ADDR1_3_FLAGS 0x72
-#define NCT38xx_I2C_ADDR1_4_FLAGS 0x73
+#define NCT38XX_I2C_ADDR1_1_FLAGS 0x70
+#define NCT38XX_I2C_ADDR1_2_FLAGS 0x71
+#define NCT38XX_I2C_ADDR1_3_FLAGS 0x72
+#define NCT38XX_I2C_ADDR1_4_FLAGS 0x73
-#define NCT38xx_I2C_ADDR2_1_FLAGS 0x74
-#define NCT38xx_I2C_ADDR2_2_FLAGS 0x75
-#define NCT38xx_I2C_ADDR2_3_FLAGS 0x76
-#define NCT38xx_I2C_ADDR2_4_FLAGS 0x77
+#define NCT38XX_I2C_ADDR2_1_FLAGS 0x74
+#define NCT38XX_I2C_ADDR2_2_FLAGS 0x75
+#define NCT38XX_I2C_ADDR2_3_FLAGS 0x76
+#define NCT38XX_I2C_ADDR2_4_FLAGS 0x77
#define NCT38XX_REG_VENDOR_ID_L 0x00
#define NCT38XX_REG_VENDOR_ID_H 0x01
@@ -34,11 +40,16 @@
#define NCT38XX_PRODUCT_ID 0xC301
-#define NCT38XXX_REG_GPIO_DATA_IN(n) (0xC0 + ((n) * 8))
-#define NCT38XXX_REG_GPIO_DATA_OUT(n) (0xC1 + ((n) * 8))
-#define NCT38XXX_REG_GPIO_DIR(n) (0xC2 + ((n) * 8))
-#define NCT38XXX_REG_GPIO_OD_SEL(n) (0xC3 + ((n) * 8))
-#define NCT38XXX_REG_MUX_CONTROL 0xD0
+#define NCT38XX_REG_GPIO_DATA_IN(n) (0xC0 + ((n) * 8))
+#define NCT38XX_REG_GPIO_DATA_OUT(n) (0xC1 + ((n) * 8))
+#define NCT38XX_REG_GPIO_DIR(n) (0xC2 + ((n) * 8))
+#define NCT38XX_REG_GPIO_OD_SEL(n) (0xC3 + ((n) * 8))
+#define NCT38XX_REG_GPIO_ALERT_RISE(n) (0xC4 + ((n) * 8))
+#define NCT38XX_REG_GPIO_ALERT_FALL(n) (0xC5 + ((n) * 8))
+#define NCT38XX_REG_GPIO_ALERT_LEVEL(n) (0xC6 + ((n) * 8))
+#define NCT38XX_REG_GPIO_ALERT_MASK(n) (0xC7 + ((n) * 8))
+#define NCT38XX_REG_MUX_CONTROL 0xD0
+#define NCT38XX_REG_GPIO_ALERT_STAT(n) (0xD4 + (n))
/* NCT3808 only supports GPIO 2/3/4/6/7 */
#define NCT38XXX_3808_VALID_GPIO_MASK 0xDC
diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h
index 6db71e4161..d2950b7b1a 100644
--- a/driver/tcpm/tcpci.h
+++ b/driver/tcpm/tcpci.h
@@ -21,6 +21,7 @@
#define TCPC_REG_ALERT 0x10
#define TCPC_REG_ALERT_MASK_ALL 0xfff
+#define TCPC_REG_ALERT_VENDOR_DEF (1<<15)
#define TCPC_REG_ALERT_VBUS_DISCNCT (1<<11)
#define TCPC_REG_ALERT_RX_BUF_OVF (1<<10)
#define TCPC_REG_ALERT_FAULT (1<<9)
diff --git a/include/ioexpander.h b/include/ioexpander.h
index e8f76d9fbc..150db65750 100644
--- a/include/ioexpander.h
+++ b/include/ioexpander.h
@@ -51,11 +51,6 @@ struct ioexpander_config_t {
/* I2C slave address */
int i2c_slave_addr;
/*
- * The extra variable used to store information which may be required
- * by the IO expander chip.
- */
- int chip_info;
- /*
* Pointer to the specific IO expander chip's ops defined in
* the struct ioexpander_drv.
*/