[PATCH 00/17][SRU][OEM-OSP1-B] thunderbolt: ICL support

classic Classic list List threaded Threaded
19 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 00/17][SRU][OEM-OSP1-B] thunderbolt: ICL support

You-Sheng Yang
From: You-Sheng Yang (楊有勝) <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

[Impact]
The Thunderbolt controller is integrated into the Ice Lake CPU itself
and requires special flows to power it on and off using force power bit
in NHI VSEC registers. Currently this is not supported until v5.4-rc1.

[Fix]
Commit 3cdb9446a117 "thunderbolt: Add support for Intel Ice Lake" along
with all the prerequisites should be backported to enable thunderbolt.

[Test Case]
Tested on two Dell XPS platforms with ICL-U and thunderbolt support.

[Regression Risk]
Medium. While v5.4 is still under development and there is already an
known issue about suspend/resume on ICL platforms with thunderbolt
enabled, it is definitely going to have follow-ups for this backport, as
well as more regression tests on previous systems before landed.

Mika Westerberg (17):
  thunderbolt: Drop duplicated get_switch_at_route()
  thunderbolt: Move LC specific functionality into a separate file
  thunderbolt: Cache adapter specific capability offset into struct port
  thunderbolt: Rename tunnel_pci to tunnel
  thunderbolt: Generalize tunnel creation functionality
  thunderbolt: Add functions for allocating and releasing HopIDs
  thunderbolt: Assign remote for both ports in case of dual link
  thunderbolt: Add helper function to iterate from one port to another
  thunderbolt: Extend tunnel creation to more than 2 adjacent switches
  thunderbolt: Correct path indices for PCIe tunnel
  thunderbolt: Move NVM upgrade support flag to struct icm
  thunderbolt: Use 32-bit writes when writing ring producer/consumer
  thunderbolt: Do not fail adding switch if some port is not implemented
  thunderbolt: Hide switch attributes that are not set
  thunderbolt: Expose active parts of NVM even if upgrade is not
    supported
  thunderbolt: Add support for Intel Ice Lake
  ACPI / property: Add two new Thunderbolt property GUIDs to the list

 drivers/acpi/property.c          |   6 +
 drivers/thunderbolt/Makefile     |   4 +-
 drivers/thunderbolt/ctl.c        |  23 ++-
 drivers/thunderbolt/eeprom.c     |   4 -
 drivers/thunderbolt/icm.c        | 239 ++++++++++++++++++++++-----
 drivers/thunderbolt/lc.c         |  21 +++
 drivers/thunderbolt/nhi.c        | 137 ++++++++++++++--
 drivers/thunderbolt/nhi.h        |  22 +++
 drivers/thunderbolt/nhi_ops.c    | 179 ++++++++++++++++++++
 drivers/thunderbolt/nhi_regs.h   |  37 +++++
 drivers/thunderbolt/path.c       | 121 ++++++++++++--
 drivers/thunderbolt/switch.c     | 270 +++++++++++++++++++++++++------
 drivers/thunderbolt/tb.c         |  79 +++++----
 drivers/thunderbolt/tb.h         | 111 +++++++++++--
 drivers/thunderbolt/tb_msgs.h    |  16 +-
 drivers/thunderbolt/tb_regs.h    |   6 +
 drivers/thunderbolt/tunnel.c     | 249 ++++++++++++++++++++++++++++
 drivers/thunderbolt/tunnel.h     |  43 +++++
 drivers/thunderbolt/tunnel_pci.c | 226 --------------------------
 drivers/thunderbolt/tunnel_pci.h |  31 ----
 drivers/thunderbolt/xdomain.c    |   5 +-
 include/linux/thunderbolt.h      |   2 +
 22 files changed, 1384 insertions(+), 447 deletions(-)
 create mode 100644 drivers/thunderbolt/lc.c
 create mode 100644 drivers/thunderbolt/nhi_ops.c
 create mode 100644 drivers/thunderbolt/tunnel.c
 create mode 100644 drivers/thunderbolt/tunnel.h
 delete mode 100644 drivers/thunderbolt/tunnel_pci.c
 delete mode 100644 drivers/thunderbolt/tunnel_pci.h

--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 01/17][SRU][OEM-OSP1-B] thunderbolt: Drop duplicated get_switch_at_route()

You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

tb_switch_find_by_route() does the same already so use it instead and
remove duplicated get_switch_at_route().

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Lukas Wunner <[hidden email]>
(cherry picked from commit 8f965efd215a09c20b0b5e5bb4e20009a954472e)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/icm.c    | 12 ++++++++----
 drivers/thunderbolt/switch.c | 18 ------------------
 drivers/thunderbolt/tb.c     |  9 ++++++---
 drivers/thunderbolt/tb.h     |  1 -
 4 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 8b7f9131e9d1..7c923e16a7d8 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -798,9 +798,11 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
  * connected another host to the same port, remove the switch
  * first.
  */
- sw = get_switch_at_route(tb->root_switch, route);
- if (sw)
+ sw = tb_switch_find_by_route(tb, route);
+ if (sw) {
  remove_switch(sw);
+ tb_switch_put(sw);
+ }
 
  sw = tb_switch_find_by_link_depth(tb, link, depth);
  if (!sw) {
@@ -1143,9 +1145,11 @@ icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
  * connected another host to the same port, remove the switch
  * first.
  */
- sw = get_switch_at_route(tb->root_switch, route);
- if (sw)
+ sw = tb_switch_find_by_route(tb, route);
+ if (sw) {
  remove_switch(sw);
+ tb_switch_put(sw);
+ }
 
  sw = tb_switch_find_by_route(tb, get_parent_route(route));
  if (!sw) {
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index f569a2673742..7fa4ab076404 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -637,24 +637,6 @@ int tb_switch_reset(struct tb *tb, u64 route)
  return res.err;
 }
 
-struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route)
-{
- u8 next_port = route; /*
-       * Routes use a stride of 8 bits,
-       * eventhough a port index has 6 bits at most.
-       * */
- if (route == 0)
- return sw;
- if (next_port > sw->config.max_port_number)
- return NULL;
- if (tb_is_upstream_port(&sw->ports[next_port]))
- return NULL;
- if (!sw->ports[next_port].remote)
- return NULL;
- return get_switch_at_route(sw->ports[next_port].remote->sw,
-   route >> TB_ROUTE_SHIFT);
-}
-
 /**
  * tb_plug_events_active() - enable/disable plug events on a switch
  *
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 30e02c716f6c..d8f4ed0f2ef8 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -258,7 +258,7 @@ static void tb_handle_hotplug(struct work_struct *work)
  if (!tcm->hotplug_active)
  goto out; /* during init, suspend or shutdown */
 
- sw = get_switch_at_route(tb->root_switch, ev->route);
+ sw = tb_switch_find_by_route(tb, ev->route);
  if (!sw) {
  tb_warn(tb,
  "hotplug event from non existent switch %llx:%x (unplug: %d)\n",
@@ -269,14 +269,14 @@ static void tb_handle_hotplug(struct work_struct *work)
  tb_warn(tb,
  "hotplug event from non existent port %llx:%x (unplug: %d)\n",
  ev->route, ev->port, ev->unplug);
- goto out;
+ goto put_sw;
  }
  port = &sw->ports[ev->port];
  if (tb_is_upstream_port(port)) {
  tb_warn(tb,
  "hotplug event for upstream port %llx:%x (unplug: %d)\n",
  ev->route, ev->port, ev->unplug);
- goto out;
+ goto put_sw;
  }
  if (ev->unplug) {
  if (port->remote) {
@@ -306,6 +306,9 @@ static void tb_handle_hotplug(struct work_struct *work)
  tb_activate_pcie_devices(tb);
  }
  }
+
+put_sw:
+ tb_switch_put(sw);
 out:
  mutex_unlock(&tb->lock);
  kfree(ev);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index f5e0282225d1..dc9c9625aeef 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -400,7 +400,6 @@ void tb_switch_suspend(struct tb_switch *sw);
 int tb_switch_resume(struct tb_switch *sw);
 int tb_switch_reset(struct tb *tb, u64 route);
 void tb_sw_set_unplugged(struct tb_switch *sw);
-struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
 struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link,
        u8 depth);
 struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid);
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 02/17][SRU][OEM-OSP1-B] thunderbolt: Move LC specific functionality into a separate file

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

We will be adding more link controller functionality in subsequent
patches and it does not make sense to keep all that in switch.c, so
separate LC functionality into its own file.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit a9be55824a10653d0247de12dc6b9a741ce3fc98)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/Makefile  |  2 +-
 drivers/thunderbolt/lc.c      | 21 +++++++++++++++++++++
 drivers/thunderbolt/switch.c  | 21 ++++++++++-----------
 drivers/thunderbolt/tb.h      |  3 +++
 drivers/thunderbolt/tb_regs.h |  2 ++
 5 files changed, 37 insertions(+), 12 deletions(-)
 create mode 100644 drivers/thunderbolt/lc.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index f2f0de27252b..8531f15d3b3c 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
 thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
-thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o
+thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
new file mode 100644
index 000000000000..2134a55ed837
--- /dev/null
+++ b/drivers/thunderbolt/lc.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Thunderbolt link controller support
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Author: Mika Westerberg <[hidden email]>
+ */
+
+#include "tb.h"
+
+/**
+ * tb_lc_read_uuid() - Read switch UUID from link controller common register
+ * @sw: Switch whose UUID is read
+ * @uuid: UUID is placed here
+ */
+int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid)
+{
+ if (!sw->cap_lc)
+ return -EINVAL;
+ return tb_sw_read(sw, uuid, TB_CFG_SWITCH, sw->cap_lc + TB_LC_FUSE, 4);
+}
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 7fa4ab076404..33488b2c4155 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1176,6 +1176,10 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
  }
  sw->cap_plug_events = cap;
 
+ cap = tb_switch_find_vse_cap(sw, TB_VSE_CAP_LINK_CONTROLLER);
+ if (cap > 0)
+ sw->cap_lc = cap;
+
  /* Root switch is always authorized */
  if (!route)
  sw->authorized = true;
@@ -1272,22 +1276,17 @@ int tb_switch_configure(struct tb_switch *sw)
 static int tb_switch_set_uuid(struct tb_switch *sw)
 {
  u32 uuid[4];
- int cap, ret;
+ int ret;
 
- ret = 0;
  if (sw->uuid)
- return ret;
+ return 0;
 
  /*
  * The newer controllers include fused UUID as part of link
  * controller specific registers
  */
- cap = tb_switch_find_vse_cap(sw, TB_VSE_CAP_LINK_CONTROLLER);
- if (cap > 0) {
- ret = tb_sw_read(sw, uuid, TB_CFG_SWITCH, cap + 3, 4);
- if (ret)
- return ret;
- } else {
+ ret = tb_lc_read_uuid(sw, uuid);
+ if (ret) {
  /*
  * ICM generates UUID based on UID and fills the upper
  * two words with ones. This is not strictly following
@@ -1302,8 +1301,8 @@ static int tb_switch_set_uuid(struct tb_switch *sw)
 
  sw->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
  if (!sw->uuid)
- ret = -ENOMEM;
- return ret;
+ return -ENOMEM;
+ return 0;
 }
 
 static int tb_switch_add_dma_port(struct tb_switch *sw)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index dc9c9625aeef..95775d41af07 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -62,6 +62,7 @@ struct tb_switch_nvm {
  * @device_name: Name of the device (or %NULL if not known)
  * @generation: Switch Thunderbolt generation
  * @cap_plug_events: Offset to the plug events capability (%0 if not found)
+ * @cap_lc: Offset to the link controller capability (%0 if not found)
  * @is_unplugged: The switch is going away
  * @drom: DROM of the switch (%NULL if not found)
  * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
@@ -96,6 +97,7 @@ struct tb_switch {
  const char *device_name;
  unsigned int generation;
  int cap_plug_events;
+ int cap_lc;
  bool is_unplugged;
  u8 *drom;
  struct tb_switch_nvm *nvm;
@@ -445,6 +447,7 @@ bool tb_path_is_invalid(struct tb_path *path);
 int tb_drom_read(struct tb_switch *sw);
 int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
 
+int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
 
 static inline int tb_route_length(u64 route)
 {
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 6f1ff04ee195..4895ae9f0b40 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -237,5 +237,7 @@ struct tb_regs_hop {
  u32 unknown3:4; /* set to zero */
 } __packed;
 
+/* Common link controller registers */
+#define TB_LC_FUSE 0x03
 
 #endif
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 03/17][SRU][OEM-OSP1-B] thunderbolt: Cache adapter specific capability offset into struct port

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

The adapter specific capability either is there or not if the port does
not hold an adapter. Instead of always finding it on-demand we read the
offset just once when the port is initialized.

While there we update the struct port documentation to follow kernel-doc
format.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit 56183c88f368eef7134c32df826792ee140f2864)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/switch.c     | 4 ++++
 drivers/thunderbolt/tb.c         | 8 ++++----
 drivers/thunderbolt/tb.h         | 2 ++
 drivers/thunderbolt/tunnel_pci.c | 9 +++------
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 33488b2c4155..e460099799df 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -586,6 +586,10 @@ static int tb_init_port(struct tb_port *port)
  port->cap_phy = cap;
  else
  tb_port_WARN(port, "non switch port without a PHY\n");
+ } else if (port->port != 0) {
+ cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
+ if (cap > 0)
+ port->cap_adap = cap;
  }
 
  tb_dump_port(port->sw->tb, &port->config);
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index d8f4ed0f2ef8..e71530d0af65 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -151,8 +151,8 @@ static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw)
  continue;
  if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN)
  continue;
- cap = tb_port_find_cap(&sw->ports[i], TB_PORT_CAP_ADAP);
- if (cap < 0)
+ cap = sw->ports[i].cap_adap;
+ if (!cap)
  continue;
  res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1);
  if (res < 0)
@@ -197,8 +197,8 @@ static void tb_activate_pcie_devices(struct tb *tb)
  }
 
  /* check whether port is already activated */
