From patchwork Mon Jan 28 20:37:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ajay Gupta X-Patchwork-Id: 10784705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 31C3D139A for ; Mon, 28 Jan 2019 20:37:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 202122C331 for ; Mon, 28 Jan 2019 20:37:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 14D0A2C32D; Mon, 28 Jan 2019 20:37:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4104A2C04C for ; Mon, 28 Jan 2019 20:37:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727616AbfA1Uhp (ORCPT ); Mon, 28 Jan 2019 15:37:45 -0500 Received: from mail-pg1-f196.google.com ([209.85.215.196]:46406 "EHLO mail-pg1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726719AbfA1Uho (ORCPT ); Mon, 28 Jan 2019 15:37:44 -0500 Received: by mail-pg1-f196.google.com with SMTP id w7so7693948pgp.13 for ; Mon, 28 Jan 2019 12:37:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=X7YJWwAzbkQTZVvk0v5qfpxGosEf+62mWyxOmlmyORI=; b=OwaUK85aucn/TaKOOxQlLyMkDOB4gEv0wYn5xA8LW1TKASeYmgiBeacLuljL2teAaV bNYe4InmDCh3zyxCLD7pq+R8y5BaWDPtoplV/TSd4c7W2p9DGc3Na7OC9xDbXq60jgFL /s5xBNfBha5LnGjEZP8p/m5SOiJzrG0HucnbB3aghoopktB5KTLlryX5hBnktbOro1PB d9l9rJOvWYhzCTtrsYxaLQGT4gECeadY6ksKCRhxQ8EK0ciUnKTEvFpBaIAXonc9TWh8 knBIoZtlLR3asA2qI4roh0GypldUASb6BWjUKeMfFGMW94JIQa4SDZLTLa7FLSmdcSQb 2mYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=X7YJWwAzbkQTZVvk0v5qfpxGosEf+62mWyxOmlmyORI=; b=ZJsKzreaXpxsULfsQZ/HKf05KlgF1NWSWoO5dcCzkQzIL+Jt1z+28ekGL5cyYQdM59 j0FHVjW8pd/mIn7ATMzgC3avLOdhB4JeLFvpIoe4Qsj/+beCrRT8hxh9wEa7eAcEmDS/ LK2KMwlnOsC4DqcmhfPOPdflgzzDx7e3QC+DhJQrZQRnFsQAXL5mH/uDDtUCdQzbmcap f7vTnS4otTl4usCefHD1WJRV8yXshWtP5EzzO47wq7wsgmVN1pm8bdIpTuGQcV7Gpk8U Xf9vP2y978shIZZMp+QbPCYGX5KYdPD3KN1azc1eWMDtpJoLqgsi1CrFeSrxZtni8U0i S25A== X-Gm-Message-State: AJcUukdN67Cda17/GjA6thMyaGAGAkhSA36iekeXHlYbKs7JVDrLeA/U biNjFR7BcfIDLN04i90BB1g= X-Google-Smtp-Source: ALg8bN5pq+0lgg5nkeqCA5prI4+c2rEnIkByy+KgbT4LB/MLCRUcLAgh4MMY5NPQ/IpbtYc8cd86TA== X-Received: by 2002:a62:6ec8:: with SMTP id j191mr23397472pfc.198.1548707863165; Mon, 28 Jan 2019 12:37:43 -0800 (PST) Received: from ajayg.nvidia.com (searspoint.nvidia.com. [216.228.112.21]) by smtp.gmail.com with ESMTPSA id t3sm34628047pgv.31.2019.01.28.12.37.42 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 28 Jan 2019 12:37:42 -0800 (PST) From: Ajay Gupta X-Google-Original-From: Ajay Gupta To: heikki.krogerus@linux.intel.com Cc: linux-usb@vger.kernel.org, Ajay Gupta Subject: [PATCH v2 2/6] usb: typec: ucsi: add ccg command framework Date: Mon, 28 Jan 2019 12:37:27 -0800 Message-Id: <20190128203731.12681-3-ajayg@nvidia.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190128203731.12681-1-ajayg@nvidia.com> References: <20190128203731.12681-1-ajayg@nvidia.com> X-NVConfidentiality: public Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Used to send various commands to ccg controller. They are mainly used during firmware update process. We wait for response after sending the command and then read the response from RAB_RESPONSE register. Signed-off-by: Ajay Gupta --- Changes from v1: - Updated commit message and dropped dev_dbg prints drivers/usb/typec/ucsi/ucsi_ccg.c | 242 ++++++++++++++++++++++++++++-- 1 file changed, 233 insertions(+), 9 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 76cf772872db..3155ee6be357 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -17,6 +17,29 @@ #include #include "ucsi.h" +#define CCGX_RAB_DEVICE_MODE 0x0000 +#define CCGX_RAB_INTR_REG 0x0006 +#define DEV_INT BIT(0) +#define PORT0_INT BIT(1) +#define PORT1_INT BIT(2) +#define UCSI_READ_INT BIT(7) +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 +#define CCGX_RAB_UCSI_CONTROL_START BIT(0) +#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) +#define DEV_REG_IDX CCGX_RAB_DEVICE_MODE +#define CCGX_RAB_RESPONSE 0x007E +#define ASYNC_EVENT BIT(7) + +/* CCGx events & async msg codes */ +#define RESET_COMPLETE 0x80 +#define EVENT_INDEX RESET_COMPLETE +#define PORT_CONNECT_DET 0x84 +#define PORT_DISCONNECT_DET 0x85 +#define ROLE_SWAP_COMPELETE 0x87 + struct ccg_dev_info { #define CCG_DEVINFO_FWMODE_SHIFT (0) #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) @@ -43,6 +66,99 @@ struct version_info { struct version_format app; }; +/* CCGx response codes */ +enum ccg_resp_code { + CMD_NO_RESP = 0x00, + CMD_SUCCESS = 0x02, + FLASH_DATA_AVAILABLE = 0x03, + CMD_INVALID = 0x05, + FLASH_UPDATE_FAIL = 0x07, + INVALID_FW = 0x08, + INVALID_ARG = 0x09, + CMD_NOT_SUPPORT = 0x0A, + TRANSACTION_FAIL = 0x0C, + PD_CMD_FAIL = 0x0D, + UNDEF_ERROR = 0x0F, + INVALID_RESP = 0x10, +}; + +static const char * const ccg_resp_strs[] = { + /* 0x00 */ "No Response.", + /* 0x01 */ "0x01", + /* 0x02 */ "HPI Command Success.", + /* 0x03 */ "Flash Data Available in data memory.", + /* 0x04 */ "0x04", + /* 0x05 */ "Invalid Command.", + /* 0x06 */ "0x06", + /* 0x07 */ "Flash write operation failed.", + /* 0x08 */ "Firmware validity check failed.", + /* 0x09 */ "Command failed due to invalid arguments.", + /* 0x0A */ "Command not supported in the current mode.", + /* 0x0B */ "0x0B", + /* 0x0C */ "Transaction Failed. GOOD_CRC was not received.", + /* 0x0D */ "PD Command Failed.", + /* 0x0E */ "0x0E", + /* 0x0F */ "Undefined Error", +}; + +static const char * const ccg_evt_strs[] = { + /* 0x80 */ "Reset Complete.", + /* 0x81 */ "Message queue overflow detected.", + /* 0x82 */ "Overcurrent Detected", + /* 0x83 */ "Overvoltage Detected", + /* 0x84 */ "Type-C Port Connect Detected", + /* 0x85 */ "Type-C Port Disconnect Detected", + /* 0x86 */ "PD Contract Negotiation Complete", + /* 0x87 */ "SWAP Complete", + /* 0x88 */ "0x88", + /* 0x89 */ "0x89", + /* 0x8A */ "PS_RDY Message Received", + /* 0x8B */ "GotoMin Message Received.", + /* 0x8C */ "Accept Message Received", + /* 0x8D */ "Reject Message Received", + /* 0x8E */ "Wait Message Received", + /* 0x8F */ "Hard Reset Received", + /* 0x90 */ "VDM Received", + /* 0x91 */ "Source Capabilities Message Received", + /* 0x92 */ "Sink Capabilities Message Received", + /* 0x93 */ "Display Port Alternate Mode entered", + /* 0x94 */ "Display Port device connected at UFP_U", + /* 0x95 */ "Display port device not connected at UFP_U", + /* 0x96 */ "Display port SID not found in Discover SID process", + /* 0x97 */ "Multiple SVIDs discovered along with DisplayPort SID", + /* 0x98 */ "DP Functionality not supported by Cable", + /* 0x99 */ "Display Port Configuration not supported by UFP", + /* 0x9A */ "Hard Reset Sent to Port Partner", + /* 0x9B */ "Soft Reset Sent to Port Partner", + /* 0x9C */ "Cable Reset Sent to EMCA", + /* 0x9D */ "Source Disabled State Entered", + /* 0x9E */ "Sender Response Timer Timeout", + /* 0x9F */ "No VDM Response Received", + /* 0xA0 */ "Unexpected Voltage on Vbus", + /* 0xA1 */ "Type-C Error Recovery", + /* 0xA2 */ "0xA2", + /* 0xA3 */ "0xA3", + /* 0xA4 */ "0xA4", + /* 0xA5 */ "0xA5", + /* 0xA6 */ "EMCA Detected", + /* 0xA7 */ "0xA7", + /* 0xA8 */ "0xA8", + /* 0xA9 */ "0xA9", + /* 0xAA */ "Rp Change Detected", +}; + +struct ccg_cmd { + u16 reg; + u32 data; + int len; + int delay; /* ms delay for cmd timeout */ +}; + +struct ccg_resp { + u8 code; + u8 length; +}; + struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; @@ -50,17 +166,14 @@ struct ucsi_ccg { struct i2c_client *client; struct ccg_dev_info info; struct version_info version[3]; + /* CCG HPI communication flags */ + unsigned long flags; +#define RESET_PENDING 0 +#define DEV_CMD_PENDING 1 + struct ccg_resp dev_resp; + u8 cmd_resp; }; -#define CCGX_RAB_DEVICE_MODE 0x0000 -#define CCGX_RAB_INTR_REG 0x0006 -#define CCGX_RAB_READ_ALL_VER 0x0010 -#define CCGX_RAB_READ_FW2_VER 0x0020 -#define CCGX_RAB_UCSI_CONTROL 0x0039 -#define CCGX_RAB_UCSI_CONTROL_START BIT(0) -#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) -#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) - static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) { struct i2c_client *client = uc->client; @@ -268,6 +381,117 @@ static int get_fw_info(struct ucsi_ccg *uc) return 0; } +static inline bool invalid_resp(int code) +{ + return (code >= INVALID_RESP); +} + +static inline bool invalid_evt(int code) +{ + unsigned long num_of_events = ARRAY_SIZE(ccg_evt_strs); + + return (code >= (EVENT_INDEX + num_of_events)) || (code < EVENT_INDEX); +} + +static void ccg_process_response(struct ucsi_ccg *uc) +{ + struct device *dev = uc->dev; + + if (uc->dev_resp.code & ASYNC_EVENT) { + if (uc->dev_resp.code == RESET_COMPLETE) { + if (test_bit(RESET_PENDING, &uc->flags)) + uc->cmd_resp = uc->dev_resp.code; + get_fw_info(uc); + } + + if (invalid_evt(uc->dev_resp.code)) + dev_err(dev, "invalid evt %d\n", uc->dev_resp.code); + } else { + if (test_bit(DEV_CMD_PENDING, &uc->flags)) { + uc->cmd_resp = uc->dev_resp.code; + clear_bit(DEV_CMD_PENDING, &uc->flags); + } else { + dev_err(dev, "dev resp 0x%04x but no cmd pending\n", + uc->dev_resp.code); + } + } +} + +static int ccg_read_response(struct ucsi_ccg *uc) +{ + unsigned long target = jiffies + msecs_to_jiffies(1000); + struct device *dev = uc->dev; + u8 intval; + int status; + + /* wait for interrupt status to get updated */ + do { + status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval, + sizeof(intval)); + if (status < 0) + return status; + + if (intval & DEV_INT) + break; + usleep_range(500, 600); + } while (time_is_after_jiffies(target)); + + if (time_is_before_jiffies(target)) { + dev_err(dev, "response timeout error\n"); + return -ETIME; + } + + status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp, + sizeof(uc->dev_resp)); + if (status < 0) + return status; + + status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval)); + if (status < 0) + return status; + + return 0; +} + +/* Caller must hold uc->lock */ +static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd) +{ + struct device *dev = uc->dev; + int ret; + + switch (cmd->reg & 0xF000) { + case DEV_REG_IDX: + set_bit(DEV_CMD_PENDING, &uc->flags); + break; + default: + dev_err(dev, "invalid cmd register\n"); + break; + } + + ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len); + if (ret < 0) + return ret; + + msleep(cmd->delay); + + ret = ccg_read_response(uc); + if (ret < 0) { + dev_err(dev, "response read error\n"); + switch (cmd->reg & 0xF000) { + case DEV_REG_IDX: + clear_bit(DEV_CMD_PENDING, &uc->flags); + break; + default: + dev_err(dev, "invalid cmd register\n"); + break; + } + return -EIO; + } + ccg_process_response(uc); + + return uc->cmd_resp; +} + static int ucsi_ccg_probe(struct i2c_client *client, const struct i2c_device_id *id) {