diff options
Diffstat (limited to 'driver/ioexpander/ioexpander_nct38xx.c')
-rw-r--r-- | driver/ioexpander/ioexpander_nct38xx.c | 470 |
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 e7fe3d9453..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 "ioexpander_nct38xx.h" -#include "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_slave_addr, - 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_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; - - 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_slave_addr, 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_slave_addr, 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_slave_addr, 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_slave_addr; - - 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_slave_addr; - - 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_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 = { - .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, -}; |