Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/chrome: cros_ec_typec: Thunderbolt support

Add support for entering and exiting Thunderbolt alt-mode using AP
driven alt-mode.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: Benson Leung <bleung@chromium.org>
Link: https://lore.kernel.org/r/20241213153543.v5.7.Ic61ced3cdfb5d6776435356061f12307da719829@changeid
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Abhishek Pandit-Subedi and committed by
Greg Kroah-Hartman
3b00be26 dbb3fc0f

+115 -11
+1
drivers/platform/chrome/Kconfig
··· 249 249 depends on USB_ROLE_SWITCH 250 250 default MFD_CROS_EC_DEV 251 251 select CROS_EC_TYPEC_ALTMODES if TYPEC_DP_ALTMODE 252 + select CROS_EC_TYPEC_ALTMODES if TYPEC_TBT_ALTMODE 252 253 help 253 254 If you say Y here, you get support for accessing Type C connector 254 255 information from the Chrome OS EC.
+12 -11
drivers/platform/chrome/cros_ec_typec.c
··· 303 303 304 304 /* 305 305 * Register TBT compatibility alt mode. The EC will not enter the mode 306 - * if it doesn't support it, so it's safe to register it unconditionally 307 - * here for now. 306 + * if it doesn't support it and it will not enter automatically by 307 + * design so we can use the |ap_driven_altmode| feature to check if we 308 + * should register it. 308 309 */ 309 - memset(&desc, 0, sizeof(desc)); 310 - desc.svid = USB_TYPEC_TBT_SID; 311 - desc.mode = TYPEC_ANY_MODE; 312 - amode = typec_port_register_altmode(port->port, &desc); 313 - if (IS_ERR(amode)) 314 - return PTR_ERR(amode); 315 - port->port_altmode[CROS_EC_ALTMODE_TBT] = amode; 316 - typec_altmode_set_drvdata(amode, port); 317 - amode->ops = &port_amode_ops; 310 + if (typec->ap_driven_altmode) { 311 + memset(&desc, 0, sizeof(desc)); 312 + desc.svid = USB_TYPEC_TBT_SID; 313 + desc.mode = TBT_MODE; 314 + amode = cros_typec_register_thunderbolt(port, &desc); 315 + if (IS_ERR(amode)) 316 + return PTR_ERR(amode); 317 + port->port_altmode[CROS_EC_ALTMODE_TBT] = amode; 318 + } 318 319 319 320 port->state.alt = NULL; 320 321 port->state.mode = TYPEC_STATE_USB;
+88
drivers/platform/chrome/cros_typec_altmode.c
··· 10 10 #include <linux/mutex.h> 11 11 #include <linux/workqueue.h> 12 12 #include <linux/usb/typec_dp.h> 13 + #include <linux/usb/typec_tbt.h> 13 14 #include <linux/usb/pd_vdo.h> 14 15 15 16 #include "cros_typec_altmode.h" ··· 73 72 74 73 if (adata->sid == USB_TYPEC_DP_SID) 75 74 req.mode_to_enter = CROS_EC_ALTMODE_DP; 75 + else if (adata->sid == USB_TYPEC_TBT_SID) 76 + req.mode_to_enter = CROS_EC_ALTMODE_TBT; 76 77 else 77 78 return -EOPNOTSUPP; 78 79 ··· 199 196 return 0; 200 197 } 201 198 199 + static int cros_typec_thunderbolt_vdm(struct typec_altmode *alt, u32 header, 200 + const u32 *data, int count) 201 + { 202 + struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt); 203 + 204 + int cmd_type = PD_VDO_CMDT(header); 205 + int cmd = PD_VDO_CMD(header); 206 + int svdm_version; 207 + 208 + svdm_version = typec_altmode_get_svdm_version(alt); 209 + if (svdm_version < 0) 210 + return svdm_version; 211 + 212 + mutex_lock(&adata->lock); 213 + 214 + switch (cmd_type) { 215 + case CMDT_INIT: 216 + if (PD_VDO_SVDM_VER(header) < svdm_version) { 217 + typec_partner_set_svdm_version(adata->port->partner, 218 + PD_VDO_SVDM_VER(header)); 219 + svdm_version = PD_VDO_SVDM_VER(header); 220 + } 221 + 222 + adata->header = VDO(adata->sid, 1, svdm_version, cmd); 223 + adata->header |= VDO_OPOS(adata->mode); 224 + 225 + switch (cmd) { 226 + case CMD_ENTER_MODE: 227 + /* Don't respond to the enter mode vdm because it 228 + * triggers mux configuration. This is handled directly 229 + * by the cros_ec_typec driver so the Thunderbolt driver 230 + * doesn't need to be involved. 231 + */ 232 + break; 233 + default: 234 + adata->header |= VDO_CMDT(CMDT_RSP_ACK); 235 + schedule_work(&adata->work); 236 + break; 237 + } 238 + 239 + break; 240 + default: 241 + break; 242 + } 243 + 244 + mutex_unlock(&adata->lock); 245 + return 0; 246 + } 247 + 248 + 202 249 static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header, 203 250 const u32 *data, int count) 204 251 { ··· 259 206 260 207 if (adata->sid == USB_TYPEC_DP_SID) 261 208 return cros_typec_displayport_vdm(alt, header, data, count); 209 + 210 + if (adata->sid == USB_TYPEC_TBT_SID) 211 + return cros_typec_thunderbolt_vdm(alt, header, data, count); 262 212 263 213 return -EINVAL; 264 214 } ··· 330 274 adata->alt = alt; 331 275 adata->port = port; 332 276 adata->ap_mode_entry = ap_mode_entry; 277 + adata->sid = desc->svid; 278 + adata->mode = desc->mode; 279 + 280 + typec_altmode_set_ops(alt, &cros_typec_altmode_ops); 281 + typec_altmode_set_drvdata(alt, adata); 282 + 283 + return alt; 284 + } 285 + #endif 286 + 287 + #if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE) 288 + struct typec_altmode * 289 + cros_typec_register_thunderbolt(struct cros_typec_port *port, 290 + struct typec_altmode_desc *desc) 291 + { 292 + struct typec_altmode *alt; 293 + struct cros_typec_altmode_data *adata; 294 + 295 + alt = typec_port_register_altmode(port->port, desc); 296 + if (IS_ERR(alt)) 297 + return alt; 298 + 299 + adata = devm_kzalloc(&alt->dev, sizeof(*adata), GFP_KERNEL); 300 + if (!adata) { 301 + typec_unregister_altmode(alt); 302 + return ERR_PTR(-ENOMEM); 303 + } 304 + 305 + INIT_WORK(&adata->work, cros_typec_altmode_work); 306 + adata->alt = alt; 307 + adata->port = port; 308 + adata->ap_mode_entry = true; 333 309 adata->sid = desc->svid; 334 310 adata->mode = desc->mode; 335 311
+14
drivers/platform/chrome/cros_typec_altmode.h
··· 34 34 return 0; 35 35 } 36 36 #endif 37 + 38 + #if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE) 39 + struct typec_altmode * 40 + cros_typec_register_thunderbolt(struct cros_typec_port *port, 41 + struct typec_altmode_desc *desc); 42 + #else 43 + static inline struct typec_altmode * 44 + cros_typec_register_thunderbolt(struct cros_typec_port *port, 45 + struct typec_altmode_desc *desc) 46 + { 47 + return typec_port_register_altmode(port->port, desc); 48 + } 49 + #endif 50 + 37 51 #endif /* __CROS_TYPEC_ALTMODE_H__ */