- cap = tb_port_find_cap(up_port, TB_PORT_CAP_ADAP);
- if (cap < 0)
+ cap = up_port->cap_adap;
+ if (!cap)
  continue;
  if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1))
  continue;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 95775d41af07..7f0814470165 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -122,6 +122,7 @@ struct tb_switch {
  * @remote: Remote port (%NULL if not connected)
  * @xdomain: Remote host (%NULL if not connected)
  * @cap_phy: Offset, zero if not found
+ * @cap_adap: Offset of the adapter specific capability (%0 if not present)
  * @port: Port number on switch
  * @disabled: Disabled by eeprom
  * @dual_link_port: If the switch is connected using two ports, points
@@ -134,6 +135,7 @@ struct tb_port {
  struct tb_port *remote;
  struct tb_xdomain *xdomain;
  int cap_phy;
+ int cap_adap;
  u8 port;
  bool disabled;
  struct tb_port *dual_link_port;
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel_pci.c
index 0637537ea53f..2de4edccbd6d 100644
--- a/drivers/thunderbolt/tunnel_pci.c
+++ b/drivers/thunderbolt/tunnel_pci.c
@@ -148,12 +148,9 @@ bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
 static int tb_pci_port_active(struct tb_port *port, bool active)
 {
  u32 word = active ? 0x80000000 : 0x0;
- int cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
- if (cap < 0) {
- tb_port_warn(port, "TB_PORT_CAP_ADAP not found: %d\n", cap);
- return cap;
- }
- return tb_port_write(port, &word, TB_CFG_PORT, cap, 1);
+ if (!port->cap_adap)
+ return -ENXIO;
+ return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
 }
 
 /**
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 04/17][SRU][OEM-OSP1-B] thunderbolt: Rename tunnel_pci to tunnel

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

In order to tunnel non-PCIe traffic as well rename tunnel_pci.[ch] to
tunnel.[ch] to reflect this fact. No functional changes.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit 1752b9f78713c7a188495319ebafbe7868718962)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/Makefile                   | 2 +-
 drivers/thunderbolt/tb.c                       | 2 +-
 drivers/thunderbolt/{tunnel_pci.c => tunnel.c} | 4 ++--
 drivers/thunderbolt/{tunnel_pci.h => tunnel.h} | 6 +++---
 4 files changed, 7 insertions(+), 7 deletions(-)
 rename drivers/thunderbolt/{tunnel_pci.c => tunnel.c} (98%)
 rename drivers/thunderbolt/{tunnel_pci.h => tunnel.h} (87%)

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 8531f15d3b3c..833bdee3cec7 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
+thunderbolt-objs := nhi.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
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index e71530d0af65..8de43a2ab205 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -12,7 +12,7 @@
 
 #include "tb.h"
 #include "tb_regs.h"
-#include "tunnel_pci.h"
+#include "tunnel.h"
 
 /**
  * struct tb_cm - Simple Thunderbolt connection manager
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel.c
similarity index 98%
rename from drivers/thunderbolt/tunnel_pci.c
rename to drivers/thunderbolt/tunnel.c
index 2de4edccbd6d..1e470564e99d 100644
--- a/drivers/thunderbolt/tunnel_pci.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Thunderbolt Cactus Ridge driver - PCIe tunnel
+ * Thunderbolt Cactus Ridge driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <[hidden email]>
  */
@@ -8,7 +8,7 @@
 #include <linux/slab.h>
 #include <linux/list.h>
 
-#include "tunnel_pci.h"
+#include "tunnel.h"
 #include "tb.h"
 
 #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...)                   \
diff --git a/drivers/thunderbolt/tunnel_pci.h b/drivers/thunderbolt/tunnel.h
similarity index 87%
rename from drivers/thunderbolt/tunnel_pci.h
rename to drivers/thunderbolt/tunnel.h
index f9b65fa1fd4d..dff0f27d6ab5 100644
--- a/drivers/thunderbolt/tunnel_pci.h
+++ b/drivers/thunderbolt/tunnel.h
@@ -1,12 +1,12 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- * Thunderbolt Cactus Ridge driver - PCIe tunnel
+ * Thunderbolt Cactus Ridge driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <[hidden email]>
  */
 
-#ifndef TB_PCI_H_
-#define TB_PCI_H_
+#ifndef TB_TUNNEL_H_
+#define TB_TUNNEL_H_
 
 #include "tb.h"
 
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 05/17][SRU][OEM-OSP1-B] thunderbolt: Generalize tunnel creation functionality

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

To be able to tunnel non-PCIe traffic, separate tunnel functionality
into generic and PCIe specific parts. Rename struct tb_pci_tunnel to
tb_tunnel, and make it hold an array of paths instead of just two.
Update all the tunneling functions to take this structure as parameter.

We also move tb_pci_port_active() to switch.c (and rename it) where we
will be keeping all port and switch related functions.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit 93f36ade5b7b82a842a3d6284b8cdb68adb93e85)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/switch.c  |  13 ++
 drivers/thunderbolt/tb.c      |  30 ++--
 drivers/thunderbolt/tb.h      |   2 +
 drivers/thunderbolt/tb_regs.h |   4 +
 drivers/thunderbolt/tunnel.c  | 298 ++++++++++++++++++++--------------
 drivers/thunderbolt/tunnel.h  |  38 +++--
 6 files changed, 235 insertions(+), 150 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index e460099799df..3c0335c46d18 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -599,6 +599,19 @@ static int tb_init_port(struct tb_port *port)
 
 }
 
+/**
+ * tb_pci_port_enable() - Enable PCIe adapter port
+ * @port: PCIe port to enable
+ * @enable: Enable/disable the PCIe adapter
+ */
+int tb_pci_port_enable(struct tb_port *port, bool enable)
+{
+ u32 word = enable ? TB_PCI_EN : 0x0;
+ if (!port->cap_adap)
+ return -ENXIO;
+ return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
+}
+
 /* switch utility functions */
 
 static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 8de43a2ab205..36dad0a00ac2 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -91,14 +91,14 @@ static void tb_scan_port(struct tb_port *port)
 static void tb_free_invalid_tunnels(struct tb *tb)
 {
  struct tb_cm *tcm = tb_priv(tb);
- struct tb_pci_tunnel *tunnel;
- struct tb_pci_tunnel *n;
+ struct tb_tunnel *tunnel;
+ struct tb_tunnel *n;
 
  list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
- if (tb_pci_is_invalid(tunnel)) {
- tb_pci_deactivate(tunnel);
+ if (tb_tunnel_is_invalid(tunnel)) {
+ tb_tunnel_deactivate(tunnel);
  list_del(&tunnel->list);
- tb_pci_free(tunnel);
+ tb_tunnel_free(tunnel);
  }
  }
 }
@@ -178,7 +178,7 @@ static void tb_activate_pcie_devices(struct tb *tb)
  struct tb_switch *sw;
  struct tb_port *up_port;
  struct tb_port *down_port;
- struct tb_pci_tunnel *tunnel;
+ struct tb_tunnel *tunnel;
  struct tb_cm *tcm = tb_priv(tb);
 
  /* scan for pcie devices at depth 1*/
@@ -214,17 +214,17 @@ static void tb_activate_pcie_devices(struct tb *tb)
      "All PCIe down ports are occupied, aborting\n");
  continue;
  }
- tunnel = tb_pci_alloc(tb, up_port, down_port);
+ tunnel = tb_tunnel_alloc_pci(tb, up_port, down_port);
  if (!tunnel) {
  tb_port_info(up_port,
      "PCIe tunnel allocation failed, aborting\n");
  continue;
  }
 
- if (tb_pci_activate(tunnel)) {
+ if (tb_tunnel_activate(tunnel)) {
  tb_port_info(up_port,
      "PCIe tunnel activation failed, aborting\n");
- tb_pci_free(tunnel);
+ tb_tunnel_free(tunnel);
  continue;
  }
 
@@ -353,13 +353,13 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
 static void tb_stop(struct tb *tb)
 {
  struct tb_cm *tcm = tb_priv(tb);
- struct tb_pci_tunnel *tunnel;
- struct tb_pci_tunnel *n;
+ struct tb_tunnel *tunnel;
+ struct tb_tunnel *n;
 
  /* tunnels are only present after everything has been initialized */
  list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
- tb_pci_deactivate(tunnel);
- tb_pci_free(tunnel);
+ tb_tunnel_deactivate(tunnel);
+ tb_tunnel_free(tunnel);
  }
  tb_switch_remove(tb->root_switch);
  tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
@@ -418,7 +418,7 @@ static int tb_suspend_noirq(struct tb *tb)
 static int tb_resume_noirq(struct tb *tb)
 {
  struct tb_cm *tcm = tb_priv(tb);
- struct tb_pci_tunnel *tunnel, *n;
+ struct tb_tunnel *tunnel, *n;
 
  tb_dbg(tb, "resuming...\n");
 
@@ -429,7 +429,7 @@ static int tb_resume_noirq(struct tb *tb)
  tb_free_invalid_tunnels(tb);
  tb_free_unplugged_children(tb->root_switch);
  list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
- tb_pci_restart(tunnel);
+ tb_tunnel_restart(tunnel);
  if (!list_empty(&tcm->tunnel_list)) {
  /*
  * the pcie links need some time to get going.
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 7f0814470165..f3b80d78b9d7 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -440,6 +440,8 @@ int tb_port_clear_counter(struct tb_port *port, int counter);
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
 
+int tb_pci_port_enable(struct tb_port *port, bool enable);
+
 struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
 void tb_path_free(struct tb_path *path);
 int tb_path_activate(struct tb_path *path);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 4895ae9f0b40..3c43c74ffd48 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -211,6 +211,10 @@ struct tb_regs_port_header {
 
 } __packed;
 
+/* PCIe adapter registers */
+
+#define TB_PCI_EN BIT(31)
+
 /* Hop register from TB_CFG_HOPS. 8 byte per entry. */
 struct tb_regs_hop {
  /* DWORD 0 */
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 1e470564e99d..20ce28276f7a 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Thunderbolt Cactus Ridge driver - Tunneling support
+ * Thunderbolt driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <[hidden email]>
+ * Copyright (C) 2019, Intel Corporation
  */
 
 #include <linux/slab.h>
@@ -11,14 +12,17 @@
 #include "tunnel.h"
 #include "tb.h"
 
+#define TB_PCI_PATH_DOWN 0
+#define TB_PCI_PATH_UP 1
+
 #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...)                   \
  do {                                                            \
- struct tb_pci_tunnel *__tunnel = (tunnel);              \
+ struct tb_tunnel *__tunnel = (tunnel);                  \
  level(__tunnel->tb, "%llx:%x <-> %llx:%x (PCI): " fmt,  \
-      tb_route(__tunnel->down_port->sw),                \
-      __tunnel->down_port->port,                        \
-      tb_route(__tunnel->up_port->sw),                  \
-      __tunnel->up_port->port,                          \
+      tb_route(__tunnel->src_port->sw),                 \
+      __tunnel->src_port->port,                         \
+      tb_route(__tunnel->dst_port->sw),                 \
+      __tunnel->dst_port->port,                         \
       ## arg);                                          \
  } while (0)
 
@@ -29,6 +33,38 @@
 #define tb_tunnel_info(tunnel, fmt, arg...) \
  __TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
 
+static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths)
+{
+ struct tb_tunnel *tunnel;
+
+ tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+ if (!tunnel)
+ return NULL;
+
+ tunnel->paths = kcalloc(npaths, sizeof(tunnel->paths[0]), GFP_KERNEL);
+ if (!tunnel->paths) {
+ tb_tunnel_free(tunnel);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&tunnel->list);
+ tunnel->tb = tb;
+ tunnel->npaths = npaths;
+
+ return tunnel;
+}
+
+static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate)
+{
+ int res;
+
+ res = tb_pci_port_enable(tunnel->src_port, activate);
+ if (res)
+ return res;
+
+ return tb_pci_port_enable(tunnel->dst_port, activate);
+}
+
 static void tb_pci_init_path(struct tb_path *path)
 {
  path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
@@ -42,7 +78,10 @@ static void tb_pci_init_path(struct tb_path *path)
 }
 
 /**
- * tb_pci_alloc() - allocate a pci tunnel
+ * tb_tunnel_alloc_pci() - allocate a pci tunnel
+ * @tb: Pointer to the domain structure
+ * @up: PCIe upstream adapter port
+ * @down: PCIe downstream adapter port
  *
  * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
  * TB_TYPE_PCIE_DOWN.
@@ -54,170 +93,185 @@ static void tb_pci_init_path(struct tb_path *path)
  * my thunderbolt devices). Therefore at most ONE path per device may be
  * activated.
  *
- * Return: Returns a tb_pci_tunnel on success or NULL on failure.
+ * Return: Returns a tb_tunnel on success or NULL on failure.
  */
-struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
-   struct tb_port *down)
+struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
+      struct tb_port *down)
 {
- struct tb_pci_tunnel *tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+ struct tb_path *path_to_up;
+ struct tb_path *path_to_down;
+ struct tb_tunnel *tunnel;
+
+ tunnel = tb_tunnel_alloc(tb, 2);
  if (!tunnel)
- goto err;
- tunnel->tb = tb;
- tunnel->down_port = down;
- tunnel->up_port = up;
- INIT_LIST_HEAD(&tunnel->list);
- tunnel->path_to_up = tb_path_alloc(up->sw->tb, 2);
- if (!tunnel->path_to_up)
- goto err;
- tunnel->path_to_down = tb_path_alloc(up->sw->tb, 2);
- if (!tunnel->path_to_down)
- goto err;
- tb_pci_init_path(tunnel->path_to_up);
- tb_pci_init_path(tunnel->path_to_down);
-
- tunnel->path_to_up->hops[0].in_port = down;
- tunnel->path_to_up->hops[0].in_hop_index = 8;
- tunnel->path_to_up->hops[0].in_counter_index = -1;
- tunnel->path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
- tunnel->path_to_up->hops[0].next_hop_index = 8;
-
- tunnel->path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
- tunnel->path_to_up->hops[1].in_hop_index = 8;
- tunnel->path_to_up->hops[1].in_counter_index = -1;
- tunnel->path_to_up->hops[1].out_port = up;
- tunnel->path_to_up->hops[1].next_hop_index = 8;
-
- tunnel->path_to_down->hops[0].in_port = up;
- tunnel->path_to_down->hops[0].in_hop_index = 8;
- tunnel->path_to_down->hops[0].in_counter_index = -1;
- tunnel->path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
- tunnel->path_to_down->hops[0].next_hop_index = 8;
-
- tunnel->path_to_down->hops[1].in_port =
- tb_upstream_port(up->sw)->remote;
- tunnel->path_to_down->hops[1].in_hop_index = 8;
- tunnel->path_to_down->hops[1].in_counter_index = -1;
- tunnel->path_to_down->hops[1].out_port = down;
- tunnel->path_to_down->hops[1].next_hop_index = 8;
- return tunnel;
+ return NULL;
 
-err:
- if (tunnel) {
- if (tunnel->path_to_down)
- tb_path_free(tunnel->path_to_down);
- if (tunnel->path_to_up)
- tb_path_free(tunnel->path_to_up);
- kfree(tunnel);
+ tunnel->activate = tb_pci_activate;
+ tunnel->src_port = down;
+ tunnel->dst_port = up;
+
+ path_to_up = tb_path_alloc(tb, 2);
+ if (!path_to_up) {
+ tb_tunnel_free(tunnel);
+ return NULL;
  }
- return NULL;
+ tunnel->paths[TB_PCI_PATH_UP] = path_to_up;
+
+ path_to_down = tb_path_alloc(tb, 2);
+ if (!path_to_down) {
+ tb_tunnel_free(tunnel);
+ return NULL;
+ }
+ tunnel->paths[TB_PCI_PATH_DOWN] = path_to_down;
+
+ tb_pci_init_path(path_to_up);
+ tb_pci_init_path(path_to_down);
+
+ path_to_up->hops[0].in_port = down;
+ path_to_up->hops[0].in_hop_index = 8;
+ path_to_up->hops[0].in_counter_index = -1;
+ path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
+ path_to_up->hops[0].next_hop_index = 8;
+
+ path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
+ path_to_up->hops[1].in_hop_index = 8;
+ path_to_up->hops[1].in_counter_index = -1;
+ path_to_up->hops[1].out_port = up;
+ path_to_up->hops[1].next_hop_index = 8;
+
+ path_to_down->hops[0].in_port = up;
+ path_to_down->hops[0].in_hop_index = 8;
+ path_to_down->hops[0].in_counter_index = -1;
+ path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
+ path_to_down->hops[0].next_hop_index = 8;
+
+ path_to_down->hops[1].in_port = tb_upstream_port(up->sw)->remote;
+ path_to_down->hops[1].in_hop_index = 8;
+ path_to_down->hops[1].in_counter_index = -1;
+ path_to_down->hops[1].out_port = down;
+ path_to_down->hops[1].next_hop_index = 8;
+
+ return tunnel;
 }
 
 /**
- * tb_pci_free() - free a tunnel
+ * tb_tunnel_free() - free a tunnel
+ * @tunnel: Tunnel to be freed
  *
  * The tunnel must have been deactivated.
  */
-void tb_pci_free(struct tb_pci_tunnel *tunnel)
+void tb_tunnel_free(struct tb_tunnel *tunnel)
 {
- if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
- tb_tunnel_WARN(tunnel, "trying to free an activated tunnel\n");
+ int i;
+
+ if (!tunnel)
  return;
+
+ for (i = 0; i < tunnel->npaths; i++) {
+ if (tunnel->paths[i] && tunnel->paths[i]->activated) {
+ tb_tunnel_WARN(tunnel,
+       "trying to free an activated tunnel\n");
+ return;
+ }
  }
- tb_path_free(tunnel->path_to_up);
- tb_path_free(tunnel->path_to_down);
+
+ for (i = 0; i < tunnel->npaths; i++) {
+ if (tunnel->paths[i])
+ tb_path_free(tunnel->paths[i]);
+ }
+
+ kfree(tunnel->paths);
  kfree(tunnel);
 }
 
 /**
- * tb_pci_is_invalid - check whether an activated path is still valid
+ * tb_tunnel_is_invalid - check whether an activated path is still valid
+ * @tunnel: Tunnel to check
  */
-bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
+bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel)
 {
- WARN_ON(!tunnel->path_to_up->activated);
- WARN_ON(!tunnel->path_to_down->activated);
+ int i;
 
- return tb_path_is_invalid(tunnel->path_to_up)
-       || tb_path_is_invalid(tunnel->path_to_down);
-}
+ for (i = 0; i < tunnel->npaths; i++) {
+ WARN_ON(!tunnel->paths[i]->activated);
+ if (tb_path_is_invalid(tunnel->paths[i]))
+ return true;
+ }
 
-/**
- * tb_pci_port_active() - activate/deactivate PCI capability
- *
- * Return: Returns 0 on success or an error code on failure.
- */
-static int tb_pci_port_active(struct tb_port *port, bool active)
-{
- u32 word = active ? 0x80000000 : 0x0;
- if (!port->cap_adap)
- return -ENXIO;
- return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
+ return false;
 }
 
 /**
- * tb_pci_restart() - activate a tunnel after a hardware reset
+ * tb_tunnel_restart() - activate a tunnel after a hardware reset
+ * @tunnel: Tunnel to restart
+ *
+ * Return: 0 on success and negative errno in case if failure
  */
-int tb_pci_restart(struct tb_pci_tunnel *tunnel)
+int tb_tunnel_restart(struct tb_tunnel *tunnel)
 {
- int res;
- tunnel->path_to_up->activated = false;
- tunnel->path_to_down->activated = false;
+ int res, i;
 
  tb_tunnel_info(tunnel, "activating\n");
 
- res = tb_path_activate(tunnel->path_to_up);
- if (res)
- goto err;
- res = tb_path_activate(tunnel->path_to_down);
- if (res)
- goto err;
+ for (i = 0; i < tunnel->npaths; i++) {
+ tunnel->paths[i]->activated = false;
+ res = tb_path_activate(tunnel->paths[i]);
+ if (res)
+ goto err;
+ }
 
- res = tb_pci_port_active(tunnel->down_port, true);
- if (res)
- goto err;
+ if (tunnel->activate) {
+ res = tunnel->activate(tunnel, true);
+ if (res)
+ goto err;
+ }
 
- res = tb_pci_port_active(tunnel->up_port, true);
- if (res)
- goto err;
  return 0;
+
 err:
  tb_tunnel_warn(tunnel, "activation failed\n");
- tb_pci_deactivate(tunnel);
+ tb_tunnel_deactivate(tunnel);
  return res;
 }
 
 /**
- * tb_pci_activate() - activate a tunnel
+ * tb_tunnel_activate() - activate a tunnel
+ * @tunnel: Tunnel to activate
  *
  * Return: Returns 0 on success or an error code on failure.
  */
-int tb_pci_activate(struct tb_pci_tunnel *tunnel)
+int tb_tunnel_activate(struct tb_tunnel *tunnel)
 {
- if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
- tb_tunnel_WARN(tunnel,
-       "trying to activate an already activated tunnel\n");
- return -EINVAL;
- }
+ int i;
 
- return tb_pci_restart(tunnel);
-}
+ tb_tunnel_info(tunnel, "activating\n");
 
