Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp736284imm; Fri, 8 Jun 2018 04:32:54 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJsXLocirOQKxMWLdQXVoL/5un+VVPTmXdDU7CA7mU47q5VUxM7PskDmacMYhYf0YThvpEQ X-Received: by 2002:a17:902:7293:: with SMTP id d19-v6mr6263368pll.142.1528457574879; Fri, 08 Jun 2018 04:32:54 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1528457574; cv=none; d=google.com; s=arc-20160816; b=OCedCCeQwzpmOLRGtkre6eQtKkT5wbW1MUiH/A+S60LbNUPsR+cTqsyRFrh+Tg0TEM 6YsuCoDFXw43uluf7c2e8kcy4i4FYKnM3HME1nThluYvv6GKAKbLdP9jFG+iptC8kr8d KIXFTNTQqqTxFtMyp5u6E3ksvW71l7ooRYbP/f17ZqUpsUwoZy2Zs1Xfwhbdx6axI77a +8ngqhP5uY8m7wKPpxl10YyeTUockRuY5EXsSfiXi2kBOYEOh/+ic8UYhOfeLCGtC1Cl iYHnSz86kfCCJ6Tu/InUrRxe6104JKEdTlkuxkP278+htV/vAmIlkbBKsuRR9ujcLJHn y5oQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=LxR5y+xpei3jUoZNBAdBLXZTXdLECQiZNgIu0r5yugs=; b=LKUOXDCG9WSCvqErOSKQkFFpBx/jcYQtHUHNdIH48hd5hLGqkJceQJARdQXp/fB34A vJKPKdceYxW8gdwThIZ8ctYMkOwSIATbUifgA1a1f05cOUqo/G8b5Tkuu4B3jI8XcoLX 5AKGRhJnRQJyUfcKFv0VBXNhyWCB14ufu1ZFpzENxqQUQOQOVKvcT2rk4CyX24KkNmHQ gBTZh7gPEJBXF4iF3Y/ae3M445W24TK0wmDyrqzKPEkRjXcXF3DDv9maYTb3YUr96W2b B4mWgNiQepgAHFQYD6C7Abfp4lrTv2Sm0O6QCn+mCVYK94tebZw+ik4WTbcChzpUmNO6 O07Q== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id w9-v6si58813682plk.28.2018.06.08.04.32.40; Fri, 08 Jun 2018 04:32:54 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752948AbeFHLaJ (ORCPT + 99 others); Fri, 8 Jun 2018 07:30:09 -0400 Received: from mga17.intel.com ([192.55.52.151]:32425 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752879AbeFHLaE (ORCPT ); Fri, 8 Jun 2018 07:30:04 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 08 Jun 2018 04:30:03 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.49,490,1520924400"; d="scan'208";a="62389035" Received: from black.fi.intel.com (HELO black.fi.intel.com.) ([10.237.72.28]) by fmsmga001.fm.intel.com with ESMTP; 08 Jun 2018 04:30:01 -0700 From: Heikki Krogerus To: Guenter Roeck , Hans de Goede Cc: Greg Kroah-Hartman , Jun Li , Mats Karrman , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH v4 8/8] usb: typec: tcpm: Support for Alternate Modes Date: Fri, 8 Jun 2018 14:29:41 +0300 Message-Id: <20180608112941.26332-9-heikki.krogerus@linux.intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180608112941.26332-1-heikki.krogerus@linux.intel.com> References: <20180608112941.26332-1-heikki.krogerus@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This adds more complete handling of VDMs and registration of partner alternate modes, and introduces callbacks for alternate mode operations. Only DFP role is supported for now. Signed-off-by: Heikki Krogerus --- drivers/usb/typec/tcpm.c | 161 +++++++++++++++++++++++++++++++-------- include/linux/usb/tcpm.h | 9 --- 2 files changed, 131 insertions(+), 39 deletions(-) diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 44ce3955cb59..e1b52b5e78b5 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #define FOREACH_STATE(S) \ @@ -169,13 +169,14 @@ enum pd_msg_request { /* Alternate mode support */ #define SVID_DISCOVERY_MAX 16 +#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX) struct pd_mode_data { int svid_index; /* current SVID index */ int nsvids; u16 svids[SVID_DISCOVERY_MAX]; int altmodes; /* number of alternate modes */ - struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX]; + struct typec_altmode_desc altmode_desc[ALTMODE_DISCOVERY_MAX]; }; struct pd_pps_data { @@ -310,8 +311,8 @@ struct tcpm_port { /* Alternate mode data */ struct pd_mode_data mode_data; - struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX * 6]; - struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX * 6]; + struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX]; + struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX]; /* Deadline in jiffies to exit src_try_wait state */ unsigned long max_wait; @@ -640,14 +641,14 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port, } EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete); -static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, +static int tcpm_mux_set(struct tcpm_port *port, int state, enum usb_role usb_role, enum typec_orientation orientation) { int ret; - tcpm_log(port, "Requesting mux mode %d, usb-role %d, orientation %d", - mode, usb_role, orientation); + tcpm_log(port, "Requesting mux state %d, usb-role %d, orientation %d", + state, usb_role, orientation); ret = typec_set_orientation(port->typec_port, orientation); if (ret) @@ -659,7 +660,7 @@ static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, return ret; } - return typec_set_mode(port->typec_port, mode); + return typec_set_mode(port->typec_port, state); } static int tcpm_set_polarity(struct tcpm_port *port, @@ -786,7 +787,7 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, else usb_role = USB_ROLE_DEVICE; - ret = tcpm_mux_set(port, TYPEC_MUX_USB, usb_role, orientation); + ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation); if (ret < 0) return ret; @@ -1013,36 +1014,57 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, pmdata->altmodes, paltmode->svid, paltmode->mode, paltmode->vdo); - port->partner_altmode[pmdata->altmodes] = - typec_partner_register_altmode(port->partner, paltmode); - if (!port->partner_altmode[pmdata->altmodes]) { - tcpm_log(port, - "Failed to register modes for SVID 0x%04x", - paltmode->svid); - return; - } pmdata->altmodes++; } } +static void tcpm_register_partner_altmodes(struct tcpm_port *port) +{ + struct pd_mode_data *modep = &port->mode_data; + struct typec_altmode *altmode; + int i; + + for (i = 0; i < modep->altmodes; i++) { + altmode = typec_partner_register_altmode(port->partner, + &modep->altmode_desc[i]); + if (!altmode) + tcpm_log(port, "Failed to register partner SVID 0x%04x", + modep->altmode_desc[i].svid); + port->partner_altmode[i] = altmode; + } +} + #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header) static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, u32 *response) { - u32 p0 = le32_to_cpu(payload[0]); - int cmd_type = PD_VDO_CMDT(p0); - int cmd = PD_VDO_CMD(p0); + struct typec_altmode *adev; + struct typec_altmode *pdev; struct pd_mode_data *modep; + u32 p[PD_MAX_PAYLOAD]; int rlen = 0; - u16 svid; + int cmd_type; + int cmd; int i; + for (i = 0; i < cnt; i++) + p[i] = le32_to_cpu(payload[i]); + + cmd_type = PD_VDO_CMDT(p[0]); + cmd = PD_VDO_CMD(p[0]); + tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d", - p0, cmd_type, cmd, cnt); + p[0], cmd_type, cmd, cnt); modep = &port->mode_data; + adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); + + pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); + switch (cmd_type) { case CMDT_INIT: switch (cmd) { @@ -1064,17 +1086,19 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, case CMD_EXIT_MODE: break; case CMD_ATTENTION: - break; + /* Attention command does not have response */ + typec_altmode_attention(adev, p[1]); + return 0; default: break; } if (rlen >= 1) { - response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK); + response[0] = p[0] | VDO_CMDT(CMDT_RSP_ACK); } else if (rlen == 0) { - response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK); + response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); rlen = 1; } else { - response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY); + response[0] = p[0] | VDO_CMDT(CMDT_RSP_BUSY); rlen = 1; } break; @@ -1107,14 +1131,39 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, svdm_consume_modes(port, payload, cnt); modep->svid_index++; if (modep->svid_index < modep->nsvids) { - svid = modep->svids[modep->svid_index]; + u16 svid = modep->svids[modep->svid_index]; response[0] = VDO(svid, 1, CMD_DISCOVER_MODES); rlen = 1; } else { - /* enter alternate mode if/when implemented */ + tcpm_register_partner_altmodes(port); } break; case CMD_ENTER_MODE: + typec_altmode_update_active(pdev, true); + + if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) { + response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE); + response[0] |= VDO_OPOS(adev->mode); + return 1; + } + return 0; + case CMD_EXIT_MODE: + typec_altmode_update_active(pdev, false); + + /* Back to USB Operation */ + WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, + NULL)); + break; + default: + break; + } + break; + case CMDT_RSP_NAK: + switch (cmd) { + case CMD_ENTER_MODE: + /* Back to USB Operation */ + WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, + NULL)); break; default: break; @@ -1124,6 +1173,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, break; } + /* Informing the alternate mode drivers about everything */ + typec_altmode_vdm(adev, p[0], &p[1], cnt); + return rlen; } @@ -1407,6 +1459,53 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, return 0; } +static void tcpm_altmode_enter(struct typec_altmode *altmode) +{ + struct tcpm_port *port = typec_altmode_get_drvdata(altmode); + u32 header; + + mutex_lock(&port->lock); + header = VDO(altmode->svid, 1, CMD_ENTER_MODE); + header |= VDO_OPOS(altmode->mode); + + tcpm_queue_vdm(port, header, NULL, 0); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + mutex_unlock(&port->lock); +} + +static void tcpm_altmode_exit(struct typec_altmode *altmode) +{ + struct tcpm_port *port = typec_altmode_get_drvdata(altmode); + u32 header; + + mutex_lock(&port->lock); + header = VDO(altmode->svid, 1, CMD_EXIT_MODE); + header |= VDO_OPOS(altmode->mode); + + tcpm_queue_vdm(port, header, NULL, 0); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + mutex_unlock(&port->lock); +} + +static int tcpm_altmode_vdm(struct typec_altmode *altmode, + u32 header, const u32 *data, int count) +{ + struct tcpm_port *port = typec_altmode_get_drvdata(altmode); + + mutex_lock(&port->lock); + tcpm_queue_vdm(port, header, data, count - 1); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + mutex_unlock(&port->lock); + + return 0; +} + +static const struct typec_altmode_ops tcpm_altmode_ops = { + .enter = tcpm_altmode_enter, + .exit = tcpm_altmode_exit, + .vdm = tcpm_altmode_vdm, +}; + /* * PD (data, control) command handling functions */ @@ -2538,7 +2637,7 @@ static int tcpm_src_attach(struct tcpm_port *port) out_disable_pd: port->tcpc->set_pd_rx(port->tcpc, false); out_disable_mux: - tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE, + tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); return ret; } @@ -2584,7 +2683,7 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_init_vconn(port); tcpm_set_current_limit(port, 0, 0); tcpm_set_polarity(port, TYPEC_POLARITY_CC1); - tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE, + tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); tcpm_set_attached_state(port, false); port->try_src_count = 0; @@ -4616,6 +4715,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) dev_name(dev), paltmode->svid); break; } + typec_altmode_set_drvdata(alt, port); + alt->ops = &tcpm_altmode_ops; port->port_altmode[i] = alt; i++; paltmode++; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index b231b9314240..8c3df3e11801 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -98,15 +98,6 @@ struct tcpc_config { #define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */ #define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */ -/* Mux modes, decoded to attributes */ -enum tcpc_mux_mode { - TYPEC_MUX_NONE = 0, /* Open switch */ - TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */ - TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */ - TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */ - TCPC_MUX_DP_ENABLED, -}; - /** * struct tcpc_dev - Port configuration and callback functions * @config: Pointer to port configuration -- 2.17.1