1 // SPDX-License-Identifier: GPL-2.0
3 * USB Typec-C Thunderbolt3 Alternate Mode driver
5 * Copyright (C) 2019 Intel Corporation
9 #include <linux/lockdep.h>
10 #include <linux/module.h>
11 #include <linux/mutex.h>
12 #include <linux/workqueue.h>
13 #include <linux/usb/pd_vdo.h>
14 #include <linux/usb/typec_altmode.h>
15 #include <linux/usb/typec_tbt.h>
19 TBT_STATE_SOP_P_ENTER,
20 TBT_STATE_SOP_PP_ENTER,
23 TBT_STATE_SOP_PP_EXIT,
29 struct typec_cable *cable;
30 struct typec_altmode *alt;
31 struct typec_altmode *plug[2];
34 struct work_struct work;
35 struct mutex lock; /* device lock */
38 static bool tbt_ready(struct typec_altmode *alt);
40 static int tbt_enter_mode(struct tbt_altmode *tbt)
42 struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P];
45 vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
46 vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
50 if (typec_cable_is_active(tbt->cable))
51 vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
53 vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
54 vdo |= plug->vdo & TBT_CABLE_ROUNDED;
55 vdo |= plug->vdo & TBT_CABLE_OPTICAL;
56 vdo |= plug->vdo & TBT_CABLE_RETIMER;
57 vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
59 vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
63 return typec_altmode_enter(tbt->alt, &vdo);
66 static void tbt_altmode_work(struct work_struct *work)
68 struct tbt_altmode *tbt = container_of(work, struct tbt_altmode, work);
71 mutex_lock(&tbt->lock);
74 case TBT_STATE_SOP_P_ENTER:
75 ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_P, NULL);
77 dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_P]->dev,
78 "failed to enter mode (%d)\n", ret);
82 case TBT_STATE_SOP_PP_ENTER:
83 ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_PP, NULL);
85 dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_PP]->dev,
86 "failed to enter mode (%d)\n", ret);
91 ret = tbt_enter_mode(tbt);
93 dev_dbg(&tbt->alt->dev, "failed to enter mode (%d)\n",
97 typec_altmode_exit(tbt->alt);
99 case TBT_STATE_SOP_PP_EXIT:
100 typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_PP);
102 case TBT_STATE_SOP_P_EXIT:
103 typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_P);
109 tbt->state = TBT_STATE_IDLE;
111 mutex_unlock(&tbt->lock);
115 for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
117 typec_altmode_put_plug(tbt->plug[i]);
122 tbt->state = TBT_STATE_ENTER;
123 schedule_work(&tbt->work);
124 mutex_unlock(&tbt->lock);
128 * If SOP' is available, enter that first (which will trigger a VDM response
129 * that will enter SOP" if available and then the port). If entering SOP' fails,
130 * stop attempting to enter either cable altmode (probably not supported) and
131 * directly enter the port altmode.
133 static int tbt_enter_modes_ordered(struct typec_altmode *alt)
135 struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
138 lockdep_assert_held(&tbt->lock);
140 if (!tbt_ready(tbt->alt))
143 if (tbt->plug[TYPEC_PLUG_SOP_P]) {
144 ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
146 for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
148 typec_altmode_put_plug(tbt->plug[i]);
157 return tbt_enter_mode(tbt);
160 static int tbt_cable_altmode_vdm(struct typec_altmode *alt,
161 enum typec_plug_index sop, const u32 hdr,
162 const u32 *vdo, int count)
164 struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
165 int cmd_type = PD_VDO_CMDT(hdr);
166 int cmd = PD_VDO_CMD(hdr);
168 mutex_lock(&tbt->lock);
170 if (tbt->state != TBT_STATE_IDLE) {
171 mutex_unlock(&tbt->lock);
180 * Following the order described in USB Type-C Spec
181 * R2.0 Section 6.7.3: SOP', SOP", then port.
183 if (sop == TYPEC_PLUG_SOP_P) {
184 if (tbt->plug[TYPEC_PLUG_SOP_PP])
185 tbt->state = TBT_STATE_SOP_PP_ENTER;
187 tbt->state = TBT_STATE_ENTER;
188 } else if (sop == TYPEC_PLUG_SOP_PP)
189 tbt->state = TBT_STATE_ENTER;
193 /* Exit in opposite order: Port, SOP", then SOP'. */
194 if (sop == TYPEC_PLUG_SOP_PP)
195 tbt->state = TBT_STATE_SOP_P_EXIT;
203 if (tbt->state != TBT_STATE_IDLE)
204 schedule_work(&tbt->work);
206 mutex_unlock(&tbt->lock);
210 static int tbt_altmode_vdm(struct typec_altmode *alt,
211 const u32 hdr, const u32 *vdo, int count)
213 struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
214 struct typec_thunderbolt_data data;
215 int cmd_type = PD_VDO_CMDT(hdr);
216 int cmd = PD_VDO_CMD(hdr);
218 mutex_lock(&tbt->lock);
220 if (tbt->state != TBT_STATE_IDLE) {
221 mutex_unlock(&tbt->lock);
227 /* Port altmode is last to enter and first to exit. */
230 memset(&data, 0, sizeof(data));
232 data.device_mode = tbt->alt->vdo;
233 data.enter_vdo = tbt->enter_vdo;
234 if (tbt->plug[TYPEC_PLUG_SOP_P])
235 data.cable_mode = tbt->plug[TYPEC_PLUG_SOP_P]->vdo;
237 typec_altmode_notify(alt, TYPEC_STATE_MODAL, &data);
240 if (tbt->plug[TYPEC_PLUG_SOP_PP])
241 tbt->state = TBT_STATE_SOP_PP_EXIT;
242 else if (tbt->plug[TYPEC_PLUG_SOP_P])
243 tbt->state = TBT_STATE_SOP_P_EXIT;
250 dev_warn(&alt->dev, "Enter Mode refused\n");
260 if (tbt->state != TBT_STATE_IDLE)
261 schedule_work(&tbt->work);
263 mutex_unlock(&tbt->lock);
268 static int tbt_altmode_activate(struct typec_altmode *alt, int activate)
270 struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
273 mutex_lock(&tbt->lock);
276 ret = tbt_enter_modes_ordered(alt);
278 ret = typec_altmode_exit(alt);
280 mutex_unlock(&tbt->lock);
285 static const struct typec_altmode_ops tbt_altmode_ops = {
286 .vdm = tbt_altmode_vdm,
287 .activate = tbt_altmode_activate
290 static const struct typec_cable_ops tbt_cable_ops = {
291 .vdm = tbt_cable_altmode_vdm,
294 static int tbt_altmode_probe(struct typec_altmode *alt)
296 struct tbt_altmode *tbt;
298 tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
302 INIT_WORK(&tbt->work, tbt_altmode_work);
303 mutex_init(&tbt->lock);
306 alt->desc = "Thunderbolt3";
307 typec_altmode_set_drvdata(alt, tbt);
308 typec_altmode_set_ops(alt, &tbt_altmode_ops);
310 if (tbt_ready(alt)) {
311 if (tbt->plug[TYPEC_PLUG_SOP_P])
312 tbt->state = TBT_STATE_SOP_P_ENTER;
313 else if (tbt->plug[TYPEC_PLUG_SOP_PP])
314 tbt->state = TBT_STATE_SOP_PP_ENTER;
316 tbt->state = TBT_STATE_ENTER;
317 schedule_work(&tbt->work);
323 static void tbt_altmode_remove(struct typec_altmode *alt)
325 struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
327 for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
329 typec_altmode_put_plug(tbt->plug[i]);
333 typec_cable_put(tbt->cable);
336 static bool tbt_ready(struct typec_altmode *alt)
338 struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
339 struct typec_altmode *plug;
344 /* Thunderbolt 3 requires a cable with eMarker */
345 tbt->cable = typec_cable_get(typec_altmode2port(tbt->alt));
349 /* We accept systems without SOP' or SOP''. This means the port altmode
350 * driver will be responsible for properly ordering entry/exit.
352 for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) {
353 plug = typec_altmode_get_plug(tbt->alt, i);
357 if (!plug || plug->svid != USB_TYPEC_TBT_SID)
360 plug->desc = "Thunderbolt3";
361 plug->cable_ops = &tbt_cable_ops;
362 typec_altmode_set_drvdata(plug, tbt);
370 static const struct typec_device_id tbt_typec_id[] = {
371 { USB_TYPEC_TBT_SID },
374 MODULE_DEVICE_TABLE(typec, tbt_typec_id);
376 static struct typec_altmode_driver tbt_altmode_driver = {
377 .id_table = tbt_typec_id,
378 .probe = tbt_altmode_probe,
379 .remove = tbt_altmode_remove,
381 .name = "typec-thunderbolt",
384 module_typec_altmode_driver(tbt_altmode_driver);
387 MODULE_LICENSE("GPL");
388 MODULE_DESCRIPTION("Thunderbolt3 USB Type-C Alternate Mode");