summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/build.mk2
-rw-r--r--driver/ioexpander_nct38xx.c204
-rw-r--r--driver/ioexpander_nct38xx.h17
-rw-r--r--driver/tcpm/nct38xx.h17
-rw-r--r--include/config.h3
5 files changed, 243 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk
index 8b5a26a2db..1e8dfa22a7 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -73,6 +73,8 @@ driver-$(CONFIG_CHARGER_SY21612)+=charger/sy21612.o
# I/O expander
driver-$(CONFIG_IO_EXPANDER_PCA9534)+=ioexpander_pca9534.o
+driver-$(CONFIG_IO_EXPANDER_NCT38XX)+=ioexpander_nct38xx.o
+
# Current/Power monitor
driver-$(CONFIG_INA219)$(CONFIG_INA231)+=ina2xx.o
diff --git a/driver/ioexpander_nct38xx.c b/driver/ioexpander_nct38xx.c
new file mode 100644
index 0000000000..3baca77ddf
--- /dev/null
+++ b/driver/ioexpander_nct38xx.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* GPIO expander for Nuvoton NCT38XX. */
+
+#include "console.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "ioexpander.h"
+#include "ioexpander_nct38xx.h"
+#include "tcpci.h"
+
+#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)
+{
+ if (chip_info == NCT38XX_VARIANT_3808) {
+ if (port == 1) {
+ CPRINTF("Port 1 is not support in NCT3808\n");
+ return EC_ERROR_INVAL;
+ }
+ if (mask & ~NCT38XXX_3808_VALID_GPIO_MASK) {
+
+ CPRINTF("GPIO%02d is not support in NCT3808\n",
+ __fls(mask));
+ return EC_ERROR_INVAL;
+ }
+ }
+
+ return EC_SUCCESS;
+
+}
+
+static int nct38xx_ioex_init(int ioex)
+{
+ int rv, val;
+ struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
+
+ /*
+ * Check the NCT38xx part number in the register DEVICE_ID[4:2]:
+ * 000: NCT3807
+ * 010: NCT3808
+ */
+ rv = i2c_read8(ioex_p->i2c_host_port, ioex_p->i2c_slave_addr,
+ TCPC_REG_BCD_DEV, &val);
+
+ if (rv)
+ 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;
+}
+
+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);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ reg = NCT38XXX_REG_GPIO_DATA_IN(port);
+ rv = i2c_read8(ioex_config[ioex].i2c_host_port,
+ ioex_config[ioex].i2c_slave_addr, reg, val);
+
+ *val = !!(*val & mask);
+ return rv;
+}
+
+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);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ reg = NCT38XXX_REG_GPIO_DATA_OUT(port);
+
+ rv = i2c_read8(ioex_config[ioex].i2c_host_port,
+ ioex_config[ioex].i2c_slave_addr, reg, &val);
+
+ if (value)
+ val |= mask;
+ else
+ val &= ~mask;
+ rv |= 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)
+{
+ int rv, reg, val, i2c_port, i2c_addr;
+ struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
+
+ 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);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ reg = NCT38XXX_REG_GPIO_DIR(port);
+ rv = i2c_read8(i2c_port, i2c_addr, reg, &val);
+ 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);
+ 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);
+ if (val & mask)
+ *flags |= GPIO_OPEN_DRAIN;
+
+ return rv;
+}
+
+static int nct38xx_ioex_set_flags_by_mask(int ioex, int port, int mask,
+ int flags)
+{
+ int rv, reg, val, i2c_port, i2c_addr;
+ struct ioexpander_config_t *ioex_p = &ioex_config[ioex];
+
+ 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);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /*
+ * GPIO port 0 muxs with alternative function. Disable the alternative
+ * function before setting flags.
+ */
+ if (port == 0) {
+ /* GPIO03 in NCT3807 is not muxed with other function. */
+ if (!(ioex_p->chip_info ==
+ NCT38XX_VARIANT_3807 && mask & 0x08)) {
+ reg = NCT38XXX_REG_MUX_CONTROL;
+ rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ val = (val | mask);
+ rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
+ }
+ }
+
+ val = flags & ~NCT38XX_SUPPORT_GPIO_FLAGS;
+ if (val) {
+ CPRINTF("Flag 0x%08x is not supported\n", val);
+ return EC_ERROR_INVAL;
+ }
+
+ /* 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);
+ if (flags & GPIO_OPEN_DRAIN)
+ val |= mask;
+ else
+ val &= ~mask;
+ rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
+
+ /* Configure the output level */
+ reg = NCT38XXX_REG_GPIO_DATA_OUT(port);
+ rv |= i2c_read8(i2c_port, i2c_addr, reg, &val);
+ if (flags & GPIO_HIGH)
+ val |= mask;
+ else if (flags & GPIO_LOW)
+ val &= ~mask;
+ rv |= i2c_write8(i2c_port, i2c_addr, reg, val);
+
+ 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;
+}
+
+const struct ioexpander_drv nct38xx_ioexpander_drv = {
+ .init = &nct38xx_ioex_init,
+ .get_level = &nct38xx_ioex_get_level,
+ .set_level = &nct38xx_ioex_set_level,
+ .get_flags_by_mask = &nct38xx_ioex_get_flags,
+ .set_flags_by_mask = &nct38xx_ioex_set_flags_by_mask,
+};
diff --git a/driver/ioexpander_nct38xx.h b/driver/ioexpander_nct38xx.h
new file mode 100644
index 0000000000..bbd5d89ac0
--- /dev/null
+++ b/driver/ioexpander_nct38xx.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_IOEXPANDER_NCT38XX_H
+#define __CROS_EC_IOEXPANDER_NCT38XX_H
+/*
+ * NCT38XX registers are defined in the driver/tcpm/nct38xx.h.
+ * No matter they are used by TCPC or IO Expander driver.
+ */
+#include "nct38xx.h"
+
+extern const struct ioexpander_drv nct38xx_ioexpander_drv;
+
+#endif /* defined(__CROS_EC_IOEXPANDER_NCT38XX_H) */
diff --git a/driver/tcpm/nct38xx.h b/driver/tcpm/nct38xx.h
index e85860f2db..ad46ee6faf 100644
--- a/driver/tcpm/nct38xx.h
+++ b/driver/tcpm/nct38xx.h
@@ -9,6 +9,14 @@
#ifndef __CROS_EC_USB_PD_TCPM_NCT38XX_H
#define __CROS_EC_USB_PD_TCPM_NCT38XX_H
+/* Chip variant ID (Part number) */
+#define NCT38XX_VARIANT_MASK 0x1C
+#define NCT38XX_VARIANT_3807 0x0
+#define NCT38XX_VARIANT_3808 0x2
+
+#define NCT38XX_SUPPORT_GPIO_FLAGS (GPIO_OPEN_DRAIN | GPIO_INPUT | \
+ GPIO_OUTPUT | GPIO_LOW | GPIO_HIGH)
+
/* I2C interface */
#define NCT38xx_I2C_ADDR1_1_FLAGS 0x70
#define NCT38xx_I2C_ADDR1_2_FLAGS 0x71
@@ -26,6 +34,15 @@
#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
+
+/* NCT3808 only supports GPIO 2/3/4/6/7 */
+#define NCT38XXX_3808_VALID_GPIO_MASK 0xDC
+
#define NCT38XX_REG_CTRL_OUT_EN 0xD2
#define NCT38XX_REG_CTRL_OUT_EN_SRCEN (1 << 0)
#define NCT38XX_REG_CTRL_OUT_EN_FASTEN (1 << 1)
diff --git a/include/config.h b/include/config.h
index 1eae83dc1a..e147b9bfb5 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2272,6 +2272,9 @@
/******************************************************************************/
+/* Support Nuvoton NCT38xx I/O expander. */
+#undef CONFIG_IO_EXPANDER_NCT38XX
+
/* Support NXP PCA9534 I/O expander. */
#undef CONFIG_IO_EXPANDER_PCA9534