1
2
3
4
5
6
7
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/property.h>
15#include <linux/slab.h>
16#include <linux/usb/typec_mux.h>
17
18static DEFINE_MUTEX(switch_lock);
19static DEFINE_MUTEX(mux_lock);
20static LIST_HEAD(switch_list);
21static LIST_HEAD(mux_list);
22
23static void *typec_switch_match(struct device_connection *con, int ep,
24 void *data)
25{
26 struct typec_switch *sw;
27
28 if (!con->fwnode) {
29 list_for_each_entry(sw, &switch_list, entry)
30 if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
31 return sw;
32 return ERR_PTR(-EPROBE_DEFER);
33 }
34
35
36
37
38
39 if (con->id && !fwnode_property_present(con->fwnode, con->id))
40 return NULL;
41
42 list_for_each_entry(sw, &switch_list, entry)
43 if (dev_fwnode(sw->dev) == con->fwnode)
44 return sw;
45
46 return con->id ? ERR_PTR(-EPROBE_DEFER) : NULL;
47}
48
49
50
51
52
53
54
55
56
57
58struct typec_switch *typec_switch_get(struct device *dev)
59{
60 struct typec_switch *sw;
61
62 mutex_lock(&switch_lock);
63 sw = device_connection_find_match(dev, "orientation-switch", NULL,
64 typec_switch_match);
65 if (!IS_ERR_OR_NULL(sw)) {
66 WARN_ON(!try_module_get(sw->dev->driver->owner));
67 get_device(sw->dev);
68 }
69 mutex_unlock(&switch_lock);
70
71 return sw;
72}
73EXPORT_SYMBOL_GPL(typec_switch_get);
74
75
76
77
78
79
80
81void typec_switch_put(struct typec_switch *sw)
82{
83 if (!IS_ERR_OR_NULL(sw)) {
84 module_put(sw->dev->driver->owner);
85 put_device(sw->dev);
86 }
87}
88EXPORT_SYMBOL_GPL(typec_switch_put);
89
90
91
92
93
94
95
96
97
98
99int typec_switch_register(struct typec_switch *sw)
100{
101 mutex_lock(&switch_lock);
102 list_add_tail(&sw->entry, &switch_list);
103 mutex_unlock(&switch_lock);
104
105 return 0;
106}
107EXPORT_SYMBOL_GPL(typec_switch_register);
108
109
110
111
112
113
114
115void typec_switch_unregister(struct typec_switch *sw)
116{
117 mutex_lock(&switch_lock);
118 list_del(&sw->entry);
119 mutex_unlock(&switch_lock);
120}
121EXPORT_SYMBOL_GPL(typec_switch_unregister);
122
123
124
125static void *typec_mux_match(struct device_connection *con, int ep, void *data)
126{
127 const struct typec_altmode_desc *desc = data;
128 struct typec_mux *mux;
129 int nval;
130 bool match;
131 u16 *val;
132 int i;
133
134 if (!con->fwnode) {
135 list_for_each_entry(mux, &mux_list, entry)
136 if (!strcmp(con->endpoint[ep], dev_name(mux->dev)))
137 return mux;
138 return ERR_PTR(-EPROBE_DEFER);
139 }
140
141
142
143
144
145 match = !con->id;
146 if (match)
147 goto find_mux;
148
149
150 if (!desc) {
151 match = fwnode_property_present(con->fwnode, "accessory");
152 if (match)
153 goto find_mux;
154 return NULL;
155 }
156
157
158 nval = fwnode_property_read_u16_array(con->fwnode, "svid", NULL, 0);
159 if (nval <= 0)
160 return NULL;
161
162 val = kcalloc(nval, sizeof(*val), GFP_KERNEL);
163 if (!val)
164 return ERR_PTR(-ENOMEM);
165
166 nval = fwnode_property_read_u16_array(con->fwnode, "svid", val, nval);
167 if (nval < 0) {
168 kfree(val);
169 return ERR_PTR(nval);
170 }
171
172 for (i = 0; i < nval; i++) {
173 match = val[i] == desc->svid;
174 if (match) {
175 kfree(val);
176 goto find_mux;
177 }
178 }
179 kfree(val);
180 return NULL;
181
182find_mux:
183 list_for_each_entry(mux, &mux_list, entry)
184 if (dev_fwnode(mux->dev) == con->fwnode)
185 return mux;
186
187 return ERR_PTR(-EPROBE_DEFER);
188}
189
190
191
192
193
194
195
196
197
198
199
200struct typec_mux *typec_mux_get(struct device *dev,
201 const struct typec_altmode_desc *desc)
202{
203 struct typec_mux *mux;
204
205 mutex_lock(&mux_lock);
206 mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
207 typec_mux_match);
208 if (!IS_ERR_OR_NULL(mux)) {
209 WARN_ON(!try_module_get(mux->dev->driver->owner));
210 get_device(mux->dev);
211 }
212 mutex_unlock(&mux_lock);
213
214 return mux;
215}
216EXPORT_SYMBOL_GPL(typec_mux_get);
217
218
219
220
221
222
223
224void typec_mux_put(struct typec_mux *mux)
225{
226 if (!IS_ERR_OR_NULL(mux)) {
227 module_put(mux->dev->driver->owner);
228 put_device(mux->dev);
229 }
230}
231EXPORT_SYMBOL_GPL(typec_mux_put);
232
233
234
235
236
237
238
239
240
241
242int typec_mux_register(struct typec_mux *mux)
243{
244 mutex_lock(&mux_lock);
245 list_add_tail(&mux->entry, &mux_list);
246 mutex_unlock(&mux_lock);
247
248 return 0;
249}
250EXPORT_SYMBOL_GPL(typec_mux_register);
251
252
253
254
255
256
257
258void typec_mux_unregister(struct typec_mux *mux)
259{
260 mutex_lock(&mux_lock);
261 list_del(&mux->entry);
262 mutex_unlock(&mux_lock);
263}
264EXPORT_SYMBOL_GPL(typec_mux_unregister);
265