GNU Linux-libre 4.19.295-gnu1
[releases.git] / 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/module.h>
13 #include <linux/mutex.h>
14 #include <linux/usb/typec_mux.h>
15
16 static DEFINE_MUTEX(switch_lock);
17 static DEFINE_MUTEX(mux_lock);
18 static LIST_HEAD(switch_list);
19 static LIST_HEAD(mux_list);
20
21 static void *typec_switch_match(struct device_connection *con, int ep,
22                                 void *data)
23 {
24         struct typec_switch *sw;
25
26         list_for_each_entry(sw, &switch_list, entry)
27                 if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
28                         return sw;
29
30         /*
31          * We only get called if a connection was found, tell the caller to
32          * wait for the switch to show up.
33          */
34         return ERR_PTR(-EPROBE_DEFER);
35 }
36
37 /**
38  * typec_switch_get - Find USB Type-C orientation switch
39  * @dev: The caller device
40  *
41  * Finds a switch linked with @dev. Returns a reference to the switch on
42  * success, NULL if no matching connection was found, or
43  * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
44  * has not been enumerated yet.
45  */
46 struct typec_switch *typec_switch_get(struct device *dev)
47 {
48         struct typec_switch *sw;
49
50         mutex_lock(&switch_lock);
51         sw = device_connection_find_match(dev, "typec-switch", NULL,
52                                           typec_switch_match);
53         if (!IS_ERR_OR_NULL(sw)) {
54                 WARN_ON(!try_module_get(sw->dev->driver->owner));
55                 get_device(sw->dev);
56         }
57         mutex_unlock(&switch_lock);
58
59         return sw;
60 }
61 EXPORT_SYMBOL_GPL(typec_switch_get);
62
63 /**
64  * typec_put_switch - Release USB Type-C orientation switch
65  * @sw: USB Type-C orientation switch
66  *
67  * Decrement reference count for @sw.
68  */
69 void typec_switch_put(struct typec_switch *sw)
70 {
71         if (!IS_ERR_OR_NULL(sw)) {
72                 module_put(sw->dev->driver->owner);
73                 put_device(sw->dev);
74         }
75 }
76 EXPORT_SYMBOL_GPL(typec_switch_put);
77
78 /**
79  * typec_switch_register - Register USB Type-C orientation switch
80  * @sw: USB Type-C orientation switch
81  *
82  * This function registers a switch that can be used for routing the correct
83  * data pairs depending on the cable plug orientation from the USB Type-C
84  * connector to the USB controllers. USB Type-C plugs can be inserted
85  * right-side-up or upside-down.
86  */
87 int typec_switch_register(struct typec_switch *sw)
88 {
89         mutex_lock(&switch_lock);
90         list_add_tail(&sw->entry, &switch_list);
91         mutex_unlock(&switch_lock);
92
93         return 0;
94 }
95 EXPORT_SYMBOL_GPL(typec_switch_register);
96
97 /**
98  * typec_switch_unregister - Unregister USB Type-C orientation switch
99  * @sw: USB Type-C orientation switch
100  *
101  * Unregister switch that was registered with typec_switch_register().
102  */
103 void typec_switch_unregister(struct typec_switch *sw)
104 {
105         mutex_lock(&switch_lock);
106         list_del(&sw->entry);
107         mutex_unlock(&switch_lock);
108 }
109 EXPORT_SYMBOL_GPL(typec_switch_unregister);
110
111 /* ------------------------------------------------------------------------- */
112
113 static void *typec_mux_match(struct device_connection *con, int ep, void *data)
114 {
115         struct typec_mux *mux;
116
117         list_for_each_entry(mux, &mux_list, entry)
118                 if (!strcmp(con->endpoint[ep], dev_name(mux->dev)))
119                         return mux;
120
121         /*
122          * We only get called if a connection was found, tell the caller to
123          * wait for the switch to show up.
124          */
125         return ERR_PTR(-EPROBE_DEFER);
126 }
127
128 /**
129  * typec_mux_get - Find USB Type-C Multiplexer
130  * @dev: The caller device
131  * @name: Mux identifier
132  *
133  * Finds a mux linked to the caller. This function is primarily meant for the
134  * Type-C drivers. Returns a reference to the mux on success, NULL if no
135  * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
136  * was found but the mux has not been enumerated yet.
137  */
138 struct typec_mux *typec_mux_get(struct device *dev, const char *name)
139 {
140         struct typec_mux *mux;
141
142         mutex_lock(&mux_lock);
143         mux = device_connection_find_match(dev, name, NULL, typec_mux_match);
144         if (!IS_ERR_OR_NULL(mux)) {
145                 WARN_ON(!try_module_get(mux->dev->driver->owner));
146                 get_device(mux->dev);
147         }
148         mutex_unlock(&mux_lock);
149
150         return mux;
151 }
152 EXPORT_SYMBOL_GPL(typec_mux_get);
153
154 /**
155  * typec_mux_put - Release handle to a Multiplexer
156  * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
157  *
158  * Decrements reference count for @mux.
159  */
160 void typec_mux_put(struct typec_mux *mux)
161 {
162         if (!IS_ERR_OR_NULL(mux)) {
163                 module_put(mux->dev->driver->owner);
164                 put_device(mux->dev);
165         }
166 }
167 EXPORT_SYMBOL_GPL(typec_mux_put);
168
169 /**
170  * typec_mux_register - Register Multiplexer routing USB Type-C pins
171  * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
172  *
173  * USB Type-C connectors can be used for alternate modes of operation besides
174  * USB when Accessory/Alternate Modes are supported. With some of those modes,
175  * the pins on the connector need to be reconfigured. This function registers
176  * multiplexer switches routing the pins on the connector.
177  */
178 int typec_mux_register(struct typec_mux *mux)
179 {
180         mutex_lock(&mux_lock);
181         list_add_tail(&mux->entry, &mux_list);
182         mutex_unlock(&mux_lock);
183
184         return 0;
185 }
186 EXPORT_SYMBOL_GPL(typec_mux_register);
187
188 /**
189  * typec_mux_unregister - Unregister Multiplexer Switch
190  * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
191  *
192  * Unregister mux that was registered with typec_mux_register().
193  */
194 void typec_mux_unregister(struct typec_mux *mux)
195 {
196         mutex_lock(&mux_lock);
197         list_del(&mux->entry);
198         mutex_unlock(&mux_lock);
199 }
200 EXPORT_SYMBOL_GPL(typec_mux_unregister);