+ for (i = 0; i < tunnel->npaths; i++) {
+ if (tunnel->paths[i]->activated) {
+ tb_tunnel_WARN(tunnel,
+       "trying to activate an already activated tunnel\n");
+ return -EINVAL;
+ }
+ }
 
+ return tb_tunnel_restart(tunnel);
+}
 
 /**
- * tb_pci_deactivate() - deactivate a tunnel
+ * tb_tunnel_deactivate() - deactivate a tunnel
+ * @tunnel: Tunnel to deactivate
  */
-void tb_pci_deactivate(struct tb_pci_tunnel *tunnel)
+void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
 {
+ int i;
+
  tb_tunnel_info(tunnel, "deactivating\n");
- /*
- * TODO: enable reset by writing 0x04000000 to TB_CAP_PCIE + 1 on up
- * port. Seems to have no effect?
- */
- tb_pci_port_active(tunnel->up_port, false);
- tb_pci_port_active(tunnel->down_port, false);
- if (tunnel->path_to_down->activated)
- tb_path_deactivate(tunnel->path_to_down);
- if (tunnel->path_to_up->activated)
- tb_path_deactivate(tunnel->path_to_up);
-}
 
+ if (tunnel->activate)
+ tunnel->activate(tunnel, false);
+
+ for (i = 0; i < tunnel->npaths; i++) {
+ if (tunnel->paths[i]->activated)
+ tb_path_deactivate(tunnel->paths[i]);
+ }
+}
diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h
index dff0f27d6ab5..b4e992165e56 100644
--- a/drivers/thunderbolt/tunnel.h
+++ b/drivers/thunderbolt/tunnel.h
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- * Thunderbolt Cactus Ridge driver - Tunneling support
+ * Thunderbolt driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <[hidden email]>
+ * Copyright (C) 2019, Intel Corporation
  */
 
 #ifndef TB_TUNNEL_H_
@@ -10,22 +11,33 @@
 
 #include "tb.h"
 
-struct tb_pci_tunnel {
+/**
+ * struct tb_tunnel - Tunnel between two ports
+ * @tb: Pointer to the domain
+ * @src_port: Source port of the tunnel
+ * @dst_port: Destination port of the tunnel
+ * @paths: All paths required by the tunnel
+ * @npaths: Number of paths in @paths
+ * @activate: Optional tunnel specific activation/deactivation
+ * @list: Tunnels are linked using this field
+ */
+struct tb_tunnel {
  struct tb *tb;
- struct tb_port *up_port;
- struct tb_port *down_port;
- struct tb_path *path_to_up;
- struct tb_path *path_to_down;
+ struct tb_port *src_port;
+ struct tb_port *dst_port;
+ struct tb_path **paths;
+ size_t npaths;
+ int (*activate)(struct tb_tunnel *tunnel, bool activate);
  struct list_head list;
 };
 
-struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
-   struct tb_port *down);
-void tb_pci_free(struct tb_pci_tunnel *tunnel);
-int tb_pci_activate(struct tb_pci_tunnel *tunnel);
-int tb_pci_restart(struct tb_pci_tunnel *tunnel);
-void tb_pci_deactivate(struct tb_pci_tunnel *tunnel);
-bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel);
+struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
+      struct tb_port *down);
+void tb_tunnel_free(struct tb_tunnel *tunnel);
+int tb_tunnel_activate(struct tb_tunnel *tunnel);
+int tb_tunnel_restart(struct tb_tunnel *tunnel);
+void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
+bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
 
 #endif
 
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 06/17][SRU][OEM-OSP1-B] thunderbolt: Add functions for allocating and releasing HopIDs

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

Each port has a separate path configuration space that is used for
finding the next hop (switch) in the path. HopID is an index to this
configuration space. HopIDs 0 - 7 are reserved by the protocol.

In order to get next available HopID for each direction we provide two
pairs of helper functions that can be used to allocate and release
HopIDs for a given port.

While there remove obsolete TODO comment.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit 0b2863ac3cfdea2e0e27f9e722d178efb367e4db)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/nhi.c    |  3 +-
 drivers/thunderbolt/switch.c | 87 +++++++++++++++++++++++++++++++++++-
 drivers/thunderbolt/tb.h     | 11 +++++
 3 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 9aa44f9762a3..cac1ead5e302 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -27,8 +27,7 @@
  * use this ring for anything else.
  */
 #define RING_E2E_UNUSED_HOPID 2
-/* HopIDs 0-7 are reserved by the Thunderbolt protocol */
-#define RING_FIRST_USABLE_HOPID 8
+#define RING_FIRST_USABLE_HOPID TB_PATH_MIN_HOPID
 
 /*
  * Minimal number of vectors when we use MSI-X. Two for control channel
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 3c0335c46d18..56234fd82bfe 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -594,11 +594,88 @@ static int tb_init_port(struct tb_port *port)
 
  tb_dump_port(port->sw->tb, &port->config);
 
- /* TODO: Read dual link port, DP port and more from EEPROM. */
+ /* Control port does not need HopID allocation */
+ if (port->port) {
+ ida_init(&port->in_hopids);
+ ida_init(&port->out_hopids);
+ }
+
  return 0;
 
 }
 
+static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
+       int max_hopid)
+{
+ int port_max_hopid;
+ struct ida *ida;
+
+ if (in) {
+ port_max_hopid = port->config.max_in_hop_id;
+ ida = &port->in_hopids;
+ } else {
+ port_max_hopid = port->config.max_out_hop_id;
+ ida = &port->out_hopids;
+ }
+
+ /* HopIDs 0-7 are reserved */
+ if (min_hopid < TB_PATH_MIN_HOPID)
+ min_hopid = TB_PATH_MIN_HOPID;
+
+ if (max_hopid < 0 || max_hopid > port_max_hopid)
+ max_hopid = port_max_hopid;
+
+ return ida_simple_get(ida, min_hopid, max_hopid + 1, GFP_KERNEL);
+}
+
+/**
+ * tb_port_alloc_in_hopid() - Allocate input HopID from port
+ * @port: Port to allocate HopID for
+ * @min_hopid: Minimum acceptable input HopID
+ * @max_hopid: Maximum acceptable input HopID
+ *
+ * Return: HopID between @min_hopid and @max_hopid or negative errno in
+ * case of error.
+ */
+int tb_port_alloc_in_hopid(struct tb_port *port, int min_hopid, int max_hopid)
+{
+ return tb_port_alloc_hopid(port, true, min_hopid, max_hopid);
+}
+
+/**
+ * tb_port_alloc_out_hopid() - Allocate output HopID from port
+ * @port: Port to allocate HopID for
+ * @min_hopid: Minimum acceptable output HopID
+ * @max_hopid: Maximum acceptable output HopID
+ *
+ * Return: HopID between @min_hopid and @max_hopid or negative errno in
+ * case of error.
+ */
+int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid)
+{
+ return tb_port_alloc_hopid(port, false, min_hopid, max_hopid);
+}
+
+/**
+ * tb_port_release_in_hopid() - Release allocated input HopID from port
+ * @port: Port whose HopID to release
+ * @hopid: HopID to release
+ */
+void tb_port_release_in_hopid(struct tb_port *port, int hopid)
+{
+ ida_simple_remove(&port->in_hopids, hopid);
+}
+
+/**
+ * tb_port_release_out_hopid() - Release allocated output HopID from port
+ * @port: Port whose HopID to release
+ * @hopid: HopID to release
+ */
+void tb_port_release_out_hopid(struct tb_port *port, int hopid)
+{
+ ida_simple_remove(&port->out_hopids, hopid);
+}
+
 /**
  * tb_pci_port_enable() - Enable PCIe adapter port
  * @port: PCIe port to enable
@@ -1055,9 +1132,17 @@ static const struct attribute_group *switch_groups[] = {
 static void tb_switch_release(struct device *dev)
 {
  struct tb_switch *sw = tb_to_switch(dev);
+ int i;
 
  dma_port_free(sw->dma_port);
 
+ for (i = 1; i <= sw->config.max_port_number; i++) {
+ if (!sw->ports[i].disabled) {
+ ida_destroy(&sw->ports[i].in_hopids);
+ ida_destroy(&sw->ports[i].out_hopids);
+ }
+ }
+
  kfree(sw->uuid);
  kfree(sw->device_name);
  kfree(sw->vendor_name);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index f3b80d78b9d7..d775dc7cb4d3 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -128,6 +128,8 @@ struct tb_switch {
  * @dual_link_port: If the switch is connected using two ports, points
  *    to the other port.
  * @link_nr: Is this primary or secondary port on the dual_link.
+ * @in_hopids: Currently allocated input HopIDs
+ * @out_hopids: Currently allocated output HopIDs
  */
 struct tb_port {
  struct tb_regs_port_header config;
@@ -140,6 +142,8 @@ struct tb_port {
  bool disabled;
  struct tb_port *dual_link_port;
  u8 link_nr:1;
+ struct ida in_hopids;
+ struct ida out_hopids;
 };
 
 /**
@@ -195,6 +199,9 @@ struct tb_path {
  int path_length; /* number of hops */
 };
 
