From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 07:24:11 +0000
Subject: [PATCH] add stmac read mac form eeprom
---
kernel/drivers/usb/typec/tcpm/tcpci.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 284 insertions(+), 26 deletions(-)
diff --git a/kernel/drivers/usb/typec/tcpm/tcpci.c b/kernel/drivers/usb/typec/tcpm/tcpci.c
index cd2dc47..17cea37 100644
--- a/kernel/drivers/usb/typec/tcpm/tcpci.c
+++ b/kernel/drivers/usb/typec/tcpm/tcpci.c
@@ -15,10 +15,24 @@
#include <linux/usb/pd.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec.h>
+#include <trace/hooks/typec.h>
#include "tcpci.h"
-#define PD_RETRY_COUNT 3
+#define PD_RETRY_COUNT_DEFAULT 3
+#define PD_RETRY_COUNT_3_0_OR_HIGHER 2
+#define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500
+#define VSINKPD_MIN_IR_DROP_MV 750
+#define VSRC_NEW_MIN_PERCENT 95
+#define VSRC_VALID_MIN_MV 500
+#define VPPS_NEW_MIN_PERCENT 95
+#define VPPS_VALID_MIN_MV 100
+#define VSINKDISCONNECT_PD_MIN_PERCENT 90
+
+#define tcpc_presenting_rd(reg, cc) \
+ (!(TCPC_ROLE_CTRL_DRP & (reg)) && \
+ (((reg) & (TCPC_ROLE_CTRL_## cc ##_MASK << TCPC_ROLE_CTRL_## cc ##_SHIFT)) == \
+ (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_## cc ##_SHIFT)))
#define tcpc_presenting_cc1_rd(reg) \
(!(TCPC_ROLE_CTRL_DRP & (reg)) && \
@@ -47,6 +61,12 @@
struct tcpci_data data;
};
+struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci)
+{
+ return tcpci->port;
+}
+EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port);
+
static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
{
return container_of(tcpc, struct tcpci, tcpc);
@@ -65,8 +85,24 @@
static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ bool vconn_pres;
+ enum typec_cc_polarity polarity = TYPEC_POLARITY_CC1;
unsigned int reg;
int ret;
+
+ ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®);
+ if (ret < 0)
+ return ret;
+
+ vconn_pres = !!(reg & TCPC_POWER_STATUS_VCONN_PRES);
+ if (vconn_pres) {
+ ret = regmap_read(tcpci->regmap, TCPC_TCPC_CTRL, ®);
+ if (ret < 0)
+ return ret;
+
+ if (reg & TCPC_TCPC_CTRL_ORIENTATION)
+ polarity = TYPEC_POLARITY_CC2;
+ }
switch (cc) {
case TYPEC_CC_RA:
@@ -102,11 +138,48 @@
break;
}
+ if (vconn_pres) {
+ if (polarity == TYPEC_POLARITY_CC2) {
+ reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
+ reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT);
+ } else {
+ reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+ }
+ }
+
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
if (ret < 0)
return ret;
return 0;
+}
+
+static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc,
+ enum typec_cc_polarity polarity)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on
+ * disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2.
+ */
+ if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >>
+ TCPC_ROLE_CTRL_CC2_SHIFT) !=
+ ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >>
+ TCPC_ROLE_CTRL_CC1_SHIFT))
+ return 0;
+
+ return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ?
+ TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT :
+ TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT,
+ TCPC_ROLE_CTRL_CC_OPEN);
}
static int tcpci_start_toggling(struct tcpc_dev *tcpc,
@@ -116,14 +189,17 @@
int ret;
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = TCPC_ROLE_CTRL_DRP;
+ int override_toggling = 0;
if (port_type != TYPEC_PORT_DRP)
return -EOPNOTSUPP;
/* Handle vendor drp toggling */
if (tcpci->data->start_drp_toggling) {
+ trace_android_vh_typec_tcpci_override_toggling(tcpci, tcpci->data,
+ &override_toggling);
ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
- if (ret < 0)
+ if (ret < 0 || override_toggling)
return ret;
}
@@ -166,7 +242,7 @@
case 0x3:
if (sink)
return TYPEC_CC_RP_3_0;
- /* fall through */
+ fallthrough;
case 0x0:
default:
return TYPEC_CC_OPEN;
@@ -191,11 +267,11 @@
*cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
TCPC_CC_STATUS_CC1_MASK,
reg & TCPC_CC_STATUS_TERM ||
- tcpc_presenting_cc1_rd(role_control));
+ tcpc_presenting_rd(role_control, CC1));
*cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
TCPC_CC_STATUS_CC2_MASK,
reg & TCPC_CC_STATUS_TERM ||
- tcpc_presenting_cc2_rd(role_control));
+ tcpc_presenting_rd(role_control, CC2));
return 0;
}
@@ -260,6 +336,14 @@
TCPC_TCPC_CTRL_ORIENTATION : 0);
}
+static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+
+ if (tcpci->data->set_partner_usb_comm_capable)
+ tcpci->data->set_partner_usb_comm_capable(tcpci, tcpci->data, capable);
+}
+
static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -275,6 +359,92 @@
return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL,
TCPC_POWER_CTRL_VCONN_ENABLE,
enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
+}
+
+static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(dev);
+ int ret;
+
+ ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE,
+ enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0);
+ return ret;
+}
+
+static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode,
+ bool pps_active, u32 requested_vbus_voltage_mv)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(dev);
+ unsigned int pwr_ctrl, threshold = 0;
+ int ret;
+
+ /*
+ * Indicates that vbus is going to go away due PR_SWAP, hard reset etc.
+ * Do not discharge vbus here.
+ */
+ if (requested_vbus_voltage_mv == 0)
+ goto write_thresh;
+
+ ret = regmap_read(tcpci->regmap, TCPC_POWER_CTRL, &pwr_ctrl);
+ if (ret < 0)
+ return ret;
+
+ if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) {
+ /* To prevent disconnect when the source is fast role swap is capable. */
+ threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV;
+ } else if (mode == TYPEC_PWR_MODE_PD) {
+ if (pps_active)
+ threshold = ((VPPS_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) -
+ VSINKPD_MIN_IR_DROP_MV - VPPS_VALID_MIN_MV) *
+ VSINKDISCONNECT_PD_MIN_PERCENT / 100;
+ else
+ threshold = ((VSRC_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) -
+ VSINKPD_MIN_IR_DROP_MV - VSRC_VALID_MIN_MV) *
+ VSINKDISCONNECT_PD_MIN_PERCENT / 100;
+ } else {
+ /* 3.5V for non-pd sink */
+ threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV;
+ }
+
+ threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB_MV;
+
+ if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX)
+ return -EINVAL;
+
+write_thresh:
+ return tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold);
+}
+
+static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(dev);
+ int ret;
+
+ /* To prevent disconnect during FRS, set disconnect threshold to 3.5V */
+ ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ?
+ TCPC_FAST_ROLE_SWAP_EN : 0);
+
+ return ret;
+}
+
+static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(dev);
+
+ if (tcpci->data->frs_sourcing_vbus)
+ tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data);
+}
+
+static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+
+ return regmap_update_bits(tcpci->regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_BIST_TM,
+ enable ? TCPC_TCPC_CTRL_BIST_TM : 0);
}
static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
@@ -315,7 +485,11 @@
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
- int ret;
+ int ret, vbus, bypass = 0;
+
+ trace_android_rvh_typec_tcpci_get_vbus(tcpci, tcpci->data, &vbus, &bypass);
+ if (bypass)
+ return vbus;
ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®);
if (ret < 0)
@@ -324,10 +498,39 @@
return !!(reg & TCPC_POWER_STATUS_VBUS_PRES);
}
+static int tcpci_check_contaminant(struct tcpc_dev *tcpc)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ int ret = 0;
+
+ trace_android_rvh_typec_tcpci_chk_contaminant(tcpci, tcpci->data, &ret);
+ return ret;
+}
+
+static bool tcpci_is_vbus_vsafe0v(struct tcpc_dev *tcpc)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, ®);
+ if (ret < 0)
+ return false;
+
+ return !!(reg & TCPC_EXTENDED_STATUS_VSAFE0V);
+}
+
static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
int ret;
+
+ if (tcpci->data->set_vbus) {
+ ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink);
+ /* Bypass when ret > 0 */
+ if (ret != 0)
+ return ret < 0 ? ret : 0;
+ }
/* Disable both source and sink first before enabling anything */
@@ -362,9 +565,8 @@
return 0;
}
-static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
- enum tcpm_transmit_type type,
- const struct pd_message *msg)
+static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type,
+ const struct pd_message *msg, unsigned int negotiated_rev)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
u16 header = msg ? le16_to_cpu(msg->header) : 0;
@@ -372,23 +574,49 @@
int ret;
cnt = msg ? pd_header_cnt(header) * 4 : 0;
- ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
- if (ret < 0)
- return ret;
+ /**
+ * TCPCI spec forbids direct access of TCPC_TX_DATA.
+ * But, since some of the chipsets offer this capability,
+ * it's fair to support both.
+ */
+ if (tcpci->data->TX_BUF_BYTE_x_hidden) {
+ u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,};
+ u8 pos = 0;
- ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
- if (ret < 0)
- return ret;
+ /* Payload + header + TCPC_TX_BYTE_CNT */
+ buf[pos++] = cnt + 2;
- if (cnt > 0) {
- ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
- &msg->payload, cnt);
+ if (msg)
+ memcpy(&buf[pos], &msg->header, sizeof(msg->header));
+
+ pos += sizeof(header);
+
+ if (cnt > 0)
+ memcpy(&buf[pos], msg->payload, cnt);
+
+ pos += cnt;
+ ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos);
if (ret < 0)
return ret;
+ } else {
+ ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
+ if (ret < 0)
+ return ret;
+
+ ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
+ if (ret < 0)
+ return ret;
+
+ if (cnt > 0) {
+ ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt);
+ if (ret < 0)
+ return ret;
+ }
}
- reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
- (type << TCPC_TRANSMIT_TYPE_SHIFT);
+ /* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */
+ reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT)
+ << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT);
ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
if (ret < 0)
return ret;
@@ -445,12 +673,22 @@
TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS;
if (tcpci->controls_vbus)
reg |= TCPC_ALERT_POWER_STATUS;
+ /* Enable VSAFE0V status interrupt when detecting VSAFE0V is supported */
+ if (tcpci->data->vbus_vsafe0v) {
+ reg |= TCPC_ALERT_EXTENDED_STATUS;
+ ret = regmap_write(tcpci->regmap, TCPC_EXTENDED_STATUS_MASK,
+ TCPC_EXTENDED_STATUS_VSAFE0V);
+ if (ret < 0)
+ return ret;
+ }
return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg);
}
irqreturn_t tcpci_irq(struct tcpci *tcpci)
{
u16 status;
+ int ret;
+ unsigned int raw;
tcpci_read16(tcpci, TCPC_ALERT, &status);
@@ -466,15 +704,12 @@
tcpm_cc_change(tcpci->port);
if (status & TCPC_ALERT_POWER_STATUS) {
- unsigned int reg;
-
- regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, ®);
-
+ regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw);
/*
* If power status mask has been reset, then the TCPC
* has reset.
*/
- if (reg == 0xff)
+ if (raw == 0xff)
tcpm_tcpc_reset(tcpci->port);
else
tcpm_vbus_change(tcpci->port);
@@ -511,6 +746,12 @@
tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
tcpm_pd_receive(tcpci->port, &msg);
+ }
+
+ if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) {
+ ret = regmap_read(tcpci->regmap, TCPC_EXTENDED_STATUS, &raw);
+ if (!ret && (raw & TCPC_EXTENDED_STATUS_VSAFE0V))
+ tcpm_vbus_change(tcpci->port);
}
if (status & TCPC_ALERT_RX_HARD_RST)
@@ -572,6 +813,7 @@
tcpci->tcpc.get_vbus = tcpci_get_vbus;
tcpci->tcpc.set_vbus = tcpci_set_vbus;
tcpci->tcpc.set_cc = tcpci_set_cc;
+ tcpci->tcpc.apply_rc = tcpci_apply_rc;
tcpci->tcpc.get_cc = tcpci_get_cc;
tcpci->tcpc.set_polarity = tcpci_set_polarity;
tcpci->tcpc.set_vconn = tcpci_set_vconn;
@@ -580,6 +822,22 @@
tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
tcpci->tcpc.set_roles = tcpci_set_roles;
tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
+ tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
+ tcpci->tcpc.enable_frs = tcpci_enable_frs;
+ tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus;
+ tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable;
+ tcpci->tcpc.check_contaminant = tcpci_check_contaminant;
+
+ if (tcpci->data->auto_discharge_disconnect) {
+ tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge;
+ tcpci->tcpc.set_auto_vbus_discharge_threshold =
+ tcpci_set_auto_vbus_discharge_threshold;
+ regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_BLEED_DISCHARGE,
+ TCPC_POWER_CTRL_BLEED_DISCHARGE);
+ }
+
+ if (tcpci->data->vbus_vsafe0v)
+ tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v;
err = tcpci_parse_config(tcpci);
if (err < 0)
@@ -646,7 +904,7 @@
/* Disable chip interrupts before unregistering port */
err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0);
if (err < 0)
- return err;
+ dev_warn(&client->dev, "Failed to disable irqs (%pe)\n", ERR_PTR(err));
tcpci_unregister_port(chip->tcpci);
--
Gitblit v1.6.2