From patchwork Thu Sep 10 03:24:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361177 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 4Bn45V5Grpz9sVB; Thu, 10 Sep 2020 13:25: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 1kGDD0-0001fA-QZ; Thu, 10 Sep 2020 03:25: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 1kGDCz-0001ez-1x for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:13 +0000 Received: from mail-pg1-f197.google.com ([209.85.215.197]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDCy-0002t7-Ko for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:12 +0000 Received: by mail-pg1-f197.google.com with SMTP id q21so3123017pgj.4 for ; Wed, 09 Sep 2020 20:25:12 -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=TCVu13xETHq+VgOPFqGPMwKd6hgOm5YWa8Ub9t7XpsU=; b=nXtpw6UqiIFqnSvesGl3SKKP4Io4plz6/3o9YV6Q6fMYqlGlZNrLvf7TC6c5HRdr// jvy3Ru8WjrwzrODFVuubZuU4Stp19Es3+SS2/k/uggmW4J/mBOKqs4L6mLcVXbTMwYUs UWGlVaW8F1n0ZRvpSGXaYHxUtEefGrx3lXdBZZ3vTMMksDoWUk4u/72S8IU/Y4cWOHMz f1aD0P03R3xNZmMB+9Ba694nxpbuSXpDTkMyaZPUb404zRwvafvR5Rtyj9TkxLaJh8u/ LDx/kxY52xLZZiVyDj49ynfqiv3Gno2j7EFHHBhg9KZGyQLSEhWi7qMC2otrCElUbmMw v+FA== X-Gm-Message-State: AOAM533gQLFxa4P2gQ+zObJ12ibFY1KmXEE1WejlZUgk+2Fj6VSH1ekz WnEoAGA9JrOxIHC8lodquFdgRieSRy9lSBtqXkHTnIis02JDSbWZpV4X2cmYv4dBm37f12CVAqV 1GHC627nzkOBqaqzawgWfnwS+v00BFuqIV13e1GYx8Q== X-Received: by 2002:aa7:8aca:0:b029:13e:d13d:a13e with SMTP id b10-20020aa78aca0000b029013ed13da13emr3515444pfd.38.1599708310846; Wed, 09 Sep 2020 20:25:10 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzNq7ZCZ75QP9PbCfm1O6TFi+uhacbk1a/OyIgZO7M/4AjC5WnhvbXpPrjjtsZ8KU7S5ZUtUw== X-Received: by 2002:aa7:8aca:0:b029:13e:d13d:a13e with SMTP id b10-20020aa78aca0000b029013ed13da13emr3515428pfd.38.1599708310404; Wed, 09 Sep 2020 20:25:10 -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 w185sm4340084pfc.36.2020.09.09.20.25.09 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:10 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 1/8][SRU][OEM-5.6] thunderbolt: Add support for separating the flush to SPI and authenticate Date: Thu, 10 Sep 2020 11:24:58 +0800 Message-Id: <20200910032505.10882-2-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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 a2ce99051c51..f6c45cbd2c4e 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) @@ -1543,7 +1552,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); @@ -1559,25 +1568,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:24:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361178 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 4Bn45Y1sbsz9sVC; Thu, 10 Sep 2020 13:25: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 1kGDD3-0001g6-W5; Thu, 10 Sep 2020 03:25: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 1kGDD1-0001fk-VV for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:15 +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 1kGDD1-0002u7-Hi for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:15 +0000 Received: by mail-pf1-f197.google.com with SMTP id q5so3581087pfl.16 for ; Wed, 09 Sep 2020 20:25:15 -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=sc5UN7bE41iEiZzCyBEp6BlykkViWpo7OI2DUcIsjSo=; b=iO8AJZZMhA4Kkf61X5D4i2ResZVSMD/135Gc5E2v4kcuGxsHalO5g7JE83tNkDgeIt Ss52oaVn8COMh/VXxLEQ79Afdt6UtESzGqTtqu8CCYuwKE2YdEK+r7M5BHB9GNGH5F2T uxXWm0fQF+dRrcxwBPKnATS9WfZPEcfXL2z5jAnD3k4c5IpxtFsfyPAywhW1+8+aveeg aHM8uvvN+O+Fejzf94NGJN8SDm/j+tPI2cnk5MTXaKh9SI7+o6nvrDE+NXuTfWGfQ0UZ Aftueh7mGaiq6XMMjFsOtbgRHgJCDrFBvEc72XJuHH3vjY/uwX45Ff5WNnQlruzhNR+e QEWw== X-Gm-Message-State: AOAM53306WUiipINt19XBWs8/XNX4dgLIZJMvRrI/ueipJuYXYUftYQn 9BkIaKIrZgUeSOJdFR5RArIGlfGJPSr8Sr045R+aDbu7PkwjWx+rjG5pfCm52mkzG3Fg2aIXWac o7uzLoimYAPysWYW8tuZEBDvvjfDkRrStTeFuZ61tFg== X-Received: by 2002:a65:5a4c:: with SMTP id z12mr2840621pgs.10.1599708313793; Wed, 09 Sep 2020 20:25:13 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyV7CLbrHaMXWV2lZREAwTozP/Er3zBLgzwf61ol0pZVP40b0lu8i9wBgw3TYwOnKv6QFPl4w== X-Received: by 2002:a65:5a4c:: with SMTP id z12mr2840609pgs.10.1599708313362; Wed, 09 Sep 2020 20:25:13 -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 kt18sm461295pjb.56.2020.09.09.20.25.12 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:12 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 2/8][SRU][OEM-5.6] thunderbolt: Implement USB3 bandwidth negotiation routines Date: Thu, 10 Sep 2020 11:24:59 +0800 Message-Id: <20200910032505.10882-3-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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 114fbe51527c..2cf331e6443b 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -765,3 +765,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:25:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361179 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 4Bn45b6gvGz9sTX; Thu, 10 Sep 2020 13:25:23 +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 1kGDD7-0001hQ-5Z; Thu, 10 Sep 2020 03:25:21 +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 1kGDD4-0001gX-MR for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:18 +0000 Received: from mail-pg1-f198.google.com ([209.85.215.198]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDD4-0002uC-0T for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:18 +0000 Received: by mail-pg1-f198.google.com with SMTP id c3so3133288pgq.9 for ; Wed, 09 Sep 2020 20:25:17 -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=51mgiz06j8uxkqek8lGG7B9uc21MpKrmqWn0g9wiY58=; b=C0YdP/v+LbQBs4lmVIVaUpKfD30ACAVDiLu/hE8a30df+LQYDgd/uOKvbqgWVE8oAm FU/YbFs3tXe4ukpYOkikS9MCFfBYEWBuYDImKh2w6Y48Lpo8hASBy+fxRff7e6z4boLF R3y02IR0wdaowtXro6y5WJPYGozJhMt8bBdBQRefmjCUKD2Ijk9zEms5E3I1rXVkV1m8 PHpn+pWBX7hy6dpuBvvS9OXIKuPkokxSiLxffLNzfpWhwFDagzgcmzs0xB8EIOa8frFU r6jKIeNRLQB1qRrWiQQdtIn0gmwlTST+t7Show8FDUN99+ela5tDnaQzFtZ8rT3MQECr xZcw== X-Gm-Message-State: AOAM530W7S7kSKahEh1GjbKqzooBObfWdXz8Ui/5lsgz7aOor8lPzeU8 IHCV8DM3RX/ieoOYYmbTckTS9LLizgqk1eA9hF6xQ9Zbo4Tz0S+o01OuSRLfDxBZweJc09AZFFE 2j/1jEpDPXxrPwTuHxVFi8Jpo6A02dlINBPouKr6nXA== X-Received: by 2002:a05:6a00:2bc:: with SMTP id q28mr3506600pfs.61.1599708316276; Wed, 09 Sep 2020 20:25:16 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwi8AwYSTTchFxyBsgTExfbZDs2fmqATeULqpf8tHYw1b/+KFj4OoeAVKnJsKld3i7DKFMY1Q== X-Received: by 2002:a05:6a00:2bc:: with SMTP id q28mr3506579pfs.61.1599708315876; Wed, 09 Sep 2020 20:25:15 -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 h5sm3495995pgn.75.2020.09.09.20.25.15 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:15 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 3/8][SRU][OEM-5.6] thunderbolt: Split common NVM functionality into a separate file Date: Thu, 10 Sep 2020 11:25:00 +0800 Message-Id: <20200910032505.10882-4-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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/Makefile | 3 + drivers/thunderbolt/domain.c | 2 +- drivers/thunderbolt/switch.c | 117 +++++++---------------------------- drivers/thunderbolt/tb.h | 26 ++++++-- 4 files changed, 49 insertions(+), 99 deletions(-) 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 b7980c856898..8f714746c77d 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/switch.c b/drivers/thunderbolt/switch.c index f6c45cbd2c4e..dc53ba4081c8 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); @@ -366,8 +358,9 @@ static int tb_switch_nvm_no_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(); @@ -378,56 +371,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_read = tb_switch_nvm_no_read; - 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; @@ -446,11 +398,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 @@ -462,7 +412,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; @@ -470,44 +420,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; @@ -519,13 +459,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 */ @@ -2774,8 +2708,3 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw, return NULL; } - -void tb_switch_exit(void) -{ - ida_destroy(&nvm_ida); -} diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 855b83b6fcbc..589517688674 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, From patchwork Thu Sep 10 03:25:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361181 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 4Bn45d0nZ5z9sVC; Thu, 10 Sep 2020 13:25:25 +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 1kGDD8-0001iE-BL; Thu, 10 Sep 2020 03:25:22 +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 1kGDD6-0001h7-FP for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:20 +0000 Received: from mail-pj1-f70.google.com ([209.85.216.70]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDD6-0002uJ-1i for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:20 +0000 Received: by mail-pj1-f70.google.com with SMTP id a8so2962688pjk.5 for ; Wed, 09 Sep 2020 20:25:19 -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=2v/gXjIYM4qZ6yu0kOipkuauwqBpgwzqrYvkhdyfL+4=; b=V5Eqv/pkXvbTTYh2CA5q1MwanVHns5UfE1wcVciquxSu114E7c1f/qdXQSGiQ0m5b2 ww9xlr4RZqcl5pubRBzIz9Maf9DcQslGpa/r7auWEoWlsiaKazAVS8sPP2gJ9LsQltxU wk6H7dfy2+8rbt8DamjPMv0NgkQ2WYkyfIO0t+w7uQLwj4ujFSybseWMpLj8AbuSNMOf lLYuV8EIOB8ui2wb0+6a3ZSZ9BuPUWf3fMwvFUcEP4PB6bCMYoswfTjX6axU6V83doYh KzIMTMbMvrZMGdwDGsXiaO0qZi59vSg3xIiSFyQ5HtoQqvCn07hWZR9/zoxqE9k+x99v iboQ== X-Gm-Message-State: AOAM5333iBMnoVP9WhReszC5s3vJo7vgo6In98GZ1Tj4/u5OxmS6LyCs pyZLK3I2cMVSgZq4r0EyGKDNI24XyAvwmkV9f/9R8hGkeB6sbj4B3xcO4XB4KPsACflQYHiaFh5 TDLc2F4recU4io+YjE7R71aWUqjnZbzy7IwgA7OEx9w== X-Received: by 2002:a17:902:788e:: with SMTP id q14mr3506897pll.140.1599708318455; Wed, 09 Sep 2020 20:25:18 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyGnH1P2dd44/K+vTjUcG89wH4QDXRGaVIq20iiRRkZgw00aWUSlhWXU+p7yzmtwPkkrIFNsA== X-Received: by 2002:a17:902:788e:: with SMTP id q14mr3506882pll.140.1599708318014; Wed, 09 Sep 2020 20:25:18 -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 v1sm473849pjh.16.2020.09.09.20.25.17 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:17 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 4/8][SRU][OEM-5.6] thunderbolt: Generalize usb4_switch_do_[read|write]_data() Date: Thu, 10 Sep 2020 11:25:01 +0800 Message-Id: <20200910032505.10882-5-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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 2cf331e6443b..27657a33c144 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--) @@ -270,10 +270,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; @@ -307,8 +308,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) @@ -441,9 +442,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; @@ -480,8 +482,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, @@ -506,9 +508,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; @@ -542,8 +545,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:25:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361182 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 4Bn45h3Sgpz9sTr; Thu, 10 Sep 2020 13:25:28 +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 1kGDDB-0001kG-OJ; Thu, 10 Sep 2020 03:25:25 +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 1kGDD9-0001jE-Lo for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:23 +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 1kGDD9-0002uU-4v for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:23 +0000 Received: by mail-pl1-f197.google.com with SMTP id s9so266790plq.15 for ; Wed, 09 Sep 2020 20:25:23 -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=y6C8qniIy442BnA4EvSj0x47cms5XPB4KKn5ysS8m14=; b=qGMs2AJuePgHyOfvcXRjFTe2sxvru2NQVgLaVh80BfsuHmPleXoTE/F4ZmOJlcWhJl GF6FspZuxDOVIZcNidTVuDgFZxayp7gtgNx5YzqPjcwyO4qv5xyu9kNOe8U75QRZ5iQU Iigqf0TgVYL/JkxNUGin6r/3RPwkEcrOb32/Tkel8/wxm8/247tvk2KmrG4htoNCiEcA NJd2WQj7Md5wXac3q1VxGMu4drxyjkBrDPI+P+NM0A3DQWXoA2Xi1o07C4vkcUGtFpQK LbvNPGITRBOjZC1DvtUfrd+F07dllKMRs06AfqzBbjFKitgsGSS32+DC7qPJ04k+zE4U 9rMA== X-Gm-Message-State: AOAM532kynYWlSHimWbQhW7L7Ei1KFeL43XH7lq2aMvdsIIu34FBGzWP NOEQZLX2ynSYmw8XoGr4Zv79+FRI+TMAFKZH95zu5VaOk9audKPCA8E/E73LktAY4E4xLE3Vghy 4M2HCdR79NkT6IL0As/mvhu2SfapOuSI35vohcZfl6Q== X-Received: by 2002:a63:c64c:: with SMTP id x12mr2879502pgg.433.1599708321211; Wed, 09 Sep 2020 20:25:21 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyP6ARVNMccJpdmyZKHtir/M1DGwoaMlD2RmdCMCOyo8klHOY3LXANrMURm38A0fAV/eRBwNA== X-Received: by 2002:a63:c64c:: with SMTP id x12mr2879476pgg.433.1599708320701; Wed, 09 Sep 2020 20:25:20 -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 e1sm1105010pfl.162.2020.09.09.20.25.19 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:20 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 5/8][SRU][OEM-5.6] thunderbolt: Implement USB4 port sideband operations for retimer access Date: Thu, 10 Sep 2020 11:25:02 +0800 Message-Id: <20200910032505.10882-6-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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/sb_regs.h | 31 +++ drivers/thunderbolt/tb.h | 16 ++ drivers/thunderbolt/tb_regs.h | 10 + drivers/thunderbolt/usb4.c | 459 ++++++++++++++++++++++++++++++++++ 4 files changed, 516 insertions(+) create mode 100644 drivers/thunderbolt/sb_regs.h 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 589517688674..1d2818446c77 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -858,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 27657a33c144..5c8b3b95ce12 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) @@ -791,6 +798,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:25:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361183 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 4Bn45m6Gfdz9sTr; Thu, 10 Sep 2020 13:25:32 +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 1kGDDG-0001n3-4G; Thu, 10 Sep 2020 03:25:30 +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 1kGDDC-0001kr-Eu for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:26 +0000 Received: from mail-pf1-f200.google.com ([209.85.210.200]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDDB-0002uY-T0 for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:26 +0000 Received: by mail-pf1-f200.google.com with SMTP id c197so3549323pfb.23 for ; Wed, 09 Sep 2020 20:25:25 -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=TR3wSI4cwrwixGhhYtb5Y9fMiqDblq6NWVg/uLpCDMs=; b=hmEQB96b1eAYFhuvNhlcPuLELcoo+cxbgY49RyO2hKGxnu0Ha4Ru30uXFsPLVCYH6D tRUPzz130QAbgubgEnCAhnDenPywABSoFtTi20u9wrWuuwAgVsd7TqI8lwkwrGXChpuL GshCas/K1/dinX7TfcOWKaQAmT2vtzt3CIYrOLUc7s1yVMBIThm0vLK2PTiVh0SQkGjz +XLuW8ei0QhfVhMQtyoXU4C4WUZ8nCkYXQq32gfj7fIL00TZCcaU1Yds3ibJNFWseysb 1mF+aT5FPDx5NHVdV2Kqbf41+Hyp4wyoDwzSNDsjl1fn7FCNHW7Qo2m/YnbPMcElhhL9 X6bA== X-Gm-Message-State: AOAM531t8Z9aJ3qdXQeBPid90g+rqY56twvZCos+2KVSJ6626dRxLz0w f0RH+pr1Xp1CvqIbYBxsDOOgVUntZkVuAcxgSIPLYJ2tZyLEvF4Io2wF7eMfHuy61tRa4wqvh01 bKnrxR3mWxOIvFzZiucDyLtRC1AyPBM1kJKQIFXeabA== X-Received: by 2002:a63:5515:: with SMTP id j21mr2773577pgb.31.1599708323876; Wed, 09 Sep 2020 20:25:23 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy5BgYl/8vaxhIxtwZ3jxZQjAKFGe8ug8xajfoFmejB1r9hUXJFPyiGI6OIbl4MbuDHFGiJGA== X-Received: by 2002:a63:5515:: with SMTP id j21mr2773501pgb.31.1599708323239; Wed, 09 Sep 2020 20:25:23 -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 n67sm3225668pgn.14.2020.09.09.20.25.22 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:22 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 6/8][SRU][OEM-5.6] thunderbolt: Add support for on-board retimers Date: Thu, 10 Sep 2020 11:25:03 +0800 Message-Id: <20200910032505.10882-7-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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 dc53ba4081c8..b7e1841c643a 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2393,6 +2393,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:25:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361184 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 4Bn45s1zpsz9sTr; Thu, 10 Sep 2020 13:25:37 +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 1kGDDK-0001oq-Al; Thu, 10 Sep 2020 03:25:34 +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 1kGDDE-0001ly-Po for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:28 +0000 Received: from mail-pg1-f200.google.com ([209.85.215.200]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kGDDE-0002ue-Ce for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:28 +0000 Received: by mail-pg1-f200.google.com with SMTP id s2so3094442pgm.18 for ; Wed, 09 Sep 2020 20:25:28 -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=99t9F6dm9JwnQ7OvsDRFkkGdnExnwAH+L9Qdxba1xWg=; b=m324vPQoYuSY7+EP+YnOZ+WVXgUGxQMuVxYFjW3QFzRNKe5Qnpt0nvBFatYbhPrtcn 1eWN8tA2hoquA/r0nXKwIpYxOG8I8kr+FNwVvto3udTNYfdhFb6LU3/7CnqFeDYCItFk UMZZdmWQdMKEwVWnt82iQOmcmyQ2+7FmlX8vP3fy+LGFBYnhZJZ47tUd07z7MH+EAP6s 6OwsNE0KLGWQVGNEFwDs48nmeK9cdfMx23Oq7AAKU38jGxxPqSNDanERzmtRzh7OVne0 QBOPr4vWa0QfaEizVMSRFoK0dAvoKjUNQsYqTMHu/L8R7S8hPZXzKbqsnM4IP0OqxyFB W79Q== X-Gm-Message-State: AOAM531qWYwAkjX+crMLIYKFeJF4O8h/rimXFCYuIhz6uNMkrRECQhua K69IGDDB2KAuQv/Maa2QJkCJz7/dPAAP/DvFKbxe4ZlNotbWinqcqtXnLhen535MuzUV87WVKMe wnQ0uQiHdBs2Uqv97PfvtIbTzTYhzmPiCVITl8PnVOA== X-Received: by 2002:a62:3146:0:b029:13e:d13d:a08e with SMTP id x67-20020a6231460000b029013ed13da08emr3551737pfx.37.1599708326633; Wed, 09 Sep 2020 20:25:26 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxDIG6TGLs7rBTzLPR+9FI1ZAYB8fcnwJVCKI7wa+Zij3P0tJ2QMGdP2e2O9h+soATxklOERg== X-Received: by 2002:a62:3146:0:b029:13e:d13d:a08e with SMTP id x67-20020a6231460000b029013ed13da08emr3551717pfx.37.1599708326248; Wed, 09 Sep 2020 20:25:26 -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 p29sm3457177pgl.34.2020.09.09.20.25.25 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:25 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 7/8][SRU][OEM-5.6] thunderbolt: Add support for authenticate on disconnect Date: Thu, 10 Sep 2020 11:25:04 +0800 Message-Id: <20200910032505.10882-8-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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 921d164b3f35..fd525923ad90 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 b7e1841c643a..a6f770386c56 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1482,8 +1482,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; @@ -1521,8 +1521,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); + } } } @@ -1532,12 +1536,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) { @@ -1595,6 +1622,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, @@ -1649,6 +1677,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:25:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Koba Ko X-Patchwork-Id: 1361185 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 4Bn45s5M0fz9sV8; Thu, 10 Sep 2020 13:25:37 +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 1kGDDK-0001p4-FO; Thu, 10 Sep 2020 03:25:34 +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 1kGDDH-0001ns-Eb for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:31 +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 1kGDDH-0002uz-3T for kernel-team@lists.ubuntu.com; Thu, 10 Sep 2020 03:25:31 +0000 Received: by mail-pf1-f197.google.com with SMTP id i16so2456946pfk.1 for ; Wed, 09 Sep 2020 20:25:31 -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=CPL1+SnOyHjEq6Z2t5fYnuWjStj71FeNSDzzcrQAHVo=; b=iDIAsSR3cJpDk/84wfecCcP97CSZOmCMtSFNUw2AmSeQ1wx9m8t+OdDc0BBLEJri3d qWIIXkzK20AacBExmRlgpnCZ3x4CfngqM8YkXVaPFLegS+Kv4aOCai/UWlz7zGhWHHjH qkhMhlQorEiKv94ty3TkEq0jhTVgn97DmOYbh1nJg12j8dBygybk3DiU98UK/QeZspuw gzlD9pN15biF13ngrTKmLtiQeNRENuh+NLq6yZ1Rm2qEFN8439pzBgkOgo0VgePjIE1y p/tTOYrYMinyM6DAqBhuTWi4y+GCPgoCCkYfSCf172fShrX4RkWhq5TYd/Tjvct9qNK8 v5Xg== X-Gm-Message-State: AOAM533DX4D4H9aW/aHwrWs/qsfgID5h7fZ7FsPbSKlvo5g+RQuuwrV8 Qa+AA0NJdO17zWjvZzBvXa5MXDSWPLMtTCz4iRfNElqgDCpzWtCrFTiVBIXfD4mkrfPPaSt2x1o htKRUnspbiA2HrRuubGTxEGclh2vJkDlKqe2HVKE7hg== X-Received: by 2002:a17:90b:3444:: with SMTP id lj4mr3286485pjb.78.1599708329608; Wed, 09 Sep 2020 20:25:29 -0700 (PDT) X-Google-Smtp-Source: ABdhPJww4psQLR3wgfLF03Pz2Jg2rPJ00etA2213Xi3q4qsxG3PEOyMMvJrttRnDxhCYUVeBuBDz/Q== X-Received: by 2002:a17:90b:3444:: with SMTP id lj4mr3286471pjb.78.1599708329276; Wed, 09 Sep 2020 20:25:29 -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 v205sm3980540pfc.110.2020.09.09.20.25.28 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Sep 2020 20:25:28 -0700 (PDT) From: Koba Ko To: kernel-team@lists.ubuntu.com Subject: [PATCH 8/8][SRU][OEM-5.6] thunderbolt: Ensure left shift of 512 does not overflow a 32 bit int Date: Thu, 10 Sep 2020 11:25:05 +0800 Message-Id: <20200910032505.10882-9-koba.ko@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200910032505.10882-1-koba.ko@canonical.com> References: <20200910032505.10882-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 5c8b3b95ce12..997a86a68cb2 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1349,7 +1349,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); } @@ -1359,7 +1359,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,