+/* HopIDs 0-7 are reserved by the Thunderbolt protocol */
+#define TB_PATH_MIN_HOPID 8
+
 /**
  * struct tb_cm_ops - Connection manager specific operations vector
  * @driver_ready: Called right after control channel is started. Used by
@@ -436,6 +443,10 @@ static inline struct tb_switch *tb_to_switch(struct device *dev)
 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
 int tb_port_add_nfc_credits(struct tb_port *port, int credits);
 int tb_port_clear_counter(struct tb_port *port, int counter);
+int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
+void tb_port_release_in_hopid(struct tb_port *port, int hopid);
+int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
+void tb_port_release_out_hopid(struct tb_port *port, int hopid);
 
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 07/17][SRU][OEM-OSP1-B] thunderbolt: Assign remote for both ports in case of dual link

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

Currently the driver only assigns remote port for the primary port if in
case of dual link. This makes things such as walking from one port to
another more complex than necessary because the code needs to change
from secondary to primary port if the path that is established is
created using secondary links.

In order to always assign both remote pointers we need to prevent the
scanning code from following the secondary link. Failing to do that
might cause problems as the same switch may be enumerated twice (or
removed in case of unplug). Handle that properly by introducing a new
function tb_port_has_remote() that returns true only for the primary
port. We also update tb_is_upstream_port() to support both dual link
ports, make it take const port pointer and move it below
tb_upstream_port() to keep similar functions close.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit dfe40ca486f60dca1e3223d82acf78cfb39925b8)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/icm.c     | 33 ++++++++++----------------------
 drivers/thunderbolt/switch.c  | 21 ++++++++++----------
 drivers/thunderbolt/tb.c      | 30 ++++++++++++++++++++---------
 drivers/thunderbolt/tb.h      | 36 ++++++++++++++++++++++++++++++-----
 drivers/thunderbolt/xdomain.c |  5 +----
 5 files changed, 73 insertions(+), 52 deletions(-)

diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 7c923e16a7d8..38b53c86a12e 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -1762,16 +1762,10 @@ static void icm_unplug_children(struct tb_switch *sw)
  for (i = 1; i <= sw->config.max_port_number; i++) {
  struct tb_port *port = &sw->ports[i];
 
- if (tb_is_upstream_port(port))
- continue;
- if (port->xdomain) {
+ if (port->xdomain)
  port->xdomain->is_unplugged = true;
- continue;
- }
- if (!port->remote)
- continue;
-
- icm_unplug_children(port->remote->sw);
+ else if (tb_port_has_remote(port))
+ icm_unplug_children(port->remote->sw);
  }
 }
 
@@ -1782,23 +1776,16 @@ static void icm_free_unplugged_children(struct tb_switch *sw)
  for (i = 1; i <= sw->config.max_port_number; i++) {
  struct tb_port *port = &sw->ports[i];
 
- if (tb_is_upstream_port(port))
- continue;
-
  if (port->xdomain && port->xdomain->is_unplugged) {
  tb_xdomain_remove(port->xdomain);
  port->xdomain = NULL;
- continue;
- }
-
- if (!port->remote)
- continue;
-
- if (port->remote->sw->is_unplugged) {
- tb_switch_remove(port->remote->sw);
- port->remote = NULL;
- } else {
- icm_free_unplugged_children(port->remote->sw);
+ } else if (tb_port_has_remote(port)) {
+ if (port->remote->sw->is_unplugged) {
+ tb_switch_remove(port->remote->sw);
+ port->remote = NULL;
+ } else {
+ icm_free_unplugged_children(port->remote->sw);
+ }
  }
  }
 }
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 56234fd82bfe..52d39b638136 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1570,14 +1570,13 @@ void tb_switch_remove(struct tb_switch *sw)
 
  /* port 0 is the switch itself and never has a remote */
  for (i = 1; i <= sw->config.max_port_number; i++) {
- if (tb_is_upstream_port(&sw->ports[i]))
- continue;
- if (sw->ports[i].remote)
+ if (tb_port_has_remote(&sw->ports[i])) {
  tb_switch_remove(sw->ports[i].remote->sw);
- sw->ports[i].remote = NULL;
- if (sw->ports[i].xdomain)
+ sw->ports[i].remote = NULL;
+ } else if (sw->ports[i].xdomain) {
  tb_xdomain_remove(sw->ports[i].xdomain);
- sw->ports[i].xdomain = NULL;
+ sw->ports[i].xdomain = NULL;
+ }
  }
 
  if (!sw->is_unplugged)
@@ -1606,7 +1605,7 @@ void tb_sw_set_unplugged(struct tb_switch *sw)
  }
  sw->is_unplugged = true;
  for (i = 0; i <= sw->config.max_port_number; i++) {
- if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote)
+ if (tb_port_has_remote(&sw->ports[i]))
  tb_sw_set_unplugged(sw->ports[i].remote->sw);
  }
 }
@@ -1648,10 +1647,10 @@ int tb_switch_resume(struct tb_switch *sw)
  /* check for surviving downstream switches */
  for (i = 1; i <= sw->config.max_port_number; i++) {
  struct tb_port *port = &sw->ports[i];
- if (tb_is_upstream_port(port))
- continue;
- if (!port->remote)
+
+ if (!tb_port_has_remote(port))
  continue;
+
  if (tb_wait_for_port(port, true) <= 0
  || tb_switch_resume(port->remote->sw)) {
  tb_port_warn(port,
@@ -1670,7 +1669,7 @@ void tb_switch_suspend(struct tb_switch *sw)
  return;
 
  for (i = 1; i <= sw->config.max_port_number; i++) {
- if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote)
+ if (tb_port_has_remote(&sw->ports[i]))
  tb_switch_suspend(sw->ports[i].remote->sw);
  }
  /*
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 36dad0a00ac2..0485f4ef9a62 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -47,7 +47,9 @@ static void tb_scan_switch(struct tb_switch *sw)
  */
 static void tb_scan_port(struct tb_port *port)
 {
+ struct tb_port *upstream_port;
  struct tb_switch *sw;
+
  if (tb_is_upstream_port(port))
  return;
  if (port->config.type != TB_TYPE_PORT)
@@ -80,8 +82,15 @@ static void tb_scan_port(struct tb_port *port)
  return;
  }
 
- port->remote = tb_upstream_port(sw);
- tb_upstream_port(sw)->remote = port;
+ /* Link the switches using both links if available */
+ upstream_port = tb_upstream_port(sw);
+ port->remote = upstream_port;
+ upstream_port->remote = port;
+ if (port->dual_link_port && upstream_port->dual_link_port) {
+ port->dual_link_port->remote = upstream_port->dual_link_port;
+ upstream_port->dual_link_port->remote = port->dual_link_port;
+ }
+
  tb_scan_switch(sw);
 }
 
@@ -111,13 +120,15 @@ static void tb_free_unplugged_children(struct tb_switch *sw)
  int i;
  for (i = 1; i <= sw->config.max_port_number; i++) {
  struct tb_port *port = &sw->ports[i];
- if (tb_is_upstream_port(port))
- continue;
- if (!port->remote)
+
+ if (!tb_port_has_remote(port))
  continue;
+
  if (port->remote->sw->is_unplugged) {
  tb_switch_remove(port->remote->sw);
  port->remote = NULL;
+ if (port->dual_link_port)
+ port->dual_link_port->remote = NULL;
  } else {
  tb_free_unplugged_children(port->remote->sw);
  }
@@ -273,18 +284,19 @@ static void tb_handle_hotplug(struct work_struct *work)
  }
  port = &sw->ports[ev->port];
  if (tb_is_upstream_port(port)) {
- tb_warn(tb,
- "hotplug event for upstream port %llx:%x (unplug: %d)\n",
- ev->route, ev->port, ev->unplug);
+ tb_dbg(tb, "hotplug event for upstream port %llx:%x (unplug: %d)\n",
+       ev->route, ev->port, ev->unplug);
  goto put_sw;
  }
  if (ev->unplug) {
- if (port->remote) {
+ if (tb_port_has_remote(port)) {
  tb_port_info(port, "unplugged\n");
  tb_sw_set_unplugged(port->remote->sw);
  tb_free_invalid_tunnels(tb);
  tb_switch_remove(port->remote->sw);
  port->remote = NULL;
+ if (port->dual_link_port)
+ port->dual_link_port->remote = NULL;
  } else {
  tb_port_info(port,
      "got unplug event for disconnected port, ignoring\n");
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index d775dc7cb4d3..0807d0714136 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -271,6 +271,19 @@ static inline struct tb_port *tb_upstream_port(struct tb_switch *sw)
  return &sw->ports[sw->config.upstream_port_number];
 }
 
+/**
+ * tb_is_upstream_port() - Is the port upstream facing
+ * @port: Port to check
+ *
+ * Returns true if @port is upstream facing port. In case of dual link
+ * ports both return true.
+ */
+static inline bool tb_is_upstream_port(const struct tb_port *port)
+{
+ const struct tb_port *upstream_port = tb_upstream_port(port->sw);
+ return port == upstream_port || port->dual_link_port == upstream_port;
+}
+
 static inline u64 tb_route(struct tb_switch *sw)
 {
  return ((u64) sw->config.route_hi) << 32 | sw->config.route_lo;
@@ -286,6 +299,24 @@ static inline struct tb_port *tb_port_at(u64 route, struct tb_switch *sw)
  return &sw->ports[port];
 }
 
+/**
+ * tb_port_has_remote() - Does the port have switch connected downstream
+ * @port: Port to check
+ *
+ * Returns true only when the port is primary port and has remote set.
+ */
+static inline bool tb_port_has_remote(const struct tb_port *port)
+{
+ if (tb_is_upstream_port(port))
+ return false;
+ if (!port->remote)
+ return false;
+ if (port->dual_link_port && port->link_nr)
+ return false;
+
+ return true;
+}
+
 static inline int tb_sw_read(struct tb_switch *sw, void *buffer,
      enum tb_cfg_space space, u32 offset, u32 length)
 {
@@ -469,11 +500,6 @@ static inline int tb_route_length(u64 route)
  return (fls64(route) + TB_ROUTE_SHIFT - 1) / TB_ROUTE_SHIFT;
 }
 
-static inline bool tb_is_upstream_port(struct tb_port *port)
-{
- return port == tb_upstream_port(port->sw);
-}
-
 /**
  * tb_downstream_route() - get route to downstream switch
  *
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index e0642dcb8b9b..367dee1a4c84 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -1288,9 +1288,6 @@ static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
  struct tb_port *port = &sw->ports[i];
  struct tb_xdomain *xd;
 
- if (tb_is_upstream_port(port))
- continue;
-
  if (port->xdomain) {
  xd = port->xdomain;
 
@@ -1305,7 +1302,7 @@ static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
    lookup->route == xd->route) {
  return xd;
  }
- } else if (port->remote) {
+ } else if (tb_port_has_remote(port)) {
  xd = switch_find_xdomain(port->remote->sw, lookup);
  if (xd)
  return xd;
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 08/17][SRU][OEM-OSP1-B] thunderbolt: Add helper function to iterate from one port to another

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

We need to be able to walk from one port to another when we are creating
paths where there are multiple switches between two ports. For this
reason introduce a new function tb_next_port_on_path().

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Lukas Wunner <[hidden email]>
(cherry picked from commit fb19fac1d734504073fee64e9f9b28ccd41ab350)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/switch.c | 54 ++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |  2 ++
 2 files changed, 56 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 52d39b638136..e81eb3751678 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -676,6 +676,60 @@ void tb_port_release_out_hopid(struct tb_port *port, int hopid)
  ida_simple_remove(&port->out_hopids, hopid);
 }
 
+/**
+ * tb_next_port_on_path() - Return next port for given port on a path
+ * @start: Start port of the walk
+ * @end: End port of the walk
+ * @prev: Previous port (%NULL if this is the first)
+ *
+ * This function can be used to walk from one port to another if they
+ * are connected through zero or more switches. If the @prev is dual
+ * link port, the function follows that link and returns another end on
+ * that same link.
+ *
+ * If the @end port has been reached, return %NULL.
+ *
+ * Domain tb->lock must be held when this function is called.
+ */
+struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
+     struct tb_port *prev)
+{
+ struct tb_port *next;
+
+ if (!prev)
+ return start;
+
+ if (prev->sw == end->sw) {
+ if (prev == end)
+ return NULL;
+ return end;
+ }
+
+ if (start->sw->config.depth < end->sw->config.depth) {
+ if (prev->remote &&
+    prev->remote->sw->config.depth > prev->sw->config.depth)
+ next = prev->remote;
+ else
+ next = tb_port_at(tb_route(end->sw), prev->sw);
+ } else {
+ if (tb_is_upstream_port(prev)) {
+ next = prev->remote;
+ } else {
+ next = tb_upstream_port(prev->sw);
+ /*
+ * Keep the same link if prev and next are both
+ * dual link ports.
+ */
+ if (next->dual_link_port &&
+    next->link_nr != prev->link_nr) {
+ next = next->dual_link_port;
+ }
+ }
+ }
+
+ return next;
+}
+
 /**
  * tb_pci_port_enable() - Enable PCIe adapter port
  * @port: PCIe port to enable
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 0807d0714136..658db0cd8a6a 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -478,6 +478,8 @@ int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
 int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_out_hopid(struct tb_port *port, int hopid);
+struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
+     struct tb_port *prev);
 
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 09/17][SRU][OEM-OSP1-B] thunderbolt: Extend tunnel creation to more than 2 adjacent switches

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

Now that we can allocate hop IDs per port on a path, we can take
advantage of this and create tunnels covering longer paths than just
between two adjacent switches. PCIe actually does not need this as it
is typically a daisy chain between two adjacent switches but this way we
do not need to hard-code creation of the tunnel.

While there add name to struct tb_path to make debugging easier, and
update kernel-doc comments.

Signed-off-by: Mika Westerberg <[hidden email]>
(cherry picked from commit 8c7acaaf020fe54baf2eccc5e1071341754d22be)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/path.c   | 121 ++++++++++++++++++++++++++++++-----
 drivers/thunderbolt/tb.h     |  54 ++++++++++++----
 drivers/thunderbolt/tunnel.c |  56 ++++------------
 3 files changed, 163 insertions(+), 68 deletions(-)

diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index a11956522bac..5b8012538bd1 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -29,23 +29,100 @@ static void tb_dump_hop(struct tb_port *port, struct tb_regs_hop *hop)
 }
 
 /**
- * tb_path_alloc() - allocate a thunderbolt path
+ * tb_path_alloc() - allocate a thunderbolt path between two ports
+ * @tb: Domain pointer
+ * @src: Source port of the path
+ * @src_hopid: HopID used for the first ingress port in the path
+ * @dst: Destination port of the path
+ * @dst_hopid: HopID used for the last egress port in the path
+ * @link_nr: Preferred link if there are dual links on the path
+ * @name: Name of the path
+ *
+ * Creates path between two ports starting with given @src_hopid. Reserves
+ * HopIDs for each port (they can be different from @src_hopid depending on
+ * how many HopIDs each port already have reserved). If there are dual
+ * links on the path, prioritizes using @link_nr.
  *
  * Return: Returns a tb_path on success or NULL on failure.
  */
-struct tb_path *tb_path_alloc(struct tb *tb, int num_hops)
+struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
+      struct tb_port *dst, int dst_hopid, int link_nr,
+      const char *name)
 {
- struct tb_path *path = kzalloc(sizeof(*path), GFP_KERNEL);
+ struct tb_port *in_port, *out_port;
+ int in_hopid, out_hopid;
+ struct tb_path *path;
+ size_t num_hops;
+ int i, ret;
+
+ path = kzalloc(sizeof(*path), GFP_KERNEL);
  if (!path)
  return NULL;
+
+ /*
+ * Number of hops on a path is the distance between the two
+ * switches plus the source adapter port.
+ */
+ num_hops = abs(tb_route_length(tb_route(src->sw)) -
+       tb_route_length(tb_route(dst->sw))) + 1;
+
  path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL);
  if (!path->hops) {
  kfree(path);
  return NULL;
  }
+
+ in_hopid = src_hopid;
+ out_port = NULL;
+
+ for (i = 0; i < num_hops; i++) {
+ in_port = tb_next_port_on_path(src, dst, out_port);
+ if (!in_port)
+ goto err;
+
+ if (in_port->dual_link_port && in_port->link_nr != link_nr)
+ in_port = in_port->dual_link_port;
+
+ ret = tb_port_alloc_in_hopid(in_port, in_hopid, in_hopid);
+ if (ret < 0)
+ goto err;
+ in_hopid = ret;
+
+ out_port = tb_next_port_on_path(src, dst, in_port);
+ if (!out_port)
+ goto err;
+
+ if (out_port->dual_link_port && out_port->link_nr != link_nr)
+ out_port = out_port->dual_link_port;
+
+ if (i == num_hops - 1)
+ ret = tb_port_alloc_out_hopid(out_port, dst_hopid,
+      dst_hopid);
+ else
+ ret = tb_port_alloc_out_hopid(out_port, -1, -1);
+
+ if (ret < 0)
+ goto err;
+ out_hopid = ret;
+
+ path->hops[i].in_hop_index = in_hopid;
+ path->hops[i].in_port = in_port;
+ path->hops[i].in_counter_index = -1;
+ path->hops[i].out_port = out_port;
+ path->hops[i].next_hop_index = out_hopid;
+
+ in_hopid = out_hopid;
+ }
+
  path->tb = tb;
  path->path_length = num_hops;
+ path->name = name;
+
  return path;
+
+err:
+ tb_path_free(path);
+ return NULL;
 }
 
 /**
@@ -53,10 +130,24 @@ struct tb_path *tb_path_alloc(struct tb *tb, int num_hops)
  */
 void tb_path_free(struct tb_path *path)
 {
+ int i;
+
  if (path->activated) {
  tb_WARN(path->tb, "trying to free an activated path\n")
  return;
  }
+
+ for (i = 0; i < path->path_length; i++) {
+ const struct tb_path_hop *hop = &path->hops[i];
+
+ if (hop->in_port)
+ tb_port_release_in_hopid(hop->in_port,
+ hop->in_hop_index);
+ if (hop->out_port)
+ tb_port_release_out_hopid(hop->out_port,
+  hop->next_hop_index);
+ }
+
  kfree(path->hops);
  kfree(path);
 }
