0010-usb-typec-add-extcon-to-tcpm.patch 7.07 KB
Newer Older
Dan Johansen's avatar
Dan Johansen committed
1
From ed2bd8b9262b5367b0e253ffbb238ee85d82f52f Mon Sep 17 00:00:00 2001
2
From: Dan Johansen <strit@manjaro.org>
Dan Johansen's avatar
Dan Johansen committed
3 4
Date: Tue, 25 May 2021 22:50:01 +0200
Subject: [PATCH] usb/typec: add extcon to tcpm
5 6

---
Dan Johansen's avatar
Dan Johansen committed
7 8
 drivers/usb/typec/tcpm/tcpm.c | 133 +++++++++++++++++++++++++++++++++-
 1 file changed, 132 insertions(+), 1 deletion(-)
9 10

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
Dan Johansen's avatar
Dan Johansen committed
11
index 64133e586c64..8fa1d0993b97 100644
12 13 14 15 16 17 18
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -8,6 +8,7 @@
 #include <linux/completion.h>
 #include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/extcon-provider.h>
19
 #include <linux/hrtimer.h>
20 21
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
Dan Johansen's avatar
Dan Johansen committed
22 23 24 25
@@ -468,6 +469,12 @@ struct tcpm_port {
 	 * SNK_READY for non-pd link.
 	 */
 	bool slow_charger_loop;
26 27 28 29 30 31 32 33 34
+
+#ifdef CONFIG_EXTCON
+	struct extcon_dev *extcon;
+	unsigned int *extcon_cables;
+#endif
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *dentry;
 	struct mutex logbuffer_lock;	/* log buffer access lock */
Dan Johansen's avatar
Dan Johansen committed
35 36 37
@@ -824,6 +831,33 @@ static void tcpm_ams_finish(struct tcpm_port *port)
 	port->ams = NONE_AMS;
 }
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
 
+static void tcpm_update_extcon_data(struct tcpm_port *port, bool attached) {
+#ifdef CONFIG_EXTCON
+	unsigned int *capability = port->extcon_cables;
+	if (port->data_role == TYPEC_HOST) {
+		extcon_set_state(port->extcon, EXTCON_USB, false);
+		extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
+	} else {
+		extcon_set_state(port->extcon, EXTCON_USB, true);
+		extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
+	}
+	while(*capability != EXTCON_NONE) {
+		union extcon_property_value val;
+		val.intval = true;
+		extcon_set_property(port->extcon, *capability, EXTCON_PROP_USB_SS, val);
+		val.intval = (port->polarity == TYPEC_POLARITY_CC2);
+		extcon_set_property(port->extcon, *capability,
+			EXTCON_PROP_USB_TYPEC_POLARITY, val);
+		extcon_sync(port->extcon, *capability);
+		capability++;
+	}
+	tcpm_log(port, "Extcon update (%s): %s, %s",
+		attached ? "attached" : "detached",
+		port->data_role == TYPEC_HOST ? "host" : "device",
+		port->polarity == TYPEC_POLARITY_CC1 ? "normal" : "flipped");
+#endif
+}
+
 static int tcpm_pd_transmit(struct tcpm_port *port,
 			    enum tcpm_transmit_type type,
 			    const struct pd_message *msg)
Dan Johansen's avatar
Dan Johansen committed
69
@@ -1036,6 +1070,8 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached,
70 71 72 73 74 75 76 77
 	typec_set_data_role(port->typec_port, data);
 	typec_set_pwr_role(port->typec_port, role);
 
+	tcpm_update_extcon_data(port, attached);
+
 	return 0;
 }
 
Dan Johansen's avatar
Dan Johansen committed
78
@@ -1484,7 +1520,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt)
79 80 81 82 83 84 85 86
 		paltmode->mode = i;
 		paltmode->vdo = p[i];
 
-		tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
+		tcpm_log(port, "Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
 			 pmdata->altmodes, paltmode->svid,
 			 paltmode->mode, paltmode->vdo);
 
Dan Johansen's avatar
Dan Johansen committed
87 88 89 90 91 92 93 94 95 96
@@ -1629,6 +1665,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
 			modep->svid_index++;
 			if (modep->svid_index < modep->nsvids) {
 				u16 svid = modep->svids[modep->svid_index];
+                 tcpm_log(port, "More modes available, sending discover");
 				response[0] = VDO(svid, 1, svdm_version, CMD_DISCOVER_MODES);
 				rlen = 1;
 			} else {
@@ -3610,6 +3647,8 @@ static void tcpm_detach(struct tcpm_port *port)
 	}
97 98 99 100 101 102 103
 
 	tcpm_reset_port(port);
+
+	tcpm_update_extcon_data(port, false);
 }
 
 static void tcpm_src_detach(struct tcpm_port *port)
Dan Johansen's avatar
Dan Johansen committed
104
@@ -5808,6 +5847,64 @@ void tcpm_tcpc_reset(struct tcpm_port *port)
105 106 107
 }
 EXPORT_SYMBOL_GPL(tcpm_tcpc_reset);
 
