summaryrefslogtreecommitdiff
path: root/driver/ioexpander/ioexpander_nct38xx.c
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /driver/ioexpander/ioexpander_nct38xx.c
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-firmware-brya-14505.71.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'driver/ioexpander/ioexpander_nct38xx.c')
-rw-r--r--driver/ioexpander/ioexpander_nct38xx.c470
1 files changed, 0 insertions, 470 deletions
diff --git a/driver/ioexpander/ioexpander_nct38xx.c b/driver/ioexpander/ioexpander_nct38xx.c
deleted file mode 100644
index 8c87a33d24..0000000000
--- a/driver/ioexpander/ioexpander_nct38xx.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * 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 "nct38xx.h"
-#include "tcpm/tcpci.h"
-
-#define CPRINTF(format, args...) cprintf(CC_GPIO, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args)
-
-/*
- * 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_data[ioex].chip_id == 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_addr_flags,
- TCPC_REG_BCD_DEV, &val);
-
- if (rv != EC_SUCCESS) {
- CPRINTF("Failed to read NCT38XX DEV ID for IOexpander %d\n",
- ioex);
- 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_addr_flags, 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;
-
- rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
- if (rv != EC_SUCCESS)
- return rv;
-
- reg = NCT38XX_REG_GPIO_DATA_IN(port);
- rv = i2c_read8(ioex_config[ioex].i2c_host_port,
- ioex_config[ioex].i2c_addr_flags, reg, val);
- if (rv != EC_SUCCESS)
- return rv;
-
- *val = !!(*val & mask);
-
- return EC_SUCCESS;
-}
-
-static int nct38xx_ioex_set_level(int ioex, int port, int mask, int value)
-{
- int rv, reg, val;
-
- rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
- if (rv != EC_SUCCESS)
- return rv;
-
- reg = NCT38XX_REG_GPIO_DATA_OUT(port);
-
- rv = i2c_read8(ioex_config[ioex].i2c_host_port,
- ioex_config[ioex].i2c_addr_flags, reg, &val);
- if (rv != EC_SUCCESS)
- return rv;
-
- if (value)
- val |= mask;
- else
- val &= ~mask;
-
- return i2c_write8(ioex_config[ioex].i2c_host_port,
- ioex_config[ioex].i2c_addr_flags, reg, val);
-}
-
-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_addr_flags;
-
- rv = nct38xx_ioex_check_is_valid(ioex, port, mask);
- 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;
-
- if (val & mask)
- *flags |= GPIO_OUTPUT;
- else
- *flags |= GPIO_INPUT;
-
- 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 = 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 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,
- 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_addr_flags;
-
- rv = nct38xx_ioex_check_is_valid(ioex, 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 (!(chip_data[ioex].chip_id ==
- NCT38XX_VARIANT_3807 && mask & 0x08)) {
- 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);
- if (rv != EC_SUCCESS)
- return rv;
- }
- }
-
- 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 = 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);
- if (rv != EC_SUCCESS)
- return rv;
-
- nct38xx_ioex_sel_int_type(i2c_port, i2c_addr, port, mask, flags);
-
- /* Configure the output level */
- 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);
- 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;
-
- if (flags & GPIO_OUTPUT)
- val |= mask;
- else
- val &= ~mask;
-
- 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_addr_flags,
- reg, &val);
- if (rv != EC_SUCCESS)
- return rv;
-
- val |= mask;
- rv = i2c_write8(ioex_p->i2c_host_port, ioex_p->i2c_addr_flags,
- 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_addr_flags,
- 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_addr_flags,
- 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_addr_flags,
- 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 + IOEX_SIGNAL_START);
- 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_addr_flags,
- 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_addr_flags, 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 = {
- .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,
- .enable_interrupt = &nct38xx_ioex_enable_interrupt,
-};