summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCHLin <CHLIN56@nuvoton.com>2019-01-24 18:55:16 +0800
committerchrome-bot <chrome-bot@chromium.org>2019-04-26 04:19:13 -0700
commit1e72facd39a9e3cc93c95471f6f4ad36d53ed248 (patch)
treec8d7c9f04ffe775407e95deee3b6b710a62a0a1f
parent696d45e6306c7cdb40024b94ec7a16327001a038 (diff)
downloadchrome-ec-1e72facd39a9e3cc93c95471f6f4ad36d53ed248.tar.gz
driver: TCPM: nct38xx: add support for NCT38XX series chips
This commit adds the driver to support for Nuvoton TCPC NCT38XX series chips. BRANCH=none BUG=none TEST=No error for "make buildall" TEST=Apply this and related CLs.Test on a reworked yorp platform with NCTT38XX AIC card. PD Functions including source, sink, and ALT DP mode work fine. Change-Id: Ibd4d3faf29afa55b150971e2aef2587686f523d5 Signed-off-by: Amit Maoz <Amit.Maoz@nuvoton.com> Signed-off-by: CHLin <CHLIN56@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/1436658 Commit-Ready: Jett Rink <jettrink@chromium.org> Tested-by: CH Lin <chlin56@nuvoton.com> Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r--driver/build.mk1
-rw-r--r--driver/tcpm/nct38xx.c328
-rw-r--r--driver/tcpm/nct38xx.h37
-rw-r--r--driver/tcpm/tcpci.h19
4 files changed, 378 insertions, 7 deletions
diff --git a/driver/build.mk b/driver/build.mk
index 616bff8ef7..57b111f8c9 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -120,6 +120,7 @@ driver-$(CONFIG_USB_PD_TCPM_ANX7447)+=tcpm/anx7447.o
driver-$(CONFIG_USB_PD_TCPM_PS8751)+=tcpm/ps8xxx.o
driver-$(CONFIG_USB_PD_TCPM_PS8805)+=tcpm/ps8xxx.o
driver-$(CONFIG_USB_PD_TCPM_TUSB422)+=tcpm/tusb422.o
+driver-$(CONFIG_USB_PD_TCPM_NCT38XX)+=tcpm/nct38xx.o
# USB mux high-level driver
driver-$(CONFIG_USBC_SS_MUX)+=usb_mux.o
diff --git a/driver/tcpm/nct38xx.c b/driver/tcpm/nct38xx.c
new file mode 100644
index 0000000000..472af584ae
--- /dev/null
+++ b/driver/tcpm/nct38xx.c
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+
+/* Type-C port manager for Nuvoton NCT38XX. */
+
+#include "common.h"
+#include "console.h"
+#include "nct38xx.h"
+#include "tcpci.h"
+
+#if !defined(CONFIG_USB_PD_TCPM_TCPCI)
+#error "NCT38XX is using part of standard TCPCI control"
+#error "Please upgrade your board configuration"
+#endif
+
+#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
+
+#define POLARITY_NORMAL 0
+#define POLARITY_FLIPPED 1
+#define POLARITY_NONE 3
+
+static int cable_polarity[CONFIG_USB_PD_PORT_COUNT];
+static unsigned char txBuf[33];
+static unsigned char rxBuf[33];
+/* Save the selected rp value */
+static int selected_rp[CONFIG_USB_PD_PORT_COUNT];
+
+static int nct38xx_tcpm_init(int port)
+{
+ int rv = 0;
+ int reg;
+
+ cable_polarity[port] = POLARITY_NONE;
+
+ rv = tcpci_tcpm_init(port);
+ if (rv)
+ return rv;
+
+ /*
+ * Write to the CONTROL_OUT_EN register to enable:
+ * [6] - CONNDIREN : Connector direction indication output enable
+ * [2] - SNKEN : VBUS sink enable output enable
+ * [0] - SRCEN : VBUS source voltage enable output enable
+ */
+ reg = NCT38XX_REG_CTRL_OUT_EN_SRCEN |
+ NCT38XX_REG_CTRL_OUT_EN_SNKEN |
+ NCT38XX_REG_CTRL_OUT_EN_CONNDIREN;
+
+ rv = tcpc_write(port, NCT38XX_REG_CTRL_OUT_EN, reg);
+ if (rv)
+ return rv;
+
+ /* Disable OVP */
+ rv = tcpc_read(port, TCPC_REG_FAULT_CTRL, &reg);
+ if (rv)
+ return rv;
+ reg = reg | TCPC_REG_FAULT_CTRL_VBUS_OVP_FAULT_DIS;
+ rv = tcpc_write(port, TCPC_REG_FAULT_CTRL, reg);
+ if (rv)
+ return rv;
+
+ /* Enable VBus monitor */
+ rv = tcpc_read(port, TCPC_REG_POWER_CTRL, &reg);
+ if (rv)
+ return rv;
+ reg = reg & ~TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS;
+ rv = tcpc_write(port, TCPC_REG_POWER_CTRL, reg);
+ if (rv)
+ return rv;
+
+ /* Start VBus monitor */
+ rv = tcpc_write(port, TCPC_REG_COMMAND,
+ TCPC_REG_COMMAND_ENABLE_VBUS_DETECT);
+ return rv;
+}
+
+static int tcpci_nct38xx_select_rp_value(int port, int rp)
+{
+ selected_rp[port] = rp;
+ return EC_SUCCESS;
+}
+
+static int auto_discharge_disconnect(int port, int enable)
+{
+ int reg, rv;
+
+ rv = tcpc_read(port, TCPC_REG_POWER_CTRL, &reg);
+ if (rv)
+ return rv;
+
+ if (enable)
+ reg = reg | TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT;
+ else
+ reg = reg & ~TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT;
+ rv = tcpc_write(port, TCPC_REG_POWER_CTRL, reg);
+ return rv;
+
+}
+
+static int tcpci_nct38xx_check_cable_polarity(int port)
+{
+ int cc, rv;
+
+ /* Try to check the polarity */
+ rv = tcpc_read(port, TCPC_REG_CC_STATUS, &cc);
+ if (rv)
+ return rv;
+
+ if (TCPC_REG_CC_STATUS_TERM(cc)) {
+ /* TCPC is presenting RD (Sink mode) */
+ if ((TCPC_REG_CC_STATUS_CC1(cc) != TYPEC_CC_VOLT_OPEN) &&
+ (TCPC_REG_CC_STATUS_CC2(cc) == TYPEC_CC_VOLT_OPEN)) {
+ /* CC1 active && CC2 open */
+ cable_polarity[port] = POLARITY_NORMAL;
+ }
+ if ((TCPC_REG_CC_STATUS_CC1(cc) == TYPEC_CC_VOLT_OPEN) &&
+ (TCPC_REG_CC_STATUS_CC2(cc) != TYPEC_CC_VOLT_OPEN)) {
+ /* CC1 open && CC2 active */
+ cable_polarity[port] = POLARITY_FLIPPED;
+ }
+ } else {
+ /* TCPC is presenting RP (Source mode) */
+ if ((TCPC_REG_CC_STATUS_CC1(cc) == TYPEC_CC_VOLT_RD) &&
+ (TCPC_REG_CC_STATUS_CC2(cc) != TYPEC_CC_VOLT_RD)) {
+ /* CC1 active && CC2 open */
+ cable_polarity[port] = POLARITY_NORMAL;
+ }
+ if ((TCPC_REG_CC_STATUS_CC1(cc) != TYPEC_CC_VOLT_RD) &&
+ (TCPC_REG_CC_STATUS_CC2(cc) == TYPEC_CC_VOLT_RD)) {
+ /* CC1 open && CC2 active */
+ cable_polarity[port] = POLARITY_FLIPPED;
+ }
+ }
+ return rv;
+}
+
+ /*
+ * TODO(crbug.com/951681): This code can be simplified once that bug is fixed.
+ */
+static int tcpci_nct38xx_set_cc(int port, int pull)
+{
+
+ int rv;
+
+ if (cable_polarity[port] == POLARITY_NONE) {
+ rv = tcpci_nct38xx_check_cable_polarity(port);
+ if (rv)
+ return rv;
+ }
+
+ if (cable_polarity[port] == POLARITY_NORMAL) {
+ rv = tcpc_write(port, TCPC_REG_ROLE_CTRL,
+ TCPC_REG_ROLE_CTRL_SET(0, selected_rp[port],
+ pull, TYPEC_CC_OPEN));
+ } else if (cable_polarity[port] == POLARITY_FLIPPED) {
+ rv = tcpc_write(port, TCPC_REG_ROLE_CTRL,
+ TCPC_REG_ROLE_CTRL_SET(0, selected_rp[port],
+ TYPEC_CC_OPEN, pull));
+ } else {
+ return -1;
+ }
+
+ return rv;
+}
+
+static int tcpci_nct38xx_get_cc(int port, int *cc1, int *cc2)
+{
+ int rv;
+ int rc;
+
+ rv = tcpc_read(port, TCPC_REG_ROLE_CTRL, &rc);
+ if (rv)
+ return rv;
+
+ rv = tcpci_tcpm_get_cc(port, cc1, cc2);
+ if (rv)
+ return rv;
+
+ if (!TCPC_REG_ROLE_CTRL_DRP(rc)) {
+ if ((*cc1 != TYPEC_CC_VOLT_OPEN) &&
+ (TCPC_REG_ROLE_CTRL_CC1(rc) == TYPEC_CC_RD))
+ *cc1 |= 0x4;
+ if ((*cc2 != TYPEC_CC_VOLT_OPEN) &&
+ (TCPC_REG_ROLE_CTRL_CC2(rc) == TYPEC_CC_RD))
+ *cc2 |= 0x04;
+ }
+
+ return rv;
+}
+
+int tcpci_nct38xx_drp_toggle(int port)
+{
+ int rv;
+
+ cable_polarity[port] = POLARITY_NONE;
+
+ /*
+ * The port was disconnected so it is probably a good place to set
+ * auto-discharge-disconnect to '0'
+ *
+ * TODO(crbug.com/951683: this should be removed when common code adds
+ * auto discharge.
+ */
+ rv = auto_discharge_disconnect(port, 0);
+ if (rv)
+ return rv;
+
+ return tcpci_tcpc_drp_toggle(port);
+
+}
+
+int tcpci_nct38xx_set_polarity(int port, int polarity)
+{
+ int rv, reg;
+
+ rv = tcpc_read(port, TCPC_REG_TCPC_CTRL, &reg);
+ if (rv)
+ return rv;
+
+ reg = polarity ? (reg | TCPC_REG_TCPC_CTRL_SET(1)) :
+ (reg & ~TCPC_REG_TCPC_CTRL_SET(1));
+
+ rv = tcpc_write(port, TCPC_REG_TCPC_CTRL, reg);
+ if (rv)
+ return rv;
+
+ /*
+ * Polarity is set after connection so it is probably a good time to set
+ * auto-discharge-disconnect to '1'
+ */
+ rv = auto_discharge_disconnect(port, 1);
+ return rv;
+}
+
+int tcpci_nct38xx_transmit(int port, enum tcpm_transmit_type type,
+ uint16_t header, const uint32_t *data)
+{
+ int rv, num_obj_byte;
+
+ num_obj_byte = PD_HEADER_CNT(header) * 4;
+
+ txBuf[0] = num_obj_byte + 2;
+
+ txBuf[1] = (unsigned char)(header & 0xFF);
+ txBuf[2] = (unsigned char)((header >> 8) & 0xFF);
+
+ if (num_obj_byte) {
+ uint32_t *buf_ptr;
+
+ buf_ptr = (uint32_t *)&txBuf[3];
+ memcpy(buf_ptr, data, num_obj_byte);
+ }
+
+ /* total write size = size header (1 byte) + message size */
+ rv = tcpc_write_block(port, TCPC_REG_TX_BYTE_CNT,
+ (const uint8_t *)txBuf, txBuf[0] + 1);
+
+ rv = tcpc_write(port, TCPC_REG_TRANSMIT, TCPC_REG_TRANSMIT_SET(type));
+ return rv;
+}
+
+static int tcpci_nct38xx_get_message_raw(int port, uint32_t *payload, int *head)
+{
+ int rv, cnt, num_obj_byte;
+
+ rv = tcpc_read(port, TCPC_REG_RX_BYTE_CNT, &cnt);
+
+ if (rv != EC_SUCCESS || cnt < 3) {
+ rv = EC_ERROR_UNKNOWN;
+ goto clear;
+ }
+
+ rv = tcpc_read_block(port, TCPC_REG_RX_BYTE_CNT, rxBuf, cnt + 1);
+ if (rv != EC_SUCCESS)
+ goto clear;
+
+ *head = *(int *)&rxBuf[2];
+ num_obj_byte = PD_HEADER_CNT(*head) * 4;
+
+ if (num_obj_byte) {
+ uint32_t *buf_ptr;
+
+ buf_ptr = (uint32_t *)&rxBuf[4];
+ memcpy(payload, buf_ptr, num_obj_byte);
+ }
+
+clear:
+ /* Read complete, clear RX status alert bit */
+ tcpc_write16(port, TCPC_REG_ALERT, TCPC_REG_ALERT_RX_STATUS);
+
+ return rv;
+}
+
+const struct tcpm_drv nct38xx_tcpm_drv = {
+ .init = &nct38xx_tcpm_init,
+ .release = &tcpci_tcpm_release,
+ .get_cc = &tcpci_nct38xx_get_cc,
+#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
+ .get_vbus_level = &tcpci_tcpm_get_vbus_level,
+#endif
+ .select_rp_value = &tcpci_nct38xx_select_rp_value,
+ .set_cc = &tcpci_nct38xx_set_cc,
+ .set_polarity = &tcpci_nct38xx_set_polarity,
+ .set_vconn = &tcpci_tcpm_set_vconn,
+ .set_msg_header = &tcpci_tcpm_set_msg_header,
+ .set_rx_enable = &tcpci_tcpm_set_rx_enable,
+ .get_message_raw = &tcpci_nct38xx_get_message_raw,
+ .transmit = &tcpci_nct38xx_transmit,
+ .tcpc_alert = &tcpci_tcpc_alert,
+#ifdef CONFIG_USB_PD_DISCHARGE_TCPC
+ .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus,
+#endif
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ .drp_toggle = &tcpci_nct38xx_drp_toggle,
+#endif
+#ifdef CONFIG_USBC_PPC
+ .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl,
+ .set_src_ctrl = &tcpci_tcpm_set_src_ctrl,
+#endif
+ .get_chip_info = &tcpci_get_chip_info,
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ .enter_low_power_mode = &tcpci_enter_low_power_mode,
+#endif
+};
diff --git a/driver/tcpm/nct38xx.h b/driver/tcpm/nct38xx.h
new file mode 100644
index 0000000000..367a30d17a
--- /dev/null
+++ b/driver/tcpm/nct38xx.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/* Nuvoton Type-C port controller */
+
+#ifndef __CROS_EC_USB_PD_TCPM_NCT38XX_H
+#define __CROS_EC_USB_PD_TCPM_NCT38XX_H
+
+/* I2C interface */
+#define NCT38xx_I2C_ADDR1_1 0xE0
+#define NCT38xx_I2C_ADDR1_2 0xE2
+#define NCT38xx_I2C_ADDR1_3 0xE4
+#define NCT38xx_I2C_ADDR1_4 0xE6
+
+#define NCT38xx_I2C_ADDR2_1 0xE8
+#define NCT38xx_I2C_ADDR2_2 0xEA
+#define NCT38xx_I2C_ADDR2_3 0xEC
+#define NCT38xx_I2C_ADDR2_4 0xEE
+
+#define NCT38XX_REG_VENDOR_ID_L 0x00
+#define NCT38XX_REG_VENDOR_ID_H 0x01
+#define NCT38XX_VENDOR_ID 0x0416
+
+#define NCT38XX_PRODUCT_ID 0xC301
+
+#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)
+#define NCT38XX_REG_CTRL_OUT_EN_SNKEN (1 << 2)
+#define NCT38XX_REG_CTRL_OUT_EN_CONNDIREN (1 << 6)
+
+extern const struct tcpm_drv nct38xx_tcpm_drv;
+
+#endif /* defined(__CROS_EC_USB_PD_TCPM_NCT38XX_H) */
diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h
index 600e28f284..674d227dca 100644
--- a/driver/tcpm/tcpci.h
+++ b/driver/tcpm/tcpci.h
@@ -61,8 +61,12 @@
#define TCPC_REG_ROLE_CTRL_CC1(reg) ((reg) & 0x3)
#define TCPC_REG_FAULT_CTRL 0x1b
+#define TCPC_REG_FAULT_CTRL_VBUS_OVP_FAULT_DIS BIT(1)
+
#define TCPC_REG_POWER_CTRL 0x1c
-#define TCPC_REG_POWER_CTRL_FORCE_DISCHARGE BIT(2)
+#define TCPC_REG_POWER_CTRL_FORCE_DISCHARGE BIT(2)
+#define TCPC_REG_POWER_CTRL_AUTO_DISCHARGE_DISCONNECT BIT(4)
+#define TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS BIT(6)
#define TCPC_REG_POWER_CTRL_SET(vconn) (vconn)
#define TCPC_REG_POWER_CTRL_VCONN(reg) ((reg) & 0x1)
@@ -82,12 +86,13 @@
#define TCPC_REG_FAULT_STATUS 0x1f
#define TCPC_REG_COMMAND 0x23
-#define TCPC_REG_COMMAND_SNK_CTRL_LOW 0x44
-#define TCPC_REG_COMMAND_SNK_CTRL_HIGH 0x55
-#define TCPC_REG_COMMAND_SRC_CTRL_LOW 0x66
-#define TCPC_REG_COMMAND_SRC_CTRL_HIGH 0x77
-#define TCPC_REG_COMMAND_LOOK4CONNECTION 0x99
-#define TCPC_REG_COMMAND_I2CIDLE 0xFF
+#define TCPC_REG_COMMAND_ENABLE_VBUS_DETECT 0x33
+#define TCPC_REG_COMMAND_SNK_CTRL_LOW 0x44
+#define TCPC_REG_COMMAND_SNK_CTRL_HIGH 0x55
+#define TCPC_REG_COMMAND_SRC_CTRL_LOW 0x66
+#define TCPC_REG_COMMAND_SRC_CTRL_HIGH 0x77
+#define TCPC_REG_COMMAND_LOOK4CONNECTION 0x99
+#define TCPC_REG_COMMAND_I2CIDLE 0xFF
#define TCPC_REG_DEV_CAP_1 0x24
#define TCPC_REG_DEV_CAP_2 0x26