Skip to contentSkip to conversation

Thunderbolt driver for Apple MacBooks

PATCH Superseded Andreas Noever 3 participants 23 messages May 31, 2014 – Jun 1, 2014

Subsystems (3)

v4 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Andreas Noever

author maintainer
#0 lore raw

v4 cover Thunderbolt driver for Apple MacBooks

Hi

This is version 4 of my Thunderbolt driver for Apple hardware (see [1] for v3).

Changes since v3:
- Fix typos and style problems caught by Bjorn.
- Changed the #ifdef CONFIG_ACPI block to cover the whole pci quirk.

Andreas

[1]: https://lkml.org/lkml/2014/5/26/308

Andreas Noever (15):
thunderbolt: Add initial cactus ridge NHI support
thunderbolt: Add control channel interface
thunderbolt: Setup control channel
thunderbolt: Add tb_regs.h
thunderbolt: Initialize root switch and ports
thunderbolt: Add thunderbolt capability handling
thunderbolt: Enable plug events
thunderbolt: Scan for downstream switches
thunderbolt: Handle hotplug events
thunderbolt: Add path setup code.
thunderbolt: Add support for simple pci tunnels
PCI: Add pci_fixup_suspend_late quirk pass
PCI: Suspend/resume quirks for Apple thunderbolt
thunderbolt: Read switch uid from EEPROM
thunderbolt: Add suspend/hibernate support

 drivers/Kconfig                   |   2 +
 drivers/Makefile                  |   1 +
 drivers/pci/pci-driver.c          |  18 +-
 drivers/pci/quirks.c              | 129 +++++++
 drivers/thunderbolt/Kconfig       |  12 +
 drivers/thunderbolt/Makefile      |   3 +
 drivers/thunderbolt/cap.c         | 116 ++++++
 drivers/thunderbolt/ctl.c         | 731 ++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/ctl.h         |  75 ++++
 drivers/thunderbolt/eeprom.c      | 189 ++++++++++
 drivers/thunderbolt/nhi.c         | 672 +++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/nhi.h         | 114 ++++++
 drivers/thunderbolt/nhi_regs.h    | 101 ++++++
 drivers/thunderbolt/path.c        | 215 +++++++++++
 drivers/thunderbolt/switch.c      | 492 +++++++++++++++++++++++++
 drivers/thunderbolt/tb.c          | 431 ++++++++++++++++++++++
 drivers/thunderbolt/tb.h          | 266 ++++++++++++++
 drivers/thunderbolt/tb_regs.h     | 213 +++++++++++
 drivers/thunderbolt/tunnel_pci.c  | 232 ++++++++++++
 drivers/thunderbolt/tunnel_pci.h  |  30 ++
 include/asm-generic/vmlinux.lds.h |   3 +
 include/linux/pci.h               |  12 +-
 22 files changed, 4052 insertions(+), 5 deletions(-)
 create mode 100644 drivers/thunderbolt/Kconfig
 create mode 100644 drivers/thunderbolt/Makefile
 create mode 100644 drivers/thunderbolt/cap.c
 create mode 100644 drivers/thunderbolt/ctl.c
 create mode 100644 drivers/thunderbolt/ctl.h
 create mode 100644 drivers/thunderbolt/eeprom.c
 create mode 100644 drivers/thunderbolt/nhi.c
 create mode 100644 drivers/thunderbolt/nhi.h
 create mode 100644 drivers/thunderbolt/nhi_regs.h
 create mode 100644 drivers/thunderbolt/path.c
 create mode 100644 drivers/thunderbolt/switch.c
 create mode 100644 drivers/thunderbolt/tb.c
 create mode 100644 drivers/thunderbolt/tb.h
 create mode 100644 drivers/thunderbolt/tb_regs.h
 create mode 100644 drivers/thunderbolt/tunnel_pci.c
 create mode 100644 drivers/thunderbolt/tunnel_pci.h
Signature · 1 line
1.9.3
author maintainer
#1 lore raw

PATCH v4 1/15 thunderbolt: Add initial cactus ridge NHI support

Thunderbolt hotplug is supposed to be handled by the firmware. But Apple decided to implement thunderbolt at the operating system level. The firmare only initializes thunderbolt devices that are present at boot time. This driver enables hotplug of thunderbolt of non-chained thunderbolt devices on Apple systems with a cactus ridge controller.

This first patch adds the Kconfig file as well the parts of the driver which talk directly to the hardware (that is pci device setup, interrupt handling and RX/TX ring management).

---
 drivers/Kconfig                |   2 +
 drivers/Makefile               |   1 +
 drivers/thunderbolt/Kconfig    |  12 +
 drivers/thunderbolt/Makefile   |   3 +
 drivers/thunderbolt/nhi.c      | 625 +++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/nhi.h      | 114 ++++++++
 drivers/thunderbolt/nhi_regs.h | 101 +++++++
 7 files changed, 858 insertions(+)
 create mode 100644 drivers/thunderbolt/Kconfig
 create mode 100644 drivers/thunderbolt/Makefile
 create mode 100644 drivers/thunderbolt/nhi.c
 create mode 100644 drivers/thunderbolt/nhi.h
 create mode 100644 drivers/thunderbolt/nhi_regs.h
drivers/Kconfig +2 -0
index 0a0a90f..07e3a73 100644
@@ -174,4 +174,6 @@ source "drivers/powercap/Kconfig"
 
 source "drivers/mcb/Kconfig"
 
+source "drivers/thunderbolt/Kconfig"
+
 endmenu
drivers/Makefile +1 -0
index 7183b6a..ce134d0 100644
@@ -157,3 +157,4 @@ obj-$(CONFIG_NTB)		+= ntb/
 obj-$(CONFIG_FMC)		+= fmc/
 obj-$(CONFIG_POWERCAP)		+= powercap/
 obj-$(CONFIG_MCB)		+= mcb/
+obj-$(CONFIG_THUNDERBOLT)	+= thunderbolt/
drivers/thunderbolt/Kconfig new file +12 -0
new file mode 100644
index 0000000..3a25529
@@ -0,0 +1,12 @@
+menuconfig THUNDERBOLT
+	tristate "Thunderbolt support for Apple devices"
+	default no
+	help
+	  Cactus Ridge Thunderbolt Controller driver
+	  This driver is required if you want to hotplug Thunderbolt devices on
+	  Apple hardware.
+
+	  Device chaining is currently not supported.
+
+	  To compile this driver a module, choose M here. The module will be
+	  called thunderbolt.
drivers/thunderbolt/Makefile new file +3 -0
new file mode 100644
index 0000000..d473ab9
@@ -0,0 +1,3 @@
+obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
+thunderbolt-objs := nhi.o
+
drivers/thunderbolt/nhi.c new file +625 -0

625 lines not on this page · raw message

drivers/thunderbolt/nhi.h new file +114 -0

114 lines not on this page · raw message

drivers/thunderbolt/nhi_regs.h new file +101 -0

101 lines not on this page · raw message

Signature · 1 line
1.9.3
author maintainer
#2 lore raw

PATCH v4 2/15 thunderbolt: Add control channel interface

In reply to Andreas Noever

Thunderbolt devices are configured by reading/writing into their configuration space (similar to pci). This is done by sending packets through the NHI (native host interface) onto the control channel.

This patch handles the low level packet based protocol and exposes higher level operations like tb_cfg_read/tb_cfg_write.

---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/ctl.c    | 731 +++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/ctl.h    |  75 +++++
 3 files changed, 807 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/ctl.c
 create mode 100644 drivers/thunderbolt/ctl.h
drivers/thunderbolt/Makefile +1 -1
index d473ab9..4b21c99 100644
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o
+thunderbolt-objs := nhi.o ctl.o
 
drivers/thunderbolt/ctl.c new file +731 -0

731 lines not on this page · raw message

drivers/thunderbolt/ctl.h new file +75 -0

75 lines not on this page · raw message

Signature · 1 line
1.9.3
author maintainer
#3 lore raw

PATCH v4 3/15 thunderbolt: Setup control channel

In reply to Andreas Noever

Add struct tb which will contain our view of the thunderbolt bus. For now it just contains a pointer to the control channel and a workqueue for hotplug events.

Add thunderbolt_alloc_and_start() and thunderbolt_shutdown_and_free() which are responsible for setup and teardown of struct tb.

---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/nhi.c    |  18 +++++-
 drivers/thunderbolt/tb.c     | 134 +++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |  35 +++++++++++
 4 files changed, 186 insertions(+), 3 deletions(-)
 create mode 100644 drivers/thunderbolt/tb.c
 create mode 100644 drivers/thunderbolt/tb.h
drivers/thunderbolt/Makefile +1 -1
index 4b21c99..1f996bb 100644
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o
+thunderbolt-objs := nhi.o ctl.o tb.o
 
