lkml.org 
[lkml]   [2018]   [Jan]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH v4 7/7] 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 is added to handle messages
    received but not yet handled.

    Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
    ---
    drivers/usb/typec/tcpm.c | 152 ++++++++++++++++++++++++++++++++++++++++++++---
    1 file changed, 143 insertions(+), 9 deletions(-)

    diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
    index b86a51c..54d17d7 100644
    --- a/drivers/usb/typec/tcpm.c
    +++ b/drivers/usb/typec/tcpm.c
    @@ -19,7 +19,9 @@
    #include <linux/slab.h>
    #include <linux/spinlock.h>
    #include <linux/usb/pd.h>
    +#include <linux/usb/pd_ado.h>
    #include <linux/usb/pd_bdo.h>
    +#include <linux/usb/pd_ext_sdb.h>
    #include <linux/usb/pd_vdo.h>
    #include <linux/usb/tcpm.h>
    #include <linux/usb/typec.h>
    @@ -113,6 +115,11 @@
    S(SNK_TRYWAIT_VBUS), \
    S(BIST_RX), \
    \
    + S(GET_STATUS_SEND), \
    + S(GET_STATUS_SEND_TIMEOUT), \
    + S(GET_PPS_STATUS_SEND), \
    + S(GET_PPS_STATUS_SEND_TIMEOUT), \
    + \
    S(ERROR_RECOVERY), \
    S(PORT_RESET), \
    S(PORT_RESET_WAIT_OFF)
    @@ -143,6 +150,7 @@ enum pd_msg_request {
    PD_MSG_NONE = 0,
    PD_MSG_CTRL_REJECT,
    PD_MSG_CTRL_WAIT,
    + PD_MSG_CTRL_NOT_SUPP,
    PD_MSG_DATA_SINK_CAP,
    PD_MSG_DATA_SOURCE_CAP,
    };
    @@ -1413,10 +1421,42 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo,
    /*
    * PD (data, control) command handling functions
    */
    +static inline enum tcpm_state ready_state(struct tcpm_port *port)
    +{
    + if (port->pwr_role == TYPEC_SOURCE)
    + return SRC_READY;
    + else
    + return SNK_READY;
    +}

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

    +static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload,
    + int cnt)
    +{
    + u32 p0 = le32_to_cpu(payload[0]);
    + unsigned int type = usb_pd_ado_type(p0);
    +
    + if (!type) {
    + tcpm_log(port, "Alert message received with no type");
    + return;
    + }
    +
    + /* Just handling non-battery alerts for now */
    + if (!(type & USB_PD_ADO_TYPE_BATT_STATUS_CHANGE)) {
    + switch (port->state) {
    + case SRC_READY:
    + case SNK_READY:
    + tcpm_set_state(port, GET_STATUS_SEND, 0);
    + break;
    + default:
    + tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
    + break;
    + }
    + }
    +}
    +
    static void tcpm_pd_data_request(struct tcpm_port *port,
    const struct pd_message *msg)
    {
    @@ -1502,6 +1542,14 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
    tcpm_set_state(port, BIST_RX, 0);
    }
    break;
    + case PD_DATA_ALERT:
    + tcpm_handle_alert(port, msg->payload, cnt);
    + break;
    + case PD_DATA_BATT_STATUS:
    + case PD_DATA_GET_COUNTRY_INFO:
    + /* Currently unsupported */
    + tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
    + break;
    default:
    tcpm_log(port, "Unhandled data message type %#x", type);
    break;
    @@ -1584,6 +1632,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
    break;
    case PD_CTRL_REJECT:
    case PD_CTRL_WAIT:
    + case PD_CTRL_NOT_SUPP:
    switch (port->state) {
    case SNK_NEGOTIATE_CAPABILITIES:
    /* USB PD specification, Figure 8-43 */
    @@ -1703,12 +1752,84 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
    break;
    }
    break;
    + case PD_CTRL_GET_SOURCE_CAP_EXT:
    + case PD_CTRL_GET_STATUS:
    + case PD_CTRL_FR_SWAP:
    + case PD_CTRL_GET_PPS_STATUS:
    + case PD_CTRL_GET_COUNTRY_CODES:
    + /* Currently not supported */
    + tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
    + break;
    default:
    tcpm_log(port, "Unhandled ctrl message type %#x", type);
    break;
    }
    }

    +static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
    + const struct pd_message *msg)
    +{
    + enum pd_ext_msg_type type = pd_header_type_le(msg->header);
    + unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header);
    + u8 *data;
    +
    + if (!(msg->ext_msg.header && PD_EXT_HDR_CHUNKED)) {
    + tcpm_log(port, "Unchunked extended messages unsupported");
    + return;
    + }
    +
    + if (data_size > (PD_EXT_MAX_CHUNK_DATA)) {
    + tcpm_log(port, "Chunk handling not yet supported");
    + return;
    + }
    +
    + data = kzalloc(data_size, GFP_KERNEL);
    + if (!data) {
    + tcpm_log(port, "Failed to allocate memory for ext msg data");
    + return;
    + }
    + memcpy(data, msg->ext_msg.data, data_size);
    +
    + switch (type) {
    + case PD_EXT_STATUS:
    + /*
    + * If PPS related events raised then get PPS status to clear
    + * (see USB PD 3.0 Spec, 6.5.2.4)
    + */
    + if (data[USB_PD_EXT_SDB_EVENT_FLAGS] & USB_PD_EXT_SDB_PPS_EVENTS)
    + tcpm_set_state(port, GET_PPS_STATUS_SEND, 0);
    + else
    + tcpm_set_state(port, ready_state(port), 0);
    + break;
    + case PD_EXT_PPS_STATUS:
    + /*
    + * For now the PPS status message is used to clear events
    + * and nothing more.
    + */
    + tcpm_set_state(port, ready_state(port), 0);
    + break;
    + case PD_EXT_SOURCE_CAP_EXT:
    + case PD_EXT_GET_BATT_CAP:
    + case PD_EXT_GET_BATT_STATUS:
    + case PD_EXT_BATT_CAP:
    + case PD_EXT_GET_MANUFACTURER_INFO:
    + case PD_EXT_MANUFACTURER_INFO:
    + case PD_EXT_SECURITY_REQUEST:
    + case PD_EXT_SECURITY_RESPONSE:
    + case PD_EXT_FW_UPDATE_REQUEST:
    + case PD_EXT_FW_UPDATE_RESPONSE:
    + case PD_EXT_COUNTRY_INFO:
    + case PD_EXT_COUNTRY_CODES:
    + tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
    + break;
    + default:
    + tcpm_log(port, "Unhandle extended message type %#x", type);
    + break;
    + }
    +
    + kfree(data);
    +}
    +
    static void tcpm_pd_rx_handler(struct work_struct *work)
    {
    struct pd_rx_event *event = container_of(work,
    @@ -1749,7 +1870,9 @@ static void tcpm_pd_rx_handler(struct work_struct *work)
    "Data role mismatch, initiating error recovery");
    tcpm_set_state(port, ERROR_RECOVERY, 0);
    } else {
    - if (cnt)
    + if (msg->header & PD_HEADER_EXT_HDR)
    + tcpm_pd_ext_msg_request(port, msg);
    + else if (cnt)
    tcpm_pd_data_request(port, msg);
    else
    tcpm_pd_ctrl_request(port, msg);
    @@ -1810,6 +1933,9 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
    case PD_MSG_CTRL_REJECT:
    tcpm_pd_send_control(port, PD_CTRL_REJECT);
    break;
    + case PD_MSG_CTRL_NOT_SUPP:
    + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP);
    + break;
    case PD_MSG_DATA_SINK_CAP:
    tcpm_pd_send_sink_caps(port);
    break;
    @@ -2561,14 +2687,6 @@ static inline enum tcpm_state hard_reset_state(struct tcpm_port *port)
    return SNK_UNATTACHED;
    }

    -static inline enum tcpm_state ready_state(struct tcpm_port *port)
    -{
    - if (port->pwr_role == TYPEC_SOURCE)
    - return SRC_READY;
    - else
    - return SNK_READY;
    -}
    -
    static inline enum tcpm_state unattached_state(struct tcpm_port *port)
    {
    if (port->port_type == TYPEC_PORT_DRP) {
    @@ -3271,6 +3389,22 @@ static void run_state_machine(struct tcpm_port *port)
    /* Always switch to unattached state */
    tcpm_set_state(port, unattached_state(port), 0);
    break;
    + case GET_STATUS_SEND:
    + tcpm_pd_send_control(port, PD_CTRL_GET_STATUS);
    + tcpm_set_state(port, GET_STATUS_SEND_TIMEOUT,
    + PD_T_SENDER_RESPONSE);
    + break;
    + case GET_STATUS_SEND_TIMEOUT:
    + tcpm_set_state(port, ready_state(port), 0);
    + break;
    + case GET_PPS_STATUS_SEND:
    + tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS);
    + tcpm_set_state(port, GET_PPS_STATUS_SEND_TIMEOUT,
    + PD_T_SENDER_RESPONSE);
    + break;
    + case GET_PPS_STATUS_SEND_TIMEOUT:
    + tcpm_set_state(port, ready_state(port), 0);
    + break;
    case ERROR_RECOVERY:
    tcpm_swap_complete(port, -EPROTO);
    tcpm_pps_complete(port, -EPROTO);
    --
    1.9.1
    \
     
     \ /
      Last update: 2018-01-02 16:51    [W:2.194 / U:0.164 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site