From b91264fe1613cbe50fc2f2a0f5aed49ddbf1d6b1 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Thu, 7 Sep 2023 21:09:14 +0200
Subject: [PATCH 435/480] usb: tcpci: Add support for extcon based input
 current limit determination

This allows backwards compatibility with DCP/CDP cahrger detection
based on D+/D- USB2.0 pins state.

Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
 drivers/usb/typec/tcpm/tcpci.c | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 19ab6647af70..427caf377126 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -7,6 +7,7 @@
 
 #include <linux/bitfield.h>
 #include <linux/delay.h>
+#include <linux/extcon.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
@@ -41,6 +42,8 @@ struct tcpci {
 
 	struct tcpc_dev tcpc;
 	struct tcpci_data *data;
+
+	struct extcon_dev *extcon;
 };
 
 struct tcpci_chip {
@@ -828,6 +831,47 @@ static int tcpci_parse_config(struct tcpci *tcpci)
 	return 0;
 }
 
+static int tcpci_get_current_limit(struct tcpc_dev *dev)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(dev);
+	int current_limit = 0;
+	unsigned long timeout;
+
+	/*
+	 * To avoid cycles in OF dependencies, we get extcon when necessary
+	 * outside of probe function.
+	 */
+	if (of_property_read_bool(tcpci->dev->of_node, "extcon") && !tcpci->extcon) {
+		tcpci->extcon = extcon_get_edev_by_phandle(tcpci->dev, 0);
+		if (IS_ERR(tcpci->extcon))
+			tcpci->extcon = NULL;
+	}
+
+	if (!tcpci->extcon)
+		return 0;
+
+	/*
+	 * USB2 Charger detection may still be in progress when we get here,
+	 * this can take upto 600ms, wait 1000ms max.
+	 */
+	timeout = jiffies + msecs_to_jiffies(1000);
+	do {
+		if (extcon_get_state(tcpci->extcon, EXTCON_CHG_USB_SDP) == 1)
+			current_limit = 500;
+
+		if (extcon_get_state(tcpci->extcon, EXTCON_CHG_USB_CDP) == 1 ||
+		    extcon_get_state(tcpci->extcon, EXTCON_CHG_USB_ACA) == 1)
+			current_limit = 1500;
+
+		if (extcon_get_state(tcpci->extcon, EXTCON_CHG_USB_DCP) == 1)
+			current_limit = 2000;
+
+		msleep(50);
+	} while (current_limit == 0 && time_before(jiffies, timeout));
+
+	return current_limit;
+}
+
 struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
 {
 	struct tcpci *tcpci;
@@ -860,6 +904,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
 	tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable;
 	tcpci->tcpc.cable_comm_capable = tcpci_cable_comm_capable;
 	tcpci->tcpc.attempt_vconn_swap_discovery = tcpci_attempt_vconn_swap_discovery;
+	tcpci->tcpc.get_current_limit = tcpci_get_current_limit;
 
 	if (tcpci->data->check_contaminant)
 		tcpci->tcpc.check_contaminant = tcpci_check_contaminant;
-- 
2.49.0