drivers/thunderbolt/nhi.c +16 -2
index d73a744..f3a4ac2 100644
@@ -16,6 +16,7 @@
 
 #include "nhi.h"
 #include "nhi_regs.h"
+#include "tb.h"
 
 #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring")
 
@@ -517,6 +518,7 @@ static void nhi_shutdown(struct tb_nhi *nhi)
 static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct tb_nhi *nhi;
+	struct tb *tb;
 	int res;
 
 	res = pcim_enable_device(pdev);
@@ -575,14 +577,26 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	/* magic value - clock related? */
 	iowrite32(3906250 / 10000, nhi->iobase + 0x38c00);
 
-	pci_set_drvdata(pdev, nhi);
+	dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n");
+	tb = thunderbolt_alloc_and_start(nhi);
+	if (!tb) {
+		/*
+		 * At this point the RX/TX rings might already have been
+		 * activated. Do a proper shutdown.
+		 */
+		nhi_shutdown(nhi);
+		return -EIO;
+	}
+	pci_set_drvdata(pdev, tb);
 
 	return 0;
 }
 
 static void nhi_remove(struct pci_dev *pdev)
 {
-	struct tb_nhi *nhi = pci_get_drvdata(pdev);
+	struct tb *tb = pci_get_drvdata(pdev);
+	struct tb_nhi *nhi = tb->nhi;
+	thunderbolt_shutdown_and_free(tb);
 	nhi_shutdown(nhi);
 }
 
drivers/thunderbolt/tb.c new file +134 -0

134 lines not on this page · raw message

drivers/thunderbolt/tb.h new file +35 -0
new file mode 100644
index 0000000..6055dfd
@@ -0,0 +1,35 @@
+/*
+ * Thunderbolt Cactus Ridge driver - bus logic (NHI independent)
+ *
+ * Copyright (c) 2014 Andreas Noever <[email protected]>
+ */
+
+#ifndef TB_H_
+#define TB_H_
+
+#include "ctl.h"
+
+/**
+ * struct tb - main thunderbolt bus structure
+ */
+struct tb {
+	struct mutex lock;	/*
+				 * Big lock. Must be held when accessing cfg or
+				 * any struct tb_switch / struct tb_port.
+				 */
+	struct tb_nhi *nhi;
+	struct tb_ctl *ctl;
+	struct workqueue_struct *wq; /* ordered workqueue for plug events */
+	bool hotplug_active; /*
+			      * tb_handle_hotplug will stop progressing plug
+			      * events and exit if this is not set (it needs to
+			      * acquire the lock one more time). Used to drain
+			      * wq after cfg has been paused.
+			      */
+
+};
+
+struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi);
+void thunderbolt_shutdown_and_free(struct tb *tb);
+
+#endif
Signature · 1 line
1.9.3
author maintainer
#4 lore raw

PATCH v4 4/15 thunderbolt: Add tb_regs.h

In reply to Andreas Noever

Every thunderbolt device consists (logically) of a switch with multiple ports. Every port contains up to four config regions (HOPS, PORT, SWITCH, COUNTERS) which are used to configure the device.

The tb_regs.h file contains all known registers and capabilities from these config regions.

---
 drivers/thunderbolt/tb.c      |   5 +
 drivers/thunderbolt/tb_regs.h | 213 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 218 insertions(+)
 create mode 100644 drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/tb.c +5 -0
index 6920979..164dea0 100644
@@ -9,6 +9,7 @@
 #include <linux/delay.h>
 
 #include "tb.h"
+#include "tb_regs.h"
 
 /* hotplug handling */
 
@@ -100,6 +101,10 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
 {
 	struct tb *tb;
 
+	BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);
+	BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4);
+	BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4);
+
 	tb = kzalloc(sizeof(*tb), GFP_KERNEL);
 	if (!tb)
 		return NULL;
drivers/thunderbolt/tb_regs.h new file +213 -0

213 lines not on this page · raw message

Signature · 1 line
1.9.3
author maintainer
#5 lore raw

PATCH v4 5/15 thunderbolt: Initialize root switch and ports

In reply to Andreas Noever

This patch adds the structures tb_switch and tb_port as well as code to initialize the root switch.

---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/switch.c | 186 +++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.c     |   8 ++
 drivers/thunderbolt/tb.h     | 138 ++++++++++++++++++++++++++++++++
 4 files changed, 333 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/switch.c
drivers/thunderbolt/Makefile +1 -1
index 1f996bb..4ac18d9 100644
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o
 
drivers/thunderbolt/switch.c new file +186 -0

186 lines not on this page · raw message

drivers/thunderbolt/tb.c +8 -0
index 164dea0..f1b6100 100644
@@ -71,6 +71,10 @@ void thunderbolt_shutdown_and_free(struct tb *tb)
 {
 	mutex_lock(&tb->lock);
 
+	if (tb->root_switch)
+		tb_switch_free(tb->root_switch);
+	tb->root_switch = NULL;
+
 	if (tb->ctl) {
 		tb_ctl_stop(tb->ctl);
 		tb_ctl_free(tb->ctl);
@@ -126,6 +130,10 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
 	 */
 	tb_ctl_start(tb->ctl);
 
+	tb->root_switch = tb_switch_alloc(tb, 0);
+	if (!tb->root_switch)
+		goto err_locked;
+
 	/* Allow tb_handle_hotplug to progress events */
 	tb->hotplug_active = true;
 	mutex_unlock(&tb->lock);
drivers/thunderbolt/tb.h +138 -0

160 lines not on this page · raw message

Signature · 1 line
1.9.3
author maintainer
#6 lore raw

PATCH v4 6/15 thunderbolt: Add thunderbolt capability handling

In reply to Andreas Noever

Thunderbolt config areas contain capability lists similar to those found on pci devices. This patch introduces a tb_find_cap utility method to search for capabilities.

---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/cap.c    | 116 +++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |   2 +
 3 files changed, 119 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/cap.c
drivers/thunderbolt/Makefile +1 -1
index 4ac18d9..617b314 100644
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o
 
drivers/thunderbolt/cap.c new file +116 -0

116 lines not on this page · raw message

drivers/thunderbolt/tb.h +2 -0
index 389dbb4..38e4c23 100644
@@ -159,6 +159,8 @@ void thunderbolt_shutdown_and_free(struct tb *tb);
 struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
 void tb_switch_free(struct tb_switch *sw);
 
+int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, u32 value);
+
 
 static inline int tb_route_length(u64 route)
 {
Signature · 1 line
1.9.3
author maintainer
#7 lore raw

PATCH v4 7/15 thunderbolt: Enable plug events

In reply to Andreas Noever

Thunderbolt switches have a plug events capability. This patch adds the tb_plug_events_active method and uses it to activate plug events during switch allocation.

---
 drivers/thunderbolt/switch.c | 46 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |  1 +
 2 files changed, 47 insertions(+)
drivers/thunderbolt/switch.c +46 -0
index e89121f..21457cc 100644
@@ -99,6 +99,39 @@ static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
 }
 
 /**
+ * tb_plug_events_active() - enable/disable plug events on a switch
+ *
+ * Also configures a sane plug_events_delay of 255ms.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+static int tb_plug_events_active(struct tb_switch *sw, bool active)
+{
+	u32 data;
+	int res;
+
+	sw->config.plug_events_delay = 0xff;
+	res = tb_sw_write(sw, ((u32 *) &sw->config) + 4, TB_CFG_SWITCH, 4, 1);
+	if (res)
+		return res;
+
+	res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1);
+	if (res)
+		return res;
+
+	if (active) {
+		data = data & 0xFFFFFF83;
+		if (sw->config.device_id == 0x1547)
+			data |= 4;
+	} else {
+		data = data | 0x7c;
+	}
+	return tb_sw_write(sw, &data, TB_CFG_SWITCH,
+			   sw->cap_plug_events + 1, 1);
+}
+
+
+/**
  * tb_switch_free() - free a tb_switch and all downstream switches
  */
 void tb_switch_free(struct tb_switch *sw)
@@ -113,6 +146,8 @@ void tb_switch_free(struct tb_switch *sw)
 		sw->ports[i].remote = NULL;
 	}
 
+	tb_plug_events_active(sw, false);
+
 	kfree(sw->ports);
 	kfree(sw);
 }
@@ -125,6 +160,7 @@ void tb_switch_free(struct tb_switch *sw)
 struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
 {
 	int i;
+	int cap;
 	struct tb_switch *sw;
 	int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route);
 	if (upstream_port < 0)
@@ -177,6 +213,16 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
 
 	/* TODO: I2C, IECS, EEPROM, link controller */
 
