Commit 9c4a81af authored by Ye Li's avatar Ye Li Committed by Jason Liu

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: default avatarYe Li <ye.li@nxp.com>
parent 70ee33d7
......@@ -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
/*
* 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;
}
/*
* 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 */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment