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 * @mux: 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); 192