+	cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS);
+	if (cap < 0) {
+		tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n");
+		goto err;
+	}
+	sw->cap_plug_events = cap;
+
+	if (tb_plug_events_active(sw, true))
+		goto err;
+
 	return sw;
 err:
 	kfree(sw->ports);
drivers/thunderbolt/tb.h +1 -0
index 38e4c23..af123c4 100644
@@ -19,6 +19,7 @@ struct tb_switch {
 	struct tb_regs_switch_header config;
 	struct tb_port *ports;
 	struct tb *tb;
+	int cap_plug_events; /* offset, zero if not found */
 };
 
 /**
Signature · 1 line
1.9.3
author maintainer
#8 lore raw

PATCH v4 8/15 thunderbolt: Scan for downstream switches

In reply to Andreas Noever

Add utility methods tb_port_state and tb_wait_for_port. Add tb_scan_switch which recursively checks for downstream switches.

---
 drivers/thunderbolt/switch.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.c     | 44 ++++++++++++++++++++
 drivers/thunderbolt/tb.h     | 16 ++++++++
 3 files changed, 157 insertions(+)
drivers/thunderbolt/switch.c +97 -0
index 21457cc..279b9c5 100644
@@ -53,6 +53,92 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
 }
 
 /**
+ * tb_port_state() - get connectedness state of a port
+ *
+ * The port must have a TB_CAP_PHY (i.e. it should be a real port).
+ *
+ * Return: Returns an enum tb_port_state on success or an error code on failure.
+ */
+static int tb_port_state(struct tb_port *port)
+{
+	struct tb_cap_phy phy;
+	int res;
+	if (port->cap_phy == 0) {
+		tb_port_WARN(port, "does not have a PHY\n");
+		return -EINVAL;
+	}
+	res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2);
+	if (res)
+		return res;
+	return phy.state;
+}
+
+/**
+ * tb_wait_for_port() - wait for a port to become ready
+ *
+ * Wait up to 1 second for a port to reach state TB_PORT_UP. If
+ * wait_if_unplugged is set then we also wait if the port is in state
+ * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after
+ * switch resume). Otherwise we only wait if a device is registered but the link
+ * has not yet been established.
+ *
+ * Return: Returns an error code on failure. Returns 0 if the port is not
+ * connected or failed to reach state TB_PORT_UP within one second. Returns 1
+ * if the port is connected and in state TB_PORT_UP.
+ */
+int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
+{
+	int retries = 10;
+	int state;
+	if (!port->cap_phy) {
+		tb_port_WARN(port, "does not have PHY\n");
+		return -EINVAL;
+	}
+	if (tb_is_upstream_port(port)) {
+		tb_port_WARN(port, "is the upstream port\n");
+		return -EINVAL;
+	}
+
+	while (retries--) {
+		state = tb_port_state(port);
+		if (state < 0)
+			return state;
+		if (state == TB_PORT_DISABLED) {
+			tb_port_info(port, "is disabled (state: 0)\n");
+			return 0;
+		}
+		if (state == TB_PORT_UNPLUGGED) {
+			if (wait_if_unplugged) {
+				/* used during resume */
+				tb_port_info(port,
+					     "is unplugged (state: 7), retrying...\n");
+				msleep(100);
+				continue;
+			}
+			tb_port_info(port, "is unplugged (state: 7)\n");
+			return 0;
+		}
+		if (state == TB_PORT_UP) {
+			tb_port_info(port,
+				     "is connected, link is up (state: 2)\n");
+			return 1;
+		}
+
+		/*
+		 * After plug-in the state is TB_PORT_CONNECTING. Give it some
+		 * time.
+		 */
+		tb_port_info(port,
+			     "is connected, link is not up (state: %d), retrying...\n",
+			     state);
+		msleep(100);
+	}
+	tb_port_warn(port,
+		     "failed to reach state TB_PORT_UP. Ignoring port...\n");
+	return 0;
+}
+
+/**
  * tb_init_port() - initialize a port
  *
  * This is a helper method for tb_switch_alloc. Does not check or initialize
@@ -63,6 +149,7 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
 static int tb_init_port(struct tb_switch *sw, u8 port_nr)
 {
 	int res;
+	int cap;
 	struct tb_port *port = &sw->ports[port_nr];
 	port->sw = sw;
 	port->port = port_nr;
@@ -71,6 +158,16 @@ static int tb_init_port(struct tb_switch *sw, u8 port_nr)
 	if (res)
 		return res;
 
+	/* Port 0 is the switch itself and has no PHY. */
+	if (port->config.type == TB_TYPE_PORT && port_nr != 0) {
+		cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY);
+
+		if (cap > 0)
+			port->cap_phy = cap;
+		else
+			tb_port_WARN(port, "non switch port without a PHY\n");
+	}
+
 	tb_dump_port(sw->tb, &port->config);
 
 	/* TODO: Read dual link port, DP port and more from EEPROM. */
drivers/thunderbolt/tb.c +44 -0

56 lines not on this page · raw message

drivers/thunderbolt/tb.h +16 -0
index af123c4..70a66fe 100644
@@ -29,6 +29,7 @@ struct tb_port {
 	struct tb_regs_port_header config;
 	struct tb_switch *sw;
 	struct tb_port *remote; /* remote port, NULL if not connected */
+	int cap_phy; /* offset, zero if not found */
 	u8 port; /* port number on switch */
 };
 
@@ -160,6 +161,8 @@ void thunderbolt_shutdown_and_free(struct tb *tb);
 struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
 void tb_switch_free(struct tb_switch *sw);
 
+int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
+
 int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, u32 value);
 
 
@@ -173,4 +176,17 @@ 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
+ *
+ * Port must not be the upstream port (otherwise a loop is created).
+ *
+ * Return: Returns a route to the switch behind @port.
+ */
+static inline u64 tb_downstream_route(struct tb_port *port)
+{
+	return tb_route(port->sw)
+	       | ((u64) port->port << (port->sw->config.depth * 8));
+}
+
 #endif
Signature · 1 line
1.9.3
author maintainer
#9 lore raw

PATCH v4 9/15 thunderbolt: Handle hotplug events

In reply to Andreas Noever

We receive a plug event callback whenever a thunderbolt device is added or removed. This patch fills in the tb_handle_hotplug method and starts reacting to these events by adding/removing switches from the hierarchy.

---
 drivers/thunderbolt/switch.c | 42 +++++++++++++++++++++++++++++++++++++++-
 drivers/thunderbolt/tb.c     | 46 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/thunderbolt/tb.h     |  3 +++
 3 files changed, 89 insertions(+), 2 deletions(-)
drivers/thunderbolt/switch.c +41 -1
index 279b9c5..c092f7c 100644
@@ -195,6 +195,24 @@ static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
 		sw->__unknown1, sw->__unknown4);
 }
 
+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 0;
+	if (tb_is_upstream_port(&sw->ports[next_port]))
+		return 0;
+	if (!sw->ports[next_port].remote)
+		return 0;
+	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
  *
@@ -243,7 +261,8 @@ void tb_switch_free(struct tb_switch *sw)
 		sw->ports[i].remote = NULL;
 	}
 
-	tb_plug_events_active(sw, false);
+	if (!sw->is_unplugged)
+		tb_plug_events_active(sw, false);
 
 	kfree(sw->ports);
 	kfree(sw);
@@ -327,3 +346,24 @@ err:
 	return NULL;
 }
 
+/**
+ * tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches
+ */
+void tb_sw_set_unpplugged(struct tb_switch *sw)
+{
+	int i;
+	if (sw == sw->tb->root_switch) {
+		tb_sw_WARN(sw, "cannot unplug root switch\n");
+		return;
+	}
+	if (sw->is_unplugged) {
+		tb_sw_WARN(sw, "is_unplugged already set\n");
+		return;
+	}
+	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)
+			tb_sw_set_unpplugged(sw->ports[i].remote->sw);
+	}
+}
+
drivers/thunderbolt/tb.c +45 -1

56 lines not on this page · raw message

