Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: API for controlling USB Type-C Multiplexers

USB Type-C connectors consist of various muxes and switches
that route the pins on the connector to the right locations.
The USB Type-C drivers need to be able to control the muxes,
as they are the ones that know things like the cable plug
orientation, and the current mode that was negotiated with
the partner.

This introduces a small API for registering and controlling
cable plug orientation switches, and separate small API for
registering and controlling pin multiplexer/demultiplexer
switches that are needed with Accessory/Alternate Modes.

Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
bdecb33a f2d9b66d

+393 -11
+62 -11
Documentation/driver-api/usb/typec.rst
··· 61 61 The port drivers will describe every Type-C port they control with struct 62 62 typec_capability data structure, and register them with the following API: 63 63 64 - .. kernel-doc:: drivers/usb/typec/typec.c 64 + .. kernel-doc:: drivers/usb/typec/class.c 65 65 :functions: typec_register_port typec_unregister_port 66 66 67 67 When registering the ports, the prefer_role member in struct typec_capability ··· 80 80 registration. The class offers the following API for registering/unregistering 81 81 partners. 82 82 83 - .. kernel-doc:: drivers/usb/typec/typec.c 83 + .. kernel-doc:: drivers/usb/typec/class.c 84 84 :functions: typec_register_partner typec_unregister_partner 85 85 86 86 The class will provide a handle to struct typec_partner if the registration was ··· 92 92 create a sysfs directory for the identity under the partner device. The result 93 93 of Discover Identity command can then be reported with the following API: 94 94 95 - .. kernel-doc:: drivers/usb/typec/typec.c 95 + .. kernel-doc:: drivers/usb/typec/class.c 96 96 :functions: typec_partner_set_identity 97 97 98 98 Registering Cables ··· 113 113 the details during registration. The class offers the following API for 114 114 registering/unregistering cables and their plugs: 115 115 116 - .. kernel-doc:: drivers/usb/typec/typec.c 116 + .. kernel-doc:: drivers/usb/typec/class.c 117 117 :functions: typec_register_cable typec_unregister_cable typec_register_plug typec_unregister_plug 118 118 119 119 The class will provide a handle to struct typec_cable and struct typec_plug if ··· 125 125 sysfs directory for the identity under the cable device. The result of Discover 126 126 Identity command can then be reported with the following API: 127 127 128 - .. kernel-doc:: drivers/usb/typec/typec.c 128 + .. kernel-doc:: drivers/usb/typec/class.c 129 129 :functions: typec_cable_set_identity 130 130 131 131 Notifications ··· 135 135 during connection of a partner or cable, the port driver must use the following 136 136 APIs to report it to the class: 137 137 138 - .. kernel-doc:: drivers/usb/typec/typec.c 138 + .. kernel-doc:: drivers/usb/typec/class.c 139 139 :functions: typec_set_data_role typec_set_pwr_role typec_set_vconn_role typec_set_pwr_opmode 140 140 141 141 Alternate Modes ··· 150 150 Ports that support Alternate Modes need to register each SVID they support with 151 151 the following API: 152 152 153 - .. kernel-doc:: drivers/usb/typec/typec.c 153 + .. kernel-doc:: drivers/usb/typec/class.c 154 154 :functions: typec_port_register_altmode 155 155 156 156 If a partner or cable plug provides a list of SVIDs as response to USB Power ··· 159 159 160 160 API for the partners: 161 161 162 - .. kernel-doc:: drivers/usb/typec/typec.c 162 + .. kernel-doc:: drivers/usb/typec/class.c 163 163 :functions: typec_partner_register_altmode 164 164 165 165 API for the Cable Plugs: 166 166 167 - .. kernel-doc:: drivers/usb/typec/typec.c 167 + .. kernel-doc:: drivers/usb/typec/class.c 168 168 :functions: typec_plug_register_altmode 169 169 170 170 So ports, partners and cable plugs will register the alternate modes with their ··· 172 172 typec_altmode on success, or NULL. The unregistration will happen with the same 173 173 function: 174 174 175 - .. kernel-doc:: drivers/usb/typec/typec.c 175 + .. kernel-doc:: drivers/usb/typec/class.c 176 176 :functions: typec_unregister_altmode 177 177 178 178 If a partner or cable plug enters or exits a mode, the port driver needs to 179 179 notify the class with the following API: 180 180 181 - .. kernel-doc:: drivers/usb/typec/typec.c 181 + .. kernel-doc:: drivers/usb/typec/class.c 182 182 :functions: typec_altmode_update_active 183 + 184 + Multiplexer/DeMultiplexer Switches 185 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 186 + 187 + USB Type-C connectors may have one or more mux/demux switches behind them. Since 188 + the plugs can be inserted right-side-up or upside-down, a switch is needed to 189 + route the correct data pairs from the connector to the USB controllers. If 190 + Alternate or Accessory Modes are supported, another switch is needed that can 191 + route the pins on the connector to some other component besides USB. USB Type-C 192 + Connector Class supplies an API for registering those switches. 193 + 194 + .. kernel-doc:: drivers/usb/typec/mux.c 195 + :functions: typec_switch_register typec_switch_unregister typec_mux_register typec_mux_unregister 196 + 197 + In most cases the same physical mux will handle both the orientation and mode. 198 + However, as the port drivers will be responsible for the orientation, and the 199 + alternate mode drivers for the mode, the two are always separated into their 200 + own logical components: "mux" for the mode and "switch" for the orientation. 201 + 202 + When a port is registered, USB Type-C Connector Class requests both the mux and 203 + the switch for the port. The drivers can then use the following API for 204 + controlling them: 205 + 206 + .. kernel-doc:: drivers/usb/typec/class.c 207 + :functions: typec_set_orientation typec_set_mode 208 + 209 + If the connector is dual-role capable, there may also be a switch for the data 210 + role. USB Type-C Connector Class does not supply separate API for them. The 211 + port drivers can use USB Role Class API with those. 212 + 213 + Illustration of the muxes behind a connector that supports an alternate mode: 214 + 215 + ------------------------ 216 + | Connector | 217 + ------------------------ 218 + | | 219 + ------------------------ 220 + \ Orientation / 221 + -------------------- 222 + | 223 + -------------------- 224 + / Mode \ 225 + ------------------------ 226 + / \ 227 + ------------------------ -------------------- 228 + | Alt Mode | / USB Role \ 229 + ------------------------ ------------------------ 230 + / \ 231 + ------------------------ ------------------------ 232 + | USB Host | | USB Device | 233 + ------------------------ ------------------------
+1
drivers/usb/typec/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 obj-$(CONFIG_TYPEC) += typec.o 3 + typec-y := class.o mux.o 3 4 obj-$(CONFIG_TYPEC_TCPM) += tcpm.o 4 5 obj-y += fusb302/ 5 6 obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
+191
drivers/usb/typec/mux.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /** 3 + * USB Type-C Multiplexer/DeMultiplexer Switch support 4 + * 5 + * Copyright (C) 2018 Intel Corporation 6 + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 + * Hans de Goede <hdegoede@redhat.com> 8 + */ 9 + 10 + #include <linux/device.h> 11 + #include <linux/list.h> 12 + #include <linux/mutex.h> 13 + #include <linux/usb/typec_mux.h> 14 + 15 + static DEFINE_MUTEX(switch_lock); 16 + static DEFINE_MUTEX(mux_lock); 17 + static LIST_HEAD(switch_list); 18 + static LIST_HEAD(mux_list); 19 + 20 + static void *typec_switch_match(struct device_connection *con, int ep, 21 + void *data) 22 + { 23 + struct typec_switch *sw; 24 + 25 + list_for_each_entry(sw, &switch_list, entry) 26 + if (!strcmp(con->endpoint[ep], dev_name(sw->dev))) 27 + return sw; 28 + 29 + /* 30 + * We only get called if a connection was found, tell the caller to 31 + * wait for the switch to show up. 32 + */ 33 + return ERR_PTR(-EPROBE_DEFER); 34 + } 35 + 36 + /** 37 + * typec_switch_get - Find USB Type-C orientation switch 38 + * @dev: The caller device 39 + * 40 + * Finds a switch linked with @dev. Returns a reference to the switch on 41 + * success, NULL if no matching connection was found, or 42 + * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch 43 + * has not been enumerated yet. 44 + */ 45 + struct typec_switch *typec_switch_get(struct device *dev) 46 + { 47 + struct typec_switch *sw; 48 + 49 + mutex_lock(&switch_lock); 50 + sw = device_connection_find_match(dev, "typec-switch", NULL, 51 + typec_switch_match); 52 + if (!IS_ERR_OR_NULL(sw)) 53 + get_device(sw->dev); 54 + mutex_unlock(&switch_lock); 55 + 56 + return sw; 57 + } 58 + EXPORT_SYMBOL_GPL(typec_switch_get); 59 + 60 + /** 61 + * typec_put_switch - Release USB Type-C orientation switch 62 + * @sw: USB Type-C orientation switch 63 + * 64 + * Decrement reference count for @sw. 65 + */ 66 + void typec_switch_put(struct typec_switch *sw) 67 + { 68 + if (!IS_ERR_OR_NULL(sw)) 69 + put_device(sw->dev); 70 + } 71 + EXPORT_SYMBOL_GPL(typec_switch_put); 72 + 73 + /** 74 + * typec_switch_register - Register USB Type-C orientation switch 75 + * @sw: USB Type-C orientation switch 76 + * 77 + * This function registers a switch that can be used for routing the correct 78 + * data pairs depending on the cable plug orientation from the USB Type-C 79 + * connector to the USB controllers. USB Type-C plugs can be inserted 80 + * right-side-up or upside-down. 81 + */ 82 + int typec_switch_register(struct typec_switch *sw) 83 + { 84 + mutex_lock(&switch_lock); 85 + list_add_tail(&sw->entry, &switch_list); 86 + mutex_unlock(&switch_lock); 87 + 88 + return 0; 89 + } 90 + EXPORT_SYMBOL_GPL(typec_switch_register); 91 + 92 + /** 93 + * typec_switch_unregister - Unregister USB Type-C orientation switch 94 + * @sw: USB Type-C orientation switch 95 + * 96 + * Unregister switch that was registered with typec_switch_register(). 97 + */ 98 + void typec_switch_unregister(struct typec_switch *sw) 99 + { 100 + mutex_lock(&switch_lock); 101 + list_del(&sw->entry); 102 + mutex_unlock(&switch_lock); 103 + } 104 + EXPORT_SYMBOL_GPL(typec_switch_unregister); 105 + 106 + /* ------------------------------------------------------------------------- */ 107 + 108 + static void *typec_mux_match(struct device_connection *con, int ep, void *data) 109 + { 110 + struct typec_mux *mux; 111 + 112 + list_for_each_entry(mux, &mux_list, entry) 113 + if (!strcmp(con->endpoint[ep], dev_name(mux->dev))) 114 + return mux; 115 + 116 + /* 117 + * We only get called if a connection was found, tell the caller to 118 + * wait for the switch to show up. 119 + */ 120 + return ERR_PTR(-EPROBE_DEFER); 121 + } 122 + 123 + /** 124 + * typec_mux_get - Find USB Type-C Multiplexer 125 + * @dev: The caller device 126 + * 127 + * Finds a mux linked to the caller. This function is primarily meant for the 128 + * Type-C drivers. Returns a reference to the mux on success, NULL if no 129 + * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection 130 + * was found but the mux has not been enumerated yet. 131 + */ 132 + struct typec_mux *typec_mux_get(struct device *dev) 133 + { 134 + struct typec_mux *mux; 135 + 136 + mutex_lock(&mux_lock); 137 + mux = device_connection_find_match(dev, "typec-mux", NULL, 138 + typec_mux_match); 139 + if (!IS_ERR_OR_NULL(mux)) 140 + get_device(mux->dev); 141 + mutex_unlock(&mux_lock); 142 + 143 + return mux; 144 + } 145 + EXPORT_SYMBOL_GPL(typec_mux_get); 146 + 147 + /** 148 + * typec_mux_put - Release handle to a Multiplexer 149 + * @mux: USB Type-C Connector Multiplexer/DeMultiplexer 150 + * 151 + * Decrements reference count for @mux. 152 + */ 153 + void typec_mux_put(struct typec_mux *mux) 154 + { 155 + if (!IS_ERR_OR_NULL(mux)) 156 + put_device(mux->dev); 157 + } 158 + EXPORT_SYMBOL_GPL(typec_mux_put); 159 + 160 + /** 161 + * typec_mux_register - Register Multiplexer routing USB Type-C pins 162 + * @mux: USB Type-C Connector Multiplexer/DeMultiplexer 163 + * 164 + * USB Type-C connectors can be used for alternate modes of operation besides 165 + * USB when Accessory/Alternate Modes are supported. With some of those modes, 166 + * the pins on the connector need to be reconfigured. This function registers 167 + * multiplexer switches routing the pins on the connector. 168 + */ 169 + int typec_mux_register(struct typec_mux *mux) 170 + { 171 + mutex_lock(&mux_lock); 172 + list_add_tail(&mux->entry, &mux_list); 173 + mutex_unlock(&mux_lock); 174 + 175 + return 0; 176 + } 177 + EXPORT_SYMBOL_GPL(typec_mux_register); 178 + 179 + /** 180 + * typec_mux_unregister - Unregister Multiplexer Switch 181 + * @sw: USB Type-C Connector Multiplexer/DeMultiplexer 182 + * 183 + * Unregister mux that was registered with typec_mux_register(). 184 + */ 185 + void typec_mux_unregister(struct typec_mux *mux) 186 + { 187 + mutex_lock(&mux_lock); 188 + list_del(&mux->entry); 189 + mutex_unlock(&mux_lock); 190 + } 191 + EXPORT_SYMBOL_GPL(typec_mux_unregister);
+70
drivers/usb/typec/typec.c drivers/usb/typec/class.c
··· 11 11 #include <linux/mutex.h> 12 12 #include <linux/slab.h> 13 13 #include <linux/usb/typec.h> 14 + #include <linux/usb/typec_mux.h> 14 15 15 16 struct typec_mode { 16 17 int index; ··· 71 70 enum typec_port_type port_type; 72 71 struct mutex port_type_lock; 73 72 73 + enum typec_orientation orientation; 74 + struct typec_switch *sw; 75 + struct typec_mux *mux; 76 + 74 77 const struct typec_capability *cap; 75 78 }; 76 79 ··· 97 92 static DEFINE_IDA(typec_index_ida); 98 93 static struct class *typec_class; 99 94 95 + /* ------------------------------------------------------------------------- */ 100 96 /* Common attributes */ 101 97 102 98 static const char * const typec_accessory_modes[] = { ··· 1143 1137 struct typec_port *port = to_typec_port(dev); 1144 1138 1145 1139 ida_simple_remove(&typec_index_ida, port->id); 1140 + typec_switch_put(port->sw); 1141 + typec_mux_put(port->mux); 1146 1142 kfree(port); 1147 1143 } 1148 1144 ··· 1254 1246 } 1255 1247 EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); 1256 1248 1249 + /* ------------------------------------------ */ 1250 + /* API for Multiplexer/DeMultiplexer Switches */ 1251 + 1252 + /** 1253 + * typec_set_orientation - Set USB Type-C cable plug orientation 1254 + * @port: USB Type-C Port 1255 + * @orientation: USB Type-C cable plug orientation 1256 + * 1257 + * Set cable plug orientation for @port. 1258 + */ 1259 + int typec_set_orientation(struct typec_port *port, 1260 + enum typec_orientation orientation) 1261 + { 1262 + int ret; 1263 + 1264 + if (port->sw) { 1265 + ret = port->sw->set(port->sw, orientation); 1266 + if (ret) 1267 + return ret; 1268 + } 1269 + 1270 + port->orientation = orientation; 1271 + 1272 + return 0; 1273 + } 1274 + EXPORT_SYMBOL_GPL(typec_set_orientation); 1275 + 1276 + /** 1277 + * typec_set_mode - Set mode of operation for USB Type-C connector 1278 + * @port: USB Type-C port for the connector 1279 + * @mode: Operation mode for the connector 1280 + * 1281 + * Set mode @mode for @port. This function will configure the muxes needed to 1282 + * enter @mode. 1283 + */ 1284 + int typec_set_mode(struct typec_port *port, int mode) 1285 + { 1286 + return port->mux ? port->mux->set(port->mux, mode) : 0; 1287 + } 1288 + EXPORT_SYMBOL_GPL(typec_set_mode); 1289 + 1257 1290 /* --------------------------------------- */ 1258 1291 1259 1292 /** ··· 1342 1293 return ERR_PTR(id); 1343 1294 } 1344 1295 1296 + port->sw = typec_switch_get(cap->fwnode ? &port->dev : parent); 1297 + if (IS_ERR(port->sw)) { 1298 + ret = PTR_ERR(port->sw); 1299 + goto err_switch; 1300 + } 1301 + 1302 + port->mux = typec_mux_get(cap->fwnode ? &port->dev : parent); 1303 + if (IS_ERR(port->mux)) { 1304 + ret = PTR_ERR(port->mux); 1305 + goto err_mux; 1306 + } 1307 + 1345 1308 if (cap->type == TYPEC_PORT_DFP) 1346 1309 role = TYPEC_SOURCE; 1347 1310 else if (cap->type == TYPEC_PORT_UFP) ··· 1391 1330 } 1392 1331 1393 1332 return port; 1333 + 1334 + err_mux: 1335 + typec_switch_put(port->sw); 1336 + 1337 + err_switch: 1338 + ida_simple_remove(&typec_index_ida, port->id); 1339 + kfree(port); 1340 + 1341 + return ERR_PTR(ret); 1394 1342 } 1395 1343 EXPORT_SYMBOL_GPL(typec_register_port); 1396 1344
+14
include/linux/usb/typec.h
··· 60 60 61 61 #define TYPEC_MAX_ACCESSORY 3 62 62 63 + enum typec_orientation { 64 + TYPEC_ORIENTATION_NONE, 65 + TYPEC_ORIENTATION_NORMAL, 66 + TYPEC_ORIENTATION_REVERSE, 67 + }; 68 + 63 69 /* 64 70 * struct usb_pd_identity - USB Power Delivery identity data 65 71 * @id_header: ID Header VDO ··· 191 185 * @pd_revision: USB Power Delivery Specification revision if supported 192 186 * @prefer_role: Initial role preference 193 187 * @accessory: Supported Accessory Modes 188 + * @sw: Cable plug orientation switch 189 + * @mux: Multiplexer switch for Alternate/Accessory Modes 194 190 * @fwnode: Optional fwnode of the port 195 191 * @try_role: Set data role preference for DRP port 196 192 * @dr_set: Set Data Role ··· 210 202 int prefer_role; 211 203 enum typec_accessory accessory[TYPEC_MAX_ACCESSORY]; 212 204 205 + struct typec_switch *sw; 206 + struct typec_mux *mux; 213 207 struct fwnode_handle *fwnode; 214 208 215 209 int (*try_role)(const struct typec_capability *, ··· 254 244 void typec_set_pwr_role(struct typec_port *port, enum typec_role role); 255 245 void typec_set_vconn_role(struct typec_port *port, enum typec_role role); 256 246 void typec_set_pwr_opmode(struct typec_port *port, enum typec_pwr_opmode mode); 247 + 248 + int typec_set_orientation(struct typec_port *port, 249 + enum typec_orientation orientation); 250 + int typec_set_mode(struct typec_port *port, int mode); 257 251 258 252 #endif /* __LINUX_USB_TYPEC_H */
+55
include/linux/usb/typec_mux.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #ifndef __USB_TYPEC_MUX 4 + #define __USB_TYPEC_MUX 5 + 6 + #include <linux/list.h> 7 + #include <linux/usb/typec.h> 8 + 9 + struct device; 10 + 11 + /** 12 + * struct typec_switch - USB Type-C cable orientation switch 13 + * @dev: Switch device 14 + * @entry: List entry 15 + * @set: Callback to the driver for setting the orientation 16 + * 17 + * USB Type-C pin flipper switch routing the correct data pairs from the 18 + * connector to the USB controller depending on the orientation of the cable 19 + * plug. 20 + */ 21 + struct typec_switch { 22 + struct device *dev; 23 + struct list_head entry; 24 + 25 + int (*set)(struct typec_switch *sw, enum typec_orientation orientation); 26 + }; 27 + 28 + /** 29 + * struct typec_switch - USB Type-C connector pin mux 30 + * @dev: Mux device 31 + * @entry: List entry 32 + * @set: Callback to the driver for setting the state of the mux 33 + * 34 + * Pin Multiplexer/DeMultiplexer switch routing the USB Type-C connector pins to 35 + * different components depending on the requested mode of operation. Used with 36 + * Accessory/Alternate modes. 37 + */ 38 + struct typec_mux { 39 + struct device *dev; 40 + struct list_head entry; 41 + 42 + int (*set)(struct typec_mux *mux, int state); 43 + }; 44 + 45 + struct typec_switch *typec_switch_get(struct device *dev); 46 + void typec_switch_put(struct typec_switch *sw); 47 + int typec_switch_register(struct typec_switch *sw); 48 + void typec_switch_unregister(struct typec_switch *sw); 49 + 50 + struct typec_mux *typec_mux_get(struct device *dev); 51 + void typec_mux_put(struct typec_mux *mux); 52 + int typec_mux_register(struct typec_mux *mux); 53 + void typec_mux_unregister(struct typec_mux *mux); 54 + 55 + #endif /* __USB_TYPEC_MUX */