Skip to content

Commit 0539c5a

Browse files
quic-kdybcioandersson
authored andcommitted
soc: qcom: pmic_glink_altmode: Consume TBT3/USB4 mode notifications
Some compute SoCs support additional operation modes, extending the existing set of USB3/safe/DP-alt-mode. The firmware performs all the necessary handshakes for us and there is no room for error on that level (i.e. the sequences will match what happens on Windows). The trade-off with that approach is that the received notifications trim some of the PDO/EUDO data (particularly the cable/plug parts), offering a set of similar-in-nature-but-not-the-same indicators. In an attempt to remedy this, I reconstructed some of it, so that the connected mux/retimer drivers can continue to behave as expected. Add support for parsing the aforementioned data coming from PMIC_GLINK and passing it on to the various Type-C components. Reviewed-by: Jack Pham <jack.pham@oss.qualcomm.com> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> Link: https://lore.kernel.org/r/20251027-topic-pg_altmode_usb4-v1-1-2931a3ecc146@oss.qualcomm.com Signed-off-by: Bjorn Andersson <andersson@kernel.org>
1 parent 42f2799 commit 0539c5a

1 file changed

Lines changed: 165 additions & 23 deletions

File tree

drivers/soc/qcom/pmic_glink_altmode.c

Lines changed: 165 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
#include <linux/soc/qcom/pdr.h>
1515
#include <drm/bridge/aux-bridge.h>
1616

17+
#include <linux/usb/pd.h>
1718
#include <linux/usb/typec_altmode.h>
1819
#include <linux/usb/typec_dp.h>
1920
#include <linux/usb/typec_mux.h>
2021
#include <linux/usb/typec_retimer.h>
22+
#include <linux/usb/typec_tbt.h>
2123

2224
#include <linux/soc/qcom/pmic_glink.h>
2325

@@ -37,11 +39,38 @@ struct usbc_write_req {
3739
__le32 reserved;
3840
};
3941

40-
#define NOTIFY_PAYLOAD_SIZE 16
42+
struct usbc_sc8280x_dp_data {
43+
u8 pin_assignment : 6;
44+
u8 hpd_state : 1;
45+
u8 hpd_irq : 1;
46+
u8 res[7];
47+
};
48+
49+
/* Used for both TBT and USB4 notifications */
50+
struct usbc_sc8280x_tbt_data {
51+
u8 usb_speed : 3;
52+
u8 cable_type : 3;
53+
/* This field is NOP on USB4, all cables support rounded rates by spec */
54+
u8 rounded_cable : 1;
55+
u8 power_limited : 1;
56+
u8 res[11];
57+
};
58+
4159
struct usbc_notify {
4260
struct pmic_glink_hdr hdr;
43-
char payload[NOTIFY_PAYLOAD_SIZE];
44-
u32 reserved;
61+
u8 port_idx;
62+
u8 orientation;
63+
u8 mux_ctrl;
64+
#define MUX_CTRL_STATE_NO_CONN 0
65+
#define MUX_CTRL_STATE_TUNNELING 4
66+
67+
u8 res;
68+
__le16 vid;
69+
__le16 svid;
70+
union usbc_sc8280x_extended_data {
71+
struct usbc_sc8280x_dp_data dp;
72+
struct usbc_sc8280x_tbt_data tbt;
73+
} extended_data;
4574
};
4675

4776
struct usbc_sc8180x_notify {
@@ -74,17 +103,20 @@ struct pmic_glink_altmode_port {
74103
struct typec_retimer *typec_retimer;
75104
struct typec_retimer_state retimer_state;
76105
struct typec_altmode dp_alt;
106+
struct typec_altmode tbt_alt;
77107

78108
struct work_struct work;
79109

80110
struct auxiliary_device *bridge;
81111

82112
enum typec_orientation orientation;
83113
u16 svid;
114+
struct usbc_sc8280x_tbt_data tbt_data;
84115
u8 dp_data;
85116
u8 mode;
86117
u8 hpd_state;
87118
u8 hpd_irq;
119+
u8 mux_ctrl;
88120
};
89121

90122
#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work)
@@ -170,6 +202,102 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
170202
dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret);
171203
}
172204