drivers/thunderbolt/tb.h +3 -0
index 70a66fe..661f182 100644
@@ -20,6 +20,7 @@ struct tb_switch {
 	struct tb_port *ports;
 	struct tb *tb;
 	int cap_plug_events; /* offset, zero if not found */
+	bool is_unplugged; /* unplugged, will go away */
 };
 
 /**
@@ -160,6 +161,8 @@ void thunderbolt_shutdown_and_free(struct tb *tb);
 
 struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
 void tb_switch_free(struct tb_switch *sw);
+void tb_sw_set_unpplugged(struct tb_switch *sw);
+struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
 
 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
 
Signature · 1 line
1.9.3
author maintainer
#10 lore raw

PATCH v4 10/15 thunderbolt: Add path setup code.

In reply to Andreas Noever

A thunderbolt path is a unidirectional channel between two thunderbolt ports. Two such paths are needed to establish a pci tunnel.

This patch introduces struct tb_path as well as a set of tb_path_* methods which are used to activate & deactivate paths.

---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/path.c   | 215 +++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/switch.c |  34 +++++++
 drivers/thunderbolt/tb.h     |  62 +++++++++++++
 4 files changed, 312 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/path.c
drivers/thunderbolt/Makefile +1 -1
index 617b314..3532f36 100644
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o
 
drivers/thunderbolt/path.c new file +215 -0

215 lines not on this page · raw message

drivers/thunderbolt/switch.c +34 -0
index c092f7c..a6968cc 100644
@@ -139,6 +139,40 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
 }
 
 /**
+ * tb_port_add_nfc_credits() - add/remove non flow controlled credits to port
+ *
+ * Change the number of NFC credits allocated to @port by @credits. To remove
+ * NFC credits pass a negative amount of credits.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int tb_port_add_nfc_credits(struct tb_port *port, int credits)
+{
+	if (credits == 0)
+		return 0;
+	tb_port_info(port,
+		     "adding %#x NFC credits (%#x -> %#x)",
+		     credits,
+		     port->config.nfc_credits,
+		     port->config.nfc_credits + credits);
+	port->config.nfc_credits += credits;
+	return tb_port_write(port, &port->config.nfc_credits,
+			     TB_CFG_PORT, 4, 1);
+}
+
+/**
+ * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int tb_port_clear_counter(struct tb_port *port, int counter)
+{
+	u32 zero[3] = { 0, 0, 0 };
+	tb_port_info(port, "clearing counter %d\n", counter);
+	return tb_port_write(port, zero, TB_CFG_COUNTERS, 3 * counter, 3);
+}
+
+/**
  * tb_init_port() - initialize a port
  *
  * This is a helper method for tb_switch_alloc. Does not check or initialize
drivers/thunderbolt/tb.h +62 -0

77 lines not on this page · raw message

Signature · 1 line
1.9.3
author maintainer
#11 lore raw

PATCH v4 11/15 thunderbolt: Add support for simple pci tunnels

In reply to Andreas Noever

A pci downstream and pci upstream port can be connected through a tunnel. To establish the tunnel we have to setup two unidirectional paths between the two ports.

Right now we only support paths with two hops (i.e. no chaining) and at most one pci device per thunderbolt device.

---
 drivers/thunderbolt/Makefile     |   2 +-
 drivers/thunderbolt/tb.c         | 135 +++++++++++++++++++++++
 drivers/thunderbolt/tb.h         |   1 +
 drivers/thunderbolt/tunnel_pci.c | 232 +++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tunnel_pci.h |  30 +++++
 5 files changed, 399 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/tunnel_pci.c
 create mode 100644 drivers/thunderbolt/tunnel_pci.h
drivers/thunderbolt/Makefile +1 -1
index 3532f36..0122ca6 100644
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o
 
drivers/thunderbolt/tb.c +135 -0

179 lines not on this page · raw message

drivers/thunderbolt/tb.h +1 -0
index 8bbdc2b..508abc4 100644
@@ -100,6 +100,7 @@ struct tb {
 	struct tb_ctl *ctl;
 	struct workqueue_struct *wq; /* ordered workqueue for plug events */
 	struct tb_switch *root_switch;
+	struct list_head tunnel_list; /* list of active PCIe tunnels */
 	bool hotplug_active; /*
 			      * tb_handle_hotplug will stop progressing plug
 			      * events and exit if this is not set (it needs to
drivers/thunderbolt/tunnel_pci.c new file +232 -0

232 lines not on this page · raw message

drivers/thunderbolt/tunnel_pci.h new file +30 -0
new file mode 100644
index 0000000..a67f93c
@@ -0,0 +1,30 @@
+/*
+ * Thunderbolt Cactus Ridge driver - PCIe tunnel
+ *
+ * Copyright (c) 2014 Andreas Noever <[email protected]>
+ */
+
+#ifndef TB_PCI_H_
+#define TB_PCI_H_
+
+#include "tb.h"
+
+struct tb_pci_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 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);
+
+#endif
+
Signature · 1 line
1.9.3
author maintainer
#12 lore raw

PATCH v4 12/15 PCI: Add pci_fixup_suspend_late quirk pass

In reply to Andreas Noever

Add pci_fixup_suspend_late as a new pci_fixup_pass. The pass is called from suspend_noirq and poweroff_noirq. Using the same pass for suspend and hibernate is consistent with resume_early which is called by resume_noirq and restore_noirq.

The new quirk pass is required for Thunderbolt support on Apple hardware.

---
 drivers/pci/pci-driver.c          | 18 ++++++++++++++----
 drivers/pci/quirks.c              |  7 +++++++
 include/asm-generic/vmlinux.lds.h |  3 +++
 include/linux/pci.h               | 12 +++++++++++-
 4 files changed, 35 insertions(+), 5 deletions(-)
drivers/pci/pci-driver.c +14 -4
index d911e0c..55dc1b8 100644
@@ -542,7 +542,7 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
 			WARN_ONCE(pci_dev->current_state != prev,
 				"PCI PM: Device state not saved by %pF\n",
 				drv->suspend_late);
-			return 0;
+			goto Fixup;
 		}
 	}
 
@@ -551,6 +551,9 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
 
 	pci_pm_set_unknown_state(pci_dev);
 
+Fixup:
+	pci_fixup_device(pci_fixup_suspend_late, pci_dev);
+
 	return 0;
 }
 
@@ -694,7 +697,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
 
 	if (!pm) {
 		pci_save_state(pci_dev);
-		return 0;
+		goto Fixup;
 	}
 
 	if (pm->suspend_noirq) {
@@ -711,7 +714,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
 			WARN_ONCE(pci_dev->current_state != prev,
 				"PCI PM: State of device not saved by %pF\n",
 				pm->suspend_noirq);
-			return 0;
+			goto Fixup;
 		}
 	}
 
@@ -735,6 +738,9 @@ static int pci_pm_suspend_noirq(struct device *dev)
 	if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
 		pci_write_config_word(pci_dev, PCI_COMMAND, 0);
 
+Fixup:
+	pci_fixup_device(pci_fixup_suspend_late, pci_dev);
+
 	return 0;
 }
 
@@ -959,8 +965,10 @@ static int pci_pm_poweroff_noirq(struct device *dev)
 	if (pci_has_legacy_pm_support(to_pci_dev(dev)))
 		return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
 
-	if (!drv || !drv->pm)
+	if (!drv || !drv->pm) {
+		pci_fixup_device(pci_fixup_suspend_late, pci_dev);
 		return 0;
+	}
 
 	if (drv->pm->poweroff_noirq) {
 		int error;
@@ -981,6 +989,8 @@ static int pci_pm_poweroff_noirq(struct device *dev)
 	if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
 		pci_write_config_word(pci_dev, PCI_COMMAND, 0);
 
+	pci_fixup_device(pci_fixup_suspend_late, pci_dev);
+
 	if (pcibios_pm_ops.poweroff_noirq)
 		return pcibios_pm_ops.poweroff_noirq(dev);
 
drivers/pci/quirks.c +7 -0
index e729206..af2eba1 100644
@@ -3024,6 +3024,8 @@ extern struct pci_fixup __start_pci_fixups_resume_early[];
 extern struct pci_fixup __end_pci_fixups_resume_early[];
 extern struct pci_fixup __start_pci_fixups_suspend[];
 extern struct pci_fixup __end_pci_fixups_suspend[];
+extern struct pci_fixup __start_pci_fixups_suspend_late[];
+extern struct pci_fixup __end_pci_fixups_suspend_late[];
 
 static bool pci_apply_fixup_final_quirks;
 
@@ -3069,6 +3071,11 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
 		end = __end_pci_fixups_suspend;
 		break;
 
+	case pci_fixup_suspend_late:
+		start = __start_pci_fixups_suspend_late;
+		end = __end_pci_fixups_suspend_late;
+		break;
+
 	default:
 		/* stupid compiler warning, you would think with an enum... */
 		return;
include/asm-generic/vmlinux.lds.h +3 -0
index 146e4ff..2dea636 100644
@@ -288,6 +288,9 @@
 		VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .;		\
 		*(.pci_fixup_suspend)					\
 		VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .;		\
+		VMLINUX_SYMBOL(__start_pci_fixups_suspend_late) = .;	\
+		*(.pci_fixup_suspend_late)				\
+		VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .;	\
 	}								\
 									\
 	/* Built-in firmware blobs */					\
