Skip to content

Commit 64f7c49

Browse files
Adam Thomsongregkh
authored andcommitted
typec: tcpm: Add support for sink PPS related messages
This commit adds sink side support for Get_Status, Status, Get_PPS_Status and PPS_Status handling. As there's the potential for a partner to respond with Not_Supported, handling of this message is also added. Sending of Not_Supported is added to handle messagescreceived but not yet handled. Signed-off-by: Adam Thomson <[email protected]> Acked-by: Heikki Krogerus <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f2a8aa0 commit 64f7c49

File tree

1 file changed

+134
-9
lines changed

1 file changed

+134
-9
lines changed

drivers/usb/typec/tcpm.c

Lines changed: 134 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#include <linux/slab.h>
2020
#include <linux/spinlock.h>
2121
#include <linux/usb/pd.h>
22+
#include <linux/usb/pd_ado.h>
2223
#include <linux/usb/pd_bdo.h>
24+
#include <linux/usb/pd_ext_sdb.h>
2325
#include <linux/usb/pd_vdo.h>
2426
#include <linux/usb/role.h>
2527
#include <linux/usb/tcpm.h>
@@ -114,6 +116,11 @@
114116
S(SNK_TRYWAIT_VBUS), \
115117
S(BIST_RX), \
116118
\
119+
S(GET_STATUS_SEND), \
120+
S(GET_STATUS_SEND_TIMEOUT), \
121+
S(GET_PPS_STATUS_SEND), \
122+
S(GET_PPS_STATUS_SEND_TIMEOUT), \
123+
\
117124
S(ERROR_RECOVERY), \
118125
S(PORT_RESET), \
119126
S(PORT_RESET_WAIT_OFF)
@@ -144,6 +151,7 @@ enum pd_msg_request {
144151
PD_MSG_NONE = 0,
145152
PD_MSG_CTRL_REJECT,
146153
PD_MSG_CTRL_WAIT,
154+
PD_MSG_CTRL_NOT_SUPP,
147155
PD_MSG_DATA_SINK_CAP,
148156
PD_MSG_DATA_SOURCE_CAP,
149157
};
@@ -1411,10 +1419,42 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo,
14111419
/*
14121420
* PD (data, control) command handling functions
14131421
*/
1422+
static inline enum tcpm_state ready_state(struct tcpm_port *port)
1423+
{
1424+
if (port->pwr_role == TYPEC_SOURCE)
1425+
return SRC_READY;
1426+
else
1427+
return SNK_READY;
1428+
}
14141429

14151430
static int tcpm_pd_send_control(struct tcpm_port *port,
14161431
enum pd_ctrl_msg_type type);
14171432

1433+
static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload,
1434+
int cnt)
1435+
{
1436+
u32 p0 = le32_to_cpu(payload[0]);
1437+
unsigned int type = usb_pd_ado_type(p0);
1438+
1439+
if (!type) {
1440+
tcpm_log(port, "Alert message received with no type");
1441+
return;
1442+
}
1443+
1444+
/* Just handling non-battery alerts for now */
1445+
if (!(type & USB_PD_ADO_TYPE_BATT_STATUS_CHANGE)) {
1446+
switch (port->state) {
1447+
case SRC_READY:
1448+
case SNK_READY:
1449+
tcpm_set_state(port, GET_STATUS_SEND, 0);
1450+
break;
1451+
default:
1452+
tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
1453+
break;
1454+
}
1455+
}
1456+
}
1457+
14181458
static void tcpm_pd_data_request(struct tcpm_port *port,
14191459
const struct pd_message *msg)
14201460
{
@@ -1502,6 +1542,14 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
15021542
tcpm_set_state(port, BIST_RX, 0);
15031543
}
15041544
break;
1545+
case PD_DATA_ALERT:
1546+
tcpm_handle_alert(port, msg->payload, cnt);
1547+
break;
1548+
case PD_DATA_BATT_STATUS:
1549+
case PD_DATA_GET_COUNTRY_INFO:
1550+
/* Currently unsupported */
1551+
tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
1552+
break;
15051553
default:
15061554
tcpm_log(port, "Unhandled data message type %#x", type);
15071555
break;
@@ -1584,6 +1632,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
15841632
break;
15851633
case PD_CTRL_REJECT:
15861634
case PD_CTRL_WAIT:
1635+
case PD_CTRL_NOT_SUPP:
15871636
switch (port->state) {
15881637
case SNK_NEGOTIATE_CAPABILITIES:
15891638
/* USB PD specification, Figure 8-43 */
@@ -1703,12 +1752,75 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
17031752
break;
17041753
}
17051754
break;
1755+
case PD_CTRL_GET_SOURCE_CAP_EXT:
1756+
case PD_CTRL_GET_STATUS:
1757+
case PD_CTRL_FR_SWAP:
1758+
case PD_CTRL_GET_PPS_STATUS:
1759+
case PD_CTRL_GET_COUNTRY_CODES:
1760+
/* Currently not supported */
1761+
tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
1762+
break;
17061763
default:
17071764
tcpm_log(port, "Unhandled ctrl message type %#x", type);
17081765
break;
17091766
}
17101767
}
17111768