205+
static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode,
206+
struct pmic_glink_altmode_port *port)
207+
{
208+
struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
209+
struct typec_thunderbolt_data tbt_data = {};
210+
u32 cable_speed;
211+
int ret;
212+
213+
/* Device Discover Mode VDO */
214+
tbt_data.device_mode = TBT_MODE;
215+
tbt_data.device_mode |= TBT_SET_ADAPTER(TBT_ADAPTER_TBT3);
216+
217+
/* Cable Discover Mode VDO */
218+
tbt_data.cable_mode = TBT_MODE;
219+
220+
if (tbt->usb_speed == 0) {
221+
cable_speed = TBT_CABLE_USB3_PASSIVE;
222+
} else if (tbt->usb_speed == 1) {
223+
cable_speed = TBT_CABLE_10_AND_20GBPS;
224+
} else {
225+
dev_err(altmode->dev,
226+
"Got illegal TBT3 cable speed value (%u), falling back to passive\n",
227+
tbt->usb_speed);
228+
cable_speed = TBT_CABLE_USB3_PASSIVE;
229+
}
230+
tbt_data.cable_mode |= TBT_SET_CABLE_SPEED(cable_speed);
231+
232+
if (tbt->cable_type) {
233+
tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE;
234+
tbt_data.cable_mode |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
235+
}
236+
237+
/* Enter Mode VDO */
238+
tbt_data.enter_vdo |= TBT_MODE;
239+
tbt_data.enter_vdo |= TBT_ENTER_MODE_CABLE_SPEED(cable_speed);
240+
241+
if (tbt->cable_type) {
242+
tbt_data.enter_vdo |= TBT_CABLE_ACTIVE_PASSIVE;
243+
tbt_data.enter_vdo |= TBT_SET_CABLE_ROUNDED(tbt->rounded_cable);
244+
}
245+
246+
port->state.alt = &port->tbt_alt;
247+
port->state.data = &tbt_data;
248+
port->state.mode = TYPEC_MODAL_STATE(port->mode);
249+
250+
ret = typec_mux_set(port->typec_mux, &port->state);
251+
if (ret)
252+
dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
253+
254+
port->retimer_state.alt = &port->tbt_alt;
255+
port->retimer_state.data = &tbt_data;
256+
port->retimer_state.mode = TYPEC_MODAL_STATE(port->mode);
257+
258+
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
259+
if (ret)
260+
dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
261+
}
262+
263+
static void pmic_glink_altmode_enable_usb4(struct pmic_glink_altmode *altmode,
264+
struct pmic_glink_altmode_port *port)
265+
{
266+
struct usbc_sc8280x_tbt_data *tbt = &port->tbt_data;
267+
struct enter_usb_data data = {};
268+
int ret;
269+
270+
data.eudo = FIELD_PREP(EUDO_USB_MODE_MASK, EUDO_USB_MODE_USB4);
271+
272+
if (tbt->usb_speed == 0) {
273+
data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
274+
} else if (tbt->usb_speed == 1) {
275+
data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN3);
276+
} else {
277+
pr_err("Got illegal USB4 cable speed value (%u), falling back to G2\n",
278+
tbt->usb_speed);
279+
data.eudo |= FIELD_PREP(EUDO_CABLE_SPEED_MASK, EUDO_CABLE_SPEED_USB4_GEN2);
280+
}
281+
282+
data.eudo |= FIELD_PREP(EUDO_CABLE_TYPE_MASK, tbt->cable_type);
283+
284+
port->state.alt = NULL;
285+
port->state.data = &data;
286+
port->state.mode = TYPEC_MODE_USB4;
287+
288+
ret = typec_mux_set(port->typec_mux, &port->state);
289+
if (ret)
290+
dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
291+
292+
port->retimer_state.alt = NULL;
293+
port->retimer_state.data = &data;
294+
port->retimer_state.mode = TYPEC_MODE_USB4;
295+
296+
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
297+
if (ret)
298+
dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
299+
}
300+
173301
static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
174302
struct pmic_glink_altmode_port *port)
175303
{
@@ -222,22 +350,24 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
222350

223351
typec_switch_set(alt_port->typec_switch, alt_port->orientation);
224352

225-
if (alt_port->svid == USB_TYPEC_DP_SID) {
226-
if (alt_port->mode == 0xff) {
227-
pmic_glink_altmode_safe(altmode, alt_port);
228-
} else {
229-
pmic_glink_altmode_enable_dp(altmode, alt_port,
230-
alt_port->mode,
231-
alt_port->hpd_state,
232-
alt_port->hpd_irq);
233-
}
353+
if (alt_port->mux_ctrl == MUX_CTRL_STATE_NO_CONN) {
354+
pmic_glink_altmode_safe(altmode, alt_port);
355+
} else if (alt_port->svid == USB_TYPEC_TBT_SID) {
356+
pmic_glink_altmode_enable_tbt(altmode, alt_port);
357+
} else if (alt_port->svid == USB_TYPEC_DP_SID) {
358+
pmic_glink_altmode_enable_dp(altmode, alt_port,
359+
alt_port->mode,
360+
alt_port->hpd_state,
361+
alt_port->hpd_irq);
234362

235363
if (alt_port->hpd_state)
236364
conn_status = connector_status_connected;
237365
else
238366
conn_status = connector_status_disconnected;
239367

240368
drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status);
369+
} else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
370+
pmic_glink_altmode_enable_usb4(altmode, alt_port);
241371
} else {
242372
pmic_glink_altmode_enable_usb(altmode, alt_port);
243373
}
@@ -314,11 +444,10 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
314444
u16 svid, const void *data, size_t len)
315445
{
316446
struct pmic_glink_altmode_port *alt_port;
447+
const struct usbc_sc8280x_tbt_data *tbt;
448+
const struct usbc_sc8280x_dp_data *dp;
317449
const struct usbc_notify *notify;
318450
u8 orientation;
319-
u8 hpd_state;
320-
u8 hpd_irq;
321-
u8 mode;
322451
u8 port;
323452

324453
if (len != sizeof(*notify)) {
@@ -329,11 +458,8 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
329458

330459
notify = data;
331460

332-
port = notify->payload[0];
333-
orientation = notify->payload[1];
334-
mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A;
335-
hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]);
336-
hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]);
461+
port = notify->port_idx;
462+
orientation = notify->orientation;
337463