@@ -94,12 +185,12 @@ void tb_path_deactivate(struct tb_path *path)
  tb_WARN(path->tb, "trying to deactivate an inactive path\n");
  return;
  }
- tb_info(path->tb,
- "deactivating path from %llx:%x to %llx:%x\n",
- tb_route(path->hops[0].in_port->sw),
- path->hops[0].in_port->port,
- tb_route(path->hops[path->path_length - 1].out_port->sw),
- path->hops[path->path_length - 1].out_port->port);
+ tb_dbg(path->tb,
+       "deactivating %s path from %llx:%x to %llx:%x\n",
+       path->name, tb_route(path->hops[0].in_port->sw),
+       path->hops[0].in_port->port,
+       tb_route(path->hops[path->path_length - 1].out_port->sw),
+       path->hops[path->path_length - 1].out_port->port);
  __tb_path_deactivate_hops(path, 0);
  __tb_path_deallocate_nfc(path, 0);
  path->activated = false;
@@ -122,12 +213,12 @@ int tb_path_activate(struct tb_path *path)
  return -EINVAL;
  }
 
- tb_info(path->tb,
- "activating path from %llx:%x to %llx:%x\n",
- tb_route(path->hops[0].in_port->sw),
- path->hops[0].in_port->port,
- tb_route(path->hops[path->path_length - 1].out_port->sw),
- path->hops[path->path_length - 1].out_port->port);
+ tb_dbg(path->tb,
+       "activating %s path from %llx:%x to %llx:%x\n",
+       path->name, tb_route(path->hops[0].in_port->sw),
+       path->hops[0].in_port->port,
+       tb_route(path->hops[path->path_length - 1].out_port->sw),
+       path->hops[path->path_length - 1].out_port->port);
 
  /* Clear counters. */
  for (i = path->path_length - 1; i >= 0; i--) {
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 658db0cd8a6a..56e1dc677470 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -148,11 +148,22 @@ struct tb_port {
 
 /**
  * struct tb_path_hop - routing information for a tb_path
+ * @in_port: Ingress port of a switch
+ * @out_port: Egress port of a switch where the packet is routed out
+ *      (must be on the same switch than @in_port)
+ * @in_hop_index: HopID where the path configuration entry is placed in
+ *  the path config space of @in_port.
+ * @in_counter_index: Used counter index (not used in the driver
+ *      currently, %-1 to disable)
+ * @next_hop_index: HopID of the packet when it is routed out from @out_port
  *
  * Hop configuration is always done on the IN port of a switch.
  * in_port and out_port have to be on the same switch. Packets arriving on
  * in_port with "hop" = in_hop_index will get routed to through out_port. The
- * next hop to take (on out_port->remote) is determined by next_hop_index.
+ * next hop to take (on out_port->remote) is determined by
+ * next_hop_index. When routing packet to another switch (out->remote is
+ * set) the @next_hop_index must match the @in_hop_index of that next
+ * hop to make routing possible.
  *
  * in_counter_index is the index of a counter (in TB_CFG_COUNTERS) on the in
  * port.
@@ -161,31 +172,50 @@ struct tb_path_hop {
  struct tb_port *in_port;
  struct tb_port *out_port;
  int in_hop_index;
- int in_counter_index; /* write -1 to disable counters for this hop. */
+ int in_counter_index;
  int next_hop_index;
 };
 
 /**
  * enum tb_path_port - path options mask
+ * @TB_PATH_NONE: Do not activate on any hop on path
+ * @TB_PATH_SOURCE: Activate on the first hop (out of src)
+ * @TB_PATH_INTERNAL: Activate on the intermediate hops (not the first/last)
+ * @TB_PATH_DESTINATION: Activate on the last hop (into dst)
+ * @TB_PATH_ALL: Activate on all hops on the path
  */
 enum tb_path_port {
  TB_PATH_NONE = 0,
- TB_PATH_SOURCE = 1, /* activate on the first hop (out of src) */
- TB_PATH_INTERNAL = 2, /* activate on other hops (not the first/last) */
- TB_PATH_DESTINATION = 4, /* activate on the last hop (into dst) */
+ TB_PATH_SOURCE = 1,
+ TB_PATH_INTERNAL = 2,
+ TB_PATH_DESTINATION = 4,
  TB_PATH_ALL = 7,
 };
 
 /**
  * struct tb_path - a unidirectional path between two ports
+ * @tb: Pointer to the domain structure
+ * @name: Name of the path (used for debugging)
+ * @nfc_credits: Number of non flow controlled credits allocated for the path
+ * @ingress_shared_buffer: Shared buffering used for ingress ports on the path
+ * @egress_shared_buffer: Shared buffering used for egress ports on the path
+ * @ingress_fc_enable: Flow control for ingress ports on the path
+ * @egress_fc_enable: Flow control for egress ports on the path
+ * @priority: Priority group if the path
+ * @weight: Weight of the path inside the priority group
+ * @drop_packages: Drop packages from queue tail or head
+ * @activated: Is the path active
+ * @hops: Path hops
+ * @path_length: How many hops the path uses
  *
- * A path consists of a number of hops (see tb_path_hop). To establish a PCIe
- * tunnel two paths have to be created between the two PCIe ports.
- *
+ * A path consists of a number of hops (see &struct tb_path_hop). To
+ * establish a PCIe tunnel two paths have to be created between the two
+ * PCIe ports.
  */
 struct tb_path {
  struct tb *tb;
- int nfc_credits; /* non flow controlled credits */
+ const char *name;
+ int nfc_credits;
  enum tb_path_port ingress_shared_buffer;
  enum tb_path_port egress_shared_buffer;
  enum tb_path_port ingress_fc_enable;
@@ -196,7 +226,7 @@ struct tb_path {
  bool drop_packages;
  bool activated;
  struct tb_path_hop *hops;
- int path_length; /* number of hops */
+ int path_length;
 };
 
 /* HopIDs 0-7 are reserved by the Thunderbolt protocol */
@@ -486,7 +516,9 @@ int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
 
 int tb_pci_port_enable(struct tb_port *port, bool enable);
 
-struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
+struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
+      struct tb_port *dst, int dst_hopid, int link_nr,
+      const char *name);
 void tb_path_free(struct tb_path *path);
 int tb_path_activate(struct tb_path *path);
 void tb_path_deactivate(struct tb_path *path);
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 20ce28276f7a..91d7e00516b4 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -12,6 +12,9 @@
 #include "tunnel.h"
 #include "tb.h"
 
+/* PCIe adapters use always HopID of 8 for both directions */
+#define TB_PCI_HOPID 8
+
 #define TB_PCI_PATH_DOWN 0
 #define TB_PCI_PATH_UP 1
 
@@ -86,21 +89,13 @@ static void tb_pci_init_path(struct tb_path *path)
  * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
  * TB_TYPE_PCIE_DOWN.
  *
- * Currently only paths consisting of two hops are supported (that is the
- * ports must be on "adjacent" switches).
- *
- * The paths are hard-coded to use hop 8 (the only working hop id available on
- * my thunderbolt devices). Therefore at most ONE path per device may be
- * activated.
- *
  * Return: Returns a tb_tunnel on success or NULL on failure.
  */
 struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
       struct tb_port *down)
 {
- struct tb_path *path_to_up;
- struct tb_path *path_to_down;
  struct tb_tunnel *tunnel;
+ struct tb_path *path;
 
  tunnel = tb_tunnel_alloc(tb, 2);
  if (!tunnel)
@@ -110,46 +105,23 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
  tunnel->src_port = down;
  tunnel->dst_port = up;
 
- path_to_up = tb_path_alloc(tb, 2);
- if (!path_to_up) {
+ path = tb_path_alloc(tb, down, TB_PCI_HOPID, up, TB_PCI_HOPID, 0,
+     "PCIe Down");
+ if (!path) {
  tb_tunnel_free(tunnel);
  return NULL;
  }
- tunnel->paths[TB_PCI_PATH_UP] = path_to_up;
+ tb_pci_init_path(path);
+ tunnel->paths[TB_PCI_PATH_UP] = path;
 
- path_to_down = tb_path_alloc(tb, 2);
- if (!path_to_down) {
+ path = tb_path_alloc(tb, up, TB_PCI_HOPID, down, TB_PCI_HOPID, 0,
+     "PCIe Up");
+ if (!path) {
  tb_tunnel_free(tunnel);
  return NULL;
  }
- tunnel->paths[TB_PCI_PATH_DOWN] = path_to_down;
-
- tb_pci_init_path(path_to_up);
- tb_pci_init_path(path_to_down);
-
- path_to_up->hops[0].in_port = down;
- path_to_up->hops[0].in_hop_index = 8;
- path_to_up->hops[0].in_counter_index = -1;
- path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
- path_to_up->hops[0].next_hop_index = 8;
-
- path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
- path_to_up->hops[1].in_hop_index = 8;
- path_to_up->hops[1].in_counter_index = -1;
- path_to_up->hops[1].out_port = up;
- path_to_up->hops[1].next_hop_index = 8;
-
- path_to_down->hops[0].in_port = up;
- path_to_down->hops[0].in_hop_index = 8;
- path_to_down->hops[0].in_counter_index = -1;
- path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
- path_to_down->hops[0].next_hop_index = 8;
-
- path_to_down->hops[1].in_port = tb_upstream_port(up->sw)->remote;
- path_to_down->hops[1].in_hop_index = 8;
- path_to_down->hops[1].in_counter_index = -1;
- path_to_down->hops[1].out_port = down;
- path_to_down->hops[1].next_hop_index = 8;
+ tb_pci_init_path(path);
+ tunnel->paths[TB_PCI_PATH_DOWN] = path;
 
  return tunnel;
 }
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 10/17][SRU][OEM-OSP1-B] thunderbolt: Correct path indices for PCIe tunnel

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

PCIe tunnel path indices got mixed up when we added support for tunnels
between switches that are not adjacent. This did not affect the
functionality as it is just an index but fix it now nevertheless to make
the code easier to understand.

Reported-by: Rajmohan Mani <[hidden email]>
Fixes: 8c7acaaf020f ("thunderbolt: Extend tunnel creation to more than 2 adjacent switches")
Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
(cherry picked from commit ce19f91eae43e39d5a1da55344756ab5a3c7e8d1)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/tunnel.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 91d7e00516b4..5506dec3e22f 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -112,7 +112,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
  return NULL;
  }
  tb_pci_init_path(path);
- tunnel->paths[TB_PCI_PATH_UP] = path;
+ tunnel->paths[TB_PCI_PATH_DOWN] = path;
 
  path = tb_path_alloc(tb, up, TB_PCI_HOPID, down, TB_PCI_HOPID, 0,
      "PCIe Up");
@@ -121,7 +121,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
  return NULL;
  }
  tb_pci_init_path(path);
