diff options
-rw-r--r-- | driver/build.mk | 2 | ||||
-rw-r--r-- | driver/ioexpander_nct38xx.c | 204 | ||||
-rw-r--r-- | driver/ioexpander_nct38xx.h | 17 | ||||
-rw-r--r-- | driver/tcpm/nct38xx.h | 17 | ||||
-rw-r--r-- | include/config.h | 3 |
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 |