From patchwork Sat Feb 2 01:05:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ajay Gupta X-Patchwork-Id: 10793911 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 0ACE2184E for ; Sat, 2 Feb 2019 01:05:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F07E632F9D for ; Sat, 2 Feb 2019 01:05:25 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E542332FA0; Sat, 2 Feb 2019 01:05:25 +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 594FA32F9D for ; Sat, 2 Feb 2019 01:05:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727101AbfBBBFY (ORCPT ); Fri, 1 Feb 2019 20:05:24 -0500 Received: from mail-pl1-f195.google.com ([209.85.214.195]:36029 "EHLO mail-pl1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726742AbfBBBFY (ORCPT ); Fri, 1 Feb 2019 20:05:24 -0500 Received: by mail-pl1-f195.google.com with SMTP id g9so4083034plo.3 for ; Fri, 01 Feb 2019 17:05:23 -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=UwTJ4YoHcO9gOD4+eevDPZ4QEdT5/1P8hF7JTltl0QM=; b=Jo1kMXrcLYCclVCGNP64CXjxcnxBJzLwxYf+UB4Y3JoeGn4/ILaDYS9goQV21lJC/X LXWlBwuBEA4YcqBEQg2nDZv2SdI57H2z5ihXCDwDLOyL0u+TU6u/Z1zl/fLcGO3/SJiC 5fqD1i4u/S3Xv+GYKjMaYzBp4ItgpaWRIwqYyf4Q2EoTa5F5Bm8Xryv+yi+nvXbH/JJ/ LgSgyscJlxJlJXx8l8FiZbRO57fPc8ggYFgnpBGhmcDP8M0CpzXVHvp+5ZXnageZA/Wb rncMceKqFuxasUyxBUZ0gmvM9RD7icmteFsfn2h2awoDyjp4cngS729705mqPQZ2JQ9j orWA== 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=UwTJ4YoHcO9gOD4+eevDPZ4QEdT5/1P8hF7JTltl0QM=; b=a+9LMIIp8O4Kb4bMGFLSP07HvfvE1PiyE1J0myhzohgJxejcdqogFZxi60A0igkRDF bMq2b4cND5yj70CHw/jpS+kvmRzAf1RXxonzjeJv5bHSr7yu1gnFex0816ZaGrYIT5SA 1Fn2XsQhk+npGRmop0PoelYrFsQbyAg+CMLtrCIWW/Jy11Bp0uV5EO7kjzeHW8HnSjHr qPpSqARdlmxvUNW0p+tE88+FQI1sSceMz26oKOWgxALHz6yVXZscjhJWp/pNPptcZ7sN 8YTv/mOca+H/FBceAMfUh4ZbGTsA11a8EKiV/y88BOV26kf296Fpoj07aLfs/x7YtQeM 9Rlg== X-Gm-Message-State: AJcUukfPN8ccSGxs87JXIdk2xY058gGp1K7MT0keW8qUYzeDFnwTJjAA Q4++VLxrTzHldxaTi5s+pps= X-Google-Smtp-Source: ALg8bN7FO977ni11tgYzplZjucsZCDrGNQf0OgFTMdjzumzsdxI0kboDZ3IfFk4IByCMlljmL7/2sw== X-Received: by 2002:a17:902:5a5:: with SMTP id f34mr42158386plf.161.1549069523065; Fri, 01 Feb 2019 17:05:23 -0800 (PST) Received: from ajayg.nvidia.com (searspoint.nvidia.com. [216.228.112.21]) by smtp.gmail.com with ESMTPSA id f6sm13158685pfg.188.2019.02.01.17.05.21 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 01 Feb 2019 17:05:22 -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 v3 2/6] usb: typec: ucsi: add ccg command framework Date: Fri, 1 Feb 2019 17:05:06 -0800 Message-Id: <20190202010510.25016-3-ajayg@nvidia.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190202010510.25016-1-ajayg@nvidia.com> References: <20190202010510.25016-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 From: Ajay Gupta 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 v2 to v3 - Removed unused structures - Used CCG_EVENT_MAX instead of long structure and sizeof() drivers/usb/typec/ucsi/ucsi_ccg.c | 172 ++++++++++++++++++++++++++++-- 1 file changed, 163 insertions(+), 9 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 3884fb41c72e..58b697d1ef8b 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -24,6 +24,29 @@ enum enum_fw_mode { FW_INVALID, }; +#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) @@ -50,6 +73,36 @@ 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, +}; + +#define CCG_EVENT_MAX (EVENT_INDEX + 43) + +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; @@ -58,17 +111,14 @@ struct ucsi_ccg { struct ccg_dev_info info; /* version info for boot, primary and secondary */ struct version_info version[FW2 + 1]; + /* 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; @@ -276,6 +326,110 @@ static int get_fw_info(struct ucsi_ccg *uc) return 0; } +static inline bool invalid_async_evt(int code) +{ + return (code >= CCG_EVENT_MAX) || (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_async_evt(uc->dev_resp.code)) + dev_err(dev, "invalid async 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) {