KernelVersion: v5.9
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
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 <mario.limonciello@dell.com>
+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.
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
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) {
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);
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Thunderbolt driver - quirks
+ *
+ * Copyright (c) 2020 Mario Limonciello <mario.limonciello@dell.com>
+ */
+
+#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);
+ }
+}
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;
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);
+ }
}
}
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)
{
&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,
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;
* @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.
u8 link;
u8 depth;
struct completion rpm_complete;
+ unsigned long quirks;
};
/**
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)
{
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
#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