Commit 5367f82a authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'thunderbolt-for-v5.13-rc1' of...

Merge tag 'thunderbolt-for-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next

Mika writes:

thunderbolt: Changes for v5.13 merge window

This includes following Thunderbolt/USB4 changes for v5.13 merge window:

  * Debugfs improvements

  * Align the inter-domain (peer-to-peer) support with the USB4
    inter-domain spec for better interoperability

  * Add support for USB4 DROM and the new product descriptor

  * More KUnit tests

  * Detailed uevent for routers

  * Few miscellaneous improvements

All these have been in linux-next without reported issues.

* tag 'thunderbolt-for-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: (24 commits)
  thunderbolt: Hide authorized attribute if router does not support PCIe tunnels
  thunderbolt: Add details to router uevent
  thunderbolt: Unlock on error path in tb_domain_add()
  thunderbolt: Add support for USB4 DROM
  thunderbolt: Check quirks in tb_switch_add()
  thunderbolt: Add KUnit tests for DMA tunnels
  thunderbolt: Add KUnit tests for XDomain properties
  net: thunderbolt: Align the driver to the USB4 networking spec
  thunderbolt: Allow multiple DMA tunnels over a single XDomain connection
  thunderbolt: Drop unused tb_port_set_initial_credits()
  thunderbolt: Use dedicated flow control for DMA tunnels
  thunderbolt: Add support for maxhopid XDomain property
  thunderbolt: Add tb_property_copy_dir()
  thunderbolt: Align XDomain protocol timeouts with the spec
  thunderbolt: Use pseudo-random number as initial property block generation
  thunderbolt: Do not re-establish XDomain DMA paths automatically
  thunderbolt: Add more logging to XDomain connections
  Documentation / thunderbolt: Drop speed/lanes entries for XDomain
  thunderbolt: Decrease control channel timeout for software connection manager
  thunderbolt: Do not pass timeout for tb_cfg_reset()
  ...
parents 9bc46a12 6f3badea
Loading
Loading
Loading
Loading
+7 −28
Original line number Diff line number Diff line
What:		/sys/bus/thunderbolt/devices/<xdomain>/rx_speed
Date:		Feb 2021
KernelVersion:	5.11
Contact:	Isaac Hazan <isaac.hazan@intel.com>
Description:	This attribute reports the XDomain RX speed per lane.
		All RX lanes run at the same speed.

What:		/sys/bus/thunderbolt/devices/<xdomain>/rx_lanes
Date:		Feb 2021
KernelVersion:	5.11
Contact:	Isaac Hazan <isaac.hazan@intel.com>
Description:	This attribute reports the number of RX lanes the XDomain
		is using simultaneously through its upstream port.

What:		/sys/bus/thunderbolt/devices/<xdomain>/tx_speed
Date:		Feb 2021
KernelVersion:	5.11
Contact:	Isaac Hazan <isaac.hazan@intel.com>
Description:	This attribute reports the XDomain TX speed per lane.
		All TX lanes run at the same speed.

What:		/sys/bus/thunderbolt/devices/<xdomain>/tx_lanes
Date:		Feb 2021
KernelVersion:	5.11
Contact:	Isaac Hazan <isaac.hazan@intel.com>
Description:	This attribute reports number of TX lanes the XDomain
		is using simultaneously through its upstream port.

What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
Date:		Jun 2018
KernelVersion:	4.17
@@ -162,6 +134,13 @@ Contact: thunderbolt-software@lists.01.org
Description:	This attribute contains name of this device extracted from
		the device DROM.

What:		/sys/bus/thunderbolt/devices/.../maxhopid
Date:		Jul 2021
KernelVersion:	5.13
Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	Only set for XDomains. The maximum HopID the other host
		supports as its input HopID.

What:		/sys/bus/thunderbolt/devices/.../rx_speed
Date:		Jan 2020
KernelVersion:	5.5
+42 −14
Original line number Diff line number Diff line
@@ -25,13 +25,13 @@
/* Protocol timeouts in ms */
#define TBNET_LOGIN_DELAY	4500
#define TBNET_LOGIN_TIMEOUT	500
#define TBNET_LOGOUT_TIMEOUT	100
#define TBNET_LOGOUT_TIMEOUT	1000