338464
if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) {
339465
dev_dbg(altmode->dev, "notification on undefined port %d\n", port);
@@ -343,9 +469,21 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod
343469
alt_port = &altmode->ports[port];
344470
alt_port->orientation = pmic_glink_altmode_orientation(orientation);
345471
alt_port->svid = svid;
346-
alt_port->mode = mode;
347-
alt_port->hpd_state = hpd_state;
348-
alt_port->hpd_irq = hpd_irq;
472+
alt_port->mux_ctrl = notify->mux_ctrl;
473+
474+
if (svid == USB_TYPEC_DP_SID) {
475+
dp = &notify->extended_data.dp;
476+
477+
alt_port->mode = dp->pin_assignment - DPAM_HPD_A;
478+
alt_port->hpd_state = dp->hpd_state;
479+
alt_port->hpd_irq = dp->hpd_irq;
480+
} else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
481+
/* Valid for both USB4 and TBT3 */
482+
tbt = &notify->extended_data.tbt;
483+
484+
alt_port->tbt_data = *tbt;
485+
}
486+
349487
schedule_work(&alt_port->work);
350488
}
351489

@@ -471,6 +609,10 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
471609
alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;
472610
alt_port->dp_alt.active = 1;
473611

612+
alt_port->tbt_alt.svid = USB_TYPEC_TBT_SID;
613+
alt_port->tbt_alt.mode = TYPEC_TBT_MODE;
614+
alt_port->tbt_alt.active = 1;
615+
474616
alt_port->typec_mux = fwnode_typec_mux_get(fwnode);
475617
if (IS_ERR(alt_port->typec_mux)) {
476618
fwnode_handle_put(fwnode);

0 commit comments

Comments
 (0)