include/linux/pci.h +11 -1
index aab57b4..4724806 100644
@@ -1458,8 +1458,9 @@ enum pci_fixup_pass {
 	pci_fixup_final,	/* Final phase of device fixups */
 	pci_fixup_enable,	/* pci_enable_device() time */
 	pci_fixup_resume,	/* pci_device_resume() */
-	pci_fixup_suspend,	/* pci_device_suspend */
+	pci_fixup_suspend,	/* pci_device_suspend() */
 	pci_fixup_resume_early, /* pci_device_resume_early() */
+	pci_fixup_suspend_late,	/* pci_device_suspend_late() */
 };
 
 /* Anonymous variables would be nice... */
@@ -1500,6 +1501,11 @@ enum pci_fixup_pass {
 	DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend,			\
 		suspend##hook, vendor, device, class,	\
 		class_shift, hook)
+#define DECLARE_PCI_FIXUP_CLASS_SUSPEND_LATE(vendor, device, class,	\
+					 class_shift, hook)		\
+	DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend_late,		\
+		suspend_late##hook, vendor, device,	\
+		class, class_shift, hook)
 
 #define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook)			\
 	DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early,			\
@@ -1525,6 +1531,10 @@ enum pci_fixup_pass {
 	DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend,			\
 		suspend##hook, vendor, device,		\
 		PCI_ANY_ID, 0, hook)
+#define DECLARE_PCI_FIXUP_SUSPEND_LATE(vendor, device, hook)		\
+	DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend_late,		\
+		suspend_late##hook, vendor, device,	\
+		PCI_ANY_ID, 0, hook)
 
 #ifdef CONFIG_PCI_QUIRKS
 void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
Signature · 1 line
1.9.3
author maintainer
#13 lore raw

PATCH v4 13/15 PCI: Suspend/resume quirks for Apple thunderbolt

In reply to Andreas Noever

Add two quirks to support thunderbolt suspend/resume on Apple systems. We need to perform two different actions during suspend and resume:

The whole controller has to be powered down before suspend. If this is not done then the native host interface device will be gone after resume if a thunderbolt device was plugged in before suspending. The controller represents itself as multiple PCI devices/bridges. To power it down we hook into the upstream bridge of the controller and call the magic ACPI methods. Power will be restored automatically during resume (by the firmware presumably).

During resume we have to wait for the native host interface to reestablish all pci tunnels. Since there is no parent-child relationship between the NHI and the bridges we have to explicitly wait for them using device_pm_wait_for_dev. We do this in the resume_noirq phase of the downstream bridges of the controller (which lead into the thunderbolt tunnels).

---
 drivers/pci/quirks.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)
drivers/pci/quirks.c +122 -0
index af2eba1..c9d6b90 100644
@@ -2992,6 +2992,128 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030,
 DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
 			 quirk_broken_intx_masking);
 
+#ifdef CONFIG_ACPI
+/* Apple systems with a Cactus Ridge Thunderbolt controller. */
+static struct dmi_system_id apple_thunderbolt_whitelist[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir5"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir6"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Apple: Shutdown Cactus Ridge Thunderbolt controller.
+ *
+ * On Apple hardware the Cactus Ridge Thunderbolt controller needs to be
+ * shutdown before suspend. Otherwise the native host interface (NHI) will not
+ * be present after resume if a device was plugged in before suspend.
+ *
+ * The thunderbolt controller consists of a pcie switch with downstream
+ * bridges leading to the NHI and to the tunnel pci bridges.
+ *
+ * This quirk cuts power to the whole chip. Therefore we have to apply it
+ * during suspend_noirq of the upstream bridge.
+ *
+ * Power is automagically restored before resume. No action is needed.
+ */
+static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
+{
+	acpi_handle bridge, SXIO, SXFP, SXLV;
+
+	if (!dmi_check_system(apple_thunderbolt_whitelist))
+		return;
+	if (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM)
+		return;
+	bridge = ACPI_HANDLE(&dev->dev);
+	if (!bridge)
+		return;
+	/*
+	 * TB bridges in external devices might have the same device id as those
+	 * on the host, but they will not have the associated ACPI methods. This
+	 * implicitly checks that we are at the right bridge.
+	 */
+	if (ACPI_FAILURE(acpi_get_handle(bridge, "DSB0.NHI0.SXIO", &SXIO))
+	    || ACPI_FAILURE(acpi_get_handle(bridge, "DSB0.NHI0.SXFP", &SXFP))
+	    || ACPI_FAILURE(acpi_get_handle(bridge, "DSB0.NHI0.SXLV", &SXLV)))
+		return;
+	dev_info(&dev->dev, "quirk: cutting power to thunderbolt controller...\n");
+
+	/* magic sequence */
+	acpi_execute_simple_method(SXIO, NULL, 1);
+	acpi_execute_simple_method(SXFP, NULL, 0);
+	msleep(300);
+	acpi_execute_simple_method(SXLV, NULL, 0);
+	acpi_execute_simple_method(SXIO, NULL, 0);
+	acpi_execute_simple_method(SXLV, NULL, 0);
+}
+DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL, 0x1547,
+			       quirk_apple_poweroff_thunderbolt);
+
+/*
+ * Apple: Wait for the thunderbolt controller to reestablish pci tunnels.
+ *
+ * During suspend the thunderbolt controller is reset and all pci
+ * tunnels are lost. The NHI driver will try to reestablish all tunnels
+ * during resume. We have to manually wait for the NHI since there is
+ * no parent child relationship between the NHI and the tunneled
+ * bridges.
+ */
+static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
+{
+	struct pci_dev *sibling = NULL;
+	struct pci_dev *nhi = NULL;
+
+	if (!dmi_check_system(apple_thunderbolt_whitelist))
+		return;
+	if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
+		return;
+	/*
+	 * Find the NHI and confirm that we are a bridge on the tb host
+	 * controller and not on a tb endpoint.
+	 */
+	sibling = pci_get_slot(dev->bus, 0x0);
+	if (sibling == dev)
+		goto out; /* we are the downstream bridge to the NHI */
+	if (!sibling || !sibling->subordinate)
+		goto out;
+	nhi = pci_get_slot(sibling->subordinate, 0x0);
+	if (!nhi)
+		goto out;
+	if (nhi->vendor != PCI_VENDOR_ID_INTEL || nhi->device != 0x1547
+			|| nhi->subsystem_vendor != 0x2222
+			|| nhi->subsystem_device != 0x1111)
+		goto out;
+	dev_info(&dev->dev, "quirk: wating for thunderbolt to reestablish pci tunnels...\n");
+	device_pm_wait_for_dev(&dev->dev, &nhi->dev);
+out:
+	pci_dev_put(nhi);
+	pci_dev_put(sibling);
+}
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x1547,
+			       quirk_apple_wait_for_thunderbolt);
+#endif
+
 static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
 			  struct pci_fixup *end)
 {
Signature · 1 line
1.9.3
author maintainer
#14 lore raw

PATCH v4 14/15 thunderbolt: Read switch uid from EEPROM

In reply to Andreas Noever

Add eeprom access code and read the uid during switch initialization. The UID will be used to check device identity after suspend.

---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/eeprom.c | 189 +++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/switch.c |   5 ++
 drivers/thunderbolt/tb.h     |   3 +
 4 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/eeprom.c
drivers/thunderbolt/Makefile +1 -1
index 0122ca6..5d1053c 100644
@@ -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
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
 
drivers/thunderbolt/eeprom.c new file +189 -0

189 lines not on this page · raw message

drivers/thunderbolt/switch.c +5 -0
index a6968cc..eef5f1c 100644
@@ -370,6 +370,11 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
 	}
 	sw->cap_plug_events = cap;
 
+	if (tb_eeprom_read_uid(sw, &sw->uid))
+		tb_sw_warn(sw, "could not read uid from eeprom\n");
+	else
+		tb_sw_info(sw, "uid: %#llx\n", sw->uid);
+
 	if (tb_plug_events_active(sw, true))
 		goto err;
 
drivers/thunderbolt/tb.h +3 -0
index 508abc4..a89087f 100644
@@ -19,6 +19,7 @@ struct tb_switch {
 	struct tb_regs_switch_header config;
 	struct tb_port *ports;
 	struct tb *tb;
+	u64 uid;
 	int cap_plug_events; /* offset, zero if not found */
 	bool is_unplugged; /* unplugged, will go away */
 };
@@ -231,6 +232,8 @@ int tb_path_activate(struct tb_path *path);
 void tb_path_deactivate(struct tb_path *path);
 bool tb_path_is_invalid(struct tb_path *path);
 