#define TBNET_RING_SIZE		256
#define TBNET_LOCAL_PATH	0xf
#define TBNET_LOGIN_RETRIES	60
#define TBNET_LOGOUT_RETRIES	5
#define TBNET_LOGOUT_RETRIES	10
#define TBNET_MATCH_FRAGS_ID	BIT(1)
#define TBNET_64K_FRAMES	BIT(2)
#define TBNET_MAX_MTU		SZ_64K
#define TBNET_FRAME_SIZE	SZ_4K
#define TBNET_MAX_PAYLOAD_SIZE	\
@@ -154,8 +154,8 @@ struct tbnet_ring {
 * @login_sent: ThunderboltIP login message successfully sent
 * @login_received: ThunderboltIP login message received from the remote
 *		    host
 * @transmit_path: HopID the other end needs to use building the
 *		   opposite side path.
 * @local_transmit_path: HopID we are using to send out packets
 * @remote_transmit_path: HopID the other end is using to send packets to us
 * @connection_lock: Lock serializing access to @login_sent,
 *		     @login_received and @transmit_path.
 * @login_retries: Number of login retries currently done
@@ -184,7 +184,8 @@ struct tbnet {
	atomic_t command_id;
	bool login_sent;
	bool login_received;
	u32 transmit_path;
	int local_transmit_path;
	int remote_transmit_path;
	struct mutex connection_lock;
	int login_retries;
	struct delayed_work login_work;
@@ -257,7 +258,7 @@ static int tbnet_login_request(struct tbnet *net, u8 sequence)
			  atomic_inc_return(&net->command_id));

	request.proto_version = TBIP_LOGIN_PROTO_VERSION;
	request.transmit_path = TBNET_LOCAL_PATH;
	request.transmit_path = net->local_transmit_path;

	return tb_xdomain_request(xd, &request, sizeof(request),
				  TB_CFG_PKG_XDOMAIN_RESP, &reply,
@@ -364,10 +365,10 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
	mutex_lock(&net->connection_lock);

	if (net->login_sent && net->login_received) {
		int retries = TBNET_LOGOUT_RETRIES;
		int ret, retries = TBNET_LOGOUT_RETRIES;

		while (send_logout && retries-- > 0) {
			int ret = tbnet_logout_request(net);
			ret = tbnet_logout_request(net);
			if (ret != -ETIMEDOUT)
				break;
		}
@@ -377,8 +378,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
		tbnet_free_buffers(&net->rx_ring);
		tbnet_free_buffers(&net->tx_ring);

		if (tb_xdomain_disable_paths(net->xd))
		ret = tb_xdomain_disable_paths(net->xd,
					       net->local_transmit_path,
					       net->rx_ring.ring->hop,
					       net->remote_transmit_path,
					       net->tx_ring.ring->hop);
		if (ret)
			netdev_warn(net->dev, "failed to disable DMA paths\n");

		tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
		net->remote_transmit_path = 0;
	}

	net->login_retries = 0;
@@ -424,7 +433,7 @@ static int tbnet_handle_packet(const void *buf, size_t size, void *data)
		if (!ret) {
			mutex_lock(&net->connection_lock);
			net->login_received = true;
			net->transmit_path = pkg->transmit_path;
			net->remote_transmit_path = pkg->transmit_path;

			/* If we reached the number of max retries or
			 * previous logout, schedule another round of
@@ -597,12 +606,18 @@ static void tbnet_connected_work(struct work_struct *work)
	if (!connected)
		return;

	ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path);
	if (ret != net->remote_transmit_path) {
		netdev_err(net->dev, "failed to allocate Rx HopID\n");
		return;
	}

	/* Both logins successful so enable the high-speed DMA paths and
	 * start the network device queue.
	 */
	ret = tb_xdomain_enable_paths(net->xd, TBNET_LOCAL_PATH,
	ret = tb_xdomain_enable_paths(net->xd, net->local_transmit_path,
				      net->rx_ring.ring->hop,
				      net->transmit_path,
				      net->remote_transmit_path,
				      net->tx_ring.ring->hop);
	if (ret) {
		netdev_err(net->dev, "failed to enable DMA paths\n");
@@ -629,6 +644,7 @@ static void tbnet_connected_work(struct work_struct *work)
err_stop_rings:
	tb_ring_stop(net->rx_ring.ring);
	tb_ring_stop(net->tx_ring.ring);
	tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
}

static void tbnet_login_work(struct work_struct *work)
@@ -851,6 +867,7 @@ static int tbnet_open(struct net_device *dev)
	struct tb_xdomain *xd = net->xd;
	u16 sof_mask, eof_mask;
	struct tb_ring *ring;
	int hopid;

	netif_carrier_off(dev);

@@ -862,6 +879,15 @@ static int tbnet_open(struct net_device *dev)
	}
	net->tx_ring.ring = ring;

	hopid = tb_xdomain_alloc_out_hopid(xd, -1);
	if (hopid < 0) {
		netdev_err(dev, "failed to allocate Tx HopID\n");
		tb_ring_free(net->tx_ring.ring);
		net->tx_ring.ring = NULL;
		return hopid;
	}
	net->local_transmit_path = hopid;

	sof_mask = BIT(TBIP_PDF_FRAME_START);
	eof_mask = BIT(TBIP_PDF_FRAME_END);

@@ -893,6 +919,8 @@ static int tbnet_stop(struct net_device *dev)

	tb_ring_free(net->rx_ring.ring);
	net->rx_ring.ring = NULL;

	tb_xdomain_release_out_hopid(net->xd, net->local_transmit_path);
	tb_ring_free(net->tx_ring.ring);
	net->tx_ring.ring = NULL;

@@ -1340,7 +1368,7 @@ static int __init tbnet_init(void)
	 * the moment.
	 */
	tb_property_add_immediate(tbnet_dir, "prtcstns",
				  TBNET_MATCH_FRAGS_ID);
				  TBNET_MATCH_FRAGS_ID | TBNET_64K_FRAMES);

	ret = tb_register_property_dir("network", tbnet_dir);
	if (ret) {
+12 −9
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@


#define TB_CTL_RX_PKG_COUNT	10
#define TB_CTL_RETRIES		4
#define TB_CTL_RETRIES		1

/**
 * struct tb_ctl - Thunderbolt control channel
@@ -29,6 +29,7 @@
 * @request_queue_lock: Lock protecting @request_queue
 * @request_queue: List of outstanding requests
 * @running: Is the control channel running at the moment
 * @timeout_msec: Default timeout for non-raw control messages
 * @callback: Callback called when hotplug message is received
 * @callback_data: Data passed to @callback
 */
@@ -43,6 +44,7 @@ struct tb_ctl {
	struct list_head request_queue;
	bool running;

	int timeout_msec;
	event_cb callback;
	void *callback_data;
};
@@ -613,6 +615,7 @@ struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl,
/**
 * tb_ctl_alloc() - allocate a control channel
 * @nhi: Pointer to NHI
 * @timeout_msec: Default timeout used with non-raw control messages
 * @cb: Callback called for plug events
 * @cb_data: Data passed to @cb
 *
@@ -620,13 +623,15 @@ struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl,
 *
 * Return: Returns a pointer on success or NULL on failure.
 */
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int timeout_msec, event_cb cb,
			    void *cb_data)
{
	int i;
	struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
	if (!ctl)
		return NULL;
	ctl->nhi = nhi;
	ctl->timeout_msec = timeout_msec;
	ctl->callback = cb;
	ctl->callback_data = cb_data;

@@ -802,14 +807,12 @@ static bool tb_cfg_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg)
 * tb_cfg_reset() - send a reset packet and wait for a response
 * @ctl: Control channel pointer
 * @route: Router string for the router to send reset
 * @timeout_msec: Timeout in ms how long to wait for the response
 *
 * If the switch at route is incorrectly configured then we will not receive a
 * reply (even though the switch will reset). The caller should check for
 * -ETIMEDOUT and attempt to reconfigure the switch.
 */
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route,
				  int timeout_msec)
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route)
{
	struct cfg_reset_pkg request = { .header = tb_cfg_make_header(route) };
	struct tb_cfg_result res = { 0 };
@@ -831,7 +834,7 @@ struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route,
	req->response_size = sizeof(reply);
	req->response_type = TB_CFG_PKG_RESET;

	res = tb_cfg_request_sync(ctl, req, timeout_msec);
	res = tb_cfg_request_sync(ctl, req, ctl->timeout_msec);

	tb_cfg_request_put(req);

@@ -1007,7 +1010,7 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,
		enum tb_cfg_space space, u32 offset, u32 length)
{
	struct tb_cfg_result res = tb_cfg_read_raw(ctl, buffer, route, port,
			space, offset, length, TB_CFG_DEFAULT_TIMEOUT);
			space, offset, length, ctl->timeout_msec);
	switch (res.err) {
	case 0:
		/* Success */
@@ -1033,7 +1036,7 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,
		 enum tb_cfg_space space, u32 offset, u32 length)
{
	struct tb_cfg_result res = tb_cfg_write_raw(ctl, buffer, route, port,
			space, offset, length, TB_CFG_DEFAULT_TIMEOUT);
			space, offset, length, ctl->timeout_msec);
	switch (res.err) {
	case 0:
		/* Success */
@@ -1071,7 +1074,7 @@ int tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route)
	u32 dummy;
	struct tb_cfg_result res = tb_cfg_read_raw(ctl, &dummy, route, 0,
						   TB_CFG_SWITCH, 0, 1,
						   TB_CFG_DEFAULT_TIMEOUT);
						   ctl->timeout_msec);
	if (res.err == 1)
		return -EIO;
	if (res.err)
