summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2017-09-12 04:38:00 -0500
committerYe Li <ye.li@nxp.com>2017-09-14 20:52:33 -0500
commitc4ef7812cfa959b9ac914c58806487a0efd035b6 (patch)
treec73dd72b53fa87f7bb27d31d7b8cd2e7f8ee6a51
parent6c0fd30e444d23d71c2cfc35924f3d23d72c0735 (diff)
MLK-16431-2 imx8qxp_mek: Add driver for USB typec port controller (TCPC)
Add an simple driver for USB typec port controller in freescale common codes. The functions in this driver help to initialize the TCPC, set and work with fixed DFP role. Will improve it later to support UFP and move to driver directory. Signed-off-by: Ye Li <ye.li@nxp.com>
-rw-r--r--board/freescale/common/Makefile1
-rw-r--r--board/freescale/common/tcpc.c223
-rw-r--r--board/freescale/common/tcpc.h88
3 files changed, 312 insertions, 0 deletions
diff --git a/board/freescale/common/Makefile b/board/freescale/common/Makefile
index 5d02216473..e3917181fc 100644
--- a/board/freescale/common/Makefile
+++ b/board/freescale/common/Makefile
@@ -85,4 +85,5 @@ obj-$(CONFIG_CMD_ESBC_VALIDATE) += fsl_validate.o cmd_esbc_validate.o
endif
obj-$(CONFIG_CHAIN_OF_TRUST) += fsl_chain_of_trust.o
+obj-$(CONFIG_USB_XHCI_IMX8) += tcpc.o
endif
diff --git a/board/freescale/common/tcpc.c b/board/freescale/common/tcpc.c
new file mode 100644
index 0000000000..5931cb1f11
--- /dev/null
+++ b/board/freescale/common/tcpc.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <i2c.h>
+#include <time.h>
+#include "tcpc.h"
+
+int tcpc_set_cc_to_source(struct udevice *i2c_dev)
+{
+ uint8_t valb;
+ int err;
+
+ valb = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+ (TCPC_ROLE_CTRL_RP_VAL_DEF <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+
+ err = dm_i2c_write(i2c_dev, TCPC_ROLE_CTRL, &valb, 1);
+ if (err)
+ printf("%s dm_i2c_write failed, err %d\n", __func__, err);
+ return err;
+}
+
+int tcpc_set_plug_orientation(struct udevice *i2c_dev, enum typec_cc_polarity polarity)
+{
+ uint8_t valb;
+ int err;
+
+ err = dm_i2c_read(i2c_dev, TCPC_TCPC_CTRL, &valb, 1);
+ if (err) {
+ printf("%s dm_i2c_read failed, err %d\n", __func__, err);
+ return -EIO;
+ }
+
+ if (polarity == TYPEC_POLARITY_CC2)
+ valb |= TCPC_TCPC_CTRL_ORIENTATION;
+ else
+ valb &= ~TCPC_TCPC_CTRL_ORIENTATION;
+
+ err = dm_i2c_write(i2c_dev, TCPC_TCPC_CTRL, &valb, 1);
+ if (err) {
+ printf("%s dm_i2c_write failed, err %d\n", __func__, err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int tcpc_get_cc_polarity(struct udevice *i2c_dev, enum typec_cc_polarity *polarity)
+{
+
+ uint8_t valb;
+ int err;
+
+ err = dm_i2c_read(i2c_dev, TCPC_CC_STATUS, &valb, 1);
+ if (err) {
+ printf("%s dm_i2c_read failed, err %d\n", __func__, err);
+ return -EIO;
+ }
+
+ debug("cc status 0x%x\n", valb);
+
+ /* Set to Rp at default */
+ if (valb & TCPC_CC_STATUS_TERM)
+ return -EPERM;
+
+ if (((valb >> TCPC_CC_STATUS_CC1_SHIFT) & TCPC_CC_STATUS_CC1_MASK) == 0x2)
+ *polarity = TYPEC_POLARITY_CC1;
+ else if (((valb >> TCPC_CC_STATUS_CC2_SHIFT) & TCPC_CC_STATUS_CC2_MASK) == 0x2)
+ *polarity = TYPEC_POLARITY_CC2;
+ else
+ return -EFAULT;
+ return 0;
+}
+
+int tcpc_clear_alert(struct udevice *i2c_dev, uint16_t clear_mask)
+{
+ int err;
+ err = dm_i2c_write(i2c_dev, TCPC_ALERT, (const uint8_t *)&clear_mask, 2);
+ if (err) {
+ printf("%s dm_i2c_write failed, err %d\n", __func__, err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int tcpc_send_command(struct udevice *i2c_dev, uint8_t command)
+{
+ int err;
+ err = dm_i2c_write(i2c_dev, TCPC_COMMAND, (const uint8_t *)&command, 1);
+ if (err) {
+ printf("%s dm_i2c_write failed, err %d\n", __func__, err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int tcpc_polling_reg(struct udevice *i2c_dev, uint8_t reg,
+ uint8_t reg_width, uint16_t mask, uint16_t value, ulong timeout_ms)
+{
+ uint16_t val = 0;
+ int err;
+ ulong start;
+
+ debug("%s reg 0x%x, mask 0x%x, value 0x%x\n", __func__, reg, mask, value);
+
+ /* TCPC registers is 8 bits or 16 bits */
+ if (reg_width != 1 && reg_width != 2)
+ return -EINVAL;
+
+ start = get_timer(0); /* Get current timestamp */
+ do {
+ err = dm_i2c_read(i2c_dev, reg, (uint8_t *)&val, reg_width);
+ if (err)
+ return -EIO;
+
+ debug("val = 0x%x\n", val);
+
+ if ((val & mask) == value)
+ return 0;
+ } while (get_timer(0) < (start + timeout_ms));
+
+ return -ETIME;
+}
+
+int tcpc_init(struct udevice *i2c_dev)
+{
+ int ret;
+
+ /* Check the Initialization Status bit in 1s */
+ ret = tcpc_polling_reg(i2c_dev, TCPC_POWER_STATUS, 1, TCPC_POWER_STATUS_UNINIT, 0, 1000);
+ if (ret) {
+ printf("%s: Polling TCPC POWER STATUS Initialization Status bit failed, ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ tcpc_clear_alert(i2c_dev, 0xffff);
+
+ return 0;
+}
+
+int tcpc_setup_dfp_mode(struct udevice *i2c_dev, ss_mux_sel ss_sel_func)
+{
+ enum typec_cc_polarity pol;
+ int ret;
+
+ tcpc_set_cc_to_source(i2c_dev);
+
+ ret = tcpc_send_command(i2c_dev, TCPC_CMD_LOOK4CONNECTION);
+ if (ret)
+ return ret;
+
+ /* At least wait tCcStatusDelay + tTCPCFilter + tCcTCPCSampleRate (max) = 200us + 500us + ?ms
+ * PTN5110 datasheet does not contain the sample rate value, according other productions,
+ * the sample rate is at ms level, about 2 ms -10ms. So wait 100ms should be enough.
+ */
+ mdelay(100);
+
+ ret = tcpc_polling_reg(i2c_dev, TCPC_ALERT, 2, TCPC_ALERT_CC_STATUS, TCPC_ALERT_CC_STATUS, 100);
+ if (ret) {
+ printf("%s: Polling ALERT register, TCPC_ALERT_CC_STATUS bit failed, ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = tcpc_get_cc_polarity(i2c_dev, &pol);
+ tcpc_clear_alert(i2c_dev, TCPC_ALERT_CC_STATUS);
+
+ if (!ret) {
+ if (pol == TYPEC_POLARITY_CC1)
+ debug("polarity cc1\n");
+ else
+ debug("polarity cc2\n");
+
+ if (ss_sel_func)
+ ss_sel_func(pol);
+
+ ret = tcpc_set_plug_orientation(i2c_dev, pol);
+ if (ret)
+ return ret;
+
+ /* Disable sink vbus */
+ ret = tcpc_send_command(i2c_dev, TCPC_CMD_DISABLE_SINK_VBUS);
+ if (ret)
+ return ret;
+
+ /* Enable source vbus default voltage */
+ ret = tcpc_send_command(i2c_dev, TCPC_CMD_SRC_VBUS_DEFAULT);
+ if (ret)
+ return ret;
+
+ /* The max vbus on time is 200ms, we add margin 100ms */
+ mdelay(300);
+
+ }
+
+ return 0;
+}
+
+int tcpc_disable_vbus(struct udevice *i2c_dev)
+{
+ int ret;
+
+ /* Disable VBUS*/
+ ret = tcpc_send_command(i2c_dev, TCPC_CMD_DISABLE_SINK_VBUS);
+ if (ret)
+ return ret;
+
+ ret = tcpc_send_command(i2c_dev, TCPC_CMD_DISABLE_SRC_VBUS);
+ if (ret)
+ return ret;
+
+ /* The max vbus off time is 0.5ms, we add margin 0.5 ms */
+ mdelay(1);
+
+ return 0;
+}
diff --git a/board/freescale/common/tcpc.h b/board/freescale/common/tcpc.h
new file mode 100644
index 0000000000..ad981d1820
--- /dev/null
+++ b/board/freescale/common/tcpc.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __TCPCI_H
+#define __TCPCI_H
+
+#include <dm.h>
+
+#define TCPC_TCPC_CTRL 0x19
+#define TCPC_TCPC_CTRL_ORIENTATION BIT(0)
+
+#define TCPC_CC_STATUS 0x1d
+#define TCPC_CC_STATUS_TERM BIT(4)
+#define TCPC_CC_STATUS_CC2_SHIFT 2
+#define TCPC_CC_STATUS_CC2_MASK 0x3
+#define TCPC_CC_STATUS_CC1_SHIFT 0
+#define TCPC_CC_STATUS_CC1_MASK 0x3
+
+#define TCPC_ROLE_CTRL 0x1a
+#define TCPC_ROLE_CTRL_DRP BIT(6)
+#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4
+#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3
+#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0
+#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1
+#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2
+#define TCPC_ROLE_CTRL_CC2_SHIFT 2
+#define TCPC_ROLE_CTRL_CC2_MASK 0x3
+#define TCPC_ROLE_CTRL_CC1_SHIFT 0
+#define TCPC_ROLE_CTRL_CC1_MASK 0x3
+#define TCPC_ROLE_CTRL_CC_RA 0x0
+#define TCPC_ROLE_CTRL_CC_RP 0x1
+#define TCPC_ROLE_CTRL_CC_RD 0x2
+#define TCPC_ROLE_CTRL_CC_OPEN 0x3
+
+#define TCPC_COMMAND 0x23
+#define TCPC_CMD_WAKE_I2C 0x11
+#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22
+#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33
+#define TCPC_CMD_DISABLE_SINK_VBUS 0x44
+#define TCPC_CMD_SINK_VBUS 0x55
+#define TCPC_CMD_DISABLE_SRC_VBUS 0x66
+#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77
+#define TCPC_CMD_SRC_VBUS_HIGH 0x88
+#define TCPC_CMD_LOOK4CONNECTION 0x99
+#define TCPC_CMD_RXONEMORE 0xAA
+#define TCPC_CMD_I2C_IDLE 0xFF
+
+#define TCPC_ALERT 0x10
+#define TCPC_ALERT_VBUS_DISCNCT BIT(11)
+#define TCPC_ALERT_RX_BUF_OVF BIT(10)
+#define TCPC_ALERT_FAULT BIT(9)
+#define TCPC_ALERT_V_ALARM_LO BIT(8)
+#define TCPC_ALERT_V_ALARM_HI BIT(7)
+#define TCPC_ALERT_TX_SUCCESS BIT(6)
+#define TCPC_ALERT_TX_DISCARDED BIT(5)
+#define TCPC_ALERT_TX_FAILED BIT(4)
+#define TCPC_ALERT_RX_HARD_RST BIT(3)
+#define TCPC_ALERT_RX_STATUS BIT(2)
+#define TCPC_ALERT_POWER_STATUS BIT(1)
+#define TCPC_ALERT_CC_STATUS BIT(0)
+
+#define TCPC_POWER_STATUS 0x1e
+#define TCPC_POWER_STATUS_UNINIT BIT(6)
+#define TCPC_POWER_STATUS_VBUS_DET BIT(3)
+#define TCPC_POWER_STATUS_VBUS_PRES BIT(2)
+
+enum typec_cc_polarity {
+ TYPEC_POLARITY_CC1,
+ TYPEC_POLARITY_CC2,
+};
+
+typedef void (*ss_mux_sel)(enum typec_cc_polarity pol);
+
+int tcpc_set_cc_to_source(struct udevice *i2c_dev);
+int tcpc_set_plug_orientation(struct udevice *i2c_dev, enum typec_cc_polarity polarity);
+int tcpc_get_cc_polarity(struct udevice *i2c_dev, enum typec_cc_polarity *polarity);
+int tcpc_clear_alert(struct udevice *i2c_dev, uint16_t clear_mask);
+int tcpc_send_command(struct udevice *i2c_dev, uint8_t command);
+int tcpc_polling_reg(struct udevice *i2c_dev, uint8_t reg,
+ uint8_t reg_width, uint16_t mask, uint16_t value, ulong timeout_ms);
+int tcpc_setup_dfp_mode(struct udevice *i2c_dev, ss_mux_sel ss_sel_func);
+int tcpc_disable_vbus(struct udevice *i2c_dev);
+int tcpc_init(struct udevice *i2c_dev);
+
+#endif /* __TCPCI_H */