summaryrefslogtreecommitdiff
path: root/driver/ioexpander
diff options
context:
space:
mode:
authorVijay Hiremath <vijay.p.hiremath@intel.com>2021-04-26 17:42:39 -0700
committerCommit Bot <commit-bot@chromium.org>2021-05-05 19:18:09 +0000
commit45cdc0f7acd939efc686b4e600b32445f1e017f3 (patch)
tree2e2639586a313b38ad5e9d2a55604861a506bf00 /driver/ioexpander
parente96d9283e51c1fc105e1cc2b7d6757105699baf3 (diff)
downloadchrome-ec-45cdc0f7acd939efc686b4e600b32445f1e017f3.tar.gz
I/O Expander: Add CCG6XX driver
Cypress CCGXXF PD has built-in I/O Expander, added driver to enable GPIO functionality. BUG=none BRANCH=none TEST=Tested on ADLRVP, ioexget & ioexset works as expected Change-Id: I8503178703ad166ac77e96d1990133c88169d23a Signed-off-by: Vijay Hiremath <vijay.p.hiremath@intel.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2853143 Tested-by: Svyatoslav Paliy <svpaliy@gmail.com> Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Diffstat (limited to 'driver/ioexpander')
-rw-r--r--driver/ioexpander/ccgxxf.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/driver/ioexpander/ccgxxf.c b/driver/ioexpander/ccgxxf.c
new file mode 100644
index 0000000000..afaa6820d5
--- /dev/null
+++ b/driver/ioexpander/ccgxxf.c
@@ -0,0 +1,130 @@
+/* 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.
+ *
+ * Cypress CCGXXF I/O Port expander (built inside PD chip) driver source
+ */
+
+#include "i2c.h"
+#include "ioexpander.h"
+
+/* Add after all include files */
+#include "ccgxxf.h"
+
+static inline int ccgxxf_read8(int ioex, int reg, int *data)
+{
+ return i2c_read8(ioex_config[ioex].i2c_host_port,
+ ioex_config[ioex].i2c_addr_flags, reg, data);
+}
+
+static inline int ccgxxf_update8(int ioex, int reg, uint8_t mask,
+ enum mask_update_action action)
+{
+ return i2c_update8(ioex_config[ioex].i2c_host_port,
+ ioex_config[ioex].i2c_addr_flags, reg, mask, action);
+}
+
+static inline int ccgxxf_write16(int ioex, uint16_t reg, uint16_t data)
+{
+ return i2c_write16(ioex_config[ioex].i2c_host_port,
+ ioex_config[ioex].i2c_addr_flags, reg, data);
+}
+
+static int ccgxxf_get_level(int ioex, int port, int mask, int *val)
+{
+ int rv;
+
+ rv = ccgxxf_read8(ioex, CCGXXF_REG_GPIO_STATUS(port), val);
+ if (!rv)
+ *val = !!(*val & mask);
+
+ return rv;
+}
+
+static int ccgxxf_set_level(int ioex, int port, int mask, int val)
+{
+ return ccgxxf_update8(ioex, CCGXXF_REG_GPIO_CONTROL(port), mask, val);
+}
+
+/*
+ * Following type of pins are supported
+ * - Output pins are supported with open-drain & pull-up
+ * - Input pins are supported with pull-up & pull-down
+ * - Analog pins
+ *
+ * TODO: Add support for 1.8V level GPIOs, after implementing it in the
+ * CCGXXF firmware.
+ */
+static int ccgxxf_set_flags_by_mask(int ioex, int port, int mask, int flags)
+{
+ uint16_t pin_mode;
+ int rv;
+
+ if (flags & GPIO_OUTPUT) {
+ if (flags & GPIO_OPEN_DRAIN) {
+ if (flags & GPIO_PULL_UP)
+ pin_mode = CCGXXF_GPIO_MODE_RES_UP;
+ else
+ pin_mode = CCGXXF_GPIO_MODE_OD_LOW;
+ } else {
+ pin_mode = CCGXXF_GPIO_MODE_STRONG;
+ }
+ } else if (flags & GPIO_INPUT) {
+ if (flags & GPIO_PULL_UP) {
+ pin_mode = CCGXXF_GPIO_MODE_RES_UP;
+ flags |= GPIO_HIGH;
+ } else if (flags & GPIO_PULL_DOWN) {
+ pin_mode = CCGXXF_GPIO_MODE_RES_DWN;
+ flags |= GPIO_LOW;
+ } else {
+ pin_mode = CCGXXF_GPIO_MODE_HIZ_DIGITAL;
+ }
+ } else if (flags & GPIO_ANALOG) {
+ pin_mode = CCGXXF_GPIO_MODE_HIZ_ANALOG;
+ } else {
+ return EC_ERROR_INVAL;
+ }
+
+ pin_mode = port | (pin_mode << CCGXXF_GPIO_PIN_MODE_SHIFT) |
+ (mask << CCGXXF_GPIO_PIN_MASK_SHIFT);
+
+ /*
+ * Before setting the GPIO mode, initilaize the pins to default value
+ * to avoid spike on pins.
+ */
+ if (flags & (GPIO_HIGH | GPIO_LOW)) {
+ rv = ccgxxf_set_level(ioex, port, mask,
+ flags & GPIO_HIGH ? 1 : 0);
+ if (rv)
+ return rv;
+ }
+
+ return ccgxxf_write16(ioex, CCGXXF_REG_GPIO_MODE, pin_mode);
+}
+
+static int ccgxxf_get_flags_by_mask(int ioex, int port, int mask, int *flags)
+{
+ /* TODO: Add it after implementing in the CCGXXF firmware. */
+ return EC_SUCCESS;
+}
+
+static int ccgxxf_enable_interrupt(int ioex, int port, int mask, int enable)
+{
+ /* CCGXXF doesn't have interrupt capability on I/O expnader pins */
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int ccgxxf_init(int ioex)
+{
+ /* TCPC init of CCGXXF should handle initialization */
+ return EC_SUCCESS;
+}
+
+const struct ioexpander_drv ccgxxf_ioexpander_drv = {
+ .init = &ccgxxf_init,
+ .get_level = &ccgxxf_get_level,
+ .set_level = &ccgxxf_set_level,
+ .get_flags_by_mask = &ccgxxf_get_flags_by_mask,
+ .set_flags_by_mask = &ccgxxf_set_flags_by_mask,
+ .enable_interrupt = &ccgxxf_enable_interrupt,
+};