+3 −5
Original line number Diff line number Diff line
@@ -21,15 +21,14 @@ struct tb_ctl;
typedef bool (*event_cb)(void *data, enum tb_cfg_pkg_type type,
			 const void *buf, size_t size);

struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data);
struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int timeout_msec, event_cb cb,
			    void *cb_data);
void tb_ctl_start(struct tb_ctl *ctl);
void tb_ctl_stop(struct tb_ctl *ctl);
void tb_ctl_free(struct tb_ctl *ctl);

/* configuration commands */

#define TB_CFG_DEFAULT_TIMEOUT 5000 /* msec */

struct tb_cfg_result {
	u64 response_route;
	u32 response_port; /*
@@ -124,8 +123,7 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route)
}

int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug);
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route,
				  int timeout_msec);
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route);
struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
				     u64 route, u32 port,
				     enum tb_cfg_space space, u32 offset,
+24 −13
Original line number Diff line number Diff line
@@ -251,6 +251,29 @@ static ssize_t counters_write(struct file *file, const char __user *user_buf,
	return ret < 0 ? ret : count;
}

static void cap_show_by_dw(struct seq_file *s, struct tb_switch *sw,
			   struct tb_port *port, unsigned int cap,
			   unsigned int offset, u8 cap_id, u8 vsec_id,
			   int dwords)
{
	int i, ret;
	u32 data;

	for (i = 0; i < dwords; i++) {
		if (port)
			ret = tb_port_read(port, &data, TB_CFG_PORT, cap + offset + i, 1);
		else
			ret = tb_sw_read(sw, &data, TB_CFG_SWITCH, cap + offset + i, 1);
		if (ret) {
			seq_printf(s, "0x%04x <not accessible>\n", cap + offset + i);
			continue;
		}

		seq_printf(s, "0x%04x %4d 0x%02x 0x%02x 0x%08x\n", cap + offset + i,
			   offset + i, cap_id, vsec_id, data);
	}
}

static void cap_show(struct seq_file *s, struct tb_switch *sw,
		     struct tb_port *port, unsigned int cap, u8 cap_id,
		     u8 vsec_id, int length)
@@ -267,10 +290,7 @@ static void cap_show(struct seq_file *s, struct tb_switch *sw,
		else
			ret = tb_sw_read(sw, data, TB_CFG_SWITCH, cap + offset, dwords);
		if (ret) {
			seq_printf(s, "0x%04x <not accessible>\n",
				   cap + offset);
			if (dwords > 1)
				seq_printf(s, "0x%04x ...\n", cap + offset + 1);
			cap_show_by_dw(s, sw, port, cap, offset, cap_id, vsec_id, length);
			return;
		}

@@ -341,15 +361,6 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s,
		} else {
			length = header.extended_short.length;
			vsec_id = header.extended_short.vsec_id;
			/*
			 * Ice Lake and Tiger Lake do not implement the
			 * full length of the capability, only first 32
			 * dwords so hard-code it here.
			 */
			if (!vsec_id &&
			    (tb_switch_is_ice_lake(port->sw) ||
			     tb_switch_is_tiger_lake(port->sw)))
				length = 32;
		}
		break;

Loading