From patchwork Thu Sep 10 03:45:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361188 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Xy2ppMz9sTq; Thu, 10 Sep 2020 13:45:38 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDWh-0003QR-Cp; Thu, 10 Sep 2020 03:45:35 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWf-0003Pq-8M for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:45:33 +0000 Received: from mail-pl1-f199.google.com ([209.85.214.199]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWe-0004Ll-PH for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:45:33 +0000 Received: by mail-pl1-f199.google.com with SMTP id bd4so285078plb.17 for ; Wed, 09 Sep 2020 20:45:32 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=opURovyCO3iBua1tp+ZW7r07ZkpWyOa3J1dHBnTRM6A=; b=V5cUaX6G2IwKbGIX/5tQzmZE3F7cmigHBCsVBVuWIKPq/6D72Tm/uQIVX9gZval2bl dbAXS2G5BhRbVjLcdvMFtBVnOhCV+vKKgyRyN7iLrjySwyky8MiDcuR4MiKx1iFwRYqq ZiFhapWx9/biRWGoLeqRbppRoVcEsJr7NWJ5S9VRw2dhkXeuBt5I7+Lzh0ioijWDondw bVa9S2vsUAomiX5vHiN0rohT6BLo70m34Y0oxyUAuuWsZzqOUZgGecTRAOH9O/satEK3 iPWtliCrpUh/UQOBqHaWRdAd2YPX7QSkuq/EAYCrDG5XzeT7n5fhzu2Wk7Dm/aAbax7l H6Qw== X-Gm-Message-State: AOAM533b2EvAQPTOfgmeDMFl3lv0N+89zTYMHMukydA2Tf//CN/kmDH4 dS/SDMW+gGUSsbEHXA31yx7Dp1qJr98bCnsuZLD+cGDPbL2IOnV3xSezOWO2flzg6SHztPV88ob uGWgChNyEt/f3SYobqbeKe85+Gd1QEPFESi66xEVNAA== X-Received: by 2002:a63:770a:: with SMTP id s10mr2987299pgc.35.1599709531035; Wed, 09 Sep 2020 20:45:31 -0700 (PDT) X-Google-Smtp-Source: ABdhPJx+vNtIBajnMq3zWP1HwrFXx0EZxXaf/Rh6hveDj5VePP5G0MjujyUY9N0iW9jYqoUrqBKGvQ== X-Received: by 2002:a63:770a:: with SMTP id s10mr2987283pgc.35.1599709530591; Wed, 09 Sep 2020 20:45:30 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id t20sm3930317pgj.27.2020.09.09.20.45.29 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:30 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 1/8][SRU][G] thunderbolt: Add support for separating the flush to SPI and authenticate Date: Thu, 10 Sep 2020 11:45:19 +0800 Message-Id: <20200910034526.12438-2-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Mario Limonciello BugLink: https://bugs.launchpad.net/bugs/1895073 This allows userspace to have a shorter period of time that the device is unusable and to call it at a more convenient time. For example flushing the image may happen while the user is using the machine and authenticating/rebooting may happen while logging out. Signed-off-by: Mario Limonciello Signed-off-by: Mika Westerberg (cherry picked from commit 4b794f8066e84818c172c81024f1d61071f14710) Signed-off-by: Koba Ko --- .../ABI/testing/sysfs-bus-thunderbolt | 11 +- drivers/thunderbolt/nvm.c | 170 ++++++++++++++++++ drivers/thunderbolt/switch.c | 42 +++-- drivers/thunderbolt/tb.h | 7 +- 4 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 drivers/thunderbolt/nvm.c diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 82e80de78dd0..7216d71ec242 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -178,11 +178,18 @@ KernelVersion: 4.13 Contact: thunderbolt-software@lists.01.org Description: When new NVM image is written to the non-active NVM area (through non_activeX NVMem device), the - authentication procedure is started by writing 1 to - this file. If everything goes well, the device is + authentication procedure is started by writing to + this file. + If everything goes well, the device is restarted with the new NVM firmware. If the image verification fails an error code is returned instead. + This file will accept writing values "1" or "2" + - Writing "1" will flush the image to the storage + area and authenticate the image in one action. + - Writing "2" will run some basic validation on the image + and flush it to the storage area. + When read holds status of the last authentication operation if an error occurred during the process. This is directly the status value from the DMA configuration diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c new file mode 100644 index 000000000000..29de6d95c6e7 --- /dev/null +++ b/drivers/thunderbolt/nvm.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NVM helpers + * + * Copyright (C) 2020, Intel Corporation + * Author: Mika Westerberg + */ + +#include +#include +#include + +#include "tb.h" + +static DEFINE_IDA(nvm_ida); + +/** + * tb_nvm_alloc() - Allocate new NVM structure + * @dev: Device owning the NVM + * + * Allocates new NVM structure with unique @id and returns it. In case + * of error returns ERR_PTR(). + */ +struct tb_nvm *tb_nvm_alloc(struct device *dev) +{ + struct tb_nvm *nvm; + int ret; + + nvm = kzalloc(sizeof(*nvm), GFP_KERNEL); + if (!nvm) + return ERR_PTR(-ENOMEM); + + ret = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL); + if (ret < 0) { + kfree(nvm); + return ERR_PTR(ret); + } + + nvm->id = ret; + nvm->dev = dev; + + return nvm; +} + +/** + * tb_nvm_add_active() - Adds active NVMem device to NVM + * @nvm: NVM structure + * @size: Size of the active NVM in bytes + * @reg_read: Pointer to the function to read the NVM (passed directly to the + * NVMem device) + * + * Registers new active NVmem device for @nvm. The @reg_read is called + * directly from NVMem so it must handle possible concurrent access if + * needed. The first parameter passed to @reg_read is @nvm structure. + * Returns %0 in success and negative errno otherwise. + */ +int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read) +{ + struct nvmem_config config; + struct nvmem_device *nvmem; + + memset(&config, 0, sizeof(config)); + + config.name = "nvm_active"; + config.reg_read = reg_read; + config.read_only = true; + config.id = nvm->id; + config.stride = 4; + config.word_size = 4; + config.size = size; + config.dev = nvm->dev; + config.owner = THIS_MODULE; + config.priv = nvm; + + nvmem = nvmem_register(&config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + nvm->active = nvmem; + return 0; +} + +/** + * tb_nvm_write_buf() - Write data to @nvm buffer + * @nvm: NVM structure + * @offset: Offset where to write the data + * @val: Data buffer to write + * @bytes: Number of bytes to write + * + * Helper function to cache the new NVM image before it is actually + * written to the flash. Copies @bytes from @val to @nvm->buf starting + * from @offset. + */ +int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val, + size_t bytes) +{ + if (!nvm->buf) { + nvm->buf = vmalloc(NVM_MAX_SIZE); + if (!nvm->buf) + return -ENOMEM; + } + + nvm->flushed = false; + nvm->buf_data_size = offset + bytes; + memcpy(nvm->buf + offset, val, bytes); + return 0; +} + +/** + * tb_nvm_add_non_active() - Adds non-active NVMem device to NVM + * @nvm: NVM structure + * @size: Size of the non-active NVM in bytes + * @reg_write: Pointer to the function to write the NVM (passed directly + * to the NVMem device) + * + * Registers new non-active NVmem device for @nvm. The @reg_write is called + * directly from NVMem so it must handle possible concurrent access if + * needed. The first parameter passed to @reg_write is @nvm structure. + * Returns %0 in success and negative errno otherwise. + */ +int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size, + nvmem_reg_write_t reg_write) +{ + struct nvmem_config config; + struct nvmem_device *nvmem; + + memset(&config, 0, sizeof(config)); + + config.name = "nvm_non_active"; + config.reg_write = reg_write; + config.root_only = true; + config.id = nvm->id; + config.stride = 4; + config.word_size = 4; + config.size = size; + config.dev = nvm->dev; + config.owner = THIS_MODULE; + config.priv = nvm; + + nvmem = nvmem_register(&config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + nvm->non_active = nvmem; + return 0; +} + +/** + * tb_nvm_free() - Release NVM and its resources + * @nvm: NVM structure to release + * + * Releases NVM and the NVMem devices if they were registered. + */ +void tb_nvm_free(struct tb_nvm *nvm) +{ + if (nvm) { + if (nvm->non_active) + nvmem_unregister(nvm->non_active); + if (nvm->active) + nvmem_unregister(nvm->active); + vfree(nvm->buf); + ida_simple_remove(&nvm_ida, nvm->id); + } + kfree(nvm); +} + +void tb_nvm_exit(void) +{ + ida_destroy(&nvm_ida); +} diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index d7d60cd9226f..0de54b150527 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -35,6 +35,11 @@ struct nvm_auth_status { u32 status; }; +enum nvm_write_ops { + WRITE_AND_AUTHENTICATE = 1, + WRITE_ONLY = 2, +}; + /* * Hold NVM authentication failure status per switch This information * needs to stay around even when the switch gets power cycled so we @@ -164,8 +169,12 @@ static int nvm_validate_and_write(struct tb_switch *sw) } if (tb_switch_is_usb4(sw)) - return usb4_switch_nvm_write(sw, 0, buf, image_size); - return dma_port_flash_write(sw->dma_port, 0, buf, image_size); + ret = usb4_switch_nvm_write(sw, 0, buf, image_size); + else + ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size); + if (!ret) + sw->nvm->flushed = true; + return ret; } static int nvm_authenticate_host_dma_port(struct tb_switch *sw) @@ -1536,7 +1545,7 @@ static ssize_t nvm_authenticate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tb_switch *sw = tb_to_switch(dev); - bool val; + int val; int ret; pm_runtime_get_sync(&sw->dev); @@ -1552,25 +1561,28 @@ static ssize_t nvm_authenticate_store(struct device *dev, goto exit_unlock; } - ret = kstrtobool(buf, &val); + ret = kstrtoint(buf, 10, &val); if (ret) goto exit_unlock; /* Always clear the authentication status */ nvm_clear_auth_status(sw); - if (val) { - if (!sw->nvm->buf) { - ret = -EINVAL; - goto exit_unlock; - } - - ret = nvm_validate_and_write(sw); - if (ret) - goto exit_unlock; + if (val > 0) { + if (!sw->nvm->flushed) { + if (!sw->nvm->buf) { + ret = -EINVAL; + goto exit_unlock; + } - sw->nvm->authenticating = true; - ret = nvm_authenticate(sw); + ret = nvm_validate_and_write(sw); + if (ret || val == WRITE_ONLY) + goto exit_unlock; + } + if (val == WRITE_AND_AUTHENTICATE) { + sw->nvm->authenticating = true; + ret = nvm_authenticate(sw); + } } exit_unlock: diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 2eb2bcd3cca3..065eb65b2695 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -29,7 +29,11 @@ * the actual NVM flash device * @buf_data_size: Number of bytes actually consumed by the new NVM * image - * @authenticating: The switch is authenticating the new NVM + * @authenticating: The device is authenticating the new NVM + * @flushed: The image has been flushed to the storage area + * + * The user of this structure needs to handle serialization of possible + * concurrent access. */ struct tb_switch_nvm { u8 major; @@ -40,6 +44,7 @@ struct tb_switch_nvm { void *buf; size_t buf_data_size; bool authenticating; + bool flushed; }; #define TB_SWITCH_KEY_SIZE 32 From patchwork Thu Sep 10 03:45:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361189 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Y12F0Tz9sTp; Thu, 10 Sep 2020 13:45:41 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDWk-0003Ru-J5; Thu, 10 Sep 2020 03:45:38 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWh-0003Qo-PV for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:45:35 +0000 Received: from mail-pf1-f198.google.com ([209.85.210.198]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWh-0004Lu-6T for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:45:35 +0000 Received: by mail-pf1-f198.google.com with SMTP id h15so3641054pfr.3 for ; Wed, 09 Sep 2020 20:45:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=jj++NGoS8FL3WtPtCUCK/S+8tPibvU6VsQiwgU2mbDU=; b=UTYWkdMUk1ThGDDJfK1kb/KhDg1yzkOftIwd1AmilajVf7lDhmIXEmTdj0kjlgiYUv 96NGvaVnUZa1va7J79Jeg+tWYa4kKJENmoXDW5aCIieEMizroLluWha8TqlyC1Bdo5In XqUREduLVQVggKuXxJaApmRw5jjYuPxowVqDjpyvVdmHKUGK6EDCw00Qnnb70uolfeVs P5Aq+/eedyQfesLB9TLWEr1tSh1JiGhRNU7VdNHU0wwJvqyWpSN6gBDftduTS6MylxL9 /WdX1GYGAKfyJpm9mKN4QzPA1tzHaJ16hiH57kVcr1tB91WP9Wm6/Oy9boz1zvj9tJ5z GEBA== X-Gm-Message-State: AOAM53094l9vkp0KZ5U2ep9r/OpgPoeuu1cBH6vTl/4rTZpC3fQ7IeEl FUGbO6r0enxGZoxCxDjLWCJuaPwciRb1s2pFFxiEKVDiEXsmiacqe51Z0XfcPNYyQBG3khsp1Kz lwcuHnTVa2q0jsEa3gR736Ad9bZsTAmWI1ld94wYYuQ== X-Received: by 2002:a17:902:aa06:b029:cf:85a7:8374 with SMTP id be6-20020a170902aa06b02900cf85a78374mr3827309plb.3.1599709533409; Wed, 09 Sep 2020 20:45:33 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzoXJrAo+PPyIFT+14Xf0/8lU1v8qVCdRbQm2hNGeO5SMS/vGQ0He+vbJ4NV30buQx4tnsUMw== X-Received: by 2002:a17:902:aa06:b029:cf:85a7:8374 with SMTP id be6-20020a170902aa06b02900cf85a78374mr3827286plb.3.1599709532987; Wed, 09 Sep 2020 20:45:32 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id bt13sm518766pjb.23.2020.09.09.20.45.32 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:32 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 2/8][SRU][G] thunderbolt: Implement USB3 bandwidth negotiation routines Date: Thu, 10 Sep 2020 11:45:20 +0800 Message-Id: <20200910034526.12438-3-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Mika Westerberg BugLink: https://bugs.launchpad.net/bugs/1895073 Each host router USB3 downstream adapter has a set of registers that are used to negotiate bandwidth between the connection manager and the internal xHCI controller. These registers allow dynamic bandwidth management for USB3 isochronous traffic based on what is actually consumed vs. allocated at any given time. Implement these USB3 bandwidth negotiation routines to allow the software connection manager take advantage of these. Signed-off-by: Mika Westerberg (cherry picked from commit 3b1d8d577ca8d0619c88ac76a943aa4ce11a3027) Signed-off-by: Koba Ko --- drivers/thunderbolt/tb.h | 9 + drivers/thunderbolt/tb_regs.h | 19 ++ drivers/thunderbolt/usb4.c | 341 ++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 065eb65b2695..855b83b6fcbc 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -840,4 +840,13 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw, const struct tb_port *port); int usb4_port_unlock(struct tb_port *port); + +int usb4_usb3_port_max_link_rate(struct tb_port *port); +int usb4_usb3_port_actual_link_rate(struct tb_port *port); +int usb4_usb3_port_allocated_bandwidth(struct tb_port *port, int *upstream_bw, + int *downstream_bw); +int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw, + int *downstream_bw); +int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw, + int *downstream_bw); #endif diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index c29c5075525a..9297f128efc4 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -337,6 +337,25 @@ struct tb_regs_port_header { #define ADP_USB3_CS_0 0x00 #define ADP_USB3_CS_0_V BIT(30) #define ADP_USB3_CS_0_PE BIT(31) +#define ADP_USB3_CS_1 0x01 +#define ADP_USB3_CS_1_CUBW_MASK GENMASK(11, 0) +#define ADP_USB3_CS_1_CDBW_MASK GENMASK(23, 12) +#define ADP_USB3_CS_1_CDBW_SHIFT 12 +#define ADP_USB3_CS_1_HCA BIT(31) +#define ADP_USB3_CS_2 0x02 +#define ADP_USB3_CS_2_AUBW_MASK GENMASK(11, 0) +#define ADP_USB3_CS_2_ADBW_MASK GENMASK(23, 12) +#define ADP_USB3_CS_2_ADBW_SHIFT 12 +#define ADP_USB3_CS_2_CMR BIT(31) +#define ADP_USB3_CS_3 0x03 +#define ADP_USB3_CS_3_SCALE_MASK GENMASK(5, 0) +#define ADP_USB3_CS_4 0x04 +#define ADP_USB3_CS_4_ALR_MASK GENMASK(6, 0) +#define ADP_USB3_CS_4_ALR_20G 0x1 +#define ADP_USB3_CS_4_ULV BIT(7) +#define ADP_USB3_CS_4_MSLR_MASK GENMASK(18, 12) +#define ADP_USB3_CS_4_MSLR_SHIFT 12 +#define ADP_USB3_CS_4_MSLR_20G 0x1 /* Hop register from TB_CFG_HOPS. 8 byte per entry. */ struct tb_regs_hop { diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 50c7534ba31e..60bdd2852c34 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -769,3 +769,344 @@ int usb4_port_unlock(struct tb_port *port) val &= ~ADP_CS_4_LCK; return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_4, 1); } + +static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, + u32 value, int timeout_msec) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); + + do { + u32 val; + int ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, offset, 1); + if (ret) + return ret; + + if ((val & bit) == value) + return 0; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), timeout)); + + return -ETIMEDOUT; +} + +/** + * usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate + * @port: USB3 adapter port + * + * Return maximum supported link rate of a USB3 adapter in Mb/s. + * Negative errno in case of error. + */ +int usb4_usb3_port_max_link_rate(struct tb_port *port) +{ + int ret, lr; + u32 val; + + if (!tb_port_is_usb3_down(port) && !tb_port_is_usb3_up(port)) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_4, 1); + if (ret) + return ret; + + lr = (val & ADP_USB3_CS_4_MSLR_MASK) >> ADP_USB3_CS_4_MSLR_SHIFT; + return lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000; +} + +/** + * usb4_usb3_port_actual_link_rate() - Established USB3 link rate + * @port: USB3 adapter port + * + * Return actual established link rate of a USB3 adapter in Mb/s. If the + * link is not up returns %0 and negative errno in case of failure. + */ +int usb4_usb3_port_actual_link_rate(struct tb_port *port) +{ + int ret, lr; + u32 val; + + if (!tb_port_is_usb3_down(port) && !tb_port_is_usb3_up(port)) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_4, 1); + if (ret) + return ret; + + if (!(val & ADP_USB3_CS_4_ULV)) + return 0; + + lr = val & ADP_USB3_CS_4_ALR_MASK; + return lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000; +} + +static int usb4_usb3_port_cm_request(struct tb_port *port, bool request) +{ + int ret; + u32 val; + + if (!tb_port_is_usb3_down(port)) + return -EINVAL; + if (tb_route(port->sw)) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_2, 1); + if (ret) + return ret; + + if (request) + val |= ADP_USB3_CS_2_CMR; + else + val &= ~ADP_USB3_CS_2_CMR; + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_2, 1); + if (ret) + return ret; + + /* + * We can use val here directly as the CMR bit is in the same place + * as HCA. Just mask out others. + */ + val &= ADP_USB3_CS_2_CMR; + return usb4_port_wait_for_bit(port, port->cap_adap + ADP_USB3_CS_1, + ADP_USB3_CS_1_HCA, val, 1500); +} + +static inline int usb4_usb3_port_set_cm_request(struct tb_port *port) +{ + return usb4_usb3_port_cm_request(port, true); +} + +static inline int usb4_usb3_port_clear_cm_request(struct tb_port *port) +{ + return usb4_usb3_port_cm_request(port, false); +} + +static unsigned int usb3_bw_to_mbps(u32 bw, u8 scale) +{ + unsigned long uframes; + + uframes = bw * 512 << scale; + return DIV_ROUND_CLOSEST(uframes * 8000, 1000 * 1000); +} + +static u32 mbps_to_usb3_bw(unsigned int mbps, u8 scale) +{ + unsigned long uframes; + + /* 1 uframe is 1/8 ms (125 us) -> 1 / 8000 s */ + uframes = ((unsigned long)mbps * 1000 * 1000) / 8000; + return DIV_ROUND_UP(uframes, 512 << scale); +} + +static int usb4_usb3_port_read_allocated_bandwidth(struct tb_port *port, + int *upstream_bw, + int *downstream_bw) +{ + u32 val, bw, scale; + int ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_2, 1); + if (ret) + return ret; + + ret = tb_port_read(port, &scale, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_3, 1); + if (ret) + return ret; + + scale &= ADP_USB3_CS_3_SCALE_MASK; + + bw = val & ADP_USB3_CS_2_AUBW_MASK; + *upstream_bw = usb3_bw_to_mbps(bw, scale); + + bw = (val & ADP_USB3_CS_2_ADBW_MASK) >> ADP_USB3_CS_2_ADBW_SHIFT; + *downstream_bw = usb3_bw_to_mbps(bw, scale); + + return 0; +} + +/** + * usb4_usb3_port_allocated_bandwidth() - Bandwidth allocated for USB3 + * @port: USB3 adapter port + * @upstream_bw: Allocated upstream bandwidth is stored here + * @downstream_bw: Allocated downstream bandwidth is stored here + * + * Stores currently allocated USB3 bandwidth into @upstream_bw and + * @downstream_bw in Mb/s. Returns %0 in case of success and negative + * errno in failure. + */ +int usb4_usb3_port_allocated_bandwidth(struct tb_port *port, int *upstream_bw, + int *downstream_bw) +{ + int ret; + + ret = usb4_usb3_port_set_cm_request(port); + if (ret) + return ret; + + ret = usb4_usb3_port_read_allocated_bandwidth(port, upstream_bw, + downstream_bw); + usb4_usb3_port_clear_cm_request(port); + + return ret; +} + +static int usb4_usb3_port_read_consumed_bandwidth(struct tb_port *port, + int *upstream_bw, + int *downstream_bw) +{ + u32 val, bw, scale; + int ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_1, 1); + if (ret) + return ret; + + ret = tb_port_read(port, &scale, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_3, 1); + if (ret) + return ret; + + scale &= ADP_USB3_CS_3_SCALE_MASK; + + bw = val & ADP_USB3_CS_1_CUBW_MASK; + *upstream_bw = usb3_bw_to_mbps(bw, scale); + + bw = (val & ADP_USB3_CS_1_CDBW_MASK) >> ADP_USB3_CS_1_CDBW_SHIFT; + *downstream_bw = usb3_bw_to_mbps(bw, scale); + + return 0; +} + +static int usb4_usb3_port_write_allocated_bandwidth(struct tb_port *port, + int upstream_bw, + int downstream_bw) +{ + u32 val, ubw, dbw, scale; + int ret; + + /* Read the used scale, hardware default is 0 */ + ret = tb_port_read(port, &scale, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_3, 1); + if (ret) + return ret; + + scale &= ADP_USB3_CS_3_SCALE_MASK; + ubw = mbps_to_usb3_bw(upstream_bw, scale); + dbw = mbps_to_usb3_bw(downstream_bw, scale); + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_2, 1); + if (ret) + return ret; + + val &= ~(ADP_USB3_CS_2_AUBW_MASK | ADP_USB3_CS_2_ADBW_MASK); + val |= dbw << ADP_USB3_CS_2_ADBW_SHIFT; + val |= ubw; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_USB3_CS_2, 1); +} + +/** + * usb4_usb3_port_allocate_bandwidth() - Allocate bandwidth for USB3 + * @port: USB3 adapter port + * @upstream_bw: New upstream bandwidth + * @downstream_bw: New downstream bandwidth + * + * This can be used to set how much bandwidth is allocated for the USB3 + * tunneled isochronous traffic. @upstream_bw and @downstream_bw are the + * new values programmed to the USB3 adapter allocation registers. If + * the values are lower than what is currently consumed the allocation + * is set to what is currently consumed instead (consumed bandwidth + * cannot be taken away by CM). The actual new values are returned in + * @upstream_bw and @downstream_bw. + * + * Returns %0 in case of success and negative errno if there was a + * failure. + */ +int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw, + int *downstream_bw) +{ + int ret, consumed_up, consumed_down, allocate_up, allocate_down; + + ret = usb4_usb3_port_set_cm_request(port); + if (ret) + return ret; + + ret = usb4_usb3_port_read_consumed_bandwidth(port, &consumed_up, + &consumed_down); + if (ret) + goto err_request; + + /* Don't allow it go lower than what is consumed */ + allocate_up = max(*upstream_bw, consumed_up); + allocate_down = max(*downstream_bw, consumed_down); + + ret = usb4_usb3_port_write_allocated_bandwidth(port, allocate_up, + allocate_down); + if (ret) + goto err_request; + + *upstream_bw = allocate_up; + *downstream_bw = allocate_down; + +err_request: + usb4_usb3_port_clear_cm_request(port); + return ret; +} + +/** + * usb4_usb3_port_release_bandwidth() - Release allocated USB3 bandwidth + * @port: USB3 adapter port + * @upstream_bw: New allocated upstream bandwidth + * @downstream_bw: New allocated downstream bandwidth + * + * Releases USB3 allocated bandwidth down to what is actually consumed. + * The new bandwidth is returned in @upstream_bw and @downstream_bw. + * + * Returns 0% in success and negative errno in case of failure. + */ +int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw, + int *downstream_bw) +{ + int ret, consumed_up, consumed_down; + + ret = usb4_usb3_port_set_cm_request(port); + if (ret) + return ret; + + ret = usb4_usb3_port_read_consumed_bandwidth(port, &consumed_up, + &consumed_down); + if (ret) + goto err_request; + + /* + * Always keep 1000 Mb/s to make sure xHCI has at least some + * bandwidth available for isochronous traffic. + */ + if (consumed_up < 1000) + consumed_up = 1000; + if (consumed_down < 1000) + consumed_down = 1000; + + ret = usb4_usb3_port_write_allocated_bandwidth(port, consumed_up, + consumed_down); + if (ret) + goto err_request; + + *upstream_bw = consumed_up; + *downstream_bw = consumed_down; + +err_request: + usb4_usb3_port_clear_cm_request(port); + return ret; +} From patchwork Thu Sep 10 03:45:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361192 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Yk0qhGz9sTd; Thu, 10 Sep 2020 13:46:17 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDXK-0003dd-Vg; Thu, 10 Sep 2020 03:46:14 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDXF-0003bu-8D for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:09 +0000 Received: from mail-pf1-f199.google.com ([209.85.210.199]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWj-0004M2-BL for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:08 +0000 Received: by mail-pf1-f199.google.com with SMTP id t201so3621173pfc.13 for ; Wed, 09 Sep 2020 20:45:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=vxZLOiyMxJsYequvAC9ZNP7QFdcdmW7+dcTgLylq/uQ=; b=s+7G3neXriKAYkPnmmt/siO6JatjDWfI0lAwc3NojA4rzDpfCiEwsxffu0kN6hNx2Q rmtCyY5TK9Df12utGKNCp0zDoPdz7QSmcfFJGtLzVmCdsZJxl79NATl079GzbF1WoHhC FmGZvnjF3fLMCFGORkjcmpkPc+AmFxihVVylW2ajlQDyMYaymf50Cxpv0CRChk6WnBth vPCLuRVHA9mx7L5iK7ldInC8S1WsQ+WNmDltXotjuoJQ/2kpGH40f4WVsO+IoeLNymDc EdQoBy9VCLPx0NRhcw+UDrd2HPC6yklxYPlIDATlwdT4WOylJwbpKxhpqL8VvxJuBY8b eywA== X-Gm-Message-State: AOAM531eMx+J5y24SbkSQcZGxM5nlpNdU0F7QiUwcpiLWFCR1osR+rQv L9D6lK42TtSGIvyGtdqqFInqHdf1EZzne4FiYpUz8N9EvyBMWsljFFKJebou7liJHffXAYom4S7 PpjsOpNFtI35je5WwnukU09UuMokdwUjj4zClK8tPOA== X-Received: by 2002:a65:6a0f:: with SMTP id m15mr2901792pgu.93.1599709535735; Wed, 09 Sep 2020 20:45:35 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwm9JMPVV1YbtUJi056OViRvOdP+v7pFLFuTq9mQzyJzZZ2FGxHBE42qvLnDNunsT8F6l3T9g== X-Received: by 2002:a65:6a0f:: with SMTP id m15mr2901777pgu.93.1599709535332; Wed, 09 Sep 2020 20:45:35 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id f28sm4082363pfq.191.2020.09.09.20.45.34 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:34 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 3/8][SRU][G] thunderbolt: Split common NVM functionality into a separate file Date: Thu, 10 Sep 2020 11:45:21 +0800 Message-Id: <20200910034526.12438-4-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Mika Westerberg BugLink: https://bugs.launchpad.net/bugs/1895073 We are going to reuse some of this functionality to implement retimer NVM upgrade so move common NVM functionality into its own file. We also rename the structure from tb_switch_nvm to tb_nvm to make it clear that it is not just for switches. Signed-off-by: Mika Westerberg (cherry picked from commit 719a5fe87ecd71d140c3ef76d855c70f82893411) Signed-off-by: Koba Ko --- drivers/thunderbolt/switch.c | 116 +++++++---------------------------- 1 file changed, 23 insertions(+), 93 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 0de54b150527..3fcd372cf097 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -13,21 +13,12 @@ #include #include #include -#include #include "tb.h" /* Switch NVM support */ -#define NVM_DEVID 0x05 -#define NVM_VERSION 0x08 #define NVM_CSS 0x10 -#define NVM_FLASH_SIZE 0x45 - -#define NVM_MIN_SIZE SZ_32K -#define NVM_MAX_SIZE SZ_512K - -static DEFINE_IDA(nvm_ida); struct nvm_auth_status { struct list_head list; @@ -337,7 +328,8 @@ static int nvm_authenticate(struct tb_switch *sw) static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, size_t bytes) { - struct tb_switch *sw = priv; + struct tb_nvm *nvm = priv; + struct tb_switch *sw = tb_to_switch(nvm->dev); int ret; pm_runtime_get_sync(&sw->dev); @@ -360,8 +352,9 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, size_t bytes) { - struct tb_switch *sw = priv; - int ret = 0; + struct tb_nvm *nvm = priv; + struct tb_switch *sw = tb_to_switch(nvm->dev); + int ret; if (!mutex_trylock(&sw->tb->lock)) return restart_syscall(); @@ -372,55 +365,15 @@ static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, * locally here and handle the special cases when the user asks * us to authenticate the image. */ - if (!sw->nvm->buf) { - sw->nvm->buf = vmalloc(NVM_MAX_SIZE); - if (!sw->nvm->buf) { - ret = -ENOMEM; - goto unlock; - } - } - - sw->nvm->buf_data_size = offset + bytes; - memcpy(sw->nvm->buf + offset, val, bytes); - -unlock: + ret = tb_nvm_write_buf(nvm, offset, val, bytes); mutex_unlock(&sw->tb->lock); return ret; } -static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id, - size_t size, bool active) -{ - struct nvmem_config config; - - memset(&config, 0, sizeof(config)); - - if (active) { - config.name = "nvm_active"; - config.reg_read = tb_switch_nvm_read; - config.read_only = true; - } else { - config.name = "nvm_non_active"; - config.reg_write = tb_switch_nvm_write; - config.root_only = true; - } - - config.id = id; - config.stride = 4; - config.word_size = 4; - config.size = size; - config.dev = &sw->dev; - config.owner = THIS_MODULE; - config.priv = sw; - - return nvmem_register(&config); -} - static int tb_switch_nvm_add(struct tb_switch *sw) { - struct nvmem_device *nvm_dev; - struct tb_switch_nvm *nvm; + struct tb_nvm *nvm; u32 val; int ret; @@ -439,11 +392,9 @@ static int tb_switch_nvm_add(struct tb_switch *sw) return 0; } - nvm = kzalloc(sizeof(*nvm), GFP_KERNEL); - if (!nvm) - return -ENOMEM; - - nvm->id = ida_simple_get(&nvm_ida, 0, 0, GFP_KERNEL); + nvm = tb_nvm_alloc(&sw->dev); + if (IS_ERR(nvm)) + return PTR_ERR(nvm); /* * If the switch is in safe-mode the only accessible portion of @@ -455,7 +406,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw) ret = nvm_read(sw, NVM_FLASH_SIZE, &val, sizeof(val)); if (ret) - goto err_ida; + goto err_nvm; hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K; nvm_size = (SZ_1M << (val & 7)) / 8; @@ -463,44 +414,34 @@ static int tb_switch_nvm_add(struct tb_switch *sw) ret = nvm_read(sw, NVM_VERSION, &val, sizeof(val)); if (ret) - goto err_ida; + goto err_nvm; nvm->major = val >> 16; nvm->minor = val >> 8; - nvm_dev = register_nvmem(sw, nvm->id, nvm_size, true); - if (IS_ERR(nvm_dev)) { - ret = PTR_ERR(nvm_dev); - goto err_ida; - } - nvm->active = nvm_dev; + ret = tb_nvm_add_active(nvm, nvm_size, tb_switch_nvm_read); + if (ret) + goto err_nvm; } if (!sw->no_nvm_upgrade) { - nvm_dev = register_nvmem(sw, nvm->id, NVM_MAX_SIZE, false); - if (IS_ERR(nvm_dev)) { - ret = PTR_ERR(nvm_dev); - goto err_nvm_active; - } - nvm->non_active = nvm_dev; + ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, + tb_switch_nvm_write); + if (ret) + goto err_nvm; } sw->nvm = nvm; return 0; -err_nvm_active: - if (nvm->active) - nvmem_unregister(nvm->active); -err_ida: - ida_simple_remove(&nvm_ida, nvm->id); - kfree(nvm); - +err_nvm: + tb_nvm_free(nvm); return ret; } static void tb_switch_nvm_remove(struct tb_switch *sw) { - struct tb_switch_nvm *nvm; + struct tb_nvm *nvm; nvm = sw->nvm; sw->nvm = NULL; @@ -512,13 +453,7 @@ static void tb_switch_nvm_remove(struct tb_switch *sw) if (!nvm->authenticating) nvm_clear_auth_status(sw); - if (nvm->non_active) - nvmem_unregister(nvm->non_active); - if (nvm->active) - nvmem_unregister(nvm->active); - ida_simple_remove(&nvm_ida, nvm->id); - vfree(nvm->buf); - kfree(nvm); + tb_nvm_free(nvm); } /* port utility functions */ @@ -2767,8 +2702,3 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw, return NULL; } - -void tb_switch_exit(void) -{ - ida_destroy(&nvm_ida); -} From patchwork Thu Sep 10 03:45:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361193 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Ym6rtsz9sTX; Thu, 10 Sep 2020 13:46:20 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDXN-0003fB-Ko; Thu, 10 Sep 2020 03:46:17 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDXF-0003bs-6V for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:09 +0000 Received: from mail-pf1-f197.google.com ([209.85.210.197]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWl-0004M8-MF for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:08 +0000 Received: by mail-pf1-f197.google.com with SMTP id k13so3614241pfh.4 for ; Wed, 09 Sep 2020 20:45:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=QOF6OLo7U/qXHPR06J25ndR1YqfV9A6pQSkqlpNMDEc=; b=c9Thk07nEN4/SQ4aVsxcUQHzZpaWPOJYM53Lqt0QoqgC4TZwxaptQMNd2owvlY83VP ZuYyguBCzGYgQCW25gIEGTkNXkjGIgZ7utLpu93gcld5yFec1XO9ZP0FEPK0tyOLODd7 +AthzIZOIrZt5VgMvoq8vi5iVO67RSV0WG27iLdpNi1bG/c8WKmSXfUcK7ugm9veQflh sR69XpbE9AKbGdbu5SDNaLHiY3/nznyDl+HoTWvaFBAUjqO9Xv1dvFGMXJYC9HpVlnbp /pPcGQWMqvzNsqpCuuTtRRbEdgbwORc/ep++Fd58dleQ4b3lnkAgCMlVhMNc0wEJo4m9 4C+w== X-Gm-Message-State: AOAM531OTua/WsJ/Ban7YgIlvKA00tFA3XCTY2tPOWXH9yAFy3suMiRQ 9OoJYs6ncQRzvy4NPSAcDJhEC9JiKU/7j8V0scRnaVow4gXhouslt15uOkkVWNziiI2NEXd2Shu 9s+N1xsL46PxptFgdFXCfSBDcVeKkv/K77iVH8RnWtw== X-Received: by 2002:a62:7a14:0:b029:13e:d13d:a04b with SMTP id v20-20020a627a140000b029013ed13da04bmr3437450pfc.17.1599709537954; Wed, 09 Sep 2020 20:45:37 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxhHnAXMc066DFXQJ2nRS8sG9nRI3Hb5u54xz7m29QqHjHZ1ysdqrJqNiTBYO7BFmjTZT/7Tg== X-Received: by 2002:a62:7a14:0:b029:13e:d13d:a04b with SMTP id v20-20020a627a140000b029013ed13da04bmr3437437pfc.17.1599709537650; Wed, 09 Sep 2020 20:45:37 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id gn24sm509058pjb.8.2020.09.09.20.45.36 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:37 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 4/8][SRU][G] thunderbolt: Generalize usb4_switch_do_[read|write]_data() Date: Thu, 10 Sep 2020 11:45:22 +0800 Message-Id: <20200910034526.12438-5-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Mika Westerberg BugLink: https://bugs.launchpad.net/bugs/1895073 Currently these functions operate on struct tb_switch but we are going to need the same functionality with retimers as well so make the two functions work with an arbitrary object that gets passed as parameter to the callbacks. Signed-off-by: Mika Westerberg (cherry picked from commit 7e72846bb97a86d19a249d230b12a6e33e947026) Signed-off-by: Koba Ko --- drivers/thunderbolt/usb4.c | 39 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 60bdd2852c34..0bbd80840d4a 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -42,8 +42,8 @@ enum usb4_switch_op { #define USB4_NVM_SECTOR_SIZE_MASK GENMASK(23, 0) -typedef int (*read_block_fn)(struct tb_switch *, unsigned int, void *, size_t); -typedef int (*write_block_fn)(struct tb_switch *, const void *, size_t); +typedef int (*read_block_fn)(void *, unsigned int, void *, size_t); +typedef int (*write_block_fn)(void *, const void *, size_t); static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, u32 value, int timeout_msec) @@ -95,8 +95,8 @@ static int usb4_switch_op_write_metadata(struct tb_switch *sw, u32 metadata) return tb_sw_write(sw, &metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); } -static int usb4_switch_do_read_data(struct tb_switch *sw, u16 address, - void *buf, size_t size, read_block_fn read_block) +static int usb4_do_read_data(u16 address, void *buf, size_t size, + read_block_fn read_block, void *read_block_data) { unsigned int retries = USB4_DATA_RETRIES; unsigned int offset; @@ -113,7 +113,7 @@ static int usb4_switch_do_read_data(struct tb_switch *sw, u16 address, dwaddress = address / 4; dwords = ALIGN(nbytes, 4) / 4; - ret = read_block(sw, dwaddress, data, dwords); + ret = read_block(read_block_data, dwaddress, data, dwords); if (ret) { if (ret == -ETIMEDOUT) { if (retries--) @@ -133,8 +133,8 @@ static int usb4_switch_do_read_data(struct tb_switch *sw, u16 address, return 0; } -static int usb4_switch_do_write_data(struct tb_switch *sw, u16 address, - const void *buf, size_t size, write_block_fn write_next_block) +static int usb4_do_write_data(unsigned int address, const void *buf, size_t size, + write_block_fn write_next_block, void *write_block_data) { unsigned int retries = USB4_DATA_RETRIES; unsigned int offset; @@ -149,7 +149,7 @@ static int usb4_switch_do_write_data(struct tb_switch *sw, u16 address, memcpy(data + offset, buf, nbytes); - ret = write_next_block(sw, data, nbytes / 4); + ret = write_next_block(write_block_data, data, nbytes / 4); if (ret) { if (ret == -ETIMEDOUT) { if (retries--) @@ -271,10 +271,11 @@ int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid) return tb_sw_read(sw, uid, TB_CFG_SWITCH, ROUTER_CS_7, 2); } -static int usb4_switch_drom_read_block(struct tb_switch *sw, +static int usb4_switch_drom_read_block(void *data, unsigned int dwaddress, void *buf, size_t dwords) { + struct tb_switch *sw = data; u8 status = 0; u32 metadata; int ret; @@ -311,8 +312,8 @@ static int usb4_switch_drom_read_block(struct tb_switch *sw, int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf, size_t size) { - return usb4_switch_do_read_data(sw, address, buf, size, - usb4_switch_drom_read_block); + return usb4_do_read_data(address, buf, size, + usb4_switch_drom_read_block, sw); } static int usb4_set_port_configured(struct tb_port *port, bool configured) @@ -445,9 +446,10 @@ int usb4_switch_nvm_sector_size(struct tb_switch *sw) return metadata & USB4_NVM_SECTOR_SIZE_MASK; } -static int usb4_switch_nvm_read_block(struct tb_switch *sw, +static int usb4_switch_nvm_read_block(void *data, unsigned int dwaddress, void *buf, size_t dwords) { + struct tb_switch *sw = data; u8 status = 0; u32 metadata; int ret; @@ -484,8 +486,8 @@ static int usb4_switch_nvm_read_block(struct tb_switch *sw, int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf, size_t size) { - return usb4_switch_do_read_data(sw, address, buf, size, - usb4_switch_nvm_read_block); + return usb4_do_read_data(address, buf, size, + usb4_switch_nvm_read_block, sw); } static int usb4_switch_nvm_set_offset(struct tb_switch *sw, @@ -510,9 +512,10 @@ static int usb4_switch_nvm_set_offset(struct tb_switch *sw, return status ? -EIO : 0; } -static int usb4_switch_nvm_write_next_block(struct tb_switch *sw, - const void *buf, size_t dwords) +static int usb4_switch_nvm_write_next_block(void *data, const void *buf, + size_t dwords) { + struct tb_switch *sw = data; u8 status; int ret; @@ -546,8 +549,8 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address, if (ret) return ret; - return usb4_switch_do_write_data(sw, address, buf, size, - usb4_switch_nvm_write_next_block); + return usb4_do_write_data(address, buf, size, + usb4_switch_nvm_write_next_block, sw); } /** From patchwork Thu Sep 10 03:45:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361191 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Yj34Hkz9sTq; Thu, 10 Sep 2020 13:46:17 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDXJ-0003cu-LT; Thu, 10 Sep 2020 03:46:13 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDXF-0003bt-8m for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:09 +0000 Received: from mail-pl1-f197.google.com ([209.85.214.197]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWo-0004MG-Tw for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:08 +0000 Received: by mail-pl1-f197.google.com with SMTP id bg4so285764plb.7 for ; Wed, 09 Sep 2020 20:45:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=LICuLlqWm1ZhshUpvC85lu9AA7XEHt+WU+Z8ZEbfuI4=; b=N3yVeneleqmMWpQdJAwwXI0wgH+IbcN5A310JOqIRlp1VBzPD0+kZbbB63pR95x9mx 7lTYmmaCGLzaKRZA+kidh+iT/12sO4fIa/vrL0UUDZOv2wedL9rDqXALtcDk3Vj/lMfV f0wLFm4Yr3OshPfKUFPcVQrAk+gl5ct26X7W0u33oSZPPlczGokXTptjl5E65mLqEc1W YuTI6J6W42h9nuof/12CauOCqz8n2rXeeGGxIkHvbE4/tYDPCuRJdtuzmGqosyVJpylS N7v+0+23Qdy8oMMWdsMteL6WfGr7Ix80Vw+pa6W6t/o504Z+vRUvjFqlAMBf6LKUyBpK BUbg== X-Gm-Message-State: AOAM5332bOKilZps07V0BJz1OYuJ10woaw42FizKo6zHO7G5U9WnXu/X dJBC82TPN5QLwC0iM7g0DHBjos4YplVBEBrEkir7HqcwNWTYSjzNdY69nwz6g51+40QZ9r+A2LT 3gfBY0siYjI6gQuTr0r4y9qWzhtP48A8PE+cvHCIsKQ== X-Received: by 2002:a17:90b:34e:: with SMTP id fh14mr3537145pjb.186.1599709540893; Wed, 09 Sep 2020 20:45:40 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwqYLnCSbEmHQgHUmHcgeHWKSbvKccZDntc+BmsEPva4N/iO9Tb/qFIFarr0qXBG0sIfiPPfA== X-Received: by 2002:a17:90b:34e:: with SMTP id fh14mr3537124pjb.186.1599709540361; Wed, 09 Sep 2020 20:45:40 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id e207sm3666563pfh.171.2020.09.09.20.45.39 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:39 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 5/8][SRU][G] thunderbolt: Implement USB4 port sideband operations for retimer access Date: Thu, 10 Sep 2020 11:45:23 +0800 Message-Id: <20200910034526.12438-6-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Rajmohan Mani BugLink: https://bugs.launchpad.net/bugs/1895073 USB4 spec specifies standard set of sideband operations that are send over the low speed link to access either retimers on the link or the link parter (the other router). The USB4 retimer spec extends these and adds operations for retimer NVM upgrade. This implements the retimer access and NVM upgrade USB4 port sideband operations which we need for retimer support in the patch that follows. Signed-off-by: Rajmohan Mani Co-developed-by: Mika Westerberg Signed-off-by: Mika Westerberg (cherry picked from commit 02d12855f51651cc9cf8e59e6cbb24a5d9e0a054) Signed-off-by: Koba Ko --- drivers/thunderbolt/Makefile | 3 + drivers/thunderbolt/domain.c | 2 +- drivers/thunderbolt/sb_regs.h | 31 +++ drivers/thunderbolt/tb.h | 42 +++- drivers/thunderbolt/tb_regs.h | 10 + drivers/thunderbolt/usb4.c | 459 ++++++++++++++++++++++++++++++++++ 6 files changed, 542 insertions(+), 5 deletions(-) create mode 100644 drivers/thunderbolt/sb_regs.h diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index eae28dd45250..7ee257cee7ff 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -2,3 +2,6 @@ obj-${CONFIG_USB4} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o +thunderbolt-objs += nvm.o + +obj-${CONFIG_USB4_KUNIT_TEST} += test.o diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 68c1b93ac5d9..bba4cbfa9759 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -812,6 +812,6 @@ void tb_domain_exit(void) { bus_unregister(&tb_bus_type); ida_destroy(&tb_domain_ida); - tb_switch_exit(); + tb_nvm_exit(); tb_xdomain_exit(); } diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h new file mode 100644 index 000000000000..0e587b7b9200 --- /dev/null +++ b/drivers/thunderbolt/sb_regs.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * USB4 port sideband registers found on routers and retimers + * + * Copyright (C) 2020, Intel Corporation + * Authors: Mika Westerberg + * Rajmohan Mani + */ + +#ifndef _SB_REGS +#define _SB_REGS + +#define USB4_SB_OPCODE 0x08 + +enum usb4_sb_opcode { + USB4_SB_OPCODE_ERR = 0x20525245, /* "ERR " */ + USB4_SB_OPCODE_ONS = 0x444d4321, /* "!CMD" */ + USB4_SB_OPCODE_ENUMERATE_RETIMERS = 0x4d554e45, /* "ENUM" */ + USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */ + USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */ + USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */ + USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */ + USB4_SB_OPCODE_NVM_AUTH_WRITE = 0x48545541, /* "AUTH" */ + USB4_SB_OPCODE_NVM_READ = 0x52524641, /* "AFRR" */ +}; + +#define USB4_SB_METADATA 0x09 +#define USB4_SB_METADATA_NVM_AUTH_WRITE_MASK GENMASK(5, 0) +#define USB4_SB_DATA 0x12 + +#endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 855b83b6fcbc..1d2818446c77 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -18,8 +18,17 @@ #include "ctl.h" #include "dma_port.h" +#define NVM_MIN_SIZE SZ_32K +#define NVM_MAX_SIZE SZ_512K + +/* Intel specific NVM offsets */ +#define NVM_DEVID 0x05 +#define NVM_VERSION 0x08 +#define NVM_FLASH_SIZE 0x45 + /** - * struct tb_switch_nvm - Structure holding switch NVM information + * struct tb_nvm - Structure holding NVM information + * @dev: Owner of the NVM * @major: Major version number of the active NVM portion * @minor: Minor version number of the active NVM portion * @id: Identifier used with both NVM portions @@ -35,7 +44,8 @@ * The user of this structure needs to handle serialization of possible * concurrent access. */ -struct tb_switch_nvm { +struct tb_nvm { + struct device *dev; u8 major; u8 minor; int id; @@ -146,7 +156,7 @@ struct tb_switch { int cap_lc; bool is_unplugged; u8 *drom; - struct tb_switch_nvm *nvm; + struct tb_nvm *nvm; bool no_nvm_upgrade; bool safe_mode; bool boot; @@ -543,7 +553,6 @@ extern struct device_type tb_switch_type; int tb_domain_init(void); void tb_domain_exit(void); -void tb_switch_exit(void); int tb_xdomain_init(void); void tb_xdomain_exit(void); @@ -576,6 +585,15 @@ static inline void tb_domain_put(struct tb *tb) put_device(&tb->dev); } +struct tb_nvm *tb_nvm_alloc(struct device *dev); +int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read); +int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val, + size_t bytes); +int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size, + nvmem_reg_write_t reg_write); +void tb_nvm_free(struct tb_nvm *nvm); +void tb_nvm_exit(void); + struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, u64 route); struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb, @@ -840,6 +858,22 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw, const struct tb_port *port); int usb4_port_unlock(struct tb_port *port); +int usb4_port_enumerate_retimers(struct tb_port *port); + +int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf, + u8 size); +int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg, + const void *buf, u8 size); +int usb4_port_retimer_is_last(struct tb_port *port, u8 index); +int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index); +int usb4_port_retimer_nvm_write(struct tb_port *port, u8 index, + unsigned int address, const void *buf, + size_t size); +int usb4_port_retimer_nvm_authenticate(struct tb_port *port, u8 index); +int usb4_port_retimer_nvm_authenticate_status(struct tb_port *port, u8 index, + u32 *status); +int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index, + unsigned int address, void *buf, size_t size); int usb4_usb3_port_max_link_rate(struct tb_port *port); int usb4_usb3_port_actual_link_rate(struct tb_port *port); diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 9297f128efc4..fbce8535b497 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -288,6 +288,16 @@ struct tb_regs_port_header { #define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT 20 /* USB4 port registers */ +#define PORT_CS_1 0x01 +#define PORT_CS_1_LENGTH_SHIFT 8 +#define PORT_CS_1_TARGET_MASK GENMASK(18, 16) +#define PORT_CS_1_TARGET_SHIFT 16 +#define PORT_CS_1_RETIMER_INDEX_SHIFT 20 +#define PORT_CS_1_WNR_WRITE BIT(24) +#define PORT_CS_1_NR BIT(25) +#define PORT_CS_1_RC BIT(26) +#define PORT_CS_1_PND BIT(31) +#define PORT_CS_2 0x02 #define PORT_CS_18 0x12 #define PORT_CS_18_BE BIT(8) #define PORT_CS_19 0x13 diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 0bbd80840d4a..ed8cffb173b3 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -10,6 +10,7 @@ #include #include +#include "sb_regs.h" #include "tb.h" #define USB4_DATA_DWORDS 16 @@ -27,6 +28,12 @@ enum usb4_switch_op { USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, }; +enum usb4_sb_target { + USB4_SB_TARGET_ROUTER, + USB4_SB_TARGET_PARTNER, + USB4_SB_TARGET_RETIMER, +}; + #define USB4_NVM_READ_OFFSET_MASK GENMASK(23, 2) #define USB4_NVM_READ_OFFSET_SHIFT 2 #define USB4_NVM_READ_LENGTH_MASK GENMASK(27, 24) @@ -795,6 +802,458 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, return -ETIMEDOUT; } +static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords) +{ + if (dwords > USB4_DATA_DWORDS) + return -EINVAL; + + return tb_port_read(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2, + dwords); +} + +static int usb4_port_write_data(struct tb_port *port, const void *data, + size_t dwords) +{ + if (dwords > USB4_DATA_DWORDS) + return -EINVAL; + + return tb_port_write(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2, + dwords); +} + +static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, + u8 index, u8 reg, void *buf, u8 size) +{ + size_t dwords = DIV_ROUND_UP(size, 4); + int ret; + u32 val; + + if (!port->cap_usb4) + return -EINVAL; + + val = reg; + val |= size << PORT_CS_1_LENGTH_SHIFT; + val |= (target << PORT_CS_1_TARGET_SHIFT) & PORT_CS_1_TARGET_MASK; + if (target == USB4_SB_TARGET_RETIMER) + val |= (index << PORT_CS_1_RETIMER_INDEX_SHIFT); + val |= PORT_CS_1_PND; + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_1, 1); + if (ret) + return ret; + + ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1, + PORT_CS_1_PND, 0, 500); + if (ret) + return ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_1, 1); + if (ret) + return ret; + + if (val & PORT_CS_1_NR) + return -ENODEV; + if (val & PORT_CS_1_RC) + return -EIO; + + return buf ? usb4_port_read_data(port, buf, dwords) : 0; +} + +static int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target, + u8 index, u8 reg, const void *buf, u8 size) +{ + size_t dwords = DIV_ROUND_UP(size, 4); + int ret; + u32 val; + + if (!port->cap_usb4) + return -EINVAL; + + if (buf) { + ret = usb4_port_write_data(port, buf, dwords); + if (ret) + return ret; + } + + val = reg; + val |= size << PORT_CS_1_LENGTH_SHIFT; + val |= PORT_CS_1_WNR_WRITE; + val |= (target << PORT_CS_1_TARGET_SHIFT) & PORT_CS_1_TARGET_MASK; + if (target == USB4_SB_TARGET_RETIMER) + val |= (index << PORT_CS_1_RETIMER_INDEX_SHIFT); + val |= PORT_CS_1_PND; + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_1, 1); + if (ret) + return ret; + + ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1, + PORT_CS_1_PND, 0, 500); + if (ret) + return ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_1, 1); + if (ret) + return ret; + + if (val & PORT_CS_1_NR) + return -ENODEV; + if (val & PORT_CS_1_RC) + return -EIO; + + return 0; +} + +static int usb4_port_sb_op(struct tb_port *port, enum usb4_sb_target target, + u8 index, enum usb4_sb_opcode opcode, int timeout_msec) +{ + ktime_t timeout; + u32 val; + int ret; + + val = opcode; + ret = usb4_port_sb_write(port, target, index, USB4_SB_OPCODE, &val, + sizeof(val)); + if (ret) + return ret; + + timeout = ktime_add_ms(ktime_get(), timeout_msec); + + do { + /* Check results */ + ret = usb4_port_sb_read(port, target, index, USB4_SB_OPCODE, + &val, sizeof(val)); + if (ret) + return ret; + + switch (val) { + case 0: + return 0; + + case USB4_SB_OPCODE_ERR: + return -EAGAIN; + + case USB4_SB_OPCODE_ONS: + return -EOPNOTSUPP; + + default: + if (val != opcode) + return -EIO; + break; + } + } while (ktime_before(ktime_get(), timeout)); + + return -ETIMEDOUT; +} + +/** + * usb4_port_enumerate_retimers() - Send RT broadcast transaction + * @port: USB4 port + * + * This forces the USB4 port to send broadcast RT transaction which + * makes the retimers on the link to assign index to themselves. Returns + * %0 in case of success and negative errno if there was an error. + */ +int usb4_port_enumerate_retimers(struct tb_port *port) +{ + u32 val; + + val = USB4_SB_OPCODE_ENUMERATE_RETIMERS; + return usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0, + USB4_SB_OPCODE, &val, sizeof(val)); +} + +static inline int usb4_port_retimer_op(struct tb_port *port, u8 index, + enum usb4_sb_opcode opcode, + int timeout_msec) +{ + return usb4_port_sb_op(port, USB4_SB_TARGET_RETIMER, index, opcode, + timeout_msec); +} + +/** + * usb4_port_retimer_read() - Read from retimer sideband registers + * @port: USB4 port + * @index: Retimer index + * @reg: Sideband register to read + * @buf: Data from @reg is stored here + * @size: Number of bytes to read + * + * Function reads retimer sideband registers starting from @reg. The + * retimer is connected to @port at @index. Returns %0 in case of + * success, and read data is copied to @buf. If there is no retimer + * present at given @index returns %-ENODEV. In any other failure + * returns negative errno. + */ +int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf, + u8 size) +{ + return usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index, reg, buf, + size); +} + +/** + * usb4_port_retimer_write() - Write to retimer sideband registers + * @port: USB4 port + * @index: Retimer index + * @reg: Sideband register to write + * @buf: Data that is written starting from @reg + * @size: Number of bytes to write + * + * Writes retimer sideband registers starting from @reg. The retimer is + * connected to @port at @index. Returns %0 in case of success. If there + * is no retimer present at given @index returns %-ENODEV. In any other + * failure returns negative errno. + */ +int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg, + const void *buf, u8 size) +{ + return usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index, reg, buf, + size); +} + +/** + * usb4_port_retimer_is_last() - Is the retimer last on-board retimer + * @port: USB4 port + * @index: Retimer index + * + * If the retimer at @index is last one (connected directly to the + * Type-C port) this function returns %1. If it is not returns %0. If + * the retimer is not present returns %-ENODEV. Otherwise returns + * negative errno. + */ +int usb4_port_retimer_is_last(struct tb_port *port, u8 index) +{ + u32 metadata; + int ret; + + ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_QUERY_LAST_RETIMER, + 500); + if (ret) + return ret; + + ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, &metadata, + sizeof(metadata)); + return ret ? ret : metadata & 1; +} + +/** + * usb4_port_retimer_nvm_sector_size() - Read retimer NVM sector size + * @port: USB4 port + * @index: Retimer index + * + * Reads NVM sector size (in bytes) of a retimer at @index. This + * operation can be used to determine whether the retimer supports NVM + * upgrade for example. Returns sector size in bytes or negative errno + * in case of error. Specifically returns %-ENODEV if there is no + * retimer at @index. + */ +int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index) +{ + u32 metadata; + int ret; + + ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE, + 500); + if (ret) + return ret; + + ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, &metadata, + sizeof(metadata)); + return ret ? ret : metadata & USB4_NVM_SECTOR_SIZE_MASK; +} + +static int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index, + unsigned int address) +{ + u32 metadata, dwaddress; + int ret; + + dwaddress = address / 4; + metadata = (dwaddress << USB4_NVM_SET_OFFSET_SHIFT) & + USB4_NVM_SET_OFFSET_MASK; + + ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata, + sizeof(metadata)); + if (ret) + return ret; + + return usb4_port_retimer_op(port, index, USB4_SB_OPCODE_NVM_SET_OFFSET, + 500); +} + +struct retimer_info { + struct tb_port *port; + u8 index; +}; + +static int usb4_port_retimer_nvm_write_next_block(void *data, const void *buf, + size_t dwords) + +{ + const struct retimer_info *info = data; + struct tb_port *port = info->port; + u8 index = info->index; + int ret; + + ret = usb4_port_retimer_write(port, index, USB4_SB_DATA, + buf, dwords * 4); + if (ret) + return ret; + + return usb4_port_retimer_op(port, index, + USB4_SB_OPCODE_NVM_BLOCK_WRITE, 1000); +} + +/** + * usb4_port_retimer_nvm_write() - Write to retimer NVM + * @port: USB4 port + * @index: Retimer index + * @address: Byte address where to start the write + * @buf: Data to write + * @size: Size in bytes how much to write + * + * Writes @size bytes from @buf to the retimer NVM. Used for NVM + * upgrade. Returns %0 if the data was written successfully and negative + * errno in case of failure. Specifically returns %-ENODEV if there is + * no retimer at @index. + */ +int usb4_port_retimer_nvm_write(struct tb_port *port, u8 index, unsigned int address, + const void *buf, size_t size) +{ + struct retimer_info info = { .port = port, .index = index }; + int ret; + + ret = usb4_port_retimer_nvm_set_offset(port, index, address); + if (ret) + return ret; + + return usb4_do_write_data(address, buf, size, + usb4_port_retimer_nvm_write_next_block, &info); +} + +/** + * usb4_port_retimer_nvm_authenticate() - Start retimer NVM upgrade + * @port: USB4 port + * @index: Retimer index + * + * After the new NVM image has been written via usb4_port_retimer_nvm_write() + * this function can be used to trigger the NVM upgrade process. If + * successful the retimer restarts with the new NVM and may not have the + * index set so one needs to call usb4_port_enumerate_retimers() to + * force index to be assigned. + */ +int usb4_port_retimer_nvm_authenticate(struct tb_port *port, u8 index) +{ + u32 val; + + /* + * We need to use the raw operation here because once the + * authentication completes the retimer index is not set anymore + * so we do not get back the status now. + */ + val = USB4_SB_OPCODE_NVM_AUTH_WRITE; + return usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index, + USB4_SB_OPCODE, &val, sizeof(val)); +} + +/** + * usb4_port_retimer_nvm_authenticate_status() - Read status of NVM upgrade + * @port: USB4 port + * @index: Retimer index + * @status: Raw status code read from metadata + * + * This can be called after usb4_port_retimer_nvm_authenticate() and + * usb4_port_enumerate_retimers() to fetch status of the NVM upgrade. + * + * Returns %0 if the authentication status was successfully read. The + * completion metadata (the result) is then stored into @status. If + * reading the status fails, returns negative errno. + */ +int usb4_port_retimer_nvm_authenticate_status(struct tb_port *port, u8 index, + u32 *status) +{ + u32 metadata, val; + int ret; + + ret = usb4_port_retimer_read(port, index, USB4_SB_OPCODE, &val, + sizeof(val)); + if (ret) + return ret; + + switch (val) { + case 0: + *status = 0; + return 0; + + case USB4_SB_OPCODE_ERR: + ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, + &metadata, sizeof(metadata)); + if (ret) + return ret; + + *status = metadata & USB4_SB_METADATA_NVM_AUTH_WRITE_MASK; + return 0; + + case USB4_SB_OPCODE_ONS: + return -EOPNOTSUPP; + + default: + return -EIO; + } +} + +static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress, + void *buf, size_t dwords) +{ + const struct retimer_info *info = data; + struct tb_port *port = info->port; + u8 index = info->index; + u32 metadata; + int ret; + + metadata = dwaddress << USB4_NVM_READ_OFFSET_SHIFT; + if (dwords < USB4_DATA_DWORDS) + metadata |= dwords << USB4_NVM_READ_LENGTH_SHIFT; + + ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata, + sizeof(metadata)); + if (ret) + return ret; + + ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_NVM_READ, 500); + if (ret) + return ret; + + return usb4_port_retimer_read(port, index, USB4_SB_DATA, buf, + dwords * 4); +} + +/** + * usb4_port_retimer_nvm_read() - Read contents of retimer NVM + * @port: USB4 port + * @index: Retimer index + * @address: NVM address (in bytes) to start reading + * @buf: Data read from NVM is stored here + * @size: Number of bytes to read + * + * Reads retimer NVM and copies the contents to @buf. Returns %0 if the + * read was successful and negative errno in case of failure. + * Specifically returns %-ENODEV if there is no retimer at @index. + */ +int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index, + unsigned int address, void *buf, size_t size) +{ + struct retimer_info info = { .port = port, .index = index }; + + return usb4_do_read_data(address, buf, size, + usb4_port_retimer_nvm_read_block, &info); +} + /** * usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate * @port: USB3 adapter port From patchwork Thu Sep 10 03:45:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361190 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Yg3T4dz9sTX; Thu, 10 Sep 2020 13:46:15 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDXI-0003cO-G1; Thu, 10 Sep 2020 03:46:12 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDXF-0003bw-8a for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:09 +0000 Received: from mail-pf1-f197.google.com ([209.85.210.197]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWr-0004MJ-6X for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:08 +0000 Received: by mail-pf1-f197.google.com with SMTP id s204so3580775pfs.18 for ; Wed, 09 Sep 2020 20:45:45 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=EaEoRBSVgmY2pLJfLi3Uh83IKK9zjtIxYKheu5Tbbh8=; b=XGNF478QCBtH4ydHJmdIg2FVlBIeliie4XCTdelA6LubmDVNIVrP70jx0uUXjMZmAw Jx6i7HBlxnj4zlLjRVuD50S4nO3iEA6Uvq3SERpdZg8l983staNORSWs16LsiYEoIwud vTe+J3yNInQ/4d9ff33Wnb8hB2/A7wM5EoyQ1f3KoPWFgph3p+KL9b4ArhCVbH+qxGM0 if8z6v2l0dY88UoZEA5I6C2bPwcQD70uu+SIB78ZxEzWKOwEpbfMh+K23OfmIPtbCz4M jmWkhBJUSteauIYhRvueDDljZr0GdmiLBINUppBJ5n20fgg6/VpqFrwNE13adyWaflLf 0B4Q== X-Gm-Message-State: AOAM531b3Ec6fmmdp+OZnBbu2wgoB4ckDkbaszD5xWLV6x8yHa0/dVz6 MhA3WdG7pkWBAnQWgrfJ36hhFl5Xc84CTahMYsL1xQob+SeCDrjH1RFIfyzVAgChzsUgFcXeOid z9MbDIp9yRjencCsWbUqPzdWOLdSQeGs/ATl7GxCgMQ== X-Received: by 2002:a62:e10b:: with SMTP id q11mr3528312pfh.117.1599709543140; Wed, 09 Sep 2020 20:45:43 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwxgowXZTTo488CeXGcjyXrnneq/8WfS+W7lGgImdYXyffo6HaCauFYWqKyGQpEKLSYO0OerQ== X-Received: by 2002:a62:e10b:: with SMTP id q11mr3528287pfh.117.1599709542530; Wed, 09 Sep 2020 20:45:42 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id k2sm4040411pfi.169.2020.09.09.20.45.41 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:42 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 6/8][SRU][G] thunderbolt: Add support for on-board retimers Date: Thu, 10 Sep 2020 11:45:24 +0800 Message-Id: <20200910034526.12438-7-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Kranthi Kuntala BugLink: https://bugs.launchpad.net/bugs/1895073 USB4 spec specifies standard access to retimers (both on-board and cable) through USB4 port sideband access. This makes it possible to upgrade their firmware in the same way than we already do with the routers. This enumerates on-board retimers under each USB4 port when the link comes up and adds them to the bus under the router the retimer belongs to. Retimers are exposed in sysfs with name like :. where device is the router the retimer belongs to, port is the USB4 port the retimer is connected to and index is the retimer index under that port (starting from 1). This applies to the upstream USB4 port as well so if there is on-board retimer between the port and the router it is also added accordingly. At this time we do not add cable retimers but there is no techincal restriction to do so in the future if needed. It is not clear whether it makes sense to upgrade their firmwares and at least Thunderbolt 3 cables it has not been done outside of lab environments. The sysfs interface is made to follow the router NVM upgrade to make it easy to extend the existing userspace (fwupd) to handle these as well. Signed-off-by: Kranthi Kuntala Co-developed-by: Mika Westerberg Signed-off-by: Mika Westerberg (cherry picked from commit dacb12877d9222e0281b8391e3361fd4c7a7435a) Signed-off-by: Koba Ko --- .../ABI/testing/sysfs-bus-thunderbolt | 33 ++ Documentation/admin-guide/thunderbolt.rst | 11 +- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/retimer.c | 485 ++++++++++++++++++ drivers/thunderbolt/sb_regs.h | 2 + drivers/thunderbolt/switch.c | 3 + drivers/thunderbolt/tb.c | 10 + drivers/thunderbolt/tb.h | 38 ++ 8 files changed, 578 insertions(+), 6 deletions(-) create mode 100644 drivers/thunderbolt/retimer.c diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 7216d71ec242..7d0500b4d58a 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -243,3 +243,36 @@ KernelVersion: 4.15 Contact: thunderbolt-software@lists.01.org Description: This contains XDomain service specific settings as bitmask. Format: %x + +What: /sys/bus/thunderbolt/devices/:./device +Date: Oct 2020 +KernelVersion: v5.9 +Contact: Mika Westerberg +Description: Retimer device identifier read from the hardware. + +What: /sys/bus/thunderbolt/devices/:./nvm_authenticate +Date: Oct 2020 +KernelVersion: v5.9 +Contact: Mika Westerberg +Description: When new NVM image is written to the non-active NVM + area (through non_activeX NVMem device), the + authentication procedure is started by writing 1 to + this file. If everything goes well, the device is + restarted with the new NVM firmware. If the image + verification fails an error code is returned instead. + + When read holds status of the last authentication + operation if an error occurred during the process. + Format: %x. + +What: /sys/bus/thunderbolt/devices/:./nvm_version +Date: Oct 2020 +KernelVersion: v5.9 +Contact: Mika Westerberg +Description: Holds retimer NVM version number. Format: %x.%x, major.minor. + +What: /sys/bus/thunderbolt/devices/:./vendor +Date: Oct 2020 +KernelVersion: v5.9 +Contact: Mika Westerberg +Description: Retimer vendor identifier read from the hardware. diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 10c4f0ce2ad0..613cb24c76c7 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -173,8 +173,8 @@ following ``udev`` rule:: ACTION=="add", SUBSYSTEM=="thunderbolt", ATTRS{iommu_dma_protection}=="1", ATTR{authorized}=="0", ATTR{authorized}="1" -Upgrading NVM on Thunderbolt device or host -------------------------------------------- +Upgrading NVM on Thunderbolt device, host or retimer +---------------------------------------------------- Since most of the functionality is handled in firmware running on a host controller or a device, it is important that the firmware can be upgraded to the latest where possible bugs in it have been fixed. @@ -185,9 +185,10 @@ for some machines: `Thunderbolt Updates `_ -Before you upgrade firmware on a device or host, please make sure it is a -suitable upgrade. Failing to do that may render the device (or host) in a -state where it cannot be used properly anymore without special tools! +Before you upgrade firmware on a device, host or retimer, please make +sure it is a suitable upgrade. Failing to do that may render the device +in a state where it cannot be used properly anymore without special +tools! Host NVM upgrade on Apple Macs is not supported. diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 7ee257cee7ff..cf7e1b42f4ad 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -2,6 +2,6 @@ obj-${CONFIG_USB4} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o -thunderbolt-objs += nvm.o +thunderbolt-objs += nvm.o retimer.o obj-${CONFIG_USB4_KUNIT_TEST} += test.o diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c new file mode 100644 index 000000000000..620bcf586ee2 --- /dev/null +++ b/drivers/thunderbolt/retimer.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thunderbolt/USB4 retimer support. + * + * Copyright (C) 2020, Intel Corporation + * Authors: Kranthi Kuntala + * Mika Westerberg + */ + +#include +#include +#include + +#include "sb_regs.h" +#include "tb.h" + +#define TB_MAX_RETIMER_INDEX 6 + +static int tb_retimer_nvm_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct tb_nvm *nvm = priv; + struct tb_retimer *rt = tb_to_retimer(nvm->dev); + int ret; + + pm_runtime_get_sync(&rt->dev); + + if (!mutex_trylock(&rt->tb->lock)) { + ret = restart_syscall(); + goto out; + } + + ret = usb4_port_retimer_nvm_read(rt->port, rt->index, offset, val, bytes); + mutex_unlock(&rt->tb->lock); + +out: + pm_runtime_mark_last_busy(&rt->dev); + pm_runtime_put_autosuspend(&rt->dev); + + return ret; +} + +static int tb_retimer_nvm_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct tb_nvm *nvm = priv; + struct tb_retimer *rt = tb_to_retimer(nvm->dev); + int ret = 0; + + if (!mutex_trylock(&rt->tb->lock)) + return restart_syscall(); + + ret = tb_nvm_write_buf(nvm, offset, val, bytes); + mutex_unlock(&rt->tb->lock); + + return ret; +} + +static int tb_retimer_nvm_add(struct tb_retimer *rt) +{ + struct tb_nvm *nvm; + u32 val, nvm_size; + int ret; + + nvm = tb_nvm_alloc(&rt->dev); + if (IS_ERR(nvm)) + return PTR_ERR(nvm); + + ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_VERSION, &val, + sizeof(val)); + if (ret) + goto err_nvm; + + nvm->major = val >> 16; + nvm->minor = val >> 8; + + ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_FLASH_SIZE, + &val, sizeof(val)); + if (ret) + goto err_nvm; + + nvm_size = (SZ_1M << (val & 7)) / 8; + nvm_size = (nvm_size - SZ_16K) / 2; + + ret = tb_nvm_add_active(nvm, nvm_size, tb_retimer_nvm_read); + if (ret) + goto err_nvm; + + ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, tb_retimer_nvm_write); + if (ret) + goto err_nvm; + + rt->nvm = nvm; + return 0; + +err_nvm: + tb_nvm_free(nvm); + return ret; +} + +static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt) +{ + unsigned int image_size, hdr_size; + const u8 *buf = rt->nvm->buf; + u16 ds_size, device; + + image_size = rt->nvm->buf_data_size; + if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE) + return -EINVAL; + + /* + * FARB pointer must point inside the image and must at least + * contain parts of the digital section we will be reading here. + */ + hdr_size = (*(u32 *)buf) & 0xffffff; + if (hdr_size + NVM_DEVID + 2 >= image_size) + return -EINVAL; + + /* Digital section start should be aligned to 4k page */ + if (!IS_ALIGNED(hdr_size, SZ_4K)) + return -EINVAL; + + /* + * Read digital section size and check that it also fits inside + * the image. + */ + ds_size = *(u16 *)(buf + hdr_size); + if (ds_size >= image_size) + return -EINVAL; + + /* + * Make sure the device ID in the image matches the retimer + * hardware. + */ + device = *(u16 *)(buf + hdr_size + NVM_DEVID); + if (device != rt->device) + return -EINVAL; + + /* Skip headers in the image */ + buf += hdr_size; + image_size -= hdr_size; + + return usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf, + image_size); +} + +static ssize_t device_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + + return sprintf(buf, "%#x\n", rt->device); +} +static DEVICE_ATTR_RO(device); + +static ssize_t nvm_authenticate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + int ret; + + if (!mutex_trylock(&rt->tb->lock)) + return restart_syscall(); + + if (!rt->nvm) + ret = -EAGAIN; + else + ret = sprintf(buf, "%#x\n", rt->auth_status); + + mutex_unlock(&rt->tb->lock); + + return ret; +} + +static ssize_t nvm_authenticate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + bool val; + int ret; + + pm_runtime_get_sync(&rt->dev); + + if (!mutex_trylock(&rt->tb->lock)) { + ret = restart_syscall(); + goto exit_rpm; + } + + if (!rt->nvm) { + ret = -EAGAIN; + goto exit_unlock; + } + + ret = kstrtobool(buf, &val); + if (ret) + goto exit_unlock; + + /* Always clear status */ + rt->auth_status = 0; + + if (val) { + if (!rt->nvm->buf) { + ret = -EINVAL; + goto exit_unlock; + } + + ret = tb_retimer_nvm_validate_and_write(rt); + if (ret) + goto exit_unlock; + + ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index); + } + +exit_unlock: + mutex_unlock(&rt->tb->lock); +exit_rpm: + pm_runtime_mark_last_busy(&rt->dev); + pm_runtime_put_autosuspend(&rt->dev); + + if (ret) + return ret; + return count; +} +static DEVICE_ATTR_RW(nvm_authenticate); + +static ssize_t nvm_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + int ret; + + if (!mutex_trylock(&rt->tb->lock)) + return restart_syscall(); + + if (!rt->nvm) + ret = -EAGAIN; + else + ret = sprintf(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor); + + mutex_unlock(&rt->tb->lock); + return ret; +} +static DEVICE_ATTR_RO(nvm_version); + +static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + + return sprintf(buf, "%#x\n", rt->vendor); +} +static DEVICE_ATTR_RO(vendor); + +static struct attribute *retimer_attrs[] = { + &dev_attr_device.attr, + &dev_attr_nvm_authenticate.attr, + &dev_attr_nvm_version.attr, + &dev_attr_vendor.attr, + NULL +}; + +static const struct attribute_group retimer_group = { + .attrs = retimer_attrs, +}; + +static const struct attribute_group *retimer_groups[] = { + &retimer_group, + NULL +}; + +static void tb_retimer_release(struct device *dev) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + + kfree(rt); +} + +struct device_type tb_retimer_type = { + .name = "thunderbolt_retimer", + .groups = retimer_groups, + .release = tb_retimer_release, +}; + +static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) +{ + struct tb_retimer *rt; + u32 vendor, device; + int ret; + + if (!port->cap_usb4) + return -EINVAL; + + ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor, + sizeof(vendor)); + if (ret) { + if (ret != -ENODEV) + tb_port_warn(port, "failed read retimer VendorId: %d\n", ret); + return ret; + } + + ret = usb4_port_retimer_read(port, index, USB4_SB_PRODUCT_ID, &device, + sizeof(device)); + if (ret) { + if (ret != -ENODEV) + tb_port_warn(port, "failed read retimer ProductId: %d\n", ret); + return ret; + } + + if (vendor != PCI_VENDOR_ID_INTEL && vendor != 0x8087) { + tb_port_info(port, "retimer NVM format of vendor %#x is not supported\n", + vendor); + return -EOPNOTSUPP; + } + + /* + * Check that it supports NVM operations. If not then don't add + * the device at all. + */ + ret = usb4_port_retimer_nvm_sector_size(port, index); + if (ret < 0) + return ret; + + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + return -ENOMEM; + + rt->index = index; + rt->vendor = vendor; + rt->device = device; + rt->auth_status = auth_status; + rt->port = port; + rt->tb = port->sw->tb; + + rt->dev.parent = &port->sw->dev; + rt->dev.bus = &tb_bus_type; + rt->dev.type = &tb_retimer_type; + dev_set_name(&rt->dev, "%s:%u.%u", dev_name(&port->sw->dev), + port->port, index); + + ret = device_register(&rt->dev); + if (ret) { + dev_err(&rt->dev, "failed to register retimer: %d\n", ret); + put_device(&rt->dev); + return ret; + } + + ret = tb_retimer_nvm_add(rt); + if (ret) { + dev_err(&rt->dev, "failed to add NVM devices: %d\n", ret); + device_del(&rt->dev); + return ret; + } + + dev_info(&rt->dev, "new retimer found, vendor=%#x device=%#x\n", + rt->vendor, rt->device); + + pm_runtime_no_callbacks(&rt->dev); + pm_runtime_set_active(&rt->dev); + pm_runtime_enable(&rt->dev); + pm_runtime_set_autosuspend_delay(&rt->dev, TB_AUTOSUSPEND_DELAY); + pm_runtime_mark_last_busy(&rt->dev); + pm_runtime_use_autosuspend(&rt->dev); + + return 0; +} + +static void tb_retimer_remove(struct tb_retimer *rt) +{ + dev_info(&rt->dev, "retimer disconnected\n"); + tb_nvm_free(rt->nvm); + device_unregister(&rt->dev); +} + +struct tb_retimer_lookup { + const struct tb_port *port; + u8 index; +}; + +static int retimer_match(struct device *dev, void *data) +{ + const struct tb_retimer_lookup *lookup = data; + struct tb_retimer *rt = tb_to_retimer(dev); + + return rt && rt->port == lookup->port && rt->index == lookup->index; +} + +static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) +{ + struct tb_retimer_lookup lookup = { .port = port, .index = index }; + struct device *dev; + + dev = device_find_child(&port->sw->dev, &lookup, retimer_match); + if (dev) + return tb_to_retimer(dev); + + return NULL; +} + +/** + * tb_retimer_scan() - Scan for on-board retimers under port + * @port: USB4 port to scan + * + * Tries to enumerate on-board retimers connected to @port. Found + * retimers are registered as children of @port. Does not scan for cable + * retimers for now. + */ +int tb_retimer_scan(struct tb_port *port) +{ + u32 status[TB_MAX_RETIMER_INDEX] = {}; + int ret, i, last_idx = 0; + + if (!port->cap_usb4) + return 0; + + /* + * Send broadcast RT to make sure retimer indices facing this + * port are set. + */ + ret = usb4_port_enumerate_retimers(port); + if (ret) + return ret; + + /* + * Before doing anything else, read the authentication status. + * If the retimer has it set, store it for the new retimer + * device instance. + */ + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) + usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); + + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { + /* + * Last retimer is true only for the last on-board + * retimer (the one connected directly to the Type-C + * port). + */ + ret = usb4_port_retimer_is_last(port, i); + if (ret > 0) + last_idx = i; + else if (ret < 0) + break; + } + + if (!last_idx) + return 0; + + /* Add on-board retimers if they do not exist already */ + for (i = 1; i <= last_idx; i++) { + struct tb_retimer *rt; + + rt = tb_port_find_retimer(port, i); + if (rt) { + put_device(&rt->dev); + } else { + ret = tb_retimer_add(port, i, status[i]); + if (ret && ret != -EOPNOTSUPP) + return ret; + } + } + + return 0; +} + +static int remove_retimer(struct device *dev, void *data) +{ + struct tb_retimer *rt = tb_to_retimer(dev); + struct tb_port *port = data; + + if (rt && rt->port == port) + tb_retimer_remove(rt); + return 0; +} + +/** + * tb_retimer_remove_all() - Remove all retimers under port + * @port: USB4 port whose retimers to remove + * + * This removes all previously added retimers under @port. + */ +void tb_retimer_remove_all(struct tb_port *port) +{ + if (port->cap_usb4) + device_for_each_child_reverse(&port->sw->dev, port, + remove_retimer); +} diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 0e587b7b9200..9dafd696612f 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -10,6 +10,8 @@ #ifndef _SB_REGS #define _SB_REGS +#define USB4_SB_VENDOR_ID 0x00 +#define USB4_SB_PRODUCT_ID 0x01 #define USB4_SB_OPCODE 0x08 enum usb4_sb_opcode { diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 3fcd372cf097..bbc01b10eadb 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2387,6 +2387,9 @@ void tb_switch_remove(struct tb_switch *sw) tb_xdomain_remove(port->xdomain); port->xdomain = NULL; } + + /* Remove any downstream retimers */ + tb_retimer_remove_all(port); } if (!sw->is_unplugged) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 107cd232f486..92a6eee88a2d 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -339,6 +339,9 @@ static void tb_scan_port(struct tb_port *port) tb_port_dbg(port, "port already has a remote\n"); return; } + + tb_retimer_scan(port); + sw = tb_switch_alloc(port->sw->tb, &port->sw->dev, tb_downstream_route(port)); if (IS_ERR(sw)) { @@ -395,6 +398,9 @@ static void tb_scan_port(struct tb_port *port) if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); + /* Scan upstream retimers */ + tb_retimer_scan(upstream_port); + /* * Create USB 3.x tunnels only when the switch is plugged to the * domain. This is because we scan the domain also during discovery @@ -473,6 +479,7 @@ static void tb_free_unplugged_children(struct tb_switch *sw) continue; if (port->remote->sw->is_unplugged) { + tb_retimer_remove_all(port); tb_remove_dp_resources(port->remote->sw); tb_switch_lane_bonding_disable(port->remote->sw); tb_switch_remove(port->remote->sw); @@ -827,6 +834,8 @@ static void tb_handle_hotplug(struct work_struct *work) goto put_sw; } if (ev->unplug) { + tb_retimer_remove_all(port); + if (tb_port_has_remote(port)) { tb_port_dbg(port, "switch unplugged\n"); tb_sw_set_unplugged(port->remote->sw); @@ -1071,6 +1080,7 @@ static int tb_free_unplugged_xdomains(struct tb_switch *sw) if (tb_is_upstream_port(port)) continue; if (port->xdomain && port->xdomain->is_unplugged) { + tb_retimer_remove_all(port); tb_xdomain_remove(port->xdomain); port->xdomain = NULL; ret++; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 1d2818446c77..f27cbcd95eb2 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -210,6 +210,28 @@ struct tb_port { struct list_head list; }; +/** + * tb_retimer: Thunderbolt retimer + * @dev: Device for the retimer + * @tb: Pointer to the domain the retimer belongs to + * @index: Retimer index facing the router USB4 port + * @vendor: Vendor ID of the retimer + * @device: Device ID of the retimer + * @port: Pointer to the lane 0 adapter + * @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise) + * @auth_status: Status of last NVM authentication + */ +struct tb_retimer { + struct device dev; + struct tb *tb; + u8 index; + u32 vendor; + u32 device; + struct tb_port *port; + struct tb_nvm *nvm; + u32 auth_status; +}; + /** * struct tb_path_hop - routing information for a tb_path * @in_port: Ingress port of a switch @@ -549,6 +571,7 @@ struct tb *icm_probe(struct tb_nhi *nhi); struct tb *tb_probe(struct tb_nhi *nhi); extern struct device_type tb_domain_type; +extern struct device_type tb_retimer_type; extern struct device_type tb_switch_type; int tb_domain_init(void); @@ -835,6 +858,21 @@ void tb_xdomain_remove(struct tb_xdomain *xd); struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, u8 depth); +int tb_retimer_scan(struct tb_port *port); +void tb_retimer_remove_all(struct tb_port *port); + +static inline bool tb_is_retimer(const struct device *dev) +{ + return dev->type == &tb_retimer_type; +} + +static inline struct tb_retimer *tb_to_retimer(struct device *dev) +{ + if (tb_is_retimer(dev)) + return container_of(dev, struct tb_retimer, dev); + return NULL; +} + int usb4_switch_setup(struct tb_switch *sw); int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid); int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf, From patchwork Thu Sep 10 03:45:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361194 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4Yp4PS5z9sTp; Thu, 10 Sep 2020 13:46:22 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDXP-0003ge-Ih; Thu, 10 Sep 2020 03:46:19 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDXF-0003bv-7u for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:09 +0000 Received: from mail-pl1-f198.google.com ([209.85.214.198]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWt-0004MP-FN for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:08 +0000 Received: by mail-pl1-f198.google.com with SMTP id s9so286833plq.15 for ; Wed, 09 Sep 2020 20:45:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=dKS1rpNGM5A4gAWqiIRGos5jXMUSl9SaMU/KXhkc/uk=; b=NSv0YeNikxpWpqvbKgpJXfUxDNxTdN0J9qZn6nB01oje7JGtBiI2tXl+oq/pjp68Rq +036y6TtyUFdRYwlEzBBbscmuT3/GwiTeY/8UZ9q/oBfeCrrqsYre3pxcdarOfVhbM8k AP5GF7d1oyOKSQ16DVQY4k9xJkj8GJvcBzwO8PDkVRBTbWKq5Y8zfvyjq+LwVlGX7d1j OjNDli/r8L5eu5mJ9Vcet/Ago8y34ePAvQAJ6pyL6t5qCByS2b/ttMfos/Fc9Lu6mGVr jxpFwdrQp5sBRQdxMCB5RZKem/G9R24xhpjUdx1/gXzSJamm10aS1bp/kDOvRh5a+edV lblw== X-Gm-Message-State: AOAM531WhfMWViKZk2PN6i+zmg5Cz4OGX9991XF9ZQH3mjFr2SOruALw 1U57ct6BCXfM6m+M906r1xc6xRLk4lOKsVEW9JTe5tAiG+Iw/eAWvSRZPqs+8Uah/fRFs342uPX OkfHQGyumejYVlnw6grL72nqCoDKdV6T2PJ6QnamqOA== X-Received: by 2002:a17:902:74c7:: with SMTP id f7mr3827215plt.144.1599709545774; Wed, 09 Sep 2020 20:45:45 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzVh46hsf3O6oHz5h0KCC5D4EqkXUhFH6sJ0AZrGUNh7lhUgyyABSplWms2xFB1XmoBgziwPA== X-Received: by 2002:a17:902:74c7:: with SMTP id f7mr3827195plt.144.1599709545156; Wed, 09 Sep 2020 20:45:45 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id t14sm3311097pgm.42.2020.09.09.20.45.44 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:44 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 7/8][SRU][G] thunderbolt: Add support for authenticate on disconnect Date: Thu, 10 Sep 2020 11:45:25 +0800 Message-Id: <20200910034526.12438-8-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Mario Limonciello BugLink: https://bugs.launchpad.net/bugs/1895073 Some external devices can support completing thunderbolt authentication when they are unplugged. For this to work though, the link controller must remain operational. The only device known to support this right now is the Dell WD19TB, so add a quirk for this. Signed-off-by: Mario Limonciello Signed-off-by: Mika Westerberg (cherry picked from commit 1cb36293833766e048cba2026dd860687a2851d9) Signed-off-by: Koba Ko --- .../ABI/testing/sysfs-bus-thunderbolt | 13 ++++++ drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/eeprom.c | 1 + drivers/thunderbolt/lc.c | 14 +++++++ drivers/thunderbolt/quirks.c | 42 +++++++++++++++++++ drivers/thunderbolt/switch.c | 40 ++++++++++++++++-- drivers/thunderbolt/tb.h | 9 ++++ drivers/thunderbolt/tb_regs.h | 1 + 8 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 drivers/thunderbolt/quirks.c diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 7d0500b4d58a..dd565c378b40 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -276,3 +276,16 @@ Date: Oct 2020 KernelVersion: v5.9 Contact: Mika Westerberg Description: Retimer vendor identifier read from the hardware. + +What: /sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect +Date: Oct 2020 +KernelVersion: v5.9 +Contact: Mario Limonciello +Description: For supported devices, automatically authenticate the new Thunderbolt + image when the device is disconnected from the host system. + + This file will accept writing values "1" or "2" + - Writing "1" will flush the image to the storage + area and prepare the device for authentication on disconnect. + - Writing "2" will run some basic validation on the image + and flush it to the storage area. diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index cf7e1b42f4ad..4ab5bfad7bfd 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -2,6 +2,6 @@ obj-${CONFIG_USB4} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o -thunderbolt-objs += nvm.o retimer.o +thunderbolt-objs += nvm.o retimer.o quirks.o obj-${CONFIG_USB4_KUNIT_TEST} += test.o diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index b451a5aa90b5..3ebca44ab3fa 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -599,6 +599,7 @@ int tb_drom_read(struct tb_switch *sw) sw->uid = header->uid; sw->vendor = header->vendor_id; sw->device = header->model_id; + tb_check_quirks(sw); crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); if (crc != header->data_crc32) { diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index bd44d50246d2..19be627d090f 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -366,3 +366,17 @@ int tb_lc_dp_sink_dealloc(struct tb_switch *sw, struct tb_port *in) tb_port_dbg(in, "sink %d de-allocated\n", sink); return 0; } + +/** + * tb_lc_force_power() - Forces LC to be powered on + * @sw: Thunderbolt switch + * + * This is useful to let authentication cycle pass even without + * a Thunderbolt link present. + */ +int tb_lc_force_power(struct tb_switch *sw) +{ + u32 in = 0xffff; + + return tb_sw_write(sw, &in, TB_CFG_SWITCH, TB_LC_POWER, 1); +} diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c new file mode 100644 index 000000000000..0525f59220ae --- /dev/null +++ b/drivers/thunderbolt/quirks.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thunderbolt driver - quirks + * + * Copyright (c) 2020 Mario Limonciello + */ + +#include "tb.h" + +static void quirk_force_power_link(struct tb_switch *sw) +{ + sw->quirks |= QUIRK_FORCE_POWER_LINK_CONTROLLER; +} + +struct tb_quirk { + u16 vendor; + u16 device; + void (*hook)(struct tb_switch *sw); +}; + +const static struct tb_quirk tb_quirks[] = { + /* Dell WD19TB supports self-authentication on unplug */ + { 0x00d4, 0xb070, quirk_force_power_link }, +}; + +/** + * tb_check_quirks() - Check for quirks to apply + * @sw: Thunderbolt switch + * + * Apply any quirks for the Thunderbolt controller + */ +void tb_check_quirks(struct tb_switch *sw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tb_quirks); i++) { + const struct tb_quirk *q = &tb_quirks[i]; + + if (sw->device == q->device && sw->vendor == q->vendor) + q->hook(sw); + } +} diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index bbc01b10eadb..1be334fe8135 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1476,8 +1476,8 @@ static ssize_t nvm_authenticate_show(struct device *dev, return sprintf(buf, "%#x\n", status); } -static ssize_t nvm_authenticate_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf, + bool disconnect) { struct tb_switch *sw = tb_to_switch(dev); int val; @@ -1515,8 +1515,12 @@ static ssize_t nvm_authenticate_store(struct device *dev, goto exit_unlock; } if (val == WRITE_AND_AUTHENTICATE) { - sw->nvm->authenticating = true; - ret = nvm_authenticate(sw); + if (disconnect) { + ret = tb_lc_force_power(sw); + } else { + sw->nvm->authenticating = true; + ret = nvm_authenticate(sw); + } } } @@ -1526,12 +1530,35 @@ static ssize_t nvm_authenticate_store(struct device *dev, pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); + return ret; +} + +static ssize_t nvm_authenticate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = nvm_authenticate_sysfs(dev, buf, false); if (ret) return ret; return count; } static DEVICE_ATTR_RW(nvm_authenticate); +static ssize_t nvm_authenticate_on_disconnect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return nvm_authenticate_show(dev, attr, buf); +} + +static ssize_t nvm_authenticate_on_disconnect_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + + ret = nvm_authenticate_sysfs(dev, buf, true); + return ret ? ret : count; +} +static DEVICE_ATTR_RW(nvm_authenticate_on_disconnect); + static ssize_t nvm_version_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1589,6 +1616,7 @@ static struct attribute *switch_attrs[] = { &dev_attr_generation.attr, &dev_attr_key.attr, &dev_attr_nvm_authenticate.attr, + &dev_attr_nvm_authenticate_on_disconnect.attr, &dev_attr_nvm_version.attr, &dev_attr_rx_speed.attr, &dev_attr_rx_lanes.attr, @@ -1643,6 +1671,10 @@ static umode_t switch_attr_is_visible(struct kobject *kobj, if (tb_route(sw)) return attr->mode; return 0; + } else if (attr == &dev_attr_nvm_authenticate_on_disconnect.attr) { + if (sw->quirks & QUIRK_FORCE_POWER_LINK_CONTROLLER) + return attr->mode; + return 0; } return sw->safe_mode ? 0 : attr->mode; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index f27cbcd95eb2..07fe08b584db 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -132,6 +132,7 @@ struct tb_switch_tmu { * @depth: Depth in the chain this switch is connected (ICM only) * @rpm_complete: Completion used to wait for runtime resume to * complete (ICM only) + * @quirks: Quirks used for this Thunderbolt switch * * When the switch is being added or removed to the domain (other * switches) you need to have domain lock held. @@ -169,6 +170,7 @@ struct tb_switch { u8 link; u8 depth; struct completion rpm_complete; + unsigned long quirks; }; /** @@ -829,6 +831,7 @@ bool tb_lc_lane_bonding_possible(struct tb_switch *sw); bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in); int tb_lc_dp_sink_alloc(struct tb_switch *sw, struct tb_port *in); int tb_lc_dp_sink_dealloc(struct tb_switch *sw, struct tb_port *in); +int tb_lc_force_power(struct tb_switch *sw); static inline int tb_route_length(u64 route) { @@ -921,4 +924,10 @@ int usb4_usb3_port_allocate_bandwidth(struct tb_port *port, int *upstream_bw, int *downstream_bw); int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw, int *downstream_bw); + +/* keep link controller awake during update */ +#define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0) + +void tb_check_quirks(struct tb_switch *sw); + #endif diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index fbce8535b497..d00dc83983ab 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -408,6 +408,7 @@ struct tb_regs_hop { #define TB_LC_SNK_ALLOCATION_SNK1_SHIFT 4 #define TB_LC_SNK_ALLOCATION_SNK1_MASK GENMASK(7, 4) #define TB_LC_SNK_ALLOCATION_SNK1_CM 0x1 +#define TB_LC_POWER 0x740 /* Link controller registers */ #define TB_LC_PORT_ATTR 0x8d From patchwork Thu Sep 10 03:45:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361199 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Bn4bt055Wz9sTd; Thu, 10 Sep 2020 13:48:10 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kGDZ9-0003yw-5C; Thu, 10 Sep 2020 03:48:07 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDZ7-0003yq-Rd for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:48:05 +0000 Received: from mail-pl1-f198.google.com ([209.85.214.198]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDWv-0004MW-Nz for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:46:08 +0000 Received: by mail-pl1-f198.google.com with SMTP id o6so287070pll.9 for ; Wed, 09 Sep 2020 20:45:49 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=umPvUG2cOKuZUmeK6r9mCBdqVp3osk+/wmNBgr5HuyE=; b=fPBePzjT7/yGTnwjqsXyD60Ua1dyPm0eNa73zguJpLb+o2VyRqTOTDyXTQAcP7XPd9 LXuWkCkhsqCALk2xQhOHjN6C0wsJu+++fGtEWUxiEiN+jaSY0muhcXNUM0/0uxAU0SlI jIzM3OJl6A9mmOxvGRPL/XDapBAjAhhcyO1D/U7ZJ604l8ELjZ7H3DBIsqgM0Pqgod5l JGCDRlCWjf2kv8hjJm/ci998yZnRlT547dtDt8fFU91vU3Fm9ZI+cCG6G+Bg9/pQFIsl ZNDa65caisMOklzE8S9xhgOoPDSb2xxuQ40M+eENIonn01J/RpymGN8TEeQVzr0Knvd+ azag== X-Gm-Message-State: AOAM5326KTBmKdNKsq6Key+0oqBp3oq3vXrhaTKL2QoKIRLvRBmnLw0g nNHszajd81vcyH+0DOf16NHDow8/dmygq0ulCd94FAxR8+rUGBHyTBbDzvRl9pw2p+h9iVDHOv3 84/4RCQ8gCXiKOutA5b9gv96FOZoU+SzqpVh0f7azhA== X-Received: by 2002:a17:90a:ea02:: with SMTP id w2mr3487492pjy.9.1599709548302; Wed, 09 Sep 2020 20:45:48 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwQ4Xftd5pSAP7dnMoHiIU4Cqrmox4UUwvAq/Dpej9WiufnXCEeb6hO2kXEAgbOqyUxhLthtw== X-Received: by 2002:a17:90a:ea02:: with SMTP id w2mr3487480pjy.9.1599709548050; Wed, 09 Sep 2020 20:45:48 -0700 (PDT) Received: from canonical.com (61-220-137-37.HINET-IP.hinet.net. [61.220.137.37]) by smtp.gmail.com with ESMTPSA id y195sm4079930pfc.137.2020.09.09.20.45.47 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:45:47 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 8/8][SRU][G] thunderbolt: Ensure left shift of 512 does not overflow a 32 bit int Date: Thu, 10 Sep 2020 11:45:26 +0800 Message-Id: <20200910034526.12438-9-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910034526.12438-1-koba.ko@canonical.com> References: <20200910034526.12438-1-koba.ko@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Colin Ian King BugLink: https://bugs.launchpad.net/bugs/1895073 The 32 bit int value 512 is being left shifted and then used in a context that expects the expression to be a larger unsigned long. There may be a potential integer overflow, so make 512 a UL before shift to avoid any such issues. Addresses-Coverity: ("Uninintentional integer overflow") Fixes: 3b1d8d577ca8 ("thunderbolt: Implement USB3 bandwidth negotiation routines") Signed-off-by: Colin Ian King Signed-off-by: Mika Westerberg (cherry picked from commit 4c767ce48cf858971545164c4c53d028e6241c07) Signed-off-by: Koba Ko --- drivers/thunderbolt/usb4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index ed8cffb173b3..06d32fef58cd 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1353,7 +1353,7 @@ static unsigned int usb3_bw_to_mbps(u32 bw, u8 scale) { unsigned long uframes; - uframes = bw * 512 << scale; + uframes = bw * 512UL << scale; return DIV_ROUND_CLOSEST(uframes * 8000, 1000 * 1000); } @@ -1363,7 +1363,7 @@ static u32 mbps_to_usb3_bw(unsigned int mbps, u8 scale) /* 1 uframe is 1/8 ms (125 us) -> 1 / 8000 s */ uframes = ((unsigned long)mbps * 1000 * 1000) / 8000; - return DIV_ROUND_UP(uframes, 512 << scale); + return DIV_ROUND_UP(uframes, 512UL << scale); } static int usb4_usb3_port_read_allocated_bandwidth(struct tb_port *port,