1769+
static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
1770+
const struct pd_message *msg)
1771+
{
1772+
enum pd_ext_msg_type type = pd_header_type_le(msg->header);
1773+
unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header);
1774+
1775+
if (!(msg->ext_msg.header && PD_EXT_HDR_CHUNKED)) {
1776+
tcpm_log(port, "Unchunked extended messages unsupported");
1777+
return;
1778+
}
1779+
1780+
if (data_size > PD_EXT_MAX_CHUNK_DATA) {
1781+
tcpm_log(port, "Chunk handling not yet supported");
1782+
return;
1783+
}
1784+
1785+
switch (type) {
1786+
case PD_EXT_STATUS:
1787+
/*
1788+
* If PPS related events raised then get PPS status to clear
1789+
* (see USB PD 3.0 Spec, 6.5.2.4)
1790+
*/
1791+
if (msg->ext_msg.data[USB_PD_EXT_SDB_EVENT_FLAGS] &
1792+
USB_PD_EXT_SDB_PPS_EVENTS)
1793+
tcpm_set_state(port, GET_PPS_STATUS_SEND, 0);
1794+
else
1795+
tcpm_set_state(port, ready_state(port), 0);
1796+
break;
1797+
case PD_EXT_PPS_STATUS:
1798+
/*
1799+
* For now the PPS status message is used to clear events
1800+
* and nothing more.
1801+
*/
1802+
tcpm_set_state(port, ready_state(port), 0);
1803+
break;
1804+
case PD_EXT_SOURCE_CAP_EXT:
1805+
case PD_EXT_GET_BATT_CAP:
1806+
case PD_EXT_GET_BATT_STATUS:
1807+
case PD_EXT_BATT_CAP:
1808+
case PD_EXT_GET_MANUFACTURER_INFO:
1809+
case PD_EXT_MANUFACTURER_INFO:
1810+
case PD_EXT_SECURITY_REQUEST:
1811+
case PD_EXT_SECURITY_RESPONSE:
1812+
case PD_EXT_FW_UPDATE_REQUEST:
1813+
case PD_EXT_FW_UPDATE_RESPONSE:
1814+
case PD_EXT_COUNTRY_INFO:
1815+
case PD_EXT_COUNTRY_CODES:
1816+
tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
1817+
break;
1818+
default:
1819+
tcpm_log(port, "Unhandled extended message type %#x", type);
1820+
break;
1821+
}
1822+
}
1823+
17121824
static void tcpm_pd_rx_handler(struct work_struct *work)
17131825
{
17141826
struct pd_rx_event *event = container_of(work,
@@ -1749,7 +1861,9 @@ static void tcpm_pd_rx_handler(struct work_struct *work)
17491861
"Data role mismatch, initiating error recovery");
17501862
tcpm_set_state(port, ERROR_RECOVERY, 0);
17511863
} else {
1752-
if (cnt)
1864+
if (msg->header & PD_HEADER_EXT_HDR)
1865+
tcpm_pd_ext_msg_request(port, msg);
1866+
else if (cnt)
17531867
tcpm_pd_data_request(port, msg);
17541868
else
17551869
tcpm_pd_ctrl_request(port, msg);
@@ -1810,6 +1924,9 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
18101924
case PD_MSG_CTRL_REJECT:
18111925
tcpm_pd_send_control(port, PD_CTRL_REJECT);
18121926
break;
1927+
case PD_MSG_CTRL_NOT_SUPP:
1928+
tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP);
1929+
break;
18131930
case PD_MSG_DATA_SINK_CAP:
18141931
tcpm_pd_send_sink_caps(port);
18151932
break;
@@ -2572,14 +2689,6 @@ static inline enum tcpm_state hard_reset_state(struct tcpm_port *port)
25722689
return SNK_UNATTACHED;
25732690
}
25742691

2575-
static inline enum tcpm_state ready_state(struct tcpm_port *port)
2576-
{
2577-
if (port->pwr_role == TYPEC_SOURCE)
2578-
return SRC_READY;
2579-
else
2580-
return SNK_READY;
2581-
}
2582-
25832692
static inline enum tcpm_state unattached_state(struct tcpm_port *port)
25842693
{
25852694
if (port->port_type == TYPEC_PORT_DRP) {
@@ -3279,6 +3388,22 @@ static void run_state_machine(struct tcpm_port *port)
32793388
/* Always switch to unattached state */
32803389
tcpm_set_state(port, unattached_state(port), 0);
32813390
break;
3391+
case GET_STATUS_SEND:
3392+
tcpm_pd_send_control(port, PD_CTRL_GET_STATUS);
3393+
tcpm_set_state(port, GET_STATUS_SEND_TIMEOUT,
3394+
PD_T_SENDER_RESPONSE);
3395+
break;
3396+
case GET_STATUS_SEND_TIMEOUT:
3397+
tcpm_set_state(port, ready_state(port), 0);
3398+
break;
3399+
case GET_PPS_STATUS_SEND:
3400+
tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS);
3401+
tcpm_set_state(port, GET_PPS_STATUS_SEND_TIMEOUT,
3402+
PD_T_SENDER_RESPONSE);
3403+
break;
3404+
case GET_PPS_STATUS_SEND_TIMEOUT:
3405+
tcpm_set_state(port, ready_state(port), 0);
3406+
break;
32823407
case ERROR_RECOVERY:
32833408
tcpm_swap_complete(port, -EPROTO);
32843409
tcpm_pps_complete(port, -EPROTO);

0 commit comments

Comments
 (0)