1 // SPDX-License-Identifier: GPL-2.0
3 * USB Type-C Multiplexer/DeMultiplexer Switch support
5 * Copyright (C) 2018 Intel Corporation
10 #include <linux/device.h>
11 #include <linux/list.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/property.h>
15 #include <linux/slab.h>
16 #include <linux/usb/typec_mux.h>
18 static DEFINE_MUTEX(switch_lock);
19 static DEFINE_MUTEX(mux_lock);
20 static LIST_HEAD(switch_list);
21 static LIST_HEAD(mux_list);
23 static void *typec_switch_match(struct device_connection *con, int ep,
26 struct typec_switch *sw;
29 list_for_each_entry(sw, &switch_list, entry)
30 if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
32 return ERR_PTR(-EPROBE_DEFER);
36 * With OF graph the mux node must have a boolean device property named
37 * "orientation-switch".
39 if (con->id && !fwnode_property_present(con->fwnode, con->id))
42 list_for_each_entry(sw, &switch_list, entry)
43 if (dev_fwnode(sw->dev) == con->fwnode)
46 return con->id ? ERR_PTR(-EPROBE_DEFER) : NULL;
50 * typec_switch_get - Find USB Type-C orientation switch
51 * @dev: The caller device
53 * Finds a switch linked with @dev. Returns a reference to the switch on
54 * success, NULL if no matching connection was found, or
55 * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
56 * has not been enumerated yet.
58 struct typec_switch *typec_switch_get(struct device *dev)
60 struct typec_switch *sw;
62 mutex_lock(&switch_lock);
63 sw = device_connection_find_match(dev, "orientation-switch", NULL,
65 if (!IS_ERR_OR_NULL(sw)) {
66 WARN_ON(!try_module_get(sw->dev->driver->owner));
69 mutex_unlock(&switch_lock);
73 EXPORT_SYMBOL_GPL(typec_switch_get);
76 * typec_put_switch - Release USB Type-C orientation switch
77 * @sw: USB Type-C orientation switch
79 * Decrement reference count for @sw.
81 void typec_switch_put(struct typec_switch *sw)
83 if (!IS_ERR_OR_NULL(sw)) {
84 module_put(sw->dev->driver->owner);
88 EXPORT_SYMBOL_GPL(typec_switch_put);
91 * typec_switch_register - Register USB Type-C orientation switch
92 * @sw: USB Type-C orientation switch
94 * This function registers a switch that can be used for routing the correct
95 * data pairs depending on the cable plug orientation from the USB Type-C
96 * connector to the USB controllers. USB Type-C plugs can be inserted
97 * right-side-up or upside-down.
99 int typec_switch_register(struct typec_switch *sw)
101 mutex_lock(&switch_lock);
102 list_add_tail(&sw->entry, &switch_list);
103 mutex_unlock(&switch_lock);
107 EXPORT_SYMBOL_GPL(typec_switch_register);
110 * typec_switch_unregister - Unregister USB Type-C orientation switch
111 * @sw: USB Type-C orientation switch
113 * Unregister switch that was registered with typec_switch_register().
115 void typec_switch_unregister(struct typec_switch *sw)
117 mutex_lock(&switch_lock);
118 list_del(&sw->entry);
119 mutex_unlock(&switch_lock);
121 EXPORT_SYMBOL_GPL(typec_switch_unregister);
123 /* ------------------------------------------------------------------------- */
125 static void *typec_mux_match(struct device_connection *con, int ep, void *data)
127 const struct typec_altmode_desc *desc = data;
128 struct typec_mux *mux;
135 list_for_each_entry(mux, &mux_list, entry)
136 if (!strcmp(con->endpoint[ep], dev_name(mux->dev)))
138 return ERR_PTR(-EPROBE_DEFER);
142 * Check has the identifier already been "consumed". If it
143 * has, no need to do any extra connection identification.
149 /* Accessory Mode muxes */
151 match = fwnode_property_present(con->fwnode, "accessory");
157 /* Alternate Mode muxes */
158 nval = fwnode_property_read_u16_array(con->fwnode, "svid", NULL, 0);
162 val = kcalloc(nval, sizeof(*val), GFP_KERNEL);
164 return ERR_PTR(-ENOMEM);
166 nval = fwnode_property_read_u16_array(con->fwnode, "svid", val, nval);
169 return ERR_PTR(nval);
172 for (i = 0; i < nval; i++) {
173 match = val[i] == desc->svid;
183 list_for_each_entry(mux, &mux_list, entry)
184 if (dev_fwnode(mux->dev) == con->fwnode)
187 return ERR_PTR(-EPROBE_DEFER);
191 * typec_mux_get - Find USB Type-C Multiplexer
192 * @dev: The caller device
193 * @desc: Alt Mode description
195 * Finds a mux linked to the caller. This function is primarily meant for the
196 * Type-C drivers. Returns a reference to the mux on success, NULL if no
197 * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
198 * was found but the mux has not been enumerated yet.
200 struct typec_mux *typec_mux_get(struct device *dev,
201 const struct typec_altmode_desc *desc)
203 struct typec_mux *mux;
205 mutex_lock(&mux_lock);
206 mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
208 if (!IS_ERR_OR_NULL(mux)) {
209 WARN_ON(!try_module_get(mux->dev->driver->owner));
210 get_device(mux->dev);
212 mutex_unlock(&mux_lock);
216 EXPORT_SYMBOL_GPL(typec_mux_get);
219 * typec_mux_put - Release handle to a Multiplexer
220 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
222 * Decrements reference count for @mux.
224 void typec_mux_put(struct typec_mux *mux)
226 if (!IS_ERR_OR_NULL(mux)) {
227 module_put(mux->dev->driver->owner);
228 put_device(mux->dev);
231 EXPORT_SYMBOL_GPL(typec_mux_put);
234 * typec_mux_register - Register Multiplexer routing USB Type-C pins
235 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
237 * USB Type-C connectors can be used for alternate modes of operation besides
238 * USB when Accessory/Alternate Modes are supported. With some of those modes,
239 * the pins on the connector need to be reconfigured. This function registers
240 * multiplexer switches routing the pins on the connector.
242 int typec_mux_register(struct typec_mux *mux)
244 mutex_lock(&mux_lock);
245 list_add_tail(&mux->entry, &mux_list);
246 mutex_unlock(&mux_lock);
250 EXPORT_SYMBOL_GPL(typec_mux_register);
253 * typec_mux_unregister - Unregister Multiplexer Switch
254 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
256 * Unregister mux that was registered with typec_mux_register().
258 void typec_mux_unregister(struct typec_mux *mux)
260 mutex_lock(&mux_lock);
261 list_del(&mux->entry);
262 mutex_unlock(&mux_lock);
264 EXPORT_SYMBOL_GPL(typec_mux_unregister);