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

usb: typec: intel_pmc_mux: Support for device role (UFP)

This adds support for device data role, and data role
swapping. The driver no longer relies on the cached role, as
it may not be valid (for example after bootup). Instead, the
role is always checked by readding the port status from IOM.

Note. After this, the orientation is always only cached, so
the driver does not support scenario where the role is set
before orientation. It means the typec drivers must always
set the orientation first before role.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Rajmohan Mani <rajmohan.mani@intel.com>
Reviewed-by: Utkarsh Patel <utkarsh.h.patel@intel.com>
Tested-by: Utkarsh Patel <utkarsh.h.patel@intel.com>
Link: https://lore.kernel.org/r/20200907142428.35838-3-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
a5a6d275 43d596e3

+39 -37
+39 -37
drivers/usb/typec/mux/intel_pmc_mux.c
··· 224 224 return pmc_usb_mux_dp_hpd(port, state->data); 225 225 } 226 226 227 - if (data->status & DP_STATUS_IRQ_HPD) 228 - return pmc_usb_mux_dp_hpd(port, state->data); 229 - 230 227 req.usage = PMC_USB_ALT_MODE; 231 228 req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 232 229 req.mode_type = PMC_USB_MODE_TYPE_DP << PMC_USB_MODE_TYPE_SHIFT; ··· 342 345 return pmc_usb_command(port, &msg, sizeof(msg)); 343 346 } 344 347 345 - static int pmc_usb_connect(struct pmc_usb_port *port) 346 - { 347 - u8 msg[2]; 348 - 349 - if (port->iom_status & IOM_PORT_STATUS_CONNECTED) 350 - return 0; 351 - 352 - msg[0] = PMC_USB_CONNECT; 353 - msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 354 - 355 - msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; 356 - msg[1] |= hsl_orientation(port) << PMC_USB_MSG_ORI_HSL_SHIFT; 357 - msg[1] |= sbu_orientation(port) << PMC_USB_MSG_ORI_AUX_SHIFT; 358 - 359 - return pmc_usb_command(port, msg, sizeof(msg)); 360 - } 361 - 362 348 static int pmc_usb_disconnect(struct pmc_usb_port *port) 363 349 { 364 350 struct typec_displayport_data data = { }; ··· 362 382 return pmc_usb_command(port, msg, sizeof(msg)); 363 383 } 364 384 385 + static int pmc_usb_connect(struct pmc_usb_port *port, enum usb_role role) 386 + { 387 + u8 ufp = role == USB_ROLE_DEVICE ? 1 : 0; 388 + u8 msg[2]; 389 + int ret; 390 + 391 + if (port->orientation == TYPEC_ORIENTATION_NONE) 392 + return -EINVAL; 393 + 394 + if (port->iom_status & IOM_PORT_STATUS_CONNECTED) { 395 + if (port->role == role || port->role == USB_ROLE_NONE) 396 + return 0; 397 + 398 + /* Role swap */ 399 + ret = pmc_usb_disconnect(port); 400 + if (ret) 401 + return ret; 402 + } 403 + 404 + msg[0] = PMC_USB_CONNECT; 405 + msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; 406 + 407 + msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; 408 + msg[1] |= ufp << PMC_USB_MSG_UFP_SHIFT; 409 + msg[1] |= hsl_orientation(port) << PMC_USB_MSG_ORI_HSL_SHIFT; 410 + msg[1] |= sbu_orientation(port) << PMC_USB_MSG_ORI_AUX_SHIFT; 411 + 412 + return pmc_usb_command(port, msg, sizeof(msg)); 413 + } 414 + 365 415 static int 366 416 pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state) 367 417 { ··· 405 395 if (state->mode == TYPEC_STATE_SAFE) 406 396 return pmc_usb_mux_safe_state(port); 407 397 if (state->mode == TYPEC_STATE_USB) 408 - return pmc_usb_connect(port); 398 + return pmc_usb_connect(port, port->role); 409 399 410 400 if (state->alt) { 411 401 switch (state->alt->svid) { ··· 420 410 /* REVISIT: Try with usb3_port set to 0? */ 421 411 break; 422 412 case TYPEC_MODE_USB3: 423 - return pmc_usb_connect(port); 413 + return pmc_usb_connect(port, port->role); 424 414 case TYPEC_MODE_USB4: 425 415 return pmc_usb_mux_usb4(port, state); 426 416 } ··· 438 428 439 429 port->orientation = orientation; 440 430 441 - if (port->role) { 442 - if (orientation == TYPEC_ORIENTATION_NONE) 443 - return pmc_usb_disconnect(port); 444 - else 445 - return pmc_usb_connect(port); 446 - } 447 - 448 431 return 0; 449 432 } 450 433 451 434 static int pmc_usb_set_role(struct usb_role_switch *sw, enum usb_role role) 452 435 { 453 436 struct pmc_usb_port *port = usb_role_switch_get_drvdata(sw); 437 + int ret; 454 438 455 439 update_port_status(port); 456 440 441 + if (role == USB_ROLE_NONE) 442 + ret = pmc_usb_disconnect(port); 443 + else 444 + ret = pmc_usb_connect(port, role); 445 + 457 446 port->role = role; 458 447 459 - if (port->orientation) { 460 - if (role == USB_ROLE_NONE) 461 - return pmc_usb_disconnect(port); 462 - else 463 - return pmc_usb_connect(port); 464 - } 465 - 466 - return 0; 448 + return ret; 467 449 } 468 450 469 451 static int pmc_usb_register_port(struct pmc_usb *pmc, int index,