diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/ioexpander/tca64xxa.c | 226 | ||||
-rw-r--r-- | driver/ioexpander/tca64xxa.h | 25 |
3 files changed, 252 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk index 6281247fd8..19b1839635 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -82,6 +82,7 @@ driver-$(CONFIG_IO_EXPANDER_NCT38XX)+=ioexpander/ioexpander_nct38xx.o driver-$(CONFIG_IO_EXPANDER_PCA9534)+=ioexpander/pca9534.o driver-$(CONFIG_IO_EXPANDER_PCA9675)+=ioexpander/pca9675.o driver-$(CONFIG_IO_EXPANDER_PCAL6408)+=ioexpander/pcal6408.o +driver-$(CONFIG_IO_EXPANDER_TCA64XXA)+=ioexpander/tca64xxa.o driver-$(CONFIG_CTN730)+=nfc/ctn730.o diff --git a/driver/ioexpander/tca64xxa.c b/driver/ioexpander/tca64xxa.c new file mode 100644 index 0000000000..0a65fdc5ca --- /dev/null +++ b/driver/ioexpander/tca64xxa.c @@ -0,0 +1,226 @@ +/* Copyright 2021 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. + */ + +#include "common.h" +#include "i2c.h" +#include "ioexpander.h" +#include "system.h" +#include "tca64xxa.h" + +/* + * This chip series contain registers in the same order. + * Difference between models is only amount of registers and + * value of which you must multiply to access specific register. + * For 16 bit series, registers are 2 byte away, so to access TCA64XXA_REG_CONF + * you must multiply it by 2. For 24 bit, they are away by 4 bytes so you + * must multiply them by 4. Flags value contains information which version + * of chip is used. + */ +#define TCA64XXA_PORT_ID(port, reg, flags) \ + ((((flags) & TCA64XXA_FLAG_VER_MASK) \ + >> TCA64XXA_FLAG_VER_OFFSET) * (reg) + (port)) + +static int tca64xxa_write_byte(int ioex, int port, int reg, uint8_t val) +{ + const struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; + const int reg_addr = TCA64XXA_PORT_ID(port, reg, ioex_p->flags); + + return i2c_write8(ioex_p->i2c_host_port, + ioex_p->i2c_slave_addr, + reg_addr, + val); +} + +static int tca64xxa_read_byte(int ioex, int port, int reg, int *val) +{ + const struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; + const int reg_addr = TCA64XXA_PORT_ID(port, reg, ioex_p->flags); + + return i2c_read8(ioex_p->i2c_host_port, + ioex_p->i2c_slave_addr, + reg_addr, + val); +} + +/* Restore default values in registers */ +static int tca64xxa_reset(int ioex, int portsCount) +{ + int port; + int ret; + + /* + * On servo_v4p1, reset pin is pulled up and it results in values + * not being restored to default ones after software reboot. + * This loop sets default values (from specification) to all registers. + */ + for (port = 0; port < portsCount; port++) { + ret = tca64xxa_write_byte(ioex, + port, + TCA64XXA_REG_OUTPUT, + TCA64XXA_DEFAULT_OUTPUT); + if (ret) + return ret; + + ret = tca64xxa_write_byte(ioex, + port, + TCA64XXA_REG_POLARITY_INV, + TCA64XXA_DEFAULT_POLARITY_INV); + if (ret) + return ret; + + ret = tca64xxa_write_byte(ioex, + port, + TCA64XXA_REG_CONF, + TCA64XXA_DEFAULT_CONF); + if (ret) + return ret; + } + + return EC_SUCCESS; +} + +/* Initialize IO expander chip/driver */ +static int tca64xxa_init(int ioex) +{ + const struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; + int portsCount; + + if (ioex_p->flags & TCA64XXA_FLAG_VER_TCA6416A) + portsCount = 2; + else if (ioex_p->flags & TCA64XXA_FLAG_VER_TCA6424A) + portsCount = 3; + else + return EC_ERROR_UNIMPLEMENTED; + + if (!system_jumped_late()) + return tca64xxa_reset(ioex, portsCount); + + return EC_SUCCESS; +} + +/* Get the current level of the IOEX pin */ +static int tca64xxa_get_level(int ioex, int port, int mask, int *val) +{ + int buf; + int ret; + + ret = tca64xxa_read_byte(ioex, port, TCA64XXA_REG_INPUT, &buf); + *val = !!(buf & mask); + + return ret; +} + +/* Set the level of the IOEX pin */ +static int tca64xxa_set_level(int ioex, int port, int mask, int val) +{ + int ret; + int v; + + ret = tca64xxa_read_byte(ioex, port, TCA64XXA_REG_OUTPUT, &v); + if (ret) + return ret; + + if (val) + v |= mask; + else + v &= ~mask; + + return tca64xxa_write_byte(ioex, port, TCA64XXA_REG_OUTPUT, v); +} + +/* Get flags for the IOEX pin */ +static int tca64xxa_get_flags_by_mask(int ioex, int port, int mask, int *flags) +{ + int ret; + int v; + + ret = tca64xxa_read_byte(ioex, port, TCA64XXA_REG_CONF, &v); + if (ret) + return ret; + + *flags = 0; + if (v & mask) { + *flags |= GPIO_INPUT; + } else { + *flags |= GPIO_OUTPUT; + + ret = tca64xxa_read_byte(ioex, port, TCA64XXA_REG_OUTPUT, &v); + if(ret) + return ret; + + if (v & mask) + *flags |= GPIO_HIGH; + else + *flags |= GPIO_LOW; + } + + return EC_SUCCESS; +} + +/* Set flags for the IOEX pin */ +static int tca64xxa_set_flags_by_mask(int ioex, int port, int mask, int flags) +{ + int ret; + int v; + + /* Output value */ + if (flags & GPIO_OUTPUT) { + ret = tca64xxa_read_byte(ioex, port, TCA64XXA_REG_OUTPUT, &v); + if (ret) + return ret; + + if (flags & GPIO_LOW) + v &= ~mask; + else if (flags & GPIO_HIGH) + v |= mask; + else + return EC_ERROR_INVAL; + + ret = tca64xxa_write_byte(ioex, port, TCA64XXA_REG_OUTPUT, v); + if (ret) + return ret; + } + + /* Configuration */ + ret = tca64xxa_read_byte(ioex, port, TCA64XXA_REG_CONF, &v); + if(ret) + return ret; + + if (flags & GPIO_INPUT) + v |= mask; + else if (flags & GPIO_OUTPUT) + v &= ~mask; + else + return EC_ERROR_INVAL; + + ret = tca64xxa_write_byte(ioex, port, TCA64XXA_REG_CONF, v); + if (ret) + return ret; + + return EC_SUCCESS; +} + +#ifdef CONFIG_IO_EXPANDER_SUPPORT_GET_PORT + +/* Read levels for whole IO expander port */ +static int tca64xxa_get_port(int ioex, int port, int *val) +{ + return tca64xxa_read_byte(ioex, port, TCA64XXA_REG_INPUT, val); +} + +#endif + +/* Driver structure */ +const struct ioexpander_drv tca64xxa_ioexpander_drv = { + .init = tca64xxa_init, + .get_level = tca64xxa_get_level, + .set_level = tca64xxa_set_level, + .get_flags_by_mask = tca64xxa_get_flags_by_mask, + .set_flags_by_mask = tca64xxa_set_flags_by_mask, + .enable_interrupt = NULL, +#ifdef CONFIG_IO_EXPANDER_SUPPORT_GET_PORT + .get_port = tca64xxa_get_port, +#endif +}; diff --git a/driver/ioexpander/tca64xxa.h b/driver/ioexpander/tca64xxa.h new file mode 100644 index 0000000000..8c3448f804 --- /dev/null +++ b/driver/ioexpander/tca64xxa.h @@ -0,0 +1,25 @@ +/* Copyright 2021 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_DRIVER_IOEXPANDER_TCA64XXA_H_ +#define __CROS_EC_DRIVER_IOEXPANDER_TCA64XXA_H_ + +#define TCA64XXA_FLAG_VER_TCA6416A 2 +#define TCA64XXA_FLAG_VER_TCA6424A 4 +#define TCA64XXA_FLAG_VER_MASK GENMASK(2, 1) +#define TCA64XXA_FLAG_VER_OFFSET 0 + +#define TCA64XXA_REG_INPUT 0 +#define TCA64XXA_REG_OUTPUT 1 +#define TCA64XXA_REG_POLARITY_INV 2 +#define TCA64XXA_REG_CONF 3 + +#define TCA64XXA_DEFAULT_OUTPUT 0xFF +#define TCA64XXA_DEFAULT_POLARITY_INV 0x00 +#define TCA64XXA_DEFAULT_CONF 0xFF + +extern const struct ioexpander_drv tca64xxa_ioexpander_drv; + +#endif /* __CROS_EC_DRIVER_IOEXPANDER_TCA64XXA_H_ */ |