- tunnel->paths[TB_PCI_PATH_DOWN] = path;
+ tunnel->paths[TB_PCI_PATH_UP] = path;
 
  return tunnel;
 }
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 11/17][SRU][OEM-OSP1-B] thunderbolt: Move NVM upgrade support flag to struct icm

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

This is depends on the controller and on the platform/CPU we are
running. Move it to struct icm so we can set it per controller.

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
Tested-by: Mario Limonciello <[hidden email]>
(cherry picked from commit f437c24bf694b0293f835dea8c25e3a5c1433d07)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/icm.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 38b53c86a12e..cdc1dfffcdb7 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -56,6 +56,7 @@
  * @safe_mode: ICM is in safe mode
  * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
  * @rpm: Does the controller support runtime PM (RTD3)
+ * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller
  * @is_supported: Checks if we can support ICM on this controller
  * @get_mode: Read and return the ICM firmware mode (optional)
  * @get_route: Find a route string for given switch
@@ -74,6 +75,7 @@ struct icm {
  int vnd_cap;
  bool safe_mode;
  bool rpm;
+ bool can_upgrade_nvm;
  bool (*is_supported)(struct tb *tb);
  int (*get_mode)(struct tb *tb);
  int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
@@ -1852,12 +1854,7 @@ static int icm_start(struct tb *tb)
  if (!tb->root_switch)
  return -ENODEV;
 
- /*
- * NVM upgrade has not been tested on Apple systems and they
- * don't provide images publicly either. To be on the safe side
- * prevent root switch NVM upgrade on Macs for now.
- */
- tb->root_switch->no_nvm_upgrade = x86_apple_machine;
+ tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm;
  tb->root_switch->rpm = icm->rpm;
 
  ret = tb_switch_add(tb->root_switch);
@@ -1956,6 +1953,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
  switch (nhi->pdev->device) {
  case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
  case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
+ icm->can_upgrade_nvm = true;
  icm->is_supported = icm_fr_is_supported;
  icm->get_route = icm_fr_get_route;
  icm->save_devices = icm_fr_save_devices;
@@ -1973,6 +1971,13 @@ struct tb *icm_probe(struct tb_nhi *nhi)
  case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI:
  case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI:
  icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
+ /*
+ * NVM upgrade has not been tested on Apple systems and
+ * they don't provide images publicly either. To be on
+ * the safe side prevent root switch NVM upgrade on Macs
+ * for now.
+ */
+ icm->can_upgrade_nvm = !x86_apple_machine;
  icm->is_supported = icm_ar_is_supported;
  icm->get_mode = icm_ar_get_mode;
  icm->get_route = icm_ar_get_route;
@@ -1988,6 +1993,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
  case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI:
  case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
  icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
+ icm->can_upgrade_nvm = !x86_apple_machine;
  icm->is_supported = icm_ar_is_supported;
  icm->get_mode = icm_ar_get_mode;
  icm->driver_ready = icm_tr_driver_ready;
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 12/17][SRU][OEM-OSP1-B] thunderbolt: Use 32-bit writes when writing ring producer/consumer

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

The register access should be using 32-bit reads/writes according to the
datasheet. With the previous generation hardware 16-bit writes have been
working but starting with ICL this is not the case anymore so fix
producer/consumer register update to use correct width register address.

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
Tested-by: Mario Limonciello <[hidden email]>
(cherry picked from commit 943795219d3cb9f8ce6ce51cad3ffe1f61e95c6b)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/nhi.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index cac1ead5e302..32713c9844d2 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -142,9 +142,20 @@ static void __iomem *ring_options_base(struct tb_ring *ring)
  return io;
 }
 
-static void ring_iowrite16desc(struct tb_ring *ring, u32 value, u32 offset)
+static void ring_iowrite_cons(struct tb_ring *ring, u16 cons)
 {
- iowrite16(value, ring_desc_base(ring) + offset);
+ /*
+ * The other 16-bits in the register is read-only and writes to it
+ * are ignored by the hardware so we can save one ioread32() by
+ * filling the read-only bits with zeroes.
+ */
+ iowrite32(cons, ring_desc_base(ring) + 8);
+}
+
+static void ring_iowrite_prod(struct tb_ring *ring, u16 prod)
+{
+ /* See ring_iowrite_cons() above for explanation */
+ iowrite32(prod << 16, ring_desc_base(ring) + 8);
 }
 
 static void ring_iowrite32desc(struct tb_ring *ring, u32 value, u32 offset)
@@ -196,7 +207,10 @@ static void ring_write_descriptors(struct tb_ring *ring)
  descriptor->sof = frame->sof;
  }
  ring->head = (ring->head + 1) % ring->size;
- ring_iowrite16desc(ring, ring->head, ring->is_tx ? 10 : 8);
+ if (ring->is_tx)
+ ring_iowrite_prod(ring, ring->head);
+ else
+ ring_iowrite_cons(ring, ring->head);
  }
 }
 
@@ -661,7 +675,7 @@ void tb_ring_stop(struct tb_ring *ring)
 
  ring_iowrite32options(ring, 0, 0);
  ring_iowrite64desc(ring, 0, 0);
- ring_iowrite16desc(ring, 0, ring->is_tx ? 10 : 8);
+ ring_iowrite32desc(ring, 0, 8);
  ring_iowrite32desc(ring, 0, 12);
  ring->head = 0;
  ring->tail = 0;
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 13/17][SRU][OEM-OSP1-B] thunderbolt: Do not fail adding switch if some port is not implemented

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

There are two ways to mark a port as unimplemented. Typical way is to
return port type as TB_TYPE_INACTIVE when its config space is read.
Alternatively if the port is not physically present (such as ports 10
and 11 in ICL) reading from port config space returns
TB_CFG_ERROR_INVALID_CONFIG_SPACE instead. Currently the driver bails
out from adding the switch if it receives any error during port
inititialization which is wrong.

Handle this properly and just leave the port as TB_TYPE_INACTIVE before
continuing to the next port.

This also allows us to get rid of special casing for Light Ridge port 5
in eeprom.c.

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
Tested-by: Mario Limonciello <[hidden email]>
(cherry picked from commit d94dcbb10183f3b384c84e65724d2b753aa53c4d)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/ctl.c    | 23 +++++++++++++++++++----
 drivers/thunderbolt/eeprom.c |  4 ----
 drivers/thunderbolt/switch.c |  8 +++++++-
 3 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c
index 73b386de4d15..d1d0c1dc8cb2 100644
--- a/drivers/thunderbolt/ctl.c
+++ b/drivers/thunderbolt/ctl.c
@@ -930,6 +930,23 @@ struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer,
  return res;
 }
 
+static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space,
+    const struct tb_cfg_result *res)
+{
+ /*
+ * For unimplemented ports access to port config space may return
+ * TB_CFG_ERROR_INVALID_CONFIG_SPACE (alternatively their type is
+ * set to TB_TYPE_INACTIVE). In the former case return -ENODEV so
+ * that the caller can mark the port as disabled.
+ */
+ if (space == TB_CFG_PORT &&
+    res->tb_error == TB_CFG_ERROR_INVALID_CONFIG_SPACE)
+ return -ENODEV;
+
+ tb_cfg_print_error(ctl, res);
+ return -EIO;
+}
+
 int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,
  enum tb_cfg_space space, u32 offset, u32 length)
 {
@@ -942,8 +959,7 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,
 
  case 1:
  /* Thunderbolt error, tb_error holds the actual number */
- tb_cfg_print_error(ctl, &res);
- return -EIO;
+ return tb_cfg_get_error(ctl, space, &res);
 
  case -ETIMEDOUT:
  tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n",
@@ -969,8 +985,7 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,
 
  case 1:
  /* Thunderbolt error, tb_error holds the actual number */
- tb_cfg_print_error(ctl, &res);
- return -EIO;
+ return tb_cfg_get_error(ctl, space, &res);
 
  case -ETIMEDOUT:
  tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n",
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 81e8ac4c5805..3b2fb4aafd92 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -525,10 +525,6 @@ int tb_drom_read(struct tb_switch *sw)
  sw->ports[3].dual_link_port = &sw->ports[4];
  sw->ports[4].dual_link_port = &sw->ports[3];
 
- /* Port 5 is inaccessible on this gen 1 controller */
- if (sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE)
- sw->ports[5].disabled = true;
-
  return 0;
  }
 
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index e81eb3751678..3473f235fe4d 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -575,8 +575,14 @@ static int tb_init_port(struct tb_port *port)
  int cap;
 
  res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
- if (res)
+ if (res) {
+ if (res == -ENODEV) {
+ tb_dbg(port->sw->tb, " Port %d: not implemented\n",
+       port->port);
+ return 0;
+ }
  return res;
+ }
 
  /* Port 0 is the switch itself and has no PHY. */
  if (port->config.type == TB_TYPE_PORT && port->port != 0) {
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 14/17][SRU][OEM-OSP1-B] thunderbolt: Hide switch attributes that are not set

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

Thunderbolt host routers may not always contain DROM that includes
device identification information. This is mostly needed for Ice Lake
systems but some Falcon Ridge controllers on PCs also do not have DROM.

In that case hide the identification attributes.

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
Tested-by: Mario Limonciello <[hidden email]>
(cherry picked from commit 58f414fa435cf728a82f435bac4781da86afb623)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/switch.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 3473f235fe4d..a73170419bad 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1159,7 +1159,19 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
  struct device *dev = container_of(kobj, struct device, kobj);
  struct tb_switch *sw = tb_to_switch(dev);
 
- if (attr == &dev_attr_key.attr) {
+ if (attr == &dev_attr_device.attr) {
+ if (!sw->device)
+ return 0;
+ } else if (attr == &dev_attr_device_name.attr) {
+ if (!sw->device_name)
+ return 0;
+ } else if (attr == &dev_attr_vendor.attr)  {
+ if (!sw->vendor)
+ return 0;
+ } else if (attr == &dev_attr_vendor_name.attr)  {
+ if (!sw->vendor_name)
+ return 0;
+ } else if (attr == &dev_attr_key.attr) {
  if (tb_route(sw) &&
     sw->tb->security_level == TB_SECURITY_SECURE &&
     sw->security_level == TB_SECURITY_SECURE)
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 15/17][SRU][OEM-OSP1-B] thunderbolt: Expose active parts of NVM even if upgrade is not supported

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

Ice Lake Thunderbolt controller NVM firmware is part of the BIOS image
which means it is not writable through the DMA port anymore. However, we
can still read it so we can keep nvm_version and active parts of NVM.
This way users still can find out the active NVM version and other
potentially useful information directly from Linux.

Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
Tested-by: Mario Limonciello <[hidden email]>
(cherry picked from commit 3f415e5ee18b0097755afc3ac3a5640b196a239e)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/switch.c | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index a73170419bad..5b05dd89e364 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -355,12 +355,14 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
  nvm->active = nvm_dev;
  }
 
- 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;
+ 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;
  }
- nvm->non_active = nvm_dev;
 
  sw->nvm = nvm;
  return 0;
@@ -389,7 +391,8 @@ static void tb_switch_nvm_remove(struct tb_switch *sw)
  if (!nvm->authenticating)
  nvm_clear_auth_status(sw);
 
- nvmem_unregister(nvm->non_active);
+ if (nvm->non_active)
+ nvmem_unregister(nvm->non_active);
  if (nvm->active)
  nvmem_unregister(nvm->active);
  ida_simple_remove(&nvm_ida, nvm->id);
@@ -1177,8 +1180,11 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
     sw->security_level == TB_SECURITY_SECURE)
  return attr->mode;
  return 0;
- } else if (attr == &dev_attr_nvm_authenticate.attr ||
-   attr == &dev_attr_nvm_version.attr) {
+ } else if (attr == &dev_attr_nvm_authenticate.attr) {
+ if (sw->dma_port && !sw->no_nvm_upgrade)
+ return attr->mode;
+ return 0;
+ } else if (attr == &dev_attr_nvm_version.attr) {
  if (sw->dma_port)
  return attr->mode;
  return 0;
@@ -1504,13 +1510,17 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
  break;
  }
 
- if (sw->no_nvm_upgrade)
+ /* Root switch DMA port requires running firmware */
+ if (!tb_route(sw) && sw->config.enabled)
  return 0;
 
  sw->dma_port = dma_port_alloc(sw);
  if (!sw->dma_port)
  return 0;
 