Dan Johansen's avatar
Dan Johansen committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
+static int tcpm_fw_get_caps_late(struct tcpm_port *port,
+			    struct fwnode_handle *fwnode)
+{
+	int ret, i;
+	ret = fwnode_property_count_u32(fwnode, "typec-altmodes");
+	if (ret > 0) {
+		u32 *props;
+		if (ret % 4) {
+			dev_err(port->dev, "Length of typec altmode array must be divisible by 4");
+			return -EINVAL;
+		}
+
+		props = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
+		if (!props) {
+			dev_err(port->dev, "Failed to allocate memory for altmode properties");
+			return -ENOMEM;
+		}
+
+		if(fwnode_property_read_u32_array(fwnode, "typec-altmodes", props, ret) < 0) {
+			dev_err(port->dev, "Failed to read altmodes from port");
+			return -EINVAL;
+		}
+
+		i = 0;
+		while (ret > 0 && i < ARRAY_SIZE(port->port_altmode)) {
+			struct typec_altmode *alt;
+			struct typec_altmode_desc alt_desc = {
+				.svid = props[i * 4],
+				.mode = props[i * 4 + 1],
+				.vdo  = props[i * 4 + 2],
+				.roles = props[i * 4 + 3],
+			};
+
+
+			tcpm_log(port, "Adding altmode SVID: 0x%04x, mode: %d, vdo: %u, role: %d",
+				alt_desc.svid, alt_desc.mode, alt_desc.vdo, alt_desc.roles);
+			alt = typec_port_register_altmode(port->typec_port,
+							  &alt_desc);
+			if (IS_ERR(alt)) {
+				tcpm_log(port,
+					 "%s: failed to register port alternate mode 0x%x",
+					 dev_name(port->dev), alt_desc.svid);
+				break;
+			}
+			typec_altmode_set_drvdata(alt, port);
+			alt->ops = &tcpm_altmode_ops;
+			port->port_altmode[i] = alt;
+			i++;
+			ret -= 4;
+		}
+	}
+	return 0;
+}
+
162 163 164 165 166 167 168
+unsigned int default_supported_cables[] = {
+	EXTCON_NONE
+};
+
 static int tcpm_fw_get_caps(struct tcpm_port *port,
 			    struct fwnode_handle *fwnode)
 {
Dan Johansen's avatar
Dan Johansen committed
169 170 171
@@ -5827,6 +5924,23 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
 	 */
 	fw_devlink_purge_absent_suppliers(fwnode);
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
 
+#ifdef CONFIG_EXTCON
+	ret = fwnode_property_count_u32(fwnode, "extcon-cables");
+	if (ret > 0) {
+		port->extcon_cables = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
+		if (!port->extcon_cables) {
+			dev_err(port->dev, "Failed to allocate memory for extcon cable types. "\
+				"Using default tyes");
+			goto extcon_default;
+		}
+		fwnode_property_read_u32_array(fwnode, "extcon-cables", port->extcon_cables, ret);
+	} else {
+extcon_default:
+		dev_info(port->dev, "No cable types defined, using default cables");
+		port->extcon_cables = default_supported_cables;
+	}
+#endif
+
 	/* USB data support is optional */
 	ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
 	if (ret == 0) {
Dan Johansen's avatar
Dan Johansen committed
193
@@ -6226,6 +6340,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
 		goto out_destroy_wq;
 
 	port->try_role = port->typec_caps.prefer_role;
+#ifdef CONFIG_EXTCON
+	port->extcon = devm_extcon_dev_allocate(dev, port->extcon_cables);
+	if (IS_ERR(port->extcon)) {
+		dev_err(dev, "Failed to allocate extcon device: %ld", PTR_ERR(port->extcon));
+		goto out_destroy_wq;
+	}
+	if((err = devm_extcon_dev_register(dev, port->extcon))) {
+		dev_err(dev, "Failed to register extcon device: %d", err);
+		goto out_destroy_wq;
+	}
+#endif
 
 	port->typec_caps.fwnode = tcpc->fwnode;
 	port->typec_caps.revision = 0x0120;	/* Type-C spec release 1.2 */
Dan Johansen's avatar
Dan Johansen committed
211 212 213 214 215 216 217 218 219 220 221 222 223
@@ -6259,6 +6384,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
 				     &tcpm_altmode_ops, port,
 				     port->port_altmode, ALTMODE_DISCOVERY_MAX);
 
+	err = tcpm_fw_get_caps_late(port, tcpc->fwnode);
+	if (err < 0) {
+		dev_err(dev, "Failed to get altmodes from fwnode");
+		goto out_destroy_wq;
+	}
+
 	mutex_lock(&port->lock);
 	tcpm_init(port);
 	mutex_unlock(&port->lock);
224
-- 
Dan Johansen's avatar
Dan Johansen committed
225
2.31.1
226