diff options
author | CHLin <CHLIN56@nuvoton.com> | 2019-06-13 14:12:59 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-07-24 09:33:46 +0000 |
commit | 2d4d05f6667d1fe26f9e9147fe8d6216c0679975 (patch) | |
tree | ebad9db53bf52bbc248cf591fb761d8b949172bb | |
parent | db49bc03b9b816e8c902a48478a18b9abe52b9a7 (diff) | |
download | chrome-ec-2d4d05f6667d1fe26f9e9147fe8d6216c0679975.tar.gz |
driver: IO expander: nct38xx: add support for NCT38XX series chips
Nuvoton TCPC NCT38XX series chips have some pins which can be used as
GPIO function. we can treat it as the IO expander chip also.
This commit adds the driver to support it.
BRANCH=none
BUG=none
TEST=No error for "make buildall"
TEST=Apply this and related CLs, manually test each API, make sure each
function works correctly with NCT3807 and NCT3808 at the same time.
Change-Id: I2254c2e867445e55d1c261172e4b7175d22f28ef
Signed-off-by: CHLin <CHLIN56@nuvoton.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1657859
Commit-Queue: CH Lin <chlin56@nuvoton.com>
Tested-by: CH Lin <chlin56@nuvoton.com>
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
-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 |