+int tb_eeprom_read_uid(struct tb_switch *sw, u64 *uid);
+
 
 static inline int tb_route_length(u64 route)
 {
Signature · 1 line
1.9.3
author maintainer
#15 lore raw

PATCH v4 15/15 thunderbolt: Add suspend/hibernate support

In reply to Andreas Noever

We use _noirq since we have to restore the pci tunnels before the pci core wakes the tunneled devices.

---
 drivers/thunderbolt/nhi.c    | 33 +++++++++++++++++
 drivers/thunderbolt/switch.c | 84 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.c     | 61 ++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |  5 +++
 4 files changed, 183 insertions(+)
drivers/thunderbolt/nhi.c +33 -0
index f3a4ac2..6f666fa 100644
@@ -7,6 +7,7 @@
  * Copyright (c) 2014 Andreas Noever <[email protected]>
  */
 
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/pci.h>
@@ -492,6 +493,22 @@ static irqreturn_t nhi_msi(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int nhi_suspend_noirq(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct tb *tb = pci_get_drvdata(pdev);
+	thunderbolt_suspend(tb);
+	return 0;
+}
+
+static int nhi_resume_noirq(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct tb *tb = pci_get_drvdata(pdev);
+	thunderbolt_resume(tb);
+	return 0;
+}
+
 static void nhi_shutdown(struct tb_nhi *nhi)
 {
 	int i;
@@ -600,6 +617,21 @@ static void nhi_remove(struct pci_dev *pdev)
 	nhi_shutdown(nhi);
 }
 
+/*
+ * The tunneled pci bridges are siblings of us. Use resume_noirq to reenable
+ * the tunnels asap. A corresponding pci quirk blocks the downstream bridges
+ * resume_noirq until we are done.
+ */
+static const struct dev_pm_ops nhi_pm_ops = {
+	.suspend_noirq = nhi_suspend_noirq,
+	.resume_noirq = nhi_resume_noirq,
+	.freeze_noirq = nhi_suspend_noirq, /*
+					    * we just disable hotplug, the
+					    * pci-tunnels stay alive.
+					    */
+	.restore_noirq = nhi_resume_noirq,
+};
+
 struct pci_device_id nhi_ids[] = {
 	{
 		/*
@@ -621,6 +653,7 @@ static struct pci_driver nhi_driver = {
 	.id_table = nhi_ids,
 	.probe = nhi_probe,
 	.remove = nhi_remove,
+	.driver.pm = &nhi_pm_ops,
 };
 
 static int __init nhi_init(void)
drivers/thunderbolt/switch.c +84 -0

93 lines not on this page · raw message

drivers/thunderbolt/tb.c +61 -0

70 lines not on this page · raw message

drivers/thunderbolt/tb.h +5 -0
index a89087f..63e89d0 100644
@@ -214,9 +214,14 @@ static inline int tb_port_write(struct tb_port *port, void *buffer,
 
 struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi);
 void thunderbolt_shutdown_and_free(struct tb *tb);
+void thunderbolt_suspend(struct tb *tb);
+void thunderbolt_resume(struct tb *tb);
 
 struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
 void tb_switch_free(struct tb_switch *sw);
+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_unpplugged(struct tb_switch *sw);
 struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
 
Signature · 1 line
1.9.3
#16 lore raw
In reply to Andreas Noever
Quoted 7 lines · Andreas Noever

Hi

This is version 4 of my Thunderbolt driver for Apple hardware (see [1] for v3).

Changes since v3:
- Fix typos and style problems caught by Bjorn.
- Changed the #ifdef CONFIG_ACPI block to cover the whole pci quirk.

Looks good, want me to take this through my tree? Matthew, any objections?

thanks,

greg k-h

#17 lore raw
In reply to Andreas Noever

T24gU2F0LCAyMDE0LTA1LTMxIGF0IDE2OjI3ICswMjAwLCBBbmRyZWFzIE5vZXZlciB3cm90ZToN Cj4gSGkNCj4gDQo+IFRoaXMgaXMgdmVyc2lvbiA0IG9mIG15IFRodW5kZXJib2x0IGRyaXZlciBm b3IgQXBwbGUgaGFyZHdhcmUgKHNlZSBbMV0gZm9yIHYzKS4NCj4gDQo+IENoYW5nZXMgc2luY2Ug djM6DQo+ICAtIEZpeCB0eXBvcyBhbmQgc3R5bGUgcHJvYmxlbXMgY2F1Z2h0IGJ5IEJqb3JuLg0K PiAgLSBDaGFuZ2VkIHRoZSAjaWZkZWYgQ09ORklHX0FDUEkgYmxvY2sgdG8gY292ZXIgdGhlIHdo b2xlIHBjaSBxdWlyay4NCg0KSGkgQW5kcmVhcywNCg0KVGhpcyBzZWVtcyB0byBiZSB3b3JraW5n IHdlbGwgb24gbXkgTUJQLiBJdCBhcHBlYXJzIHRvIGJyb2FkbHkgd29yayBvbg0KbXkgTWFjIFBy bywgd2hpY2ggaGFzIFRodW5kZXJib2x0IDIgaGFyZHdhcmUgLSBJIGFkZGVkIHRoZSBQQ0kgSUQs IGFuZA0KbG9hZGluZyB0aGUgdGh1bmRlcmJvbHQgZHJpdmVyIGFmdGVyIHRoZSBkZXZpY2UgaXMg cGx1Z2dlZCBpbiB3b3JrcywgYnV0DQppdCB3b24ndCByZWNvZ25pc2UgaG90cGx1ZyBldmVudHMu IEkgZG9uJ3QgYXBwZWFyIHRvIGdldCBhbnkgaW50ZXJydXB0cw0KZnJvbSB0aGUgVGh1bmRlcmJv bHQgY29udHJvbGxlci4gQW55IGlkZWEgd2hhdCBtaWdodCBiZSBoYXBwZW5pbmcgdGhlcmU/DQoN CkFzIGZhciBhcyB0aGUgcXVpcmtzIGdvIC0gcGVyaGFwcyBzb21ldGhpbmcgbGlrZSB0aGlzIHdv dWxkIGJlDQpyZWFzb25hYmxlLCByYXRoZXIgdGhhbiBtYWludGFpbmluZyBhIGxpc3Qgb2YgbWFj aGluZXM/DQoNCmRpZmYgLS1naXQgYS9kcml2ZXJzL3BjaS9xdWlya3MuYyBiL2RyaXZlcnMvcGNp L3F1aXJrcy5jDQppbmRleCBjOWQ2YjkwLi5jMzE3MGQ0IDEwMDY0NA0KLS0tIGEvZHJpdmVycy9w Y2kvcXVpcmtzLmMNCisrKyBiL2RyaXZlcnMvcGNpL3F1aXJrcy5jDQpAQCAtMjk5MywzNSArMjk5 Myw2IEBAIERFQ0xBUkVfUENJX0ZJWFVQX0hFQURFUigweDE4MTQsIDB4MDYwMSwgLyoNClJhbGlu ayBSVDI4MDAgODAyLjExbiBQQ0kgKi8NCiAJCQkgcXVpcmtfYnJva2VuX2ludHhfbWFza2luZyk7 DQogDQogI2lmZGVmIENPTkZJR19BQ1BJDQotLyogQXBwbGUgc3lzdGVtcyB3aXRoIGEgQ2FjdHVz IFJpZGdlIFRodW5kZXJib2x0IGNvbnRyb2xsZXIuICovDQotc3RhdGljIHN0cnVjdCBkbWlfc3lz dGVtX2lkIGFwcGxlX3RodW5kZXJib2x0X3doaXRlbGlzdFtdID0gew0KLQl7DQotCQkubWF0Y2hl cyA9IHsNCi0JCQlETUlfTUFUQ0goRE1JX0JPQVJEX1ZFTkRPUiwgIkFwcGxlIEluYy4iKSwNCi0J CQlETUlfTUFUQ0goRE1JX1BST0RVQ1RfTkFNRSwgIk1hY0Jvb2tQcm85IiksDQotCQl9LA0KLQl9 LA0KLQl7DQotCQkubWF0Y2hlcyA9IHsNCi0JCQlETUlfTUFUQ0goRE1JX0JPQVJEX1ZFTkRPUiwg IkFwcGxlIEluYy4iKSwNCi0JCQlETUlfTUFUQ0goRE1JX1BST0RVQ1RfTkFNRSwgIk1hY0Jvb2tQ cm8xMCIpLA0KLQkJfSwNCi0JfSwNCi0Jew0KLQkJLm1hdGNoZXMgPSB7DQotCQkJRE1JX01BVENI KERNSV9CT0FSRF9WRU5ET1IsICJBcHBsZSBJbmMuIiksDQotCQkJRE1JX01BVENIKERNSV9QUk9E VUNUX05BTUUsICJNYWNCb29rQWlyNSIpLA0KLQkJfSwNCi0JfSwNCi0Jew0KLQkJLm1hdGNoZXMg PSB7DQotCQkJRE1JX01BVENIKERNSV9CT0FSRF9WRU5ET1IsICJBcHBsZSBJbmMuIiksDQotCQkJ RE1JX01BVENIKERNSV9QUk9EVUNUX05BTUUsICJNYWNCb29rQWlyNiIpLA0KLQkJfSwNCi0JfSwN Ci0JeyB9DQotfTsNCi0NCiAvKg0KICAqIEFwcGxlOiBTaHV0ZG93biBDYWN0dXMgUmlkZ2UgVGh1 bmRlcmJvbHQgY29udHJvbGxlci4NCiAgKg0KQEAgLTMwNDEsNyArMzAxMiw3IEBAIHN0YXRpYyB2 b2lkDQpxdWlya19hcHBsZV9wb3dlcm9mZl90aHVuZGVyYm9sdChzdHJ1Y3QgcGNpX2RldiAqZGV2 KQ0KIHsNCiAJYWNwaV9oYW5kbGUgYnJpZGdlLCBTWElPLCBTWEZQLCBTWExWOw0KIA0KLQlpZiAo IWRtaV9jaGVja19zeXN0ZW0oYXBwbGVfdGh1bmRlcmJvbHRfd2hpdGVsaXN0KSkNCisJaWYgKCFk bWlfbWF0Y2goRE1JX0JPQVJEX1ZFTkRPUiwgIkFwcGxlIEluYy4iKSkNCiAJCXJldHVybjsNCiAJ aWYgKHBjaV9wY2llX3R5cGUoZGV2KSAhPSBQQ0lfRVhQX1RZUEVfVVBTVFJFQU0pDQogCQlyZXR1 cm47DQpAQCAtMzA4NCw3ICszMDU1LDcgQEAgc3RhdGljIHZvaWQNCnF1aXJrX2FwcGxlX3dhaXRf Zm9yX3RodW5kZXJib2x0KHN0cnVjdCBwY2lfZGV2ICpkZXYpDQogCXN0cnVjdCBwY2lfZGV2ICpz aWJsaW5nID0gTlVMTDsNCiAJc3RydWN0IHBjaV9kZXYgKm5oaSA9IE5VTEw7DQogDQotCWlmICgh ZG1pX2NoZWNrX3N5c3RlbShhcHBsZV90aHVuZGVyYm9sdF93aGl0ZWxpc3QpKQ0KKwlpZiAoIWRt aV9tYXRjaChETUlfQk9BUkRfVkVORE9SLCAiQXBwbGUgSW5jLiIpKQ0KIAkJcmV0dXJuOw0KIAlp ZiAocGNpX3BjaWVfdHlwZShkZXYpICE9IFBDSV9FWFBfVFlQRV9ET1dOU1RSRUFNKQ0KIAkJcmV0 dXJuOw0KDQoNCi0tIA0KTWF0dGhldyBHYXJyZXR0IDxtYXR0aGV3LmdhcnJldHRAbmVidWxhLmNv bT4NCg==

author maintainer
#18 lore raw
Quoted 9 lines · Matthew Garrett
Quoted 7 lines · Andreas Noever

Hi

This is version 4 of my Thunderbolt driver for Apple hardware (see [1] for v3).

Changes since v3:
- Fix typos and style problems caught by Bjorn.
- Changed the #ifdef CONFIG_ACPI block to cover the whole pci quirk.

Quoted 7 lines

Hi Andreas,

This seems to be working well on my MBP. It appears to broadly work on my Mac Pro, which has Thunderbolt 2 hardware - I added the PCI ID, and loading the thunderbolt driver after the device is plugged in works, but it won't recognise hotplug events. I don't appear to get any interrupts from the Thunderbolt controller. Any idea what might be happening there?

So the communication with the controller works (dmesg dumps a list of ports etc.)? Do you get plug events ("resetting error on port ...")? You could try to play around with tb_plug_events_active, if you want to experiment. I can also take another look at what OS X does once I get back to my workstation (when I worked on this part falcon ridge was not jet released, so maybe they do things differently now).

Quoted 2 lines

As far as the quirks go - perhaps something like this would be reasonable, rather than maintaining a list of machines?

I have obtained ACPI dumps from a late 2013 MBP and from a MacPro (both are falcon ridge devices) and these contain a few firmware changes. For example SXIO, SXIL and SXLV are gone so the shutdown quirk will fail. With some luck that means that the shutdown quirk is no longer required for falcon ridge hardware.

Can you try the following on your MacPro?
- Boot with your ACPI patch, nothing plugged in, no thunderbolt driver
- Verify that NHI is present (lspci)
- Plug in a tb device
- Suspend and resume
- Check that the NHI is still responding using lspci -vv
On my cactus ridge MBP the device id of the NHI now reads 0xffff (the device is gone, Linux just doesn't know). If the NHI is still alive then they fixed their hardware :)

Andreas

Quoted 65 lines
drivers/pci/quirks.c +0 -0
index c9d6b90..c3170d4 100644
@@ -2993,35 +2993,6 @@ DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /*

Ralink RT2800 802.11n PCI */

                         quirk_broken_intx_masking);

#ifdef CONFIG_ACPI

drivers/pci/quirks.c +1 -30
-/* Apple systems with a Cactus Ridge Thunderbolt controller. */
-static struct dmi_system_id apple_thunderbolt_whitelist[] = {
-       {
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9"),
-               },
-       },
-       {
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10"),
-               },
-       },
-       {
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir5"),
-               },
-       },
-       {
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir6"),
-               },
-       },
-       { }
-};
-
 /*
  * Apple: Shutdown Cactus Ridge Thunderbolt controller.
  *
@@ -3041,7 +3012,7 @@ static void
 quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
 {
        acpi_handle bridge, SXIO, SXFP, SXLV;
 
-       if (!dmi_check_system(apple_thunderbolt_whitelist))
+       if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc."))
                return;
        if (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM)
                return;
drivers/pci/quirks.c +1 -1
@@ -3084,7 +3055,7 @@ static void
 quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
        struct pci_dev *sibling = NULL;
        struct pci_dev *nhi = NULL;
 
-       if (!dmi_check_system(apple_thunderbolt_whitelist))
+       if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc."))
                return;
        if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
                return;

--
Matthew Garrett <[email protected]>

#19 lore raw

T24gU3VuLCAyMDE0LTA2LTAxIGF0IDEyOjExICswMjAwLCBBbmRyZWFzIE5vZXZlciB3cm90ZToN Cj4gT24gU3VuLCBKdW4gMSwgMjAxNCBhdCAxOjUxIEFNLCBNYXR0aGV3IEdhcnJldHQNCj4gPG1h dHRoZXcuZ2FycmV0dEBuZWJ1bGEuY29tPiB3cm90ZToNCj4gPiBUaGlzIHNlZW1zIHRvIGJlIHdv cmtpbmcgd2VsbCBvbiBteSBNQlAuIEl0IGFwcGVhcnMgdG8gYnJvYWRseSB3b3JrIG9uDQo+ID4g bXkgTWFjIFBybywgd2hpY2ggaGFzIFRodW5kZXJib2x0IDIgaGFyZHdhcmUgLSBJIGFkZGVkIHRo ZSBQQ0kgSUQsIGFuZA0KPiA+IGxvYWRpbmcgdGhlIHRodW5kZXJib2x0IGRyaXZlciBhZnRlciB0 aGUgZGV2aWNlIGlzIHBsdWdnZWQgaW4gd29ya3MsIGJ1dA0KPiA+IGl0IHdvbid0IHJlY29nbmlz ZSBob3RwbHVnIGV2ZW50cy4gSSBkb24ndCBhcHBlYXIgdG8gZ2V0IGFueSBpbnRlcnJ1cHRzDQo+ ID4gZnJvbSB0aGUgVGh1bmRlcmJvbHQgY29udHJvbGxlci4gQW55IGlkZWEgd2hhdCBtaWdodCBi ZSBoYXBwZW5pbmcgdGhlcmU/DQo+IFNvIHRoZSBjb21tdW5pY2F0aW9uIHdpdGggdGhlIGNvbnRy b2xsZXIgd29ya3MgKGRtZXNnIGR1bXBzIGEgbGlzdCBvZg0KPiBwb3J0cyBldGMuKT8gRG8geW91 IGdldCBwbHVnIGV2ZW50cyAoInJlc2V0dGluZyBlcnJvciBvbiBwb3J0IC4uLiIpPw0KPiBZb3Ug Y291bGQgdHJ5IHRvIHBsYXkgYXJvdW5kIHdpdGggdGJfcGx1Z19ldmVudHNfYWN0aXZlLCBpZiB5 b3Ugd2FudA0KPiB0byBleHBlcmltZW50LiBJIGNhbiBhbHNvIHRha2UgYW5vdGhlciBsb29rIGF0 IHdoYXQgT1MgWCBkb2VzIG9uY2UgSQ0KPiBnZXQgYmFjayB0byBteSB3b3Jrc3RhdGlvbiAod2hl biBJIHdvcmtlZCBvbiB0aGlzIHBhcnQgZmFsY29uIHJpZGdlDQo+IHdhcyBub3QgamV0IHJlbGVh c2VkLCBzbyBtYXliZSB0aGV5IGRvIHRoaW5ncyBkaWZmZXJlbnRseSBub3cpLg0KDQpZZWFoLCB0 aGF0IHdhcyBpdC4gSSdsbCBtYWlsIHRoZSBwYXRjaCBzZXBhcmF0ZWx5Lg0KDQoNCj4gPiBBcyBm YXIgYXMgdGhlIHF1aXJrcyBnbyAtIHBlcmhhcHMgc29tZXRoaW5nIGxpa2UgdGhpcyB3b3VsZCBi ZQ0KPiA+IHJlYXNvbmFibGUsIHJhdGhlciB0aGFuIG1haW50YWluaW5nIGEgbGlzdCBvZiBtYWNo aW5lcz8NCj4gSSBoYXZlIG9idGFpbmVkIEFDUEkgZHVtcHMgZnJvbSBhIGxhdGUgMjAxMyBNQlAg YW5kIGZyb20gYSBNYWNQcm8NCj4gKGJvdGggYXJlIGZhbGNvbiByaWRnZSBkZXZpY2VzKSBhbmQg dGhlc2UgY29udGFpbiBhIGZldyBmaXJtd2FyZQ0KPiBjaGFuZ2VzLiBGb3IgZXhhbXBsZSBTWElP LCBTWElMIGFuZCBTWExWIGFyZSBnb25lIHNvIHRoZSBzaHV0ZG93bg0KPiBxdWlyayB3aWxsIGZh aWwuIFdpdGggc29tZSBsdWNrIHRoYXQgbWVhbnMgdGhhdCB0aGUgc2h1dGRvd24gcXVpcmsgaXMN Cj4gbm8gbG9uZ2VyIHJlcXVpcmVkIGZvciBmYWxjb24gcmlkZ2UgaGFyZHdhcmUuDQoNClllYWgs IGl0IHNlZW1zIEkgZG9uJ3QgbmVlZCB0aGUgc3VzcGVuZCBxdWlyayAtIHRoZSBOSEkgaXMgc3Rp bGwgdGhlcmUNCndpdGhvdXQgaXQuIEkgc3RpbGwgdGhpbmsgd2Ugc2hvdWxkIG1ha2UgdGhlIHF1 aXJrIGdlbmVyYWwgcmF0aGVyIHRoYW4NCnR5aW5nIGl0IHRvIHRoZSBtYWNoaW5lcywgdGhlIHdv cnN0IGNhc2UgaXMgdGhhdCBpdCdsbCBqdXN0IGRvIG5vdGhpbmcuDQoNCi0tIA0KTWF0dGhldyBH YXJyZXR0IDxtYXR0aGV3LmdhcnJldHRAbmVidWxhLmNvbT4NCg==

author maintainer
#20 lore raw
Quoted 8 lines · Matthew Garrett
Quoted 7 lines · Andreas Noever
Quoted 5 lines · Matthew Garrett

This seems to be working well on my MBP. It appears to broadly work on my Mac Pro, which has Thunderbolt 2 hardware - I added the PCI ID, and loading the thunderbolt driver after the device is plugged in works, but it won't recognise hotplug events. I don't appear to get any interrupts from the Thunderbolt controller. Any idea what might be happening there?

Quoted 8 lines
Quoted 6 lines

So the communication with the controller works (dmesg dumps a list of ports etc.)? Do you get plug events ("resetting error on port ...")? You could try to play around with tb_plug_events_active, if you want to experiment. I can also take another look at what OS X does once I get back to my workstation (when I worked on this part falcon ridge was not jet released, so maybe they do things differently now).

Yeah, that was it. I'll mail the patch separately.

Great

Quoted 9 lines
Quoted 8 lines
Quoted 3 lines

As far as the quirks go - perhaps something like this would be reasonable, rather than maintaining a list of machines?

I have obtained ACPI dumps from a late 2013 MBP and from a MacPro (both are falcon ridge devices) and these contain a few firmware changes. For example SXIO, SXIL and SXLV are gone so the shutdown quirk will fail. With some luck that means that the shutdown quirk is no longer required for falcon ridge hardware.

Quoted 3 lines

Yeah, it seems I don't need the suspend quirk - the NHI is still there without it. I still think we should make the quirk general rather than tying it to the machines, the worst case is that it'll just do nothing.

Ok, agreed. The "wait" quirk has to run on all machines and the other one will fail if the ACPI methods are not there. Should I resend the series or just the patch or should I (or do you want to) make a separate patch?

Andreas

#21 lore raw

T24gU3VuLCAyMDE0LTA2LTAxIGF0IDIyOjQ1ICswMjAwLCBBbmRyZWFzIE5vZXZlciB3cm90ZToN Cj4gT24gU3VuLCBKdW4gMSwgMjAxNCBhdCA2OjA3IFBNLCBNYXR0aGV3IEdhcnJldHQNCj4gPG1h dHRoZXcuZ2FycmV0dEBuZWJ1bGEuY29tPiB3cm90ZToNCj4gPiBZZWFoLCBpdCBzZWVtcyBJIGRv bid0IG5lZWQgdGhlIHN1c3BlbmQgcXVpcmsgLSB0aGUgTkhJIGlzIHN0aWxsIHRoZXJlDQo+ID4g d2l0aG91dCBpdC4gSSBzdGlsbCB0aGluayB3ZSBzaG91bGQgbWFrZSB0aGUgcXVpcmsgZ2VuZXJh bCByYXRoZXIgdGhhbg0KPiA+IHR5aW5nIGl0IHRvIHRoZSBtYWNoaW5lcywgdGhlIHdvcnN0IGNh c2UgaXMgdGhhdCBpdCdsbCBqdXN0IGRvIG5vdGhpbmcuDQo+IE9rLCBhZ3JlZWQuIFRoZSAid2Fp dCIgcXVpcmsgaGFzIHRvIHJ1biBvbiBhbGwgbWFjaGluZXMgYW5kIHRoZSBvdGhlcg0KPiBvbmUg d2lsbCBmYWlsIGlmIHRoZSBBQ1BJIG1ldGhvZHMgYXJlIG5vdCB0aGVyZS4gU2hvdWxkIEkgcmVz ZW5kIHRoZQ0KPiBzZXJpZXMgb3IganVzdCB0aGUgcGF0Y2ggb3Igc2hvdWxkIEkgKG9yIGRvIHlv dSB3YW50IHRvKSBtYWtlIGENCj4gc2VwYXJhdGUgcGF0Y2g/DQoNClByb2JhYmx5IGJlc3QgdG8g YXNrIEdyZWcgLSBJJ20gZmluZSB3aXRoIHRoaXMgc3R1ZmYgZ29pbmcgdGhyb3VnaCBoaXMNCnRy ZWUgKG5vdGUgdG8gR3JlZzogdGhlIEFwcGxlIEFDUEkgcGF0Y2hlcyBJIGp1c3Qgc2VudCBuZWVk IHRvIGJlIG1lcmdlZA0KYmVmb3JlIHRoaXMgc3R1ZmYgd2lsbCB3b3JrIHByb3Blcmx5KQ0KDQot LSANCk1hdHRoZXcgR2FycmV0dCA8bWF0dGhldy5nYXJyZXR0QG5lYnVsYS5jb20+DQo=

#22 lore raw
Quoted 6 lines · Matthew Garrett
Quoted 5 lines · Andreas Noever
Quoted 3 lines · Matthew Garrett

Yeah, it seems I don't need the suspend quirk - the NHI is still there without it. I still think we should make the quirk general rather than tying it to the machines, the worst case is that it'll just do nothing.

Quoted 8 lines
Quoted 4 lines

Ok, agreed. The "wait" quirk has to run on all machines and the other one will fail if the ACPI methods are not there. Should I resend the series or just the patch or should I (or do you want to) make a separate patch?

Probably best to ask Greg - I'm fine with this stuff going through his tree (note to Greg: the Apple ACPI patches I just sent need to be merged before this stuff will work properly)

Ok, I'll still queue these up, I don't want the to be lost at all, and will wait for the ACPI patches to land before starting to complain that things don't work on my box :)

thanks,

greg k-h