+ if (sw->no_nvm_upgrade)
+ return 0;
+
  /*
  * Check status of the previous flash authentication. If there
  * is one we need to power cycle the switch in any case to make
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 16/17][SRU][OEM-OSP1-B] thunderbolt: Add support for Intel Ice Lake

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

The Thunderbolt controller is integrated into the Ice Lake CPU itself
and requires special flows to power it on and off using force power bit
in NHI VSEC registers. Runtime PM (RTD3) and Sx flows also differ from
the discrete solutions. Now the firmware notifies the driver whether
RTD3 entry or exit are possible. The driver is responsible of sending
Go2Sx command through link controller mailbox when system enters Sx
states (suspend-to-mem/disk). Rest of the ICM firwmare flows follow
Titan Ridge.

Signed-off-by: Raanan Avargil <[hidden email]>
Signed-off-by: Mika Westerberg <[hidden email]>
Reviewed-by: Yehezkel Bernat <[hidden email]>
Tested-by: Mario Limonciello <[hidden email]>
(cherry picked from commit 3cdb9446a117d5d63af823bde6fe6babc312e77b)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/thunderbolt/Makefile   |   2 +-
 drivers/thunderbolt/icm.c      | 176 ++++++++++++++++++++++++++++++--
 drivers/thunderbolt/nhi.c      | 112 +++++++++++++++++++--
 drivers/thunderbolt/nhi.h      |  22 ++++
 drivers/thunderbolt/nhi_ops.c  | 179 +++++++++++++++++++++++++++++++++
 drivers/thunderbolt/nhi_regs.h |  37 +++++++
 drivers/thunderbolt/switch.c   |   2 +
 drivers/thunderbolt/tb_msgs.h  |  16 ++-
 include/linux/thunderbolt.h    |   2 +
 9 files changed, 526 insertions(+), 22 deletions(-)
 create mode 100644 drivers/thunderbolt/nhi_ops.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 833bdee3cec7..9f6152ded705 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.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
diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index cdc1dfffcdb7..f21a916b31fe 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -57,15 +57,18 @@
  * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
  * @rpm: Does the controller support runtime PM (RTD3)
  * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller
+ * @veto: Is RTD3 veto in effect
  * @is_supported: Checks if we can support ICM on this controller
  * @get_mode: Read and return the ICM firmware mode (optional)
  * @get_route: Find a route string for given switch
  * @save_devices: Ask ICM to save devices to ACL when suspending (optional)
  * @driver_ready: Send driver ready message to ICM
+ * @set_uuid: Set UUID for the root switch (optional)
  * @device_connected: Handle device connected ICM message
  * @device_disconnected: Handle device disconnected ICM message
  * @xdomain_connected - Handle XDomain connected ICM message
  * @xdomain_disconnected - Handle XDomain disconnected ICM message
+ * @rtd3_veto: Handle RTD3 veto notification ICM message
  */
 struct icm {
  struct mutex request_lock;
@@ -76,6 +79,7 @@ struct icm {
  bool safe_mode;
  bool rpm;
  bool can_upgrade_nvm;
+ bool veto;
  bool (*is_supported)(struct tb *tb);
  int (*get_mode)(struct tb *tb);
  int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
@@ -83,6 +87,7 @@ struct icm {
  int (*driver_ready)(struct tb *tb,
     enum tb_security_level *security_level,
     size_t *nboot_acl, bool *rpm);
+ void (*set_uuid)(struct tb *tb);
  void (*device_connected)(struct tb *tb,
  const struct icm_pkg_header *hdr);
  void (*device_disconnected)(struct tb *tb,
@@ -91,6 +96,7 @@ struct icm {
   const struct icm_pkg_header *hdr);
  void (*xdomain_disconnected)(struct tb *tb,
      const struct icm_pkg_header *hdr);
+ void (*rtd3_veto)(struct tb *tb, const struct icm_pkg_header *hdr);
 };
 
 struct icm_notification {
@@ -236,6 +242,43 @@ static int icm_request(struct tb *tb, const void *request, size_t request_size,
  return -ETIMEDOUT;
 }
 
+/*
+ * If rescan is queued to run (we are resuming), postpone it to give the
+ * firmware some more time to send device connected notifications for next
+ * devices in the chain.
+ */
+static void icm_postpone_rescan(struct tb *tb)
+{
+ struct icm *icm = tb_priv(tb);
+
+ if (delayed_work_pending(&icm->rescan_work))
+ mod_delayed_work(tb->wq, &icm->rescan_work,
+ msecs_to_jiffies(500));
+}
+
+static void icm_veto_begin(struct tb *tb)
+{
+ struct icm *icm = tb_priv(tb);
+
+ if (!icm->veto) {
+ icm->veto = true;
+ /* Keep the domain powered while veto is in effect */
+ pm_runtime_get(&tb->dev);
+ }
+}
+
+static void icm_veto_end(struct tb *tb)
+{
+ struct icm *icm = tb_priv(tb);
+
+ if (icm->veto) {
+ icm->veto = false;
+ /* Allow the domain suspend now */
+ pm_runtime_mark_last_busy(&tb->dev);
+ pm_runtime_put_autosuspend(&tb->dev);
+ }
+}
+
 static bool icm_fr_is_supported(struct tb *tb)
 {
  return !x86_apple_machine;
@@ -459,14 +502,16 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
  return 0;
 }
 
-static void add_switch(struct tb_switch *parent_sw, u64 route,
-       const uuid_t *uuid, const u8 *ep_name,
-       size_t ep_name_size, u8 connection_id, u8 connection_key,
-       u8 link, u8 depth, enum tb_security_level security_level,
-       bool authorized, bool boot)
+static struct tb_switch *add_switch(struct tb_switch *parent_sw, u64 route,
+    const uuid_t *uuid, const u8 *ep_name,
+    size_t ep_name_size, u8 connection_id,
+    u8 connection_key, u8 link, u8 depth,
+    enum tb_security_level security_level,
+    bool authorized, bool boot)
 {
  const struct intel_vss *vss;
  struct tb_switch *sw;
+ int ret;
 
  pm_runtime_get_sync(&parent_sw->dev);
 
@@ -496,14 +541,18 @@ static void add_switch(struct tb_switch *parent_sw, u64 route,
  tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
  tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);
 
- if (tb_switch_add(sw)) {
+ ret = tb_switch_add(sw);
+ if (ret) {
  tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
  tb_switch_put(sw);
+ sw = ERR_PTR(ret);
  }
 
 out:
  pm_runtime_mark_last_busy(&parent_sw->dev);
  pm_runtime_put_autosuspend(&parent_sw->dev);
+
+ return sw;
 }
 
 static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
@@ -592,6 +641,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
  u64 route;
  int ret;
 
+ icm_postpone_rescan(tb);
+
  link = pkg->link_info & ICM_LINK_INFO_LINK_MASK;
  depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
  ICM_LINK_INFO_DEPTH_SHIFT;
@@ -1017,7 +1068,8 @@ static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
 }
 
 static void
-icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+__icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
+  bool force_rtd3)
 {
  const struct icm_tr_event_device_connected *pkg =
  (const struct icm_tr_event_device_connected *)hdr;
@@ -1027,6 +1079,8 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
  bool authorized, boot;
  u64 route;
 
+ icm_postpone_rescan(tb);
+
  /*
  * Currently we don't use the QoS information coming with the
  * device connected message so simply just ignore that extra
@@ -1082,13 +1136,21 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
  return;
  }
 
- add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name,
-   sizeof(pkg->ep_name), pkg->connection_id,
-   0, 0, 0, security_level, authorized, boot);
+ sw = add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name,
+ sizeof(pkg->ep_name), pkg->connection_id, 0, 0, 0,
+ security_level, authorized, boot);
+ if (!IS_ERR(sw) && force_rtd3)
+ sw->rpm = true;
 
  tb_switch_put(parent_sw);
 }
 
+static void
+icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ __icm_tr_device_connected(tb, hdr, false);
+}
+
 static void
 icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
 {
@@ -1392,6 +1454,61 @@ static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids,
  return 0;
 }
 
+static int
+icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+    size_t *nboot_acl, bool *rpm)
+{
+ struct icm_tr_pkg_driver_ready_response reply;
+ struct icm_pkg_driver_ready request = {
+ .hdr.code = ICM_DRIVER_READY,
+ };
+ int ret;
+
+ memset(&reply, 0, sizeof(reply));
+ ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+  1, 20000);
+ if (ret)
+ return ret;
+
+ /* Ice Lake always supports RTD3 */
+ if (rpm)
+ *rpm = true;
+
+ return 0;
+}
+
+static void icm_icl_set_uuid(struct tb *tb)
+{
+ struct tb_nhi *nhi = tb->nhi;
+ u32 uuid[4];
+
+ pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]);
+ pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]);
+ uuid[2] = 0xffffffff;
+ uuid[3] = 0xffffffff;
+
+ tb->root_switch->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
+}
+
+static void
+icm_icl_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ __icm_tr_device_connected(tb, hdr, true);
+}
+
+static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+ const struct icm_icl_event_rtd3_veto *pkg =
+ (const struct icm_icl_event_rtd3_veto *)hdr;
+
+ tb_dbg(tb, "ICM rtd3 veto=0x%08x\n", pkg->veto_reason);
+
+ if (pkg->veto_reason)
+ icm_veto_begin(tb);
+ else
+ icm_veto_end(tb);
+}
+
 static void icm_handle_notification(struct work_struct *work)
 {
  struct icm_notification *n = container_of(work, typeof(*n), work);
@@ -1419,6 +1536,9 @@ static void icm_handle_notification(struct work_struct *work)
  case ICM_EVENT_XDOMAIN_DISCONNECTED:
  icm->xdomain_disconnected(tb, n->pkg);
  break;
+ case ICM_EVENT_RTD3_VETO:
+ icm->rtd3_veto(tb, n->pkg);
+ break;
  }
  }
 
@@ -1810,6 +1930,13 @@ static void icm_complete(struct tb *tb)
  if (tb->nhi->going_away)
  return;
 
+ /*
+ * If RTD3 was vetoed before we entered system suspend allow it
+ * again now before driver ready is sent. Firmware sends a new RTD3
+ * veto if it is still the case after we have sent it driver ready
+ * command.
+ */
+ icm_veto_end(tb);
  icm_unplug_children(tb->root_switch);
 
  /*
@@ -1857,6 +1984,9 @@ static int icm_start(struct tb *tb)
  tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm;
  tb->root_switch->rpm = icm->rpm;
 
+ if (icm->set_uuid)
+ icm->set_uuid(tb);
+
  ret = tb_switch_add(tb->root_switch);
  if (ret) {
  tb_switch_put(tb->root_switch);
@@ -1937,6 +2067,19 @@ static const struct tb_cm_ops icm_tr_ops = {
  .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
 };
 
+/* Ice Lake */
+static const struct tb_cm_ops icm_icl_ops = {
+ .driver_ready = icm_driver_ready,
+ .start = icm_start,
+ .stop = icm_stop,
+ .complete = icm_complete,
+ .runtime_suspend = icm_runtime_suspend,
+ .runtime_resume = icm_runtime_resume,
+ .handle_event = icm_handle_event,
+ .approve_xdomain_paths = icm_tr_approve_xdomain_paths,
+ .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
+};
+
 struct tb *icm_probe(struct tb_nhi *nhi)
 {
  struct icm *icm;
@@ -2003,6 +2146,19 @@ struct tb *icm_probe(struct tb_nhi *nhi)
  icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
  tb->cm_ops = &icm_tr_ops;
  break;
+
+ case PCI_DEVICE_ID_INTEL_ICL_NHI0:
+ case PCI_DEVICE_ID_INTEL_ICL_NHI1:
+ icm->is_supported = icm_ar_is_supported;
+ icm->driver_ready = icm_icl_driver_ready;
+ icm->set_uuid = icm_icl_set_uuid;
+ icm->device_connected = icm_icl_device_connected;
+ icm->device_disconnected = icm_tr_device_disconnected;
+ icm->xdomain_connected = icm_tr_xdomain_connected;
+ icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
+ icm->rtd3_veto = icm_icl_rtd3_veto;
+ tb->cm_ops = &icm_icl_ops;
+ break;
  }
 
  if (!icm->is_supported || !icm->is_supported(tb)) {
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 32713c9844d2..4bd854e53a3f 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -15,6 +15,7 @@
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/property.h>
 
 #include "nhi.h"
 #include "nhi_regs.h"
@@ -858,12 +859,52 @@ static irqreturn_t nhi_msi(int irq, void *data)
  return IRQ_HANDLED;
 }
 
-static int nhi_suspend_noirq(struct device *dev)
+static int __nhi_suspend_noirq(struct device *dev, bool wakeup)
 {
  struct pci_dev *pdev = to_pci_dev(dev);
  struct tb *tb = pci_get_drvdata(pdev);
+ struct tb_nhi *nhi = tb->nhi;
+ int ret;
+
+ ret = tb_domain_suspend_noirq(tb);
+ if (ret)
+ return ret;
+
+ if (nhi->ops && nhi->ops->suspend_noirq) {
+ ret = nhi->ops->suspend_noirq(tb->nhi, wakeup);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nhi_suspend_noirq(struct device *dev)
+{
+ return __nhi_suspend_noirq(dev, device_may_wakeup(dev));
+}
+
+static bool nhi_wake_supported(struct pci_dev *pdev)
+{
+ u8 val;
+
+ /*
+ * If power rails are sustainable for wakeup from S4 this
+ * property is set by the BIOS.
+ */
+ if (device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val))
+ return !!val;
+
+ return true;
+}
+
+static int nhi_poweroff_noirq(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ bool wakeup;
 
- return tb_domain_suspend_noirq(tb);
+ wakeup = device_may_wakeup(dev) && nhi_wake_supported(pdev);
+ return __nhi_suspend_noirq(dev, wakeup);
 }
 
 static void nhi_enable_int_throttling(struct tb_nhi *nhi)
@@ -886,16 +927,24 @@ static int nhi_resume_noirq(struct device *dev)
 {
  struct pci_dev *pdev = to_pci_dev(dev);
  struct tb *tb = pci_get_drvdata(pdev);
+ struct tb_nhi *nhi = tb->nhi;
+ int ret;
 
  /*
  * Check that the device is still there. It may be that the user
  * unplugged last device which causes the host controller to go
  * away on PCs.
  */
- if (!pci_device_is_present(pdev))
- tb->nhi->going_away = true;
- else
+ if (!pci_device_is_present(pdev)) {
+ nhi->going_away = true;
+ } else {
+ if (nhi->ops && nhi->ops->resume_noirq) {
+ ret = nhi->ops->resume_noirq(nhi);
+ if (ret)
+ return ret;
+ }
  nhi_enable_int_throttling(tb->nhi);
+ }
 
  return tb_domain_resume_noirq(tb);
 }
@@ -928,16 +977,35 @@ static int nhi_runtime_suspend(struct device *dev)
 {
  struct pci_dev *pdev = to_pci_dev(dev);
  struct tb *tb = pci_get_drvdata(pdev);
+ struct tb_nhi *nhi = tb->nhi;
+ int ret;
+
+ ret = tb_domain_runtime_suspend(tb);
+ if (ret)
+ return ret;
 
- return tb_domain_runtime_suspend(tb);
+ if (nhi->ops && nhi->ops->runtime_suspend) {
+ ret = nhi->ops->runtime_suspend(tb->nhi);
+ if (ret)
+ return ret;
+ }
+ return 0;
 }
 
 static int nhi_runtime_resume(struct device *dev)
 {
  struct pci_dev *pdev = to_pci_dev(dev);
  struct tb *tb = pci_get_drvdata(pdev);
+ struct tb_nhi *nhi = tb->nhi;
+ int ret;
+
+ if (nhi->ops && nhi->ops->runtime_resume) {
+ ret = nhi->ops->runtime_resume(nhi);
+ if (ret)
+ return ret;
+ }
 
- nhi_enable_int_throttling(tb->nhi);
+ nhi_enable_int_throttling(nhi);
  return tb_domain_runtime_resume(tb);
 }
 
@@ -965,6 +1033,9 @@ static void nhi_shutdown(struct tb_nhi *nhi)
  flush_work(&nhi->interrupt_work);
  }
  ida_destroy(&nhi->msix_ida);
+
+ if (nhi->ops && nhi->ops->shutdown)
+ nhi->ops->shutdown(nhi);
 }
 
 static int nhi_init_msi(struct tb_nhi *nhi)
@@ -1009,12 +1080,27 @@ static int nhi_init_msi(struct tb_nhi *nhi)
  return 0;
 }
 
+static bool nhi_imr_valid(struct pci_dev *pdev)
+{
+ u8 val;
+
+ if (!device_property_read_u8(&pdev->dev, "IMR_VALID", &val))
+ return !!val;
+
+ return true;
+}
+
 static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
  struct tb_nhi *nhi;
  struct tb *tb;
  int res;
 
