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

usb: typec: tcpm: Determine common SVDM Version

PD Spec Revision 3.0 Version 2.0 + ECNs 2020-12-10
6.4.4.2.3 Structured VDM Version
"The Structured VDM Version field of the Discover Identity Command
sent and received during VDM discovery Shall be used to determine the
lowest common Structured VDM Version supported by the Port Partners or
Cable Plug and Shall continue to operate using this Specification
Revision until they are Detached."

Also clear the fields newly defined in SVDM version 2.0 if the
negotiated SVDM version is 1.0.

Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Kyle Tso <kyletso@google.com>
Link: https://lore.kernel.org/r/20210205033415.3320439-4-kyletso@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kyle Tso and committed by
Greg Kroah-Hartman
5e1d4c49 31737c27

+61 -10
+61 -10
drivers/usb/typec/tcpm/tcpm.c
··· 1475 1475 const u32 *p, int cnt, u32 *response, 1476 1476 enum adev_actions *adev_action) 1477 1477 { 1478 + struct typec_port *typec = port->typec_port; 1478 1479 struct typec_altmode *pdev; 1479 1480 struct pd_mode_data *modep; 1481 + int svdm_version; 1480 1482 int rlen = 0; 1481 1483 int cmd_type; 1482 1484 int cmd; ··· 1495 1493 pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX, 1496 1494 PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); 1497 1495 1496 + svdm_version = typec_get_negotiated_svdm_version(typec); 1497 + if (svdm_version < 0) 1498 + return 0; 1499 + 1498 1500 switch (cmd_type) { 1499 1501 case CMDT_INIT: 1500 1502 switch (cmd) { ··· 1506 1500 if (PD_VDO_VID(p[0]) != USB_SID_PD) 1507 1501 break; 1508 1502 1503 + if (PD_VDO_SVDM_VER(p[0]) < svdm_version) 1504 + typec_partner_set_svdm_version(port->partner, 1505 + PD_VDO_SVDM_VER(p[0])); 1509 1506 /* 6.4.4.3.1: Only respond as UFP (device) */ 1510 1507 if (port->data_role == TYPEC_DEVICE && 1511 1508 port->nr_snk_vdo) { 1512 - for (i = 0; i < port->nr_snk_vdo; i++) 1509 + /* 1510 + * Product Type DFP and Connector Type are not defined in SVDM 1511 + * version 1.0 and shall be set to zero. 1512 + */ 1513 + if (typec_get_negotiated_svdm_version(typec) < SVDM_VER_2_0) 1514 + response[1] = port->snk_vdo[0] & ~IDH_DFP_MASK 1515 + & ~IDH_CONN_MASK; 1516 + else 1517 + response[1] = port->snk_vdo[0]; 1518 + for (i = 1; i < port->nr_snk_vdo; i++) 1513 1519 response[i + 1] = port->snk_vdo[i]; 1514 1520 rlen = port->nr_snk_vdo + 1; 1515 1521 } ··· 1550 1532 response[0] = p[0] | VDO_CMDT(CMDT_RSP_BUSY); 1551 1533 rlen = 1; 1552 1534 } 1535 + response[0] = (response[0] & ~VDO_SVDM_VERS_MASK) | 1536 + (VDO_SVDM_VERS(typec_get_negotiated_svdm_version(typec))); 1553 1537 break; 1554 1538 case CMDT_RSP_ACK: 1555 1539 /* silently drop message if we are not connected */ ··· 1562 1542 1563 1543 switch (cmd) { 1564 1544 case CMD_DISCOVER_IDENT: 1545 + if (PD_VDO_SVDM_VER(p[0]) < svdm_version) 1546 + typec_partner_set_svdm_version(port->partner, 1547 + PD_VDO_SVDM_VER(p[0])); 1565 1548 /* 6.4.4.3.1 */ 1566 1549 svdm_consume_identity(port, p, cnt); 1567 - response[0] = VDO(USB_SID_PD, 1, SVDM_VER_1_0, CMD_DISCOVER_SVID); 1550 + response[0] = VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version(typec), 1551 + CMD_DISCOVER_SVID); 1568 1552 rlen = 1; 1569 1553 break; 1570 1554 case CMD_DISCOVER_SVID: 1571 1555 /* 6.4.4.3.2 */ 1572 1556 if (svdm_consume_svids(port, p, cnt)) { 1573 - response[0] = VDO(USB_SID_PD, 1, SVDM_VER_1_0, 1574 - CMD_DISCOVER_SVID); 1557 + response[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); 1575 1558 rlen = 1; 1576 1559 } else if (modep->nsvids && supports_modal(port)) { 1577 - response[0] = VDO(modep->svids[0], 1, SVDM_VER_1_0, 1560 + response[0] = VDO(modep->svids[0], 1, svdm_version, 1578 1561 CMD_DISCOVER_MODES); 1579 1562 rlen = 1; 1580 1563 } ··· 1588 1565 modep->svid_index++; 1589 1566 if (modep->svid_index < modep->nsvids) { 1590 1567 u16 svid = modep->svids[modep->svid_index]; 1591 - response[0] = VDO(svid, 1, SVDM_VER_1_0, CMD_DISCOVER_MODES); 1568 + response[0] = VDO(svid, 1, svdm_version, CMD_DISCOVER_MODES); 1592 1569 rlen = 1; 1593 1570 } else { 1594 1571 tcpm_register_partner_altmodes(port); ··· 1615 1592 /* Unrecognized SVDM */ 1616 1593 response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); 1617 1594 rlen = 1; 1595 + response[0] = (response[0] & ~VDO_SVDM_VERS_MASK) | 1596 + (VDO_SVDM_VERS(svdm_version)); 1618 1597 break; 1619 1598 } 1620 1599 break; ··· 1636 1611 /* Unrecognized SVDM */ 1637 1612 response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); 1638 1613 rlen = 1; 1614 + response[0] = (response[0] & ~VDO_SVDM_VERS_MASK) | 1615 + (VDO_SVDM_VERS(svdm_version)); 1639 1616 break; 1640 1617 } 1641 1618 port->vdm_sm_running = false; ··· 1645 1618 default: 1646 1619 response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); 1647 1620 rlen = 1; 1621 + response[0] = (response[0] & ~VDO_SVDM_VERS_MASK) | 1622 + (VDO_SVDM_VERS(svdm_version)); 1648 1623 port->vdm_sm_running = false; 1649 1624 break; 1650 1625 } ··· 1724 1695 break; 1725 1696 case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL: 1726 1697 if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) { 1727 - response[0] = VDO(adev->svid, 1, SVDM_VER_1_0, CMD_EXIT_MODE); 1698 + int svdm_version = typec_get_negotiated_svdm_version( 1699 + port->typec_port); 1700 + if (svdm_version < 0) 1701 + break; 1702 + 1703 + response[0] = VDO(adev->svid, 1, svdm_version, 1704 + CMD_EXIT_MODE); 1728 1705 response[0] |= VDO_OPOS(adev->mode); 1729 1706 rlen = 1; 1730 1707 } ··· 1757 1722 static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, 1758 1723 const u32 *data, int count) 1759 1724 { 1725 + int svdm_version = typec_get_negotiated_svdm_version(port->typec_port); 1760 1726 u32 header; 1727 + 1728 + if (svdm_version < 0) 1729 + return; 1761 1730 1762 1731 if (WARN_ON(count > VDO_MAX_SIZE - 1)) 1763 1732 count = VDO_MAX_SIZE - 1; 1764 1733 1765 1734 /* set VDM header with VID & CMD */ 1766 1735 header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? 1767 - 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), SVDM_VER_1_0, cmd); 1736 + 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), 1737 + svdm_version, cmd); 1768 1738 tcpm_queue_vdm(port, header, data, count); 1769 1739 } 1770 1740 ··· 2062 2022 static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) 2063 2023 { 2064 2024 struct tcpm_port *port = typec_altmode_get_drvdata(altmode); 2025 + int svdm_version; 2065 2026 u32 header; 2066 2027 2067 - header = VDO(altmode->svid, vdo ? 2 : 1, SVDM_VER_1_0, CMD_ENTER_MODE); 2028 + svdm_version = typec_get_negotiated_svdm_version(port->typec_port); 2029 + if (svdm_version < 0) 2030 + return svdm_version; 2031 + 2032 + header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); 2068 2033 header |= VDO_OPOS(altmode->mode); 2069 2034 2070 2035 tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0); ··· 2079 2034 static int tcpm_altmode_exit(struct typec_altmode *altmode) 2080 2035 { 2081 2036 struct tcpm_port *port = typec_altmode_get_drvdata(altmode); 2037 + int svdm_version; 2082 2038 u32 header; 2083 2039 2084 - header = VDO(altmode->svid, 1, SVDM_VER_1_0, CMD_EXIT_MODE); 2040 + svdm_version = typec_get_negotiated_svdm_version(port->typec_port); 2041 + if (svdm_version < 0) 2042 + return svdm_version; 2043 + 2044 + header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE); 2085 2045 header |= VDO_OPOS(altmode->mode); 2086 2046 2087 2047 tcpm_queue_vdm_unlocked(port, header, NULL, 0); ··· 6027 5977 port->typec_caps.fwnode = tcpc->fwnode; 6028 5978 port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ 6029 5979 port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ 5980 + port->typec_caps.svdm_version = SVDM_VER_2_0; 6030 5981 port->typec_caps.driver_data = port; 6031 5982 port->typec_caps.ops = &tcpm_ops; 6032 5983 port->typec_caps.orientation_aware = 1;