xref: /openbmc/linux/drivers/usb/typec/mux.c (revision 93dd2112)
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  * @name: Mux identifier
127  *
128  * Finds a mux linked to the caller. This function is primarily meant for the
129  * Type-C drivers. Returns a reference to the mux on success, NULL if no
130  * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
131  * was found but the mux has not been enumerated yet.
132  */
133 struct typec_mux *typec_mux_get(struct device *dev, const char *name)
134 {
135 	struct typec_mux *mux;
136 
137 	mutex_lock(&mux_lock);
138 	mux = device_connection_find_match(dev, name, NULL, 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