+ if (!nhi_imr_valid(pdev)) {
+ dev_warn(&pdev->dev, "firmware image not valid, aborting\n");
+ return -ENODEV;
+ }
+
  res = pcim_enable_device(pdev);
  if (res) {
  dev_err(&pdev->dev, "cannot enable PCI device, aborting\n");
@@ -1032,6 +1118,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
  return -ENOMEM;
 
  nhi->pdev = pdev;
+ nhi->ops = (const struct tb_nhi_ops *)id->driver_data;
  /* cannot fail - table is allocated bin pcim_iomap_regions */
  nhi->iobase = pcim_iomap_table(pdev)[0];
  nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
@@ -1064,6 +1151,12 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
  pci_set_master(pdev);
 
+ if (nhi->ops && nhi->ops->init) {
+ res = nhi->ops->init(nhi);
+ if (res)
+ return res;
+ }
+
  tb = icm_probe(nhi);
  if (!tb)
  tb = tb_probe(nhi);
@@ -1124,6 +1217,7 @@ static const struct dev_pm_ops nhi_pm_ops = {
  .restore_noirq = nhi_resume_noirq,
  .suspend = nhi_suspend,
  .freeze = nhi_suspend,
+ .poweroff_noirq = nhi_poweroff_noirq,
  .poweroff = nhi_suspend,
  .complete = nhi_complete,
  .runtime_suspend = nhi_runtime_suspend,
@@ -1171,6 +1265,10 @@ static struct pci_device_id nhi_ids[] = {
  { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) },
  { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) },
  { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI0),
+  .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1),
+  .driver_data = (kernel_ulong_t)&icl_nhi_ops },
 
  { 0,}
 };
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index 1b5d47ecd3ed..b7b973949f8e 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -30,6 +30,26 @@ enum nhi_mailbox_cmd {
 int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data);
 enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi);
 
+/**
+ * struct tb_nhi_ops - NHI specific optional operations
+ * @init: NHI specific initialization
+ * @suspend_noirq: NHI specific suspend_noirq hook
+ * @resume_noirq: NHI specific resume_noirq hook
+ * @runtime_suspend: NHI specific runtime_suspend hook
+ * @runtime_resume: NHI specific runtime_resume hook
+ * @shutdown: NHI specific shutdown
+ */
+struct tb_nhi_ops {
+ int (*init)(struct tb_nhi *nhi);
+ int (*suspend_noirq)(struct tb_nhi *nhi, bool wakeup);
+ int (*resume_noirq)(struct tb_nhi *nhi);
+ int (*runtime_suspend)(struct tb_nhi *nhi);
+ int (*runtime_resume)(struct tb_nhi *nhi);
+ void (*shutdown)(struct tb_nhi *nhi);
+};
+
+extern const struct tb_nhi_ops icl_nhi_ops;
+
 /*
  * PCI IDs used in this driver from Win Ridge forward. There is no
  * need for the PCI quirk anymore as we will use ICM also on Apple
@@ -51,5 +71,7 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi);
 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea
 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb
 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef
+#define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d
+#define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17
 
 #endif
diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c
new file mode 100644
index 000000000000..61cd09cef943
--- /dev/null
+++ b/drivers/thunderbolt/nhi_ops.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NHI specific operations
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Author: Mika Westerberg <[hidden email]>
+ */
+
+#include <linux/delay.h>
+#include <linux/suspend.h>
+
+#include "nhi.h"
+#include "nhi_regs.h"
+#include "tb.h"
+
+/* Ice Lake specific NHI operations */
+
+#define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */
+
+static int check_for_device(struct device *dev, void *data)
+{
+ return tb_is_switch(dev);
+}
+
+static bool icl_nhi_is_device_connected(struct tb_nhi *nhi)
+{
+ struct tb *tb = pci_get_drvdata(nhi->pdev);
+ int ret;
+
+ ret = device_for_each_child(&tb->root_switch->dev, NULL,
+    check_for_device);
+ return ret > 0;
+}
+
+static int icl_nhi_force_power(struct tb_nhi *nhi, bool power)
+{
+ u32 vs_cap;
+
+ /*
+ * The Thunderbolt host controller is present always in Ice Lake
+ * but the firmware may not be loaded and running (depending
+ * whether there is device connected and so on). Each time the
+ * controller is used we need to "Force Power" it first and wait
+ * for the firmware to indicate it is up and running. This "Force
+ * Power" is really not about actually powering on/off the
+ * controller so it is accessible even if "Force Power" is off.
+ *
+ * The actual power management happens inside shared ACPI power
+ * resources using standard ACPI methods.
+ */
+ pci_read_config_dword(nhi->pdev, VS_CAP_22, &vs_cap);
+ if (power) {
+ vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK;
+ vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT;
+ vs_cap |= VS_CAP_22_FORCE_POWER;
+ } else {
+ vs_cap &= ~VS_CAP_22_FORCE_POWER;
+ }
+ pci_write_config_dword(nhi->pdev, VS_CAP_22, vs_cap);
+
+ if (power) {
+ unsigned int retries = 10;
+ u32 val;
+
+ /* Wait until the firmware tells it is up and running */
+ do {
+ pci_read_config_dword(nhi->pdev, VS_CAP_9, &val);
+ if (val & VS_CAP_9_FW_READY)
+ return 0;
+ msleep(250);
+ } while (--retries);
+
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd)
+{
+ u32 data;
+
+ pci_read_config_dword(nhi->pdev, VS_CAP_19, &data);
+ data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK;
+ pci_write_config_dword(nhi->pdev, VS_CAP_19, data | VS_CAP_19_VALID);
+}
+
+static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout)
+{
+ unsigned long end;
+ u32 data;
+
+ if (!timeout)
+ goto clear;
+
+ end = jiffies + msecs_to_jiffies(timeout);
+ do {
+ pci_read_config_dword(nhi->pdev, VS_CAP_18, &data);
+ if (data & VS_CAP_18_DONE)
+ goto clear;
+ msleep(100);
+ } while (time_before(jiffies, end));
+
+ return -ETIMEDOUT;
+
+clear:
+ /* Clear the valid bit */
+ pci_write_config_dword(nhi->pdev, VS_CAP_19, 0);
+ return 0;
+}
+
+static void icl_nhi_set_ltr(struct tb_nhi *nhi)
+{
+ u32 max_ltr, ltr;
+
+ pci_read_config_dword(nhi->pdev, VS_CAP_16, &max_ltr);
+ max_ltr &= 0xffff;
+ /* Program the same value for both snoop and no-snoop */
+ ltr = max_ltr << 16 | max_ltr;
+ pci_write_config_dword(nhi->pdev, VS_CAP_15, ltr);
+}
+
+static int icl_nhi_suspend(struct tb_nhi *nhi)
+{
+ int ret;
+
+ if (icl_nhi_is_device_connected(nhi))
+ return 0;
+
+ /*
+ * If there is no device connected we need to perform both: a
+ * handshake through LC mailbox and force power down before
+ * entering D3.
+ */
+ icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET);
+ ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return icl_nhi_force_power(nhi, false);
+}
+
+static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup)
+{
+ enum icl_lc_mailbox_cmd cmd;
+
+ if (!pm_suspend_via_firmware())
+ return icl_nhi_suspend(nhi);
+
+ cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE;
+ icl_nhi_lc_mailbox_cmd(nhi, cmd);
+ return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT);
+}
+
+static int icl_nhi_resume(struct tb_nhi *nhi)
+{
+ int ret;
+
+ ret = icl_nhi_force_power(nhi, true);
+ if (ret)
+ return ret;
+
+ icl_nhi_set_ltr(nhi);
+ return 0;
+}
+
+static void icl_nhi_shutdown(struct tb_nhi *nhi)
+{
+ icl_nhi_force_power(nhi, false);
+}
+
+const struct tb_nhi_ops icl_nhi_ops = {
+ .init = icl_nhi_resume,
+ .suspend_noirq = icl_nhi_suspend_noirq,
+ .resume_noirq = icl_nhi_resume,
+ .runtime_suspend = icl_nhi_suspend,
+ .runtime_resume = icl_nhi_resume,
+ .shutdown = icl_nhi_shutdown,
+};
diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h
index a60bd98c1d04..0d4970dcef84 100644
--- a/drivers/thunderbolt/nhi_regs.h
+++ b/drivers/thunderbolt/nhi_regs.h
@@ -124,4 +124,41 @@ struct ring_desc {
 #define REG_FW_STS_ICM_EN_INVERT BIT(1)
 #define REG_FW_STS_ICM_EN BIT(0)
 
+/* ICL NHI VSEC registers */
+
+/* FW ready */
+#define VS_CAP_9 0xc8
+#define VS_CAP_9_FW_READY BIT(31)
+/* UUID */
+#define VS_CAP_10 0xcc
+#define VS_CAP_11 0xd0
+/* LTR */
+#define VS_CAP_15 0xe0
+#define VS_CAP_16 0xe4
+/* TBT2PCIe */
+#define VS_CAP_18 0xec
+#define VS_CAP_18_DONE BIT(0)
+/* PCIe2TBT */
+#define VS_CAP_19 0xf0
+#define VS_CAP_19_VALID BIT(0)
+#define VS_CAP_19_CMD_SHIFT 1
+#define VS_CAP_19_CMD_MASK GENMASK(7, 1)
+/* Force power */
+#define VS_CAP_22 0xfc
+#define VS_CAP_22_FORCE_POWER BIT(1)
+#define VS_CAP_22_DMA_DELAY_MASK GENMASK(31, 24)
+#define VS_CAP_22_DMA_DELAY_SHIFT 24
+
+/**
+ * enum icl_lc_mailbox_cmd - ICL specific LC mailbox commands
+ * @ICL_LC_GO2SX: Ask LC to enter Sx without wake
+ * @ICL_LC_GO2SX_NO_WAKE: Ask LC to enter Sx with wake
+ * @ICL_LC_PREPARE_FOR_RESET: Prepare LC for reset
+ */
+enum icl_lc_mailbox_cmd {
+ ICL_LC_GO2SX = 0x02,
+ ICL_LC_GO2SX_NO_WAKE = 0x03,
+ ICL_LC_PREPARE_FOR_RESET = 0x21,
+};
+
 #endif
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 5b05dd89e364..b3d1c55ede32 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1281,6 +1281,8 @@ static int tb_switch_get_generation(struct tb_switch *sw)
  case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE:
  case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE:
  case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE:
+ case PCI_DEVICE_ID_INTEL_ICL_NHI0:
+ case PCI_DEVICE_ID_INTEL_ICL_NHI1:
  return 3;
 
  default:
diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h
index 02c84aa3d018..f3d923ec6c0c 100644
--- a/drivers/thunderbolt/tb_msgs.h
+++ b/drivers/thunderbolt/tb_msgs.h
@@ -104,10 +104,11 @@ enum icm_pkg_code {
 };
 
 enum icm_event_code {
- ICM_EVENT_DEVICE_CONNECTED = 3,
- ICM_EVENT_DEVICE_DISCONNECTED = 4,
- ICM_EVENT_XDOMAIN_CONNECTED = 6,
- ICM_EVENT_XDOMAIN_DISCONNECTED = 7,
+ ICM_EVENT_DEVICE_CONNECTED = 0x3,
+ ICM_EVENT_DEVICE_DISCONNECTED = 0x4,
+ ICM_EVENT_XDOMAIN_CONNECTED = 0x6,
+ ICM_EVENT_XDOMAIN_DISCONNECTED = 0x7,
+ ICM_EVENT_RTD3_VETO = 0xa,
 };
 
 struct icm_pkg_header {
@@ -463,6 +464,13 @@ struct icm_tr_pkg_disconnect_xdomain_response {
  uuid_t remote_uuid;
 };
 
+/* Ice Lake messages */
+
+struct icm_icl_event_rtd3_veto {
+ struct icm_pkg_header hdr;
+ u32 veto_reason;
+};
+
 /* XDomain messages */
 
 struct tb_xdomain_header {
diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
index bf6ec83e60ee..043a96f61337 100644
--- a/include/linux/thunderbolt.h
+++ b/include/linux/thunderbolt.h
@@ -421,6 +421,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
  * @lock: Must be held during ring creation/destruction. Is acquired by
  *  interrupt_work when dispatching interrupts to individual rings.
  * @pdev: Pointer to the PCI device
+ * @ops: NHI specific optional ops
  * @iobase: MMIO space of the NHI
  * @tx_rings: All Tx rings available on this host controller
  * @rx_rings: All Rx rings available on this host controller
@@ -434,6 +435,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
 struct tb_nhi {
  spinlock_t lock;
  struct pci_dev *pdev;
+ const struct tb_nhi_ops *ops;
  void __iomem *iobase;
  struct tb_ring **tx_rings;
  struct tb_ring **rx_rings;
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

[PATCH 17/17][SRU][OEM-OSP1-B] ACPI / property: Add two new Thunderbolt property GUIDs to the list

You-Sheng Yang
In reply to this post by You-Sheng Yang
From: Mika Westerberg <[hidden email]>

BugLink: https://bugs.launchpad.net/bugs/1844680

Ice Lake Thunderbolt controller includes two new device property
compatible properties that we need to be able to extract in the driver
so add them to the growing array of GUIDs.

Signed-off-by: Mika Westerberg <[hidden email]>
Acked-by: Rafael J. Wysocki <[hidden email]>
(cherry picked from commit dfda204198848b47bdb98ab83b94dbb7c7692b55)
Signed-off-by: You-Sheng Yang <[hidden email]>
---
 drivers/acpi/property.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index bd533f68b1de..76955a8fa86b 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -42,6 +42,12 @@ static const guid_t prp_guids[] = {
  /* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */
  GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
   0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
+ /* Thunderbolt GUID for IMR_VALID: c44d002f-69f9-4e7d-a904-a7baabdf43f7 */
+ GUID_INIT(0xc44d002f, 0x69f9, 0x4e7d,
+  0xa9, 0x04, 0xa7, 0xba, 0xab, 0xdf, 0x43, 0xf7),
+ /* Thunderbolt GUID for WAKE_SUPPORTED: 6c501103-c189-4296-ba72-9bf5a26ebe5d */
+ GUID_INIT(0x6c501103, 0xc189, 0x4296,
+  0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d),
 };
 
 static const guid_t ads_guid =
--
2.20.1


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

APPLIED Re: [PATCH 00/17][SRU][OEM-OSP1-B] thunderbolt: ICL support

Timo Aaltonen-6
In reply to this post by You-Sheng Yang
On 1.10.2019 15.45, You-Sheng Yang wrote:
> From: You-Sheng Yang (楊有勝) <[hidden email]>
>
> BugLink: https://bugs.launchpad.net/bugs/1844680
>
> [Impact]
> The Thunderbolt controller is integrated into the Ice Lake CPU itself
> and requires special flows to power it on and off using force power bit
> in NHI VSEC registers. Currently this is not supported until v5.4-rc1.

applied to osp1 oem-next, thanks


--
t

--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team