From beeb22f5e8ce2101b302377e887a82a9c7f8807b Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 28 Sep 2016 18:40:25 -0700 Subject: [PATCH 1/6] usb: pd: Miscellaneous compliance fixes Fix several issues which help address PD compliance testing: - Soft reset should be issued instead of hard reset in cases where an unexpected message is received. - In source mode, send a Reject if the Request PDO object also exceeds the advertised current capability. Fix the incorrect bitmask in the PD_RDO_FIXED_CURR macro. - Handle possibility of quick back-to-back RX messages by flushing the sm worker to ensure the last message was fully processed before overwriting the buffer. - Discard PING messages as they are not applicable to Type-C. - Respond to GET_SINK_CAP while in source mode, and vice versa, respond to GET_SOURCE_CAP when in sink mode. - Move pd->hard_reset=false to PE_SRC/SNK_TRANSITION_TO_DEFAULT for consistency. - Change default sink capabilities to advertise only 5V @ 900mA since the policy engine will not automatically request higher voltages. If userspace wants to request greater than 5V it should also update the sink capabilities. - Reset the protocol counters at the beginning of PE_SNK_STARTUP. Change-Id: I44598eb0b49efd763f86f303c70e8b018fca994b Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 69 +++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 915080a5b817..65579e53c944 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -195,7 +195,7 @@ static void *usbpd_ipc_log; #define PD_RDO_MISMATCH(rdo) ((rdo) >> 26 & 1) #define PD_RDO_USB_COMM(rdo) ((rdo) >> 25 & 1) #define PD_RDO_NO_USB_SUSP(rdo) ((rdo) >> 24 & 1) -#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 19 & 0x3FF) +#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 10 & 0x3FF) #define PD_RDO_FIXED_CURR_MINMAX(rdo) ((rdo) & 0x3FF) #define PD_SRC_PDO_TYPE(pdo) (((pdo) >> 30) & 3) @@ -253,10 +253,7 @@ static bool ss_dev = true; module_param(ss_dev, bool, S_IRUSR | S_IWUSR); static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ - -static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */ - 0x0002D096, /* 9V @ 1.5A */ - 0x0003C064 }; /* 12V @ 1A */ +static const u32 default_snk_caps[] = { 0x2601905A }; /* 5V @ 900mA */ struct vdm_tx { u32 data[7]; @@ -534,6 +531,10 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, pd->rx_msgid = PD_MSG_HDR_ID(header); + /* discard Pings */ + if (PD_MSG_HDR_TYPE(header) == MSG_PING && !len) + return; + /* check header's count field to see if it matches len */ if (PD_MSG_HDR_COUNT(header) != (len / 4)) { usbpd_err(&pd->dev, "header count (%d) mismatch, len=%ld\n", @@ -541,10 +542,17 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, return; } + /* block until previous message has been consumed by usbpd_sm */ + if (pd->rx_msg_type) + flush_work(&pd->sm_work); + pd->rx_msg_type = PD_MSG_HDR_TYPE(header); pd->rx_msg_len = PD_MSG_HDR_COUNT(header); memcpy(&pd->rx_payload, buf, len); + usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n", + pd->rx_msg_type, pd->rx_msg_len); + queue_work(pd->wq, &pd->sm_work); } @@ -647,7 +655,11 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_SRC_NEGOTIATE_CAPABILITY: - if (PD_RDO_OBJ_POS(pd->rdo) != 1) { + if (PD_RDO_OBJ_POS(pd->rdo) != 1 || + PD_RDO_FIXED_CURR(pd->rdo) > + PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps) || + PD_RDO_FIXED_CURR_MINMAX(pd->rdo) > + PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps)) { /* send Reject */ ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG); if (ret) { @@ -717,6 +729,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_SRC_TRANSITION_TO_DEFAULT: + pd->hard_reset = false; + if (pd->vconn_enabled) regulator_disable(pd->vconn); regulator_disable(pd->vbus); @@ -786,9 +800,11 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } + /* Reset protocol layer */ + pd->tx_msgid = 0; + pd->rx_msgid = -1; pd->rx_msg_len = 0; pd->rx_msg_type = 0; - pd->rx_msgid = -1; if (!pd->in_pr_swap) { if (pd->pd_phy_opened) { @@ -841,8 +857,11 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SNK_SELECT_CAPABILITY: ret = pd_send_msg(pd, MSG_REQUEST, &pd->rdo, 1, SOP_MSG); - if (ret) + if (ret) { usbpd_err(&pd->dev, "Error sending Request\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); + break; + } /* wait for ACCEPT */ hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), @@ -861,6 +880,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_SNK_TRANSITION_TO_DEFAULT: + pd->hard_reset = false; + if (pd->current_dr != DR_UFP) { extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0); @@ -877,8 +898,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->vconn_enabled = false; } - pd->tx_msgid = 0; - val.intval = pd->requested_voltage; /* set range back to 5V */ power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &val); @@ -1339,7 +1358,8 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); else usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); - pd->hard_reset = false; + + return; } /* Soft reset? */ @@ -1424,6 +1444,9 @@ static void usbpd_sm(struct work_struct *w) if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); + } else if (data_recvd || ctrl_recvd) { + usbpd_err(&pd->dev, "Unexpected message received\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } else { usbpd_set_state(pd, PE_SRC_HARD_RESET); } @@ -1439,6 +1462,14 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } + } else if (ctrl_recvd == MSG_GET_SINK_CAP) { + ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, + default_snk_caps, + ARRAY_SIZE(default_snk_caps), SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Sink Caps\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); + } } else if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); @@ -1538,6 +1569,9 @@ static void usbpd_sm(struct work_struct *w) else usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); + } else if (pd->rx_msg_type) { + usbpd_err(&pd->dev, "Invalid response to sink request\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } else { /* timed out; go to hard reset */ usbpd_set_state(pd, PE_SNK_HARD_RESET); @@ -1566,9 +1600,9 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_READY: - if (data_recvd == MSG_SOURCE_CAPABILITIES) + if (data_recvd == MSG_SOURCE_CAPABILITIES) { usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); - else if (ctrl_recvd == MSG_GET_SINK_CAP) { + } else if (ctrl_recvd == MSG_GET_SINK_CAP) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, default_snk_caps, ARRAY_SIZE(default_snk_caps), SOP_MSG); @@ -1576,6 +1610,15 @@ static void usbpd_sm(struct work_struct *w) usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } + } else if (ctrl_recvd == MSG_GET_SOURCE_CAP) { + ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, + default_src_caps, + ARRAY_SIZE(default_src_caps), SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending SRC CAPs\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); + break; + } } else if (ctrl_recvd == MSG_DR_SWAP) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SNK_HARD_RESET); From 7ce154ad5da4291d9b668ad14d21fe6b08d1f039 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 28 Sep 2016 18:40:32 -0700 Subject: [PATCH 2/6] usb: pd: Correctly handle invalid Structured VDMs As UFP since the policy engine only responds to the Discover Identity command, all other commands are unrecognized and therefore should be NAKed. Fix the handling as follows: - Ensure the standard Discover Identity was sent using SID of 0xFF00, otherwise it is invalid - Don't NAK incoming Attention messages - When sending a NAK, respond with the same SVID of the initiating command, whether it was correct or not - Enter Mode is not supported in general, but the NAK should also be sent back with the same mode index - Re-issue Discover SVIDs when more than 12 SVIDs are returned - Clean up macro name prefixes from 'VDM' to 'SVDM' to emphasize they are accessing structured VDM specific bits Change-Id: Id2590065a01e46f8da70733d55f3bfabff5b532d Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 92 +++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 65579e53c944..08464424e331 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -223,9 +223,10 @@ static void *usbpd_ipc_log; /* VDM header is the first 32-bit object following the 16-bit PD header */ #define VDM_HDR_SVID(hdr) ((hdr) >> 16) -#define VDM_HDR_TYPE(hdr) ((hdr) & 0x8000) -#define VDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3) -#define VDM_HDR_CMD(hdr) ((hdr) & 0x1f) +#define VDM_IS_SVDM(hdr) ((hdr) & 0x8000) +#define SVDM_HDR_OBJ_POS(hdr) (((hdr) >> 8) & 0x7) +#define SVDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3) +#define SVDM_HDR_CMD(hdr) ((hdr) & 0x1f) #define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \ (((svid) << 16) | (1 << 15) | ((ver) << 13) \ @@ -312,6 +313,7 @@ struct usbpd { enum vdm_state vdm_state; u16 *discovered_svids; + int num_svids; struct vdm_tx *vdm_tx_retry; struct list_head vdm_tx_queue; struct list_head svid_handlers; @@ -948,10 +950,10 @@ int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr) /* already connected with this SVID discovered? */ if (pd->vdm_state >= DISCOVERED_SVIDS) { - u16 *psvid; + int i; - for (psvid = pd->discovered_svids; *psvid; psvid++) { - if (*psvid == hdlr->svid) { + for (i = 0; i < pd->num_svids; i++) { + if (pd->discovered_svids[i] == hdlr->svid) { if (hdlr->connect) hdlr->connect(hdlr); break; @@ -1013,8 +1015,8 @@ static void handle_vdm_rx(struct usbpd *pd) u16 svid = VDM_HDR_SVID(vdm_hdr); u16 *psvid; u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */ - u8 cmd = VDM_HDR_CMD(vdm_hdr); - u8 cmd_type = VDM_HDR_CMD_TYPE(vdm_hdr); + u8 cmd = SVDM_HDR_CMD(vdm_hdr); + u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr); struct usbpd_svid_handler *handler; usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n", @@ -1024,7 +1026,7 @@ static void handle_vdm_rx(struct usbpd *pd) handler = find_svid_handler(pd, svid); /* Unstructured VDM */ - if (!VDM_HDR_TYPE(vdm_hdr)) { + if (!VDM_IS_SVDM(vdm_hdr)) { if (handler && handler->vdm_received) handler->vdm_received(handler, vdm_hdr, vdos, num_vdos); return; @@ -1035,7 +1037,7 @@ static void handle_vdm_rx(struct usbpd *pd) switch (cmd_type) { case SVDM_CMD_TYPE_INITIATOR: - if (cmd == USBPD_SVDM_DISCOVER_IDENTITY) { + if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) { u32 tx_vdos[3] = { ID_HDR_USB_HOST | ID_HDR_USB_DEVICE | ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID, @@ -1046,9 +1048,9 @@ static void handle_vdm_rx(struct usbpd *pd) usbpd_send_svdm(pd, USBPD_SID, cmd, SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3); - } else { - usbpd_send_svdm(pd, USBPD_SID, cmd, - SVDM_CMD_TYPE_RESP_NAK, 0, NULL, 0); + } else if (cmd != USBPD_SVDM_ATTENTION) { + usbpd_send_svdm(pd, svid, cmd, SVDM_CMD_TYPE_RESP_NAK, + SVDM_HDR_OBJ_POS(vdm_hdr), NULL, 0); } break; @@ -1080,19 +1082,37 @@ static void handle_vdm_rx(struct usbpd *pd) kfree(pd->vdm_tx_retry); pd->vdm_tx_retry = NULL; - kfree(pd->discovered_svids); - - /* TODO: handle > 12 SVIDs */ - pd->discovered_svids = kzalloc((2 * num_vdos + 1) * - sizeof(u16), - GFP_KERNEL); if (!pd->discovered_svids) { - usbpd_err(&pd->dev, "unable to allocate SVIDs\n"); - break; + pd->num_svids = 2 * num_vdos; + pd->discovered_svids = kcalloc(pd->num_svids, + sizeof(u16), + GFP_KERNEL); + if (!pd->discovered_svids) { + usbpd_err(&pd->dev, "unable to allocate SVIDs\n"); + break; + } + + psvid = pd->discovered_svids; + } else { /* handle > 12 SVIDs */ + void *ptr; + size_t oldsize = pd->num_svids * sizeof(u16); + size_t newsize = oldsize + + (2 * num_vdos * sizeof(u16)); + + ptr = krealloc(pd->discovered_svids, newsize, + GFP_KERNEL); + if (!ptr) { + usbpd_err(&pd->dev, "unable to realloc SVIDs\n"); + break; + } + + pd->discovered_svids = ptr; + psvid = pd->discovered_svids + pd->num_svids; + memset(psvid, 0, (2 * num_vdos)); + pd->num_svids += 2 * num_vdos; } /* convert 32-bit VDOs to list of 16-bit SVIDs */ - psvid = pd->discovered_svids; for (i = 0; i < num_vdos * 2; i++) { /* * Within each 32-bit VDO, @@ -1116,8 +1136,22 @@ static void handle_vdm_rx(struct usbpd *pd) usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n", svid); *psvid++ = svid; + } + } - /* if SVID supported notify handler */ + /* if more than 12 SVIDs, resend the request */ + if (num_vdos == 6 && vdos[5] != 0) { + usbpd_send_svdm(pd, USBPD_SID, + USBPD_SVDM_DISCOVER_SVIDS, + SVDM_CMD_TYPE_INITIATOR, 0, + NULL, 0); + break; + } + + /* now that all SVIDs are discovered, notify handlers */ + for (i = 0; i < pd->num_svids; i++) { + svid = pd->discovered_svids[i]; + if (svid) { handler = find_svid_handler(pd, svid); if (handler && handler->connect) handler->connect(handler); @@ -1193,7 +1227,7 @@ static void handle_vdm_tx(struct usbpd *pd) SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending VDM command %d\n", - VDM_HDR_CMD(vdm_tx->data[0])); + SVDM_HDR_CMD(vdm_tx->data[0])); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_SOFT_RESET : PE_SNK_SEND_SOFT_RESET); @@ -1208,12 +1242,13 @@ static void handle_vdm_tx(struct usbpd *pd) * special case: keep initiated Discover ID/SVIDs * around in case we need to re-try when receiving BUSY */ - if (VDM_HDR_TYPE(vdm_hdr) && - VDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR && - VDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) { + if (VDM_IS_SVDM(vdm_hdr) && + SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR && + SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) { if (pd->vdm_tx_retry) { usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n", - VDM_HDR_CMD(pd->vdm_tx_retry->data[0])); + SVDM_HDR_CMD( + pd->vdm_tx_retry->data[0])); kfree(pd->vdm_tx_retry); } pd->vdm_tx_retry = vdm_tx; @@ -1235,6 +1270,7 @@ static void reset_vdm_state(struct usbpd *pd) pd->vdm_tx_retry = NULL; kfree(pd->discovered_svids); pd->discovered_svids = NULL; + pd->num_svids = 0; while (!list_empty(&pd->vdm_tx_queue)) { struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue, From 3bf9e3840071d88b78f70a076da9bdd6c020c474 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 28 Sep 2016 18:40:36 -0700 Subject: [PATCH 3/6] usb: pd: Simplify VDM tx handling VDMs, especially structured ones, are almost always handled as requests and responses. Hence it does not make sense to allow more than one outgoing VDM at a time, so get rid of the vdm_tx_queue list and simplify it to a single-issued packet. Because port partners can only have one VDM request/response in flight, also handle the case when a new structured request is received before the previous response has been sent. In that case we simply discard the queued message as it's now invalid. Change-Id: I144f8158dbf8be0babf5516b01d084fd053413c3 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 55 ++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 08464424e331..d8ddebdfaaf0 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -259,7 +259,6 @@ static const u32 default_snk_caps[] = { 0x2601905A }; /* 5V @ 900mA */ struct vdm_tx { u32 data[7]; int size; - struct list_head entry; }; struct usbpd { @@ -314,8 +313,8 @@ struct usbpd { enum vdm_state vdm_state; u16 *discovered_svids; int num_svids; + struct vdm_tx *vdm_tx; struct vdm_tx *vdm_tx_retry; - struct list_head vdm_tx_queue; struct list_head svid_handlers; struct list_head instance; @@ -975,7 +974,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) { struct vdm_tx *vdm_tx; - if (!pd->in_explicit_contract) + if (!pd->in_explicit_contract || pd->vdm_tx) return -EBUSY; vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL); @@ -988,8 +987,10 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) vdm_tx->size = num_vdos + 1; /* include the header */ /* VDM will get sent in PE_SRC/SNK_READY state handling */ - list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue); - queue_work(pd->wq, &pd->sm_work); + pd->vdm_tx = vdm_tx; + + /* slight delay before queuing to prioritize handling of incoming VDM */ + hrtimer_start(&pd->timer, ms_to_ktime(5), HRTIMER_MODE_REL); return 0; } @@ -1037,6 +1038,18 @@ static void handle_vdm_rx(struct usbpd *pd) switch (cmd_type) { case SVDM_CMD_TYPE_INITIATOR: + /* + * if this interrupts a previous exchange, abort the previous + * outgoing response + */ + if (pd->vdm_tx) { + usbpd_dbg(&pd->dev, "Discarding previously queued SVDM tx (SVID:0x%04x)\n", + VDM_HDR_SVID(pd->vdm_tx->data[0])); + + kfree(pd->vdm_tx); + pd->vdm_tx = NULL; + } + if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) { u32 tx_vdos[3] = { ID_HDR_USB_HOST | ID_HDR_USB_DEVICE | @@ -1201,7 +1214,7 @@ static void handle_vdm_rx(struct usbpd *pd) } /* wait tVDMBusy, then retry */ - list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue); + pd->vdm_tx = pd->vdm_tx_retry; pd->vdm_tx_retry = NULL; hrtimer_start(&pd->timer, ms_to_ktime(VDM_BUSY_TIME), HRTIMER_MODE_REL); @@ -1218,16 +1231,14 @@ static void handle_vdm_tx(struct usbpd *pd) int ret; /* only send one VDM at a time */ - if (!list_empty(&pd->vdm_tx_queue)) { - struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue, - struct vdm_tx, entry); - u32 vdm_hdr = vdm_tx->data[0]; + if (pd->vdm_tx) { + u32 vdm_hdr = pd->vdm_tx->data[0]; - ret = pd_send_msg(pd, MSG_VDM, vdm_tx->data, vdm_tx->size, - SOP_MSG); + ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data, + pd->vdm_tx->size, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending VDM command %d\n", - SVDM_HDR_CMD(vdm_tx->data[0])); + SVDM_HDR_CMD(pd->vdm_tx->data[0])); usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_SOFT_RESET : PE_SNK_SEND_SOFT_RESET); @@ -1236,8 +1247,6 @@ static void handle_vdm_tx(struct usbpd *pd) return; } - list_del(&vdm_tx->entry); - /* * special case: keep initiated Discover ID/SVIDs * around in case we need to re-try when receiving BUSY @@ -1251,10 +1260,12 @@ static void handle_vdm_tx(struct usbpd *pd) pd->vdm_tx_retry->data[0])); kfree(pd->vdm_tx_retry); } - pd->vdm_tx_retry = vdm_tx; + pd->vdm_tx_retry = pd->vdm_tx; } else { - kfree(vdm_tx); + kfree(pd->vdm_tx); } + + pd->vdm_tx = NULL; } } @@ -1271,13 +1282,8 @@ static void reset_vdm_state(struct usbpd *pd) kfree(pd->discovered_svids); pd->discovered_svids = NULL; pd->num_svids = 0; - while (!list_empty(&pd->vdm_tx_queue)) { - struct vdm_tx *vdm_tx = - list_first_entry(&pd->vdm_tx_queue, - struct vdm_tx, entry); - list_del(&vdm_tx->entry); - kfree(vdm_tx); - } + kfree(pd->vdm_tx); + pd->vdm_tx = NULL; } static void dr_swap(struct usbpd *pd) @@ -2497,7 +2503,6 @@ struct usbpd *usbpd_create(struct device *parent) pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); - INIT_LIST_HEAD(&pd->vdm_tx_queue); INIT_LIST_HEAD(&pd->svid_handlers); /* force read initial power_supply values */ From b08f588476adf8952efd3ae969787c5be1519f42 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 21 Sep 2016 12:03:29 -0700 Subject: [PATCH 4/6] usb: pd: Prevent sleep when state machine in progress With PM autosleep enabled, the system may aggressively enter and re-enter suspend in between calls to queue_work() and when the state machine function gets executed, which may at best result in numerous suspend/resume cycles and at worse starve the PD state machine from even executing. Fix this by calling pm_stay_awake() whenever the work is queued and relinquish with pm_relax() when no further work is to be done. Change-Id: I71d8ea0c69a7b174c2280f92d1f5342e5e50d9b0 Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 92 ++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index d8ddebdfaaf0..5b1b28dd8c5c 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -266,6 +266,7 @@ struct usbpd { struct workqueue_struct *wq; struct work_struct sm_work; struct hrtimer timer; + bool sm_queued; struct extcon_dev *extcon; @@ -483,6 +484,17 @@ static void pd_send_hard_reset(struct usbpd *pd) pd->hard_reset = true; } +static void kick_sm(struct usbpd *pd, int ms) +{ + pm_stay_awake(&pd->dev); + pd->sm_queued = true; + + if (ms) + hrtimer_start(&pd->timer, ms_to_ktime(ms), HRTIMER_MODE_REL); + else + queue_work(pd->wq, &pd->sm_work); +} + static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) { if (type != HARD_RESET_SIG) { @@ -495,7 +507,7 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset = true; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); } static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, @@ -554,7 +566,7 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n", pd->rx_msg_type, pd->rx_msg_len); - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); } static void phy_shutdown(struct usbpd *pd) @@ -596,7 +608,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->in_pr_swap = false; set_power_role(pd, PR_NONE); pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; /* Source states */ @@ -643,16 +655,14 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_state = PE_SRC_SEND_CAPABILITIES; if (pd->in_pr_swap) { pd->in_pr_swap = false; - hrtimer_start(&pd->timer, - ms_to_ktime(SWAP_SOURCE_START_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SWAP_SOURCE_START_TIME); break; } /* fall-through */ case PE_SRC_SEND_CAPABILITIES: - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; case PE_SRC_NEGOTIATE_CAPABILITY: @@ -762,7 +772,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SRC_HARD_RESET: case PE_SNK_HARD_RESET: /* hard reset may sleep; handle it in the workqueue */ - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; case PE_SRC_SEND_SOFT_RESET: @@ -780,8 +790,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } /* wait for ACCEPT */ - hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SENDER_RESPONSE_TIME); break; /* Sink states */ @@ -836,11 +845,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SNK_WAIT_FOR_CAPABILITIES: if (pd->rx_msg_len && pd->rx_msg_type) - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); else - hrtimer_start(&pd->timer, - ms_to_ktime(SINK_WAIT_CAP_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SINK_WAIT_CAP_TIME); break; case PE_SNK_EVALUATE_CAPABILITY: @@ -865,14 +872,12 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } /* wait for ACCEPT */ - hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SENDER_RESPONSE_TIME); break; case PE_SNK_TRANSITION_SINK: /* wait for PS_RDY */ - hrtimer_start(&pd->timer, ms_to_ktime(PS_TRANSITION_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, PS_TRANSITION_TIME); break; case PE_SNK_READY: @@ -905,9 +910,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_voltage = pd->requested_voltage; /* max time for hard reset to toggle vbus off/on */ - hrtimer_start(&pd->timer, - ms_to_ktime(SNK_HARD_RESET_RECOVER_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SNK_HARD_RESET_RECOVER_TIME); break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: @@ -924,8 +927,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd_phy_update_roles(pd->current_dr, PR_SRC); /* wait for PS_RDY */ - hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF), - HRTIMER_MODE_REL); + kick_sm(pd, PS_SOURCE_OFF); break; default: @@ -990,7 +992,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) pd->vdm_tx = vdm_tx; /* slight delay before queuing to prioritize handling of incoming VDM */ - hrtimer_start(&pd->timer, ms_to_ktime(5), HRTIMER_MODE_REL); + kick_sm(pd, 5); return 0; } @@ -1216,8 +1218,7 @@ static void handle_vdm_rx(struct usbpd *pd) /* wait tVDMBusy, then retry */ pd->vdm_tx = pd->vdm_tx_retry; pd->vdm_tx_retry = NULL; - hrtimer_start(&pd->timer, ms_to_ktime(VDM_BUSY_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, VDM_BUSY_TIME); break; default: break; @@ -1326,6 +1327,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_state_strings[pd->current_state]); hrtimer_cancel(&pd->timer); + pd->sm_queued = false; if (pd->rx_msg_len) data_recvd = pd->rx_msg_type; @@ -1335,7 +1337,7 @@ static void usbpd_sm(struct work_struct *w) /* Disconnect? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) { if (pd->current_state == PE_UNKNOWN) - return; + goto sm_done; usbpd_info(&pd->dev, "USB PD disconnect\n"); @@ -1389,7 +1391,7 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_UNKNOWN; - return; + goto sm_done; } /* Hard reset? */ @@ -1401,7 +1403,7 @@ static void usbpd_sm(struct work_struct *w) else usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); - return; + goto sm_done; } /* Soft reset? */ @@ -1462,8 +1464,7 @@ static void usbpd_sm(struct work_struct *w) break; } - hrtimer_start(&pd->timer, ms_to_ktime(SRC_CAP_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SRC_CAP_TIME); break; } @@ -1478,8 +1479,7 @@ static void usbpd_sm(struct work_struct *w) /* wait for REQUEST */ pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT; - hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SENDER_RESPONSE_TIME); break; case PE_SRC_SEND_CAPABILITIES_WAIT: @@ -1543,9 +1543,7 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - hrtimer_start(&pd->timer, - ms_to_ktime(SRC_TRANSITION_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SRC_TRANSITION_TIME); break; } else { if (data_recvd == MSG_VDM) @@ -1708,7 +1706,7 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_TYPEC_MODE, &val); if (val.intval == POWER_SUPPLY_TYPEC_NONE) { pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); } } break; @@ -1786,8 +1784,7 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - hrtimer_start(&pd->timer, ms_to_ktime(SRC_TRANSITION_TIME), - HRTIMER_MODE_REL); + kick_sm(pd, SRC_TRANSITION_TIME); break; case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: @@ -1812,8 +1809,7 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON; - hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_ON), - HRTIMER_MODE_REL); + kick_sm(pd, PS_SOURCE_ON); break; case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: @@ -1871,6 +1867,10 @@ static void usbpd_sm(struct work_struct *w) /* Rx message should have been consumed now */ pd->rx_msg_type = pd->rx_msg_len = 0; + +sm_done: + if (!pd->sm_queued) + pm_relax(&pd->dev); } static inline const char *src_current(enum power_supply_typec_mode typec_mode) @@ -1982,7 +1982,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) switch (typec_mode) { /* Disconnect */ case POWER_SUPPLY_TYPEC_NONE: - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); break; /* Sink states */ @@ -1994,7 +1994,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) if (pd->current_pr != PR_SINK || pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) { pd->current_pr = PR_SINK; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); } break; @@ -2006,7 +2006,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) "" : " (powered)"); if (pd->current_pr != PR_SRC) { pd->current_pr = PR_SRC; - queue_work(pd->wq, &pd->sm_work); + kick_sm(pd, 0); } break; @@ -2444,6 +2444,10 @@ struct usbpd *usbpd_create(struct device *parent) if (ret) goto free_pd; + ret = device_init_wakeup(&pd->dev, true); + if (ret) + goto free_pd; + ret = device_add(&pd->dev); if (ret) goto free_pd; From d1d42ada3dd73c313db2ffd2811d2b83841d5eb9 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Thu, 29 Sep 2016 23:50:40 -0700 Subject: [PATCH 5/6] usb: pd: Support VCONN Swap Support incoming VCONN Swap requests by accepting and turning off/on VCONN. Due to HW board limitations, if VCONN is being sourced from the VBUS input we cannot support enabling VCONN while as a sink and greater than 5V has been negotiated on VBUS. In that case, reject the request. Add a device tree property that indicates whether the board is configured for separate VCONN supply. Change-Id: If3a9aa316ae08a80468631f3d536a1b345e21b18 Signed-off-by: Jack Pham --- .../devicetree/bindings/usb/qpnp-pdphy.txt | 3 + drivers/usb/pd/policy_engine.c | 96 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt index f5c6651affea..cd1386512bd3 100644 --- a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt +++ b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt @@ -40,6 +40,9 @@ Optional properties: - vconn-supply: Regulator that enables VCONN source output. This will be supplied on the USB CC line that is not used for communication when Ra resistance is detected. +- qcom,vconn-uses-external-source: Indicates whether VCONN supply is sourced + from an external regulator. If omitted, then it is + assumed it is connected to VBUS. Example: qcom,qpnp-pdphy@1700 { diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 5b1b28dd8c5c..114824e5ba3f 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -59,6 +59,7 @@ enum usbpd_state { PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_WAIT_SOURCE_ON, + PE_VCS_WAIT_FOR_VCONN, }; static const char * const usbpd_state_strings[] = { @@ -94,6 +95,7 @@ static const char * const usbpd_state_strings[] = { "PRS_SRC_SNK_Send_Swap", "PRS_SRC_SNK_Transition_to_off", "PRS_SRC_SNK_Wait_Source_on", + "VCS_Wait_for_VCONN", }; enum usbpd_control_msg_type { @@ -170,6 +172,7 @@ static void *usbpd_ipc_log; #define PS_SOURCE_OFF 750 #define SWAP_SOURCE_START_TIME 20 #define VDM_BUSY_TIME 50 +#define VCONN_ON_TIME 100 /* tPSHardReset + tSafe0V + tSrcRecover + tSrcTurnOn */ #define SNK_HARD_RESET_RECOVER_TIME (35 + 650 + 1000 + 275) @@ -305,6 +308,7 @@ struct usbpd { struct regulator *vbus; struct regulator *vconn; bool vconn_enabled; + bool vconn_is_external; u8 tx_msgid; u8 rx_msgid; @@ -439,6 +443,12 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) } pd->requested_voltage = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + + /* Can't sink more than 5V if VCONN is sourced from the VBUS input */ + if (pd->vconn_enabled && !pd->vconn_is_external && + pd->requested_voltage > 5000000) + return -ENOTSUPP; + pd->requested_current = curr; pd->requested_pdo = pdo_pos; pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10, @@ -1314,6 +1324,34 @@ static void dr_swap(struct usbpd *pd) pd_phy_update_roles(pd->current_dr, pd->current_pr); } + +static void vconn_swap(struct usbpd *pd) +{ + int ret; + + if (pd->vconn_enabled) { + pd->current_state = PE_VCS_WAIT_FOR_VCONN; + kick_sm(pd, VCONN_ON_TIME); + } else { + ret = regulator_enable(pd->vconn); + if (ret) { + usbpd_err(&pd->dev, "Unable to enable vconn\n"); + return; + } + + pd->vconn_enabled = true; + + ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending PS_RDY\n"); + usbpd_set_state(pd, pd->current_pr == PR_SRC ? + PE_SRC_SEND_SOFT_RESET : + PE_SNK_SEND_SOFT_RESET); + return; + } + } +} + /* Handles current state and determines transitions */ static void usbpd_sm(struct work_struct *w) { @@ -1545,6 +1583,15 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; kick_sm(pd, SRC_TRANSITION_TIME); break; + } else if (ctrl_recvd == MSG_VCONN_SWAP) { + ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Accept\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); + break; + } + + vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); @@ -1689,6 +1736,32 @@ static void usbpd_sm(struct work_struct *w) pd->in_pr_swap = true; usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; + } else if (ctrl_recvd == MSG_VCONN_SWAP) { + /* + * if VCONN is connected to VBUS, make sure we are + * not in high voltage contract, otherwise reject. + */ + if (!pd->vconn_is_external && + (pd->requested_voltage > 5000000)) { + ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, + SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Reject\n"); + usbpd_set_state(pd, + PE_SNK_SEND_SOFT_RESET); + } + + break; + } + + ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending Accept\n"); + usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); + break; + } + + vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); @@ -1859,6 +1932,26 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_STARTUP); break; + case PE_VCS_WAIT_FOR_VCONN: + if (ctrl_recvd == MSG_PS_RDY) { + /* + * hopefully redundant check but in case not enabled + * avoids unbalanced regulator disable count + */ + if (pd->vconn_enabled) + regulator_disable(pd->vconn); + pd->vconn_enabled = false; + + pd->current_state = pd->current_pr == PR_SRC ? + PE_SRC_READY : PE_SNK_READY; + } else { + /* timed out; go to hard reset */ + usbpd_set_state(pd, pd->current_pr == PR_SRC ? + PE_SRC_HARD_RESET : PE_SNK_HARD_RESET); + } + + break; + default: usbpd_err(&pd->dev, "Unhandled state %s\n", usbpd_state_strings[pd->current_state]); @@ -2503,6 +2596,9 @@ struct usbpd *usbpd_create(struct device *parent) goto unreg_psy; } + pd->vconn_is_external = device_property_present(parent, + "qcom,vconn-uses-external-source"); + pd->current_pr = PR_NONE; pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); From 9ed824e16b72ed509e4718c815c3202dfd6ce6e9 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Fri, 30 Sep 2016 17:32:01 -0700 Subject: [PATCH 6/6] power_supply: Add additional USB PD properties Add additional properties for USB PD usage: - POWER_SUPPLY_PROP_PD_IN_HARD_RESET is set when PD is undergoing a hard reset - POWER_SUPPLY_PROP_PD_CURRENT_MAX used to indicate the current limit as negotiated over PD - POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED is set when USB suspend current limit must be honored when USB bus is suspended Also add enums to distinguish PD activity state: inactive, active. Change-Id: I0a3d98ac1e1202e5b655b2e3a8102691b61359c8 Signed-off-by: Jack Pham --- drivers/power/power_supply_sysfs.c | 3 +++ include/linux/power_supply.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 8af1eb66c699..0619b314b7de 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -267,6 +267,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_power_role), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), + POWER_SUPPLY_ATTR(pd_in_hard_reset), + POWER_SUPPLY_ATTR(pd_current_max), + POWER_SUPPLY_ATTR(pd_usb_suspend_supported), POWER_SUPPLY_ATTR(charger_temp), POWER_SUPPLY_ATTR(charger_temp_max), POWER_SUPPLY_ATTR(parallel_disable), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c477f60c3f01..218cd875ee5a 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -216,6 +216,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, POWER_SUPPLY_PROP_PARALLEL_DISABLE,