xref: /linux/drivers/thunderbolt/xdomain.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
1fd3b339cSMika Westerberg // SPDX-License-Identifier: GPL-2.0
2d1ff7024SMika Westerberg /*
3d1ff7024SMika Westerberg  * Thunderbolt XDomain discovery protocol support
4d1ff7024SMika Westerberg  *
5d1ff7024SMika Westerberg  * Copyright (C) 2017, Intel Corporation
6d1ff7024SMika Westerberg  * Authors: Michael Jamet <michael.jamet@intel.com>
7d1ff7024SMika Westerberg  *          Mika Westerberg <mika.westerberg@linux.intel.com>
8d1ff7024SMika Westerberg  */
9d1ff7024SMika Westerberg 
10d1ff7024SMika Westerberg #include <linux/device.h>
115cc0df9cSIsaac Hazan #include <linux/delay.h>
12d1ff7024SMika Westerberg #include <linux/kmod.h>
13d1ff7024SMika Westerberg #include <linux/module.h>
142d8ff0b5SMika Westerberg #include <linux/pm_runtime.h>
15a6932c3fSMika Westerberg #include <linux/prandom.h>
1687fa05b6SAndy Shevchenko #include <linux/string_helpers.h>
17d1ff7024SMika Westerberg #include <linux/utsname.h>
18d1ff7024SMika Westerberg #include <linux/uuid.h>
19d1ff7024SMika Westerberg #include <linux/workqueue.h>
20d1ff7024SMika Westerberg 
21d1ff7024SMika Westerberg #include "tb.h"
22d1ff7024SMika Westerberg 
238e1de704SMika Westerberg #define XDOMAIN_SHORT_TIMEOUT			100	/* ms */
243bb16333SMika Westerberg #define XDOMAIN_DEFAULT_TIMEOUT			1000	/* ms */
258e1de704SMika Westerberg #define XDOMAIN_BONDING_TIMEOUT			10000	/* ms */
268e1de704SMika Westerberg #define XDOMAIN_RETRIES				10
2746b494f2SMika Westerberg #define XDOMAIN_DEFAULT_MAX_HOPID		15
28d1ff7024SMika Westerberg 
298e1de704SMika Westerberg enum {
308e1de704SMika Westerberg 	XDOMAIN_STATE_INIT,
318e1de704SMika Westerberg 	XDOMAIN_STATE_UUID,
328e1de704SMika Westerberg 	XDOMAIN_STATE_LINK_STATUS,
338e1de704SMika Westerberg 	XDOMAIN_STATE_LINK_STATE_CHANGE,
348e1de704SMika Westerberg 	XDOMAIN_STATE_LINK_STATUS2,
358e1de704SMika Westerberg 	XDOMAIN_STATE_BONDING_UUID_LOW,
368e1de704SMika Westerberg 	XDOMAIN_STATE_BONDING_UUID_HIGH,
378e1de704SMika Westerberg 	XDOMAIN_STATE_PROPERTIES,
388e1de704SMika Westerberg 	XDOMAIN_STATE_ENUMERATED,
398e1de704SMika Westerberg 	XDOMAIN_STATE_ERROR,
408e1de704SMika Westerberg };
418e1de704SMika Westerberg 
428e1de704SMika Westerberg static const char * const state_names[] = {
438e1de704SMika Westerberg 	[XDOMAIN_STATE_INIT] = "INIT",
448e1de704SMika Westerberg 	[XDOMAIN_STATE_UUID] = "UUID",
458e1de704SMika Westerberg 	[XDOMAIN_STATE_LINK_STATUS] = "LINK_STATUS",
468e1de704SMika Westerberg 	[XDOMAIN_STATE_LINK_STATE_CHANGE] = "LINK_STATE_CHANGE",
478e1de704SMika Westerberg 	[XDOMAIN_STATE_LINK_STATUS2] = "LINK_STATUS2",
488e1de704SMika Westerberg 	[XDOMAIN_STATE_BONDING_UUID_LOW] = "BONDING_UUID_LOW",
498e1de704SMika Westerberg 	[XDOMAIN_STATE_BONDING_UUID_HIGH] = "BONDING_UUID_HIGH",
508e1de704SMika Westerberg 	[XDOMAIN_STATE_PROPERTIES] = "PROPERTIES",
518e1de704SMika Westerberg 	[XDOMAIN_STATE_ENUMERATED] = "ENUMERATED",
528e1de704SMika Westerberg 	[XDOMAIN_STATE_ERROR] = "ERROR",
538e1de704SMika Westerberg };
548e1de704SMika Westerberg 
55d1ff7024SMika Westerberg struct xdomain_request_work {
56d1ff7024SMika Westerberg 	struct work_struct work;
57d1ff7024SMika Westerberg 	struct tb_xdp_header *pkg;
58d1ff7024SMika Westerberg 	struct tb *tb;
59d1ff7024SMika Westerberg };
60d1ff7024SMika Westerberg 
615ca67688SMika Westerberg static bool tb_xdomain_enabled = true;
625ca67688SMika Westerberg module_param_named(xdomain, tb_xdomain_enabled, bool, 0444);
635ca67688SMika Westerberg MODULE_PARM_DESC(xdomain, "allow XDomain protocol (default: true)");
645ca67688SMika Westerberg 
6546b494f2SMika Westerberg /*
6646b494f2SMika Westerberg  * Serializes access to the properties and protocol handlers below. If
6746b494f2SMika Westerberg  * you need to take both this lock and the struct tb_xdomain lock, take
6846b494f2SMika Westerberg  * this one first.
6946b494f2SMika Westerberg  */
70d1ff7024SMika Westerberg static DEFINE_MUTEX(xdomain_lock);
71d1ff7024SMika Westerberg 
72d1ff7024SMika Westerberg /* Properties exposed to the remote domains */
73d1ff7024SMika Westerberg static struct tb_property_dir *xdomain_property_dir;
74d1ff7024SMika Westerberg static u32 xdomain_property_block_gen;
75d1ff7024SMika Westerberg 
76d1ff7024SMika Westerberg /* Additional protocol handlers */
77d1ff7024SMika Westerberg static LIST_HEAD(protocol_handlers);
78d1ff7024SMika Westerberg 
79d1ff7024SMika Westerberg /* UUID for XDomain discovery protocol: b638d70e-42ff-40bb-97c2-90e2c0b2ff07 */
80d1ff7024SMika Westerberg static const uuid_t tb_xdp_uuid =
81d1ff7024SMika Westerberg 	UUID_INIT(0xb638d70e, 0x42ff, 0x40bb,
82d1ff7024SMika Westerberg 		  0x97, 0xc2, 0x90, 0xe2, 0xc0, 0xb2, 0xff, 0x07);
83d1ff7024SMika Westerberg 
845ca67688SMika Westerberg bool tb_is_xdomain_enabled(void)
855ca67688SMika Westerberg {
86c6da62a2SMika Westerberg 	return tb_xdomain_enabled && tb_acpi_is_xdomain_allowed();
875ca67688SMika Westerberg }
885ca67688SMika Westerberg 
89d1ff7024SMika Westerberg static bool tb_xdomain_match(const struct tb_cfg_request *req,
90d1ff7024SMika Westerberg 			     const struct ctl_pkg *pkg)
91d1ff7024SMika Westerberg {
92d1ff7024SMika Westerberg 	switch (pkg->frame.eof) {
93d1ff7024SMika Westerberg 	case TB_CFG_PKG_ERROR:
94d1ff7024SMika Westerberg 		return true;
95d1ff7024SMika Westerberg 
96d1ff7024SMika Westerberg 	case TB_CFG_PKG_XDOMAIN_RESP: {
97d1ff7024SMika Westerberg 		const struct tb_xdp_header *res_hdr = pkg->buffer;
98d1ff7024SMika Westerberg 		const struct tb_xdp_header *req_hdr = req->request;
99d1ff7024SMika Westerberg 
100d1ff7024SMika Westerberg 		if (pkg->frame.size < req->response_size / 4)
101d1ff7024SMika Westerberg 			return false;
102d1ff7024SMika Westerberg 
103d1ff7024SMika Westerberg 		/* Make sure route matches */
104d1ff7024SMika Westerberg 		if ((res_hdr->xd_hdr.route_hi & ~BIT(31)) !=
105d1ff7024SMika Westerberg 		     req_hdr->xd_hdr.route_hi)
106d1ff7024SMika Westerberg 			return false;
107d1ff7024SMika Westerberg 		if ((res_hdr->xd_hdr.route_lo) != req_hdr->xd_hdr.route_lo)
108d1ff7024SMika Westerberg 			return false;
109d1ff7024SMika Westerberg 
110d1ff7024SMika Westerberg 		/* Check that the XDomain protocol matches */
111d1ff7024SMika Westerberg 		if (!uuid_equal(&res_hdr->uuid, &req_hdr->uuid))
112d1ff7024SMika Westerberg 			return false;
113d1ff7024SMika Westerberg 
114d1ff7024SMika Westerberg 		return true;
115d1ff7024SMika Westerberg 	}
116d1ff7024SMika Westerberg 
117d1ff7024SMika Westerberg 	default:
118d1ff7024SMika Westerberg 		return false;
119d1ff7024SMika Westerberg 	}
120d1ff7024SMika Westerberg }
121d1ff7024SMika Westerberg 
122d1ff7024SMika Westerberg static bool tb_xdomain_copy(struct tb_cfg_request *req,
123d1ff7024SMika Westerberg 			    const struct ctl_pkg *pkg)
124d1ff7024SMika Westerberg {
125d1ff7024SMika Westerberg 	memcpy(req->response, pkg->buffer, req->response_size);
126d1ff7024SMika Westerberg 	req->result.err = 0;
127d1ff7024SMika Westerberg 	return true;
128d1ff7024SMika Westerberg }
129d1ff7024SMika Westerberg 
130d1ff7024SMika Westerberg static void response_ready(void *data)
131d1ff7024SMika Westerberg {
132d1ff7024SMika Westerberg 	tb_cfg_request_put(data);
133d1ff7024SMika Westerberg }
134d1ff7024SMika Westerberg 
135d1ff7024SMika Westerberg static int __tb_xdomain_response(struct tb_ctl *ctl, const void *response,
136d1ff7024SMika Westerberg 				 size_t size, enum tb_cfg_pkg_type type)
137d1ff7024SMika Westerberg {
138d1ff7024SMika Westerberg 	struct tb_cfg_request *req;
139d1ff7024SMika Westerberg 
140d1ff7024SMika Westerberg 	req = tb_cfg_request_alloc();
141d1ff7024SMika Westerberg 	if (!req)
142d1ff7024SMika Westerberg 		return -ENOMEM;
143d1ff7024SMika Westerberg 
144d1ff7024SMika Westerberg 	req->match = tb_xdomain_match;
145d1ff7024SMika Westerberg 	req->copy = tb_xdomain_copy;
146d1ff7024SMika Westerberg 	req->request = response;
147d1ff7024SMika Westerberg 	req->request_size = size;
148d1ff7024SMika Westerberg 	req->request_type = type;
149d1ff7024SMika Westerberg 
150d1ff7024SMika Westerberg 	return tb_cfg_request(ctl, req, response_ready, req);
151d1ff7024SMika Westerberg }
152d1ff7024SMika Westerberg 
153d1ff7024SMika Westerberg /**
154d1ff7024SMika Westerberg  * tb_xdomain_response() - Send a XDomain response message
155d1ff7024SMika Westerberg  * @xd: XDomain to send the message
156d1ff7024SMika Westerberg  * @response: Response to send
157d1ff7024SMika Westerberg  * @size: Size of the response
158d1ff7024SMika Westerberg  * @type: PDF type of the response
159d1ff7024SMika Westerberg  *
160d1ff7024SMika Westerberg  * This can be used to send a XDomain response message to the other
161d1ff7024SMika Westerberg  * domain. No response for the message is expected.
162d1ff7024SMika Westerberg  *
163d1ff7024SMika Westerberg  * Return: %0 in case of success and negative errno in case of failure
164d1ff7024SMika Westerberg  */
165d1ff7024SMika Westerberg int tb_xdomain_response(struct tb_xdomain *xd, const void *response,
166d1ff7024SMika Westerberg 			size_t size, enum tb_cfg_pkg_type type)
167d1ff7024SMika Westerberg {
168d1ff7024SMika Westerberg 	return __tb_xdomain_response(xd->tb->ctl, response, size, type);
169d1ff7024SMika Westerberg }
170d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_response);
171d1ff7024SMika Westerberg 
172d1ff7024SMika Westerberg static int __tb_xdomain_request(struct tb_ctl *ctl, const void *request,
173d1ff7024SMika Westerberg 	size_t request_size, enum tb_cfg_pkg_type request_type, void *response,
174d1ff7024SMika Westerberg 	size_t response_size, enum tb_cfg_pkg_type response_type,
175d1ff7024SMika Westerberg 	unsigned int timeout_msec)
176d1ff7024SMika Westerberg {
177d1ff7024SMika Westerberg 	struct tb_cfg_request *req;
178d1ff7024SMika Westerberg 	struct tb_cfg_result res;
179d1ff7024SMika Westerberg 
180d1ff7024SMika Westerberg 	req = tb_cfg_request_alloc();
181d1ff7024SMika Westerberg 	if (!req)
182d1ff7024SMika Westerberg 		return -ENOMEM;
183d1ff7024SMika Westerberg 
184d1ff7024SMika Westerberg 	req->match = tb_xdomain_match;
185d1ff7024SMika Westerberg 	req->copy = tb_xdomain_copy;
186d1ff7024SMika Westerberg 	req->request = request;
187d1ff7024SMika Westerberg 	req->request_size = request_size;
188d1ff7024SMika Westerberg 	req->request_type = request_type;
189d1ff7024SMika Westerberg 	req->response = response;
190d1ff7024SMika Westerberg 	req->response_size = response_size;
191d1ff7024SMika Westerberg 	req->response_type = response_type;
192d1ff7024SMika Westerberg 
193d1ff7024SMika Westerberg 	res = tb_cfg_request_sync(ctl, req, timeout_msec);
194d1ff7024SMika Westerberg 
195d1ff7024SMika Westerberg 	tb_cfg_request_put(req);
196d1ff7024SMika Westerberg 
197d1ff7024SMika Westerberg 	return res.err == 1 ? -EIO : res.err;
198d1ff7024SMika Westerberg }
199d1ff7024SMika Westerberg 
200d1ff7024SMika Westerberg /**
201d1ff7024SMika Westerberg  * tb_xdomain_request() - Send a XDomain request
202d1ff7024SMika Westerberg  * @xd: XDomain to send the request
203d1ff7024SMika Westerberg  * @request: Request to send
204d1ff7024SMika Westerberg  * @request_size: Size of the request in bytes
205d1ff7024SMika Westerberg  * @request_type: PDF type of the request
206d1ff7024SMika Westerberg  * @response: Response is copied here
207d1ff7024SMika Westerberg  * @response_size: Expected size of the response in bytes
208d1ff7024SMika Westerberg  * @response_type: Expected PDF type of the response
209d1ff7024SMika Westerberg  * @timeout_msec: Timeout in milliseconds to wait for the response
210d1ff7024SMika Westerberg  *
211d1ff7024SMika Westerberg  * This function can be used to send XDomain control channel messages to
212d1ff7024SMika Westerberg  * the other domain. The function waits until the response is received
213d1ff7024SMika Westerberg  * or when timeout triggers. Whichever comes first.
214d1ff7024SMika Westerberg  *
215d1ff7024SMika Westerberg  * Return: %0 in case of success and negative errno in case of failure
216d1ff7024SMika Westerberg  */
217d1ff7024SMika Westerberg int tb_xdomain_request(struct tb_xdomain *xd, const void *request,
218d1ff7024SMika Westerberg 	size_t request_size, enum tb_cfg_pkg_type request_type,
219d1ff7024SMika Westerberg 	void *response, size_t response_size,
220d1ff7024SMika Westerberg 	enum tb_cfg_pkg_type response_type, unsigned int timeout_msec)
221d1ff7024SMika Westerberg {
222d1ff7024SMika Westerberg 	return __tb_xdomain_request(xd->tb->ctl, request, request_size,
223d1ff7024SMika Westerberg 				    request_type, response, response_size,
224d1ff7024SMika Westerberg 				    response_type, timeout_msec);
225d1ff7024SMika Westerberg }
226d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_request);
227d1ff7024SMika Westerberg 
228d1ff7024SMika Westerberg static inline void tb_xdp_fill_header(struct tb_xdp_header *hdr, u64 route,
229d1ff7024SMika Westerberg 	u8 sequence, enum tb_xdp_type type, size_t size)
230d1ff7024SMika Westerberg {
231d1ff7024SMika Westerberg 	u32 length_sn;
232d1ff7024SMika Westerberg 
233d1ff7024SMika Westerberg 	length_sn = (size - sizeof(hdr->xd_hdr)) / 4;
234d1ff7024SMika Westerberg 	length_sn |= (sequence << TB_XDOMAIN_SN_SHIFT) & TB_XDOMAIN_SN_MASK;
235d1ff7024SMika Westerberg 
236d1ff7024SMika Westerberg 	hdr->xd_hdr.route_hi = upper_32_bits(route);
237d1ff7024SMika Westerberg 	hdr->xd_hdr.route_lo = lower_32_bits(route);
238d1ff7024SMika Westerberg 	hdr->xd_hdr.length_sn = length_sn;
239d1ff7024SMika Westerberg 	hdr->type = type;
240d1ff7024SMika Westerberg 	memcpy(&hdr->uuid, &tb_xdp_uuid, sizeof(tb_xdp_uuid));
241d1ff7024SMika Westerberg }
242d1ff7024SMika Westerberg 
24319813551SKees Cook static int tb_xdp_handle_error(const struct tb_xdp_error_response *res)
244d1ff7024SMika Westerberg {
24519813551SKees Cook 	if (res->hdr.type != ERROR_RESPONSE)
246d1ff7024SMika Westerberg 		return 0;
247d1ff7024SMika Westerberg 
24819813551SKees Cook 	switch (res->error) {
249d1ff7024SMika Westerberg 	case ERROR_UNKNOWN_PACKET:
250d1ff7024SMika Westerberg 	case ERROR_UNKNOWN_DOMAIN:
251d1ff7024SMika Westerberg 		return -EIO;
252d1ff7024SMika Westerberg 	case ERROR_NOT_SUPPORTED:
253*668906cfSMika Westerberg 		return -EOPNOTSUPP;
254d1ff7024SMika Westerberg 	case ERROR_NOT_READY:
255d1ff7024SMika Westerberg 		return -EAGAIN;
256d1ff7024SMika Westerberg 	default:
257d1ff7024SMika Westerberg 		break;
258d1ff7024SMika Westerberg 	}
259d1ff7024SMika Westerberg 
260d1ff7024SMika Westerberg 	return 0;
261d1ff7024SMika Westerberg }
262d1ff7024SMika Westerberg 
2633b4b3235SMika Westerberg static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry,
2648e1de704SMika Westerberg 			       uuid_t *uuid, u64 *remote_route)
2653b4b3235SMika Westerberg {
2663b4b3235SMika Westerberg 	struct tb_xdp_uuid_response res;
2673b4b3235SMika Westerberg 	struct tb_xdp_uuid req;
2683b4b3235SMika Westerberg 	int ret;
2693b4b3235SMika Westerberg 
2703b4b3235SMika Westerberg 	memset(&req, 0, sizeof(req));
2713b4b3235SMika Westerberg 	tb_xdp_fill_header(&req.hdr, route, retry % 4, UUID_REQUEST,
2723b4b3235SMika Westerberg 			   sizeof(req));
2733b4b3235SMika Westerberg 
2743b4b3235SMika Westerberg 	memset(&res, 0, sizeof(res));
2753b4b3235SMika Westerberg 	ret = __tb_xdomain_request(ctl, &req, sizeof(req),
2763b4b3235SMika Westerberg 				   TB_CFG_PKG_XDOMAIN_REQ, &res, sizeof(res),
2773b4b3235SMika Westerberg 				   TB_CFG_PKG_XDOMAIN_RESP,
2783b4b3235SMika Westerberg 				   XDOMAIN_DEFAULT_TIMEOUT);
2793b4b3235SMika Westerberg 	if (ret)
2803b4b3235SMika Westerberg 		return ret;
2813b4b3235SMika Westerberg 
28219813551SKees Cook 	ret = tb_xdp_handle_error(&res.err);
2833b4b3235SMika Westerberg 	if (ret)
2843b4b3235SMika Westerberg 		return ret;
2853b4b3235SMika Westerberg 
2863b4b3235SMika Westerberg 	uuid_copy(uuid, &res.src_uuid);
2878e1de704SMika Westerberg 	*remote_route = (u64)res.src_route_hi << 32 | res.src_route_lo;
2888e1de704SMika Westerberg 
2893b4b3235SMika Westerberg 	return 0;
2903b4b3235SMika Westerberg }
2913b4b3235SMika Westerberg 
2923b4b3235SMika Westerberg static int tb_xdp_uuid_response(struct tb_ctl *ctl, u64 route, u8 sequence,
2933b4b3235SMika Westerberg 				const uuid_t *uuid)
2943b4b3235SMika Westerberg {
2953b4b3235SMika Westerberg 	struct tb_xdp_uuid_response res;
2963b4b3235SMika Westerberg 
2973b4b3235SMika Westerberg 	memset(&res, 0, sizeof(res));
2983b4b3235SMika Westerberg 	tb_xdp_fill_header(&res.hdr, route, sequence, UUID_RESPONSE,
2993b4b3235SMika Westerberg 			   sizeof(res));
3003b4b3235SMika Westerberg 
3013b4b3235SMika Westerberg 	uuid_copy(&res.src_uuid, uuid);
3023b4b3235SMika Westerberg 	res.src_route_hi = upper_32_bits(route);
3033b4b3235SMika Westerberg 	res.src_route_lo = lower_32_bits(route);
3043b4b3235SMika Westerberg 
3053b4b3235SMika Westerberg 	return __tb_xdomain_response(ctl, &res, sizeof(res),
3063b4b3235SMika Westerberg 				     TB_CFG_PKG_XDOMAIN_RESP);
3073b4b3235SMika Westerberg }
3083b4b3235SMika Westerberg 
309d1ff7024SMika Westerberg static int tb_xdp_error_response(struct tb_ctl *ctl, u64 route, u8 sequence,
310d1ff7024SMika Westerberg 				 enum tb_xdp_error error)
311d1ff7024SMika Westerberg {
312d1ff7024SMika Westerberg 	struct tb_xdp_error_response res;
313d1ff7024SMika Westerberg 
314d1ff7024SMika Westerberg 	memset(&res, 0, sizeof(res));
315d1ff7024SMika Westerberg 	tb_xdp_fill_header(&res.hdr, route, sequence, ERROR_RESPONSE,
316d1ff7024SMika Westerberg 			   sizeof(res));
317d1ff7024SMika Westerberg 	res.error = error;
318d1ff7024SMika Westerberg 
319d1ff7024SMika Westerberg 	return __tb_xdomain_response(ctl, &res, sizeof(res),
320d1ff7024SMika Westerberg 				     TB_CFG_PKG_XDOMAIN_RESP);
321d1ff7024SMika Westerberg }
322d1ff7024SMika Westerberg 
323d1ff7024SMika Westerberg static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route,
324d1ff7024SMika Westerberg 	const uuid_t *src_uuid, const uuid_t *dst_uuid, int retry,
325d1ff7024SMika Westerberg 	u32 **block, u32 *generation)
326d1ff7024SMika Westerberg {
327d1ff7024SMika Westerberg 	struct tb_xdp_properties_response *res;
328d1ff7024SMika Westerberg 	struct tb_xdp_properties req;
329d1ff7024SMika Westerberg 	u16 data_len, len;
330d1ff7024SMika Westerberg 	size_t total_size;
331d1ff7024SMika Westerberg 	u32 *data = NULL;
332d1ff7024SMika Westerberg 	int ret;
333d1ff7024SMika Westerberg 
334d1ff7024SMika Westerberg 	total_size = sizeof(*res) + TB_XDP_PROPERTIES_MAX_DATA_LENGTH * 4;
335d1ff7024SMika Westerberg 	res = kzalloc(total_size, GFP_KERNEL);
336d1ff7024SMika Westerberg 	if (!res)
337d1ff7024SMika Westerberg 		return -ENOMEM;
338d1ff7024SMika Westerberg 
339d1ff7024SMika Westerberg 	memset(&req, 0, sizeof(req));
340d1ff7024SMika Westerberg 	tb_xdp_fill_header(&req.hdr, route, retry % 4, PROPERTIES_REQUEST,
341d1ff7024SMika Westerberg 			   sizeof(req));
342d1ff7024SMika Westerberg 	memcpy(&req.src_uuid, src_uuid, sizeof(*src_uuid));
343d1ff7024SMika Westerberg 	memcpy(&req.dst_uuid, dst_uuid, sizeof(*dst_uuid));
344d1ff7024SMika Westerberg 
345d1ff7024SMika Westerberg 	data_len = 0;
346d1ff7024SMika Westerberg 
347d1ff7024SMika Westerberg 	do {
348d1ff7024SMika Westerberg 		ret = __tb_xdomain_request(ctl, &req, sizeof(req),
349d1ff7024SMika Westerberg 					   TB_CFG_PKG_XDOMAIN_REQ, res,
350d1ff7024SMika Westerberg 					   total_size, TB_CFG_PKG_XDOMAIN_RESP,
351d1ff7024SMika Westerberg 					   XDOMAIN_DEFAULT_TIMEOUT);
352d1ff7024SMika Westerberg 		if (ret)
353d1ff7024SMika Westerberg 			goto err;
354d1ff7024SMika Westerberg 
35519813551SKees Cook 		ret = tb_xdp_handle_error(&res->err);
356d1ff7024SMika Westerberg 		if (ret)
357d1ff7024SMika Westerberg 			goto err;
358d1ff7024SMika Westerberg 
359d1ff7024SMika Westerberg 		/*
360d1ff7024SMika Westerberg 		 * Package length includes the whole payload without the
361d1ff7024SMika Westerberg 		 * XDomain header. Validate first that the package is at
362d1ff7024SMika Westerberg 		 * least size of the response structure.
363d1ff7024SMika Westerberg 		 */
364d1ff7024SMika Westerberg 		len = res->hdr.xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK;
365d1ff7024SMika Westerberg 		if (len < sizeof(*res) / 4) {
366d1ff7024SMika Westerberg 			ret = -EINVAL;
367d1ff7024SMika Westerberg 			goto err;
368d1ff7024SMika Westerberg 		}
369d1ff7024SMika Westerberg 
370d1ff7024SMika Westerberg 		len += sizeof(res->hdr.xd_hdr) / 4;
371d1ff7024SMika Westerberg 		len -= sizeof(*res) / 4;
372d1ff7024SMika Westerberg 
373d1ff7024SMika Westerberg 		if (res->offset != req.offset) {
374d1ff7024SMika Westerberg 			ret = -EINVAL;
375d1ff7024SMika Westerberg 			goto err;
376d1ff7024SMika Westerberg 		}
377d1ff7024SMika Westerberg 
378d1ff7024SMika Westerberg 		/*
379d1ff7024SMika Westerberg 		 * First time allocate block that has enough space for
380d1ff7024SMika Westerberg 		 * the whole properties block.
381d1ff7024SMika Westerberg 		 */
382d1ff7024SMika Westerberg 		if (!data) {
383d1ff7024SMika Westerberg 			data_len = res->data_length;
384d1ff7024SMika Westerberg 			if (data_len > TB_XDP_PROPERTIES_MAX_LENGTH) {
385d1ff7024SMika Westerberg 				ret = -E2BIG;
386d1ff7024SMika Westerberg 				goto err;
387d1ff7024SMika Westerberg 			}
388d1ff7024SMika Westerberg 
389d1ff7024SMika Westerberg 			data = kcalloc(data_len, sizeof(u32), GFP_KERNEL);
390d1ff7024SMika Westerberg 			if (!data) {
391d1ff7024SMika Westerberg 				ret = -ENOMEM;
392d1ff7024SMika Westerberg 				goto err;
393d1ff7024SMika Westerberg 			}
394d1ff7024SMika Westerberg 		}
395d1ff7024SMika Westerberg 
396d1ff7024SMika Westerberg 		memcpy(data + req.offset, res->data, len * 4);
397d1ff7024SMika Westerberg 		req.offset += len;
398d1ff7024SMika Westerberg 	} while (!data_len || req.offset < data_len);
399d1ff7024SMika Westerberg 
400d1ff7024SMika Westerberg 	*block = data;
401d1ff7024SMika Westerberg 	*generation = res->generation;
402d1ff7024SMika Westerberg 
403d1ff7024SMika Westerberg 	kfree(res);
404d1ff7024SMika Westerberg 
405d1ff7024SMika Westerberg 	return data_len;
406d1ff7024SMika Westerberg 
407d1ff7024SMika Westerberg err:
408d1ff7024SMika Westerberg 	kfree(data);
409d1ff7024SMika Westerberg 	kfree(res);
410d1ff7024SMika Westerberg 
411d1ff7024SMika Westerberg 	return ret;
412d1ff7024SMika Westerberg }
413d1ff7024SMika Westerberg 
414d1ff7024SMika Westerberg static int tb_xdp_properties_response(struct tb *tb, struct tb_ctl *ctl,
41546b494f2SMika Westerberg 	struct tb_xdomain *xd, u8 sequence, const struct tb_xdp_properties *req)
416d1ff7024SMika Westerberg {
417d1ff7024SMika Westerberg 	struct tb_xdp_properties_response *res;
418d1ff7024SMika Westerberg 	size_t total_size;
419d1ff7024SMika Westerberg 	u16 len;
420d1ff7024SMika Westerberg 	int ret;
421d1ff7024SMika Westerberg 
422d1ff7024SMika Westerberg 	/*
423d1ff7024SMika Westerberg 	 * Currently we expect all requests to be directed to us. The
424d1ff7024SMika Westerberg 	 * protocol supports forwarding, though which we might add
425d1ff7024SMika Westerberg 	 * support later on.
426d1ff7024SMika Westerberg 	 */
42746b494f2SMika Westerberg 	if (!uuid_equal(xd->local_uuid, &req->dst_uuid)) {
42846b494f2SMika Westerberg 		tb_xdp_error_response(ctl, xd->route, sequence,
429d1ff7024SMika Westerberg 				      ERROR_UNKNOWN_DOMAIN);
430d1ff7024SMika Westerberg 		return 0;
431d1ff7024SMika Westerberg 	}
432d1ff7024SMika Westerberg 
43346b494f2SMika Westerberg 	mutex_lock(&xd->lock);
434d1ff7024SMika Westerberg 
43546b494f2SMika Westerberg 	if (req->offset >= xd->local_property_block_len) {
43646b494f2SMika Westerberg 		mutex_unlock(&xd->lock);
437d1ff7024SMika Westerberg 		return -EINVAL;
438d1ff7024SMika Westerberg 	}
439d1ff7024SMika Westerberg 
44046b494f2SMika Westerberg 	len = xd->local_property_block_len - req->offset;
441d1ff7024SMika Westerberg 	len = min_t(u16, len, TB_XDP_PROPERTIES_MAX_DATA_LENGTH);
442d1ff7024SMika Westerberg 	total_size = sizeof(*res) + len * 4;
443d1ff7024SMika Westerberg 
444d1ff7024SMika Westerberg 	res = kzalloc(total_size, GFP_KERNEL);
445d1ff7024SMika Westerberg 	if (!res) {
44646b494f2SMika Westerberg 		mutex_unlock(&xd->lock);
447d1ff7024SMika Westerberg 		return -ENOMEM;
448d1ff7024SMika Westerberg 	}
449d1ff7024SMika Westerberg 
45046b494f2SMika Westerberg 	tb_xdp_fill_header(&res->hdr, xd->route, sequence, PROPERTIES_RESPONSE,
451d1ff7024SMika Westerberg 			   total_size);
45246b494f2SMika Westerberg 	res->generation = xd->local_property_block_gen;
45346b494f2SMika Westerberg 	res->data_length = xd->local_property_block_len;
454d1ff7024SMika Westerberg 	res->offset = req->offset;
45546b494f2SMika Westerberg 	uuid_copy(&res->src_uuid, xd->local_uuid);
456d1ff7024SMika Westerberg 	uuid_copy(&res->dst_uuid, &req->src_uuid);
45746b494f2SMika Westerberg 	memcpy(res->data, &xd->local_property_block[req->offset], len * 4);
458d1ff7024SMika Westerberg 
45946b494f2SMika Westerberg 	mutex_unlock(&xd->lock);
460d1ff7024SMika Westerberg 
461d1ff7024SMika Westerberg 	ret = __tb_xdomain_response(ctl, res, total_size,
462d1ff7024SMika Westerberg 				    TB_CFG_PKG_XDOMAIN_RESP);
463d1ff7024SMika Westerberg 
464d1ff7024SMika Westerberg 	kfree(res);
465d1ff7024SMika Westerberg 	return ret;
466d1ff7024SMika Westerberg }
467d1ff7024SMika Westerberg 
468d1ff7024SMika Westerberg static int tb_xdp_properties_changed_request(struct tb_ctl *ctl, u64 route,
469d1ff7024SMika Westerberg 					     int retry, const uuid_t *uuid)
470d1ff7024SMika Westerberg {
471d1ff7024SMika Westerberg 	struct tb_xdp_properties_changed_response res;
472d1ff7024SMika Westerberg 	struct tb_xdp_properties_changed req;
473d1ff7024SMika Westerberg 	int ret;
474d1ff7024SMika Westerberg 
475d1ff7024SMika Westerberg 	memset(&req, 0, sizeof(req));
476d1ff7024SMika Westerberg 	tb_xdp_fill_header(&req.hdr, route, retry % 4,
477d1ff7024SMika Westerberg 			   PROPERTIES_CHANGED_REQUEST, sizeof(req));
478d1ff7024SMika Westerberg 	uuid_copy(&req.src_uuid, uuid);
479d1ff7024SMika Westerberg 
480d1ff7024SMika Westerberg 	memset(&res, 0, sizeof(res));
481d1ff7024SMika Westerberg 	ret = __tb_xdomain_request(ctl, &req, sizeof(req),
482d1ff7024SMika Westerberg 				   TB_CFG_PKG_XDOMAIN_REQ, &res, sizeof(res),
483d1ff7024SMika Westerberg 				   TB_CFG_PKG_XDOMAIN_RESP,
484d1ff7024SMika Westerberg 				   XDOMAIN_DEFAULT_TIMEOUT);
485d1ff7024SMika Westerberg 	if (ret)
486d1ff7024SMika Westerberg 		return ret;
487d1ff7024SMika Westerberg 
48819813551SKees Cook 	return tb_xdp_handle_error(&res.err);
489d1ff7024SMika Westerberg }
490d1ff7024SMika Westerberg 
491d1ff7024SMika Westerberg static int
492d1ff7024SMika Westerberg tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, u8 sequence)
493d1ff7024SMika Westerberg {
494d1ff7024SMika Westerberg 	struct tb_xdp_properties_changed_response res;
495d1ff7024SMika Westerberg 
496d1ff7024SMika Westerberg 	memset(&res, 0, sizeof(res));
497d1ff7024SMika Westerberg 	tb_xdp_fill_header(&res.hdr, route, sequence,
498d1ff7024SMika Westerberg 			   PROPERTIES_CHANGED_RESPONSE, sizeof(res));
499d1ff7024SMika Westerberg 	return __tb_xdomain_response(ctl, &res, sizeof(res),
500d1ff7024SMika Westerberg 				     TB_CFG_PKG_XDOMAIN_RESP);
501d1ff7024SMika Westerberg }
502d1ff7024SMika Westerberg 
5038e1de704SMika Westerberg static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route,
5048e1de704SMika Westerberg 					    u8 sequence, u8 *slw, u8 *tlw,
5058e1de704SMika Westerberg 					    u8 *sls, u8 *tls)
5068e1de704SMika Westerberg {
5078e1de704SMika Westerberg 	struct tb_xdp_link_state_status_response res;
5088e1de704SMika Westerberg 	struct tb_xdp_link_state_status req;
5098e1de704SMika Westerberg 	int ret;
5108e1de704SMika Westerberg 
5118e1de704SMika Westerberg 	memset(&req, 0, sizeof(req));
5128e1de704SMika Westerberg 	tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_STATUS_REQUEST,
5138e1de704SMika Westerberg 			   sizeof(req));
5148e1de704SMika Westerberg 
5158e1de704SMika Westerberg 	memset(&res, 0, sizeof(res));
5168e1de704SMika Westerberg 	ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ,
5178e1de704SMika Westerberg 				   &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP,
5188e1de704SMika Westerberg 				   XDOMAIN_DEFAULT_TIMEOUT);
5198e1de704SMika Westerberg 	if (ret)
5208e1de704SMika Westerberg 		return ret;
5218e1de704SMika Westerberg 
5228e1de704SMika Westerberg 	ret = tb_xdp_handle_error(&res.err);
5238e1de704SMika Westerberg 	if (ret)
5248e1de704SMika Westerberg 		return ret;
5258e1de704SMika Westerberg 
5268e1de704SMika Westerberg 	if (res.status != 0)
5278e1de704SMika Westerberg 		return -EREMOTEIO;
5288e1de704SMika Westerberg 
5298e1de704SMika Westerberg 	*slw = res.slw;
5308e1de704SMika Westerberg 	*tlw = res.tlw;
5318e1de704SMika Westerberg 	*sls = res.sls;
5328e1de704SMika Westerberg 	*tls = res.tls;
5338e1de704SMika Westerberg 
5348e1de704SMika Westerberg 	return 0;
5358e1de704SMika Westerberg }
5368e1de704SMika Westerberg 
5378e1de704SMika Westerberg static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl,
5388e1de704SMika Westerberg 					     struct tb_xdomain *xd, u8 sequence)
5398e1de704SMika Westerberg {
5408e1de704SMika Westerberg 	struct tb_xdp_link_state_status_response res;
54117fb1a3dSMika Westerberg 	struct tb_port *port = tb_xdomain_downstream_port(xd);
5428e1de704SMika Westerberg 	u32 val[2];
5438e1de704SMika Westerberg 	int ret;
5448e1de704SMika Westerberg 
5458e1de704SMika Westerberg 	memset(&res, 0, sizeof(res));
5468e1de704SMika Westerberg 	tb_xdp_fill_header(&res.hdr, xd->route, sequence,
5478e1de704SMika Westerberg 			   LINK_STATE_STATUS_RESPONSE, sizeof(res));
5488e1de704SMika Westerberg 
5498e1de704SMika Westerberg 	ret = tb_port_read(port, val, TB_CFG_PORT,
5508e1de704SMika Westerberg 			   port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val));
5518e1de704SMika Westerberg 	if (ret)
5528e1de704SMika Westerberg 		return ret;
5538e1de704SMika Westerberg 
5548e1de704SMika Westerberg 	res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >>
5558e1de704SMika Westerberg 			LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT;
5568e1de704SMika Westerberg 	res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >>
5578e1de704SMika Westerberg 			LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT;
5588e1de704SMika Westerberg 	res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK;
5598e1de704SMika Westerberg 	res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >>
5608e1de704SMika Westerberg 			LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
5618e1de704SMika Westerberg 
5628e1de704SMika Westerberg 	return __tb_xdomain_response(ctl, &res, sizeof(res),
5638e1de704SMika Westerberg 				     TB_CFG_PKG_XDOMAIN_RESP);
5648e1de704SMika Westerberg }
5658e1de704SMika Westerberg 
5668e1de704SMika Westerberg static int tb_xdp_link_state_change_request(struct tb_ctl *ctl, u64 route,
5678e1de704SMika Westerberg 					    u8 sequence, u8 tlw, u8 tls)
5688e1de704SMika Westerberg {
5698e1de704SMika Westerberg 	struct tb_xdp_link_state_change_response res;
5708e1de704SMika Westerberg 	struct tb_xdp_link_state_change req;
5718e1de704SMika Westerberg 	int ret;
5728e1de704SMika Westerberg 
5738e1de704SMika Westerberg 	memset(&req, 0, sizeof(req));
5748e1de704SMika Westerberg 	tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_CHANGE_REQUEST,
5758e1de704SMika Westerberg 			   sizeof(req));
5768e1de704SMika Westerberg 	req.tlw = tlw;
5778e1de704SMika Westerberg 	req.tls = tls;
5788e1de704SMika Westerberg 
5798e1de704SMika Westerberg 	memset(&res, 0, sizeof(res));
5808e1de704SMika Westerberg 	ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ,
5818e1de704SMika Westerberg 				   &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP,
5828e1de704SMika Westerberg 				   XDOMAIN_DEFAULT_TIMEOUT);
5838e1de704SMika Westerberg 	if (ret)
5848e1de704SMika Westerberg 		return ret;
5858e1de704SMika Westerberg 
5868e1de704SMika Westerberg 	ret = tb_xdp_handle_error(&res.err);
5878e1de704SMika Westerberg 	if (ret)
5888e1de704SMika Westerberg 		return ret;
5898e1de704SMika Westerberg 
5908e1de704SMika Westerberg 	return res.status != 0 ? -EREMOTEIO : 0;
5918e1de704SMika Westerberg }
5928e1de704SMika Westerberg 
5938e1de704SMika Westerberg static int tb_xdp_link_state_change_response(struct tb_ctl *ctl, u64 route,
5948e1de704SMika Westerberg 					     u8 sequence, u32 status)
5958e1de704SMika Westerberg {
5968e1de704SMika Westerberg 	struct tb_xdp_link_state_change_response res;
5978e1de704SMika Westerberg 
5988e1de704SMika Westerberg 	memset(&res, 0, sizeof(res));
5998e1de704SMika Westerberg 	tb_xdp_fill_header(&res.hdr, route, sequence, LINK_STATE_CHANGE_RESPONSE,
6008e1de704SMika Westerberg 			   sizeof(res));
6018e1de704SMika Westerberg 
6028e1de704SMika Westerberg 	res.status = status;
6038e1de704SMika Westerberg 
6048e1de704SMika Westerberg 	return __tb_xdomain_response(ctl, &res, sizeof(res),
6058e1de704SMika Westerberg 				     TB_CFG_PKG_XDOMAIN_RESP);
6068e1de704SMika Westerberg }
6078e1de704SMika Westerberg 
608d1ff7024SMika Westerberg /**
609d1ff7024SMika Westerberg  * tb_register_protocol_handler() - Register protocol handler
610d1ff7024SMika Westerberg  * @handler: Handler to register
611d1ff7024SMika Westerberg  *
612d1ff7024SMika Westerberg  * This allows XDomain service drivers to hook into incoming XDomain
613d1ff7024SMika Westerberg  * messages. After this function is called the service driver needs to
614d1ff7024SMika Westerberg  * be able to handle calls to callback whenever a package with the
615d1ff7024SMika Westerberg  * registered protocol is received.
616d1ff7024SMika Westerberg  */
617d1ff7024SMika Westerberg int tb_register_protocol_handler(struct tb_protocol_handler *handler)
618d1ff7024SMika Westerberg {
619d1ff7024SMika Westerberg 	if (!handler->uuid || !handler->callback)
620d1ff7024SMika Westerberg 		return -EINVAL;
621d1ff7024SMika Westerberg 	if (uuid_equal(handler->uuid, &tb_xdp_uuid))
622d1ff7024SMika Westerberg 		return -EINVAL;
623d1ff7024SMika Westerberg 
624d1ff7024SMika Westerberg 	mutex_lock(&xdomain_lock);
625d1ff7024SMika Westerberg 	list_add_tail(&handler->list, &protocol_handlers);
626d1ff7024SMika Westerberg 	mutex_unlock(&xdomain_lock);
627d1ff7024SMika Westerberg 
628d1ff7024SMika Westerberg 	return 0;
629d1ff7024SMika Westerberg }
630d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_register_protocol_handler);
631d1ff7024SMika Westerberg 
632d1ff7024SMika Westerberg /**
633d1ff7024SMika Westerberg  * tb_unregister_protocol_handler() - Unregister protocol handler
634d1ff7024SMika Westerberg  * @handler: Handler to unregister
635d1ff7024SMika Westerberg  *
636d1ff7024SMika Westerberg  * Removes the previously registered protocol handler.
637d1ff7024SMika Westerberg  */
638d1ff7024SMika Westerberg void tb_unregister_protocol_handler(struct tb_protocol_handler *handler)
639d1ff7024SMika Westerberg {
640d1ff7024SMika Westerberg 	mutex_lock(&xdomain_lock);
641d1ff7024SMika Westerberg 	list_del_init(&handler->list);
642d1ff7024SMika Westerberg 	mutex_unlock(&xdomain_lock);
643d1ff7024SMika Westerberg }
644d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_unregister_protocol_handler);
645d1ff7024SMika Westerberg 
64646b494f2SMika Westerberg static void update_property_block(struct tb_xdomain *xd)
6478a00c67eSMika Westerberg {
6488a00c67eSMika Westerberg 	mutex_lock(&xdomain_lock);
64946b494f2SMika Westerberg 	mutex_lock(&xd->lock);
65046b494f2SMika Westerberg 	/*
65146b494f2SMika Westerberg 	 * If the local property block is not up-to-date, rebuild it now
65246b494f2SMika Westerberg 	 * based on the global property template.
65346b494f2SMika Westerberg 	 */
65446b494f2SMika Westerberg 	if (!xd->local_property_block ||
65546b494f2SMika Westerberg 	    xd->local_property_block_gen < xdomain_property_block_gen) {
65646b494f2SMika Westerberg 		struct tb_property_dir *dir;
65746b494f2SMika Westerberg 		int ret, block_len;
65846b494f2SMika Westerberg 		u32 *block;
65946b494f2SMika Westerberg 
66046b494f2SMika Westerberg 		dir = tb_property_copy_dir(xdomain_property_dir);
66146b494f2SMika Westerberg 		if (!dir) {
66246b494f2SMika Westerberg 			dev_warn(&xd->dev, "failed to copy properties\n");
66346b494f2SMika Westerberg 			goto out_unlock;
6648a00c67eSMika Westerberg 		}
66546b494f2SMika Westerberg 
66646b494f2SMika Westerberg 		/* Fill in non-static properties now */
66746b494f2SMika Westerberg 		tb_property_add_text(dir, "deviceid", utsname()->nodename);
66846b494f2SMika Westerberg 		tb_property_add_immediate(dir, "maxhopid", xd->local_max_hopid);
66946b494f2SMika Westerberg 
67046b494f2SMika Westerberg 		ret = tb_property_format_dir(dir, NULL, 0);
67146b494f2SMika Westerberg 		if (ret < 0) {
67246b494f2SMika Westerberg 			dev_warn(&xd->dev, "local property block creation failed\n");
67346b494f2SMika Westerberg 			tb_property_free_dir(dir);
67446b494f2SMika Westerberg 			goto out_unlock;
67546b494f2SMika Westerberg 		}
67646b494f2SMika Westerberg 
67746b494f2SMika Westerberg 		block_len = ret;
67846b494f2SMika Westerberg 		block = kcalloc(block_len, sizeof(*block), GFP_KERNEL);
67946b494f2SMika Westerberg 		if (!block) {
68046b494f2SMika Westerberg 			tb_property_free_dir(dir);
68146b494f2SMika Westerberg 			goto out_unlock;
68246b494f2SMika Westerberg 		}
68346b494f2SMika Westerberg 
68446b494f2SMika Westerberg 		ret = tb_property_format_dir(dir, block, block_len);
68546b494f2SMika Westerberg 		if (ret) {
68646b494f2SMika Westerberg 			dev_warn(&xd->dev, "property block generation failed\n");
68746b494f2SMika Westerberg 			tb_property_free_dir(dir);
68846b494f2SMika Westerberg 			kfree(block);
68946b494f2SMika Westerberg 			goto out_unlock;
69046b494f2SMika Westerberg 		}
69146b494f2SMika Westerberg 
69246b494f2SMika Westerberg 		tb_property_free_dir(dir);
69346b494f2SMika Westerberg 		/* Release the previous block */
69446b494f2SMika Westerberg 		kfree(xd->local_property_block);
69546b494f2SMika Westerberg 		/* Assign new one */
69646b494f2SMika Westerberg 		xd->local_property_block = block;
69746b494f2SMika Westerberg 		xd->local_property_block_len = block_len;
69846b494f2SMika Westerberg 		xd->local_property_block_gen = xdomain_property_block_gen;
69946b494f2SMika Westerberg 	}
70046b494f2SMika Westerberg 
70146b494f2SMika Westerberg out_unlock:
70246b494f2SMika Westerberg 	mutex_unlock(&xd->lock);
7038a00c67eSMika Westerberg 	mutex_unlock(&xdomain_lock);
7048a00c67eSMika Westerberg }
7058a00c67eSMika Westerberg 
706308092d0SMika Westerberg static void start_handshake(struct tb_xdomain *xd)
707308092d0SMika Westerberg {
708308092d0SMika Westerberg 	xd->state = XDOMAIN_STATE_INIT;
709308092d0SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
710308092d0SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
711308092d0SMika Westerberg }
712308092d0SMika Westerberg 
713308092d0SMika Westerberg /* Can be called from state_work */
714308092d0SMika Westerberg static void __stop_handshake(struct tb_xdomain *xd)
715308092d0SMika Westerberg {
716308092d0SMika Westerberg 	cancel_delayed_work_sync(&xd->properties_changed_work);
717308092d0SMika Westerberg 	xd->properties_changed_retries = 0;
718308092d0SMika Westerberg 	xd->state_retries = 0;
719308092d0SMika Westerberg }
720308092d0SMika Westerberg 
721308092d0SMika Westerberg static void stop_handshake(struct tb_xdomain *xd)
722308092d0SMika Westerberg {
723308092d0SMika Westerberg 	cancel_delayed_work_sync(&xd->state_work);
724308092d0SMika Westerberg 	__stop_handshake(xd);
725308092d0SMika Westerberg }
726308092d0SMika Westerberg 
727d1ff7024SMika Westerberg static void tb_xdp_handle_request(struct work_struct *work)
728d1ff7024SMika Westerberg {
729d1ff7024SMika Westerberg 	struct xdomain_request_work *xw = container_of(work, typeof(*xw), work);
730d1ff7024SMika Westerberg 	const struct tb_xdp_header *pkg = xw->pkg;
731d1ff7024SMika Westerberg 	const struct tb_xdomain_header *xhdr = &pkg->xd_hdr;
732d1ff7024SMika Westerberg 	struct tb *tb = xw->tb;
733d1ff7024SMika Westerberg 	struct tb_ctl *ctl = tb->ctl;
73446b494f2SMika Westerberg 	struct tb_xdomain *xd;
735d1ff7024SMika Westerberg 	const uuid_t *uuid;
736d1ff7024SMika Westerberg 	int ret = 0;
7379a03c3d3SDan Carpenter 	u32 sequence;
738d1ff7024SMika Westerberg 	u64 route;
739d1ff7024SMika Westerberg 
740d1ff7024SMika Westerberg 	route = ((u64)xhdr->route_hi << 32 | xhdr->route_lo) & ~BIT_ULL(63);
741d1ff7024SMika Westerberg 	sequence = xhdr->length_sn & TB_XDOMAIN_SN_MASK;
742d1ff7024SMika Westerberg 	sequence >>= TB_XDOMAIN_SN_SHIFT;
743d1ff7024SMika Westerberg 
744d1ff7024SMika Westerberg 	mutex_lock(&tb->lock);
745d1ff7024SMika Westerberg 	if (tb->root_switch)
746d1ff7024SMika Westerberg 		uuid = tb->root_switch->uuid;
747d1ff7024SMika Westerberg 	else
748d1ff7024SMika Westerberg 		uuid = NULL;
749d1ff7024SMika Westerberg 	mutex_unlock(&tb->lock);
750d1ff7024SMika Westerberg 
751d1ff7024SMika Westerberg 	if (!uuid) {
752d1ff7024SMika Westerberg 		tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_READY);
753d1ff7024SMika Westerberg 		goto out;
754d1ff7024SMika Westerberg 	}
755d1ff7024SMika Westerberg 
75646b494f2SMika Westerberg 	xd = tb_xdomain_find_by_route_locked(tb, route);
75746b494f2SMika Westerberg 	if (xd)
75846b494f2SMika Westerberg 		update_property_block(xd);
75946b494f2SMika Westerberg 
760d1ff7024SMika Westerberg 	switch (pkg->type) {
761d1ff7024SMika Westerberg 	case PROPERTIES_REQUEST:
7628e1de704SMika Westerberg 		tb_dbg(tb, "%llx: received XDomain properties request\n", route);
76346b494f2SMika Westerberg 		if (xd) {
76446b494f2SMika Westerberg 			ret = tb_xdp_properties_response(tb, ctl, xd, sequence,
765d1ff7024SMika Westerberg 				(const struct tb_xdp_properties *)pkg);
76646b494f2SMika Westerberg 		}
767d1ff7024SMika Westerberg 		break;
768d1ff7024SMika Westerberg 
76946b494f2SMika Westerberg 	case PROPERTIES_CHANGED_REQUEST:
7708e1de704SMika Westerberg 		tb_dbg(tb, "%llx: received XDomain properties changed request\n",
7718e1de704SMika Westerberg 		       route);
7728e1de704SMika Westerberg 
773d1ff7024SMika Westerberg 		ret = tb_xdp_properties_changed_response(ctl, route, sequence);
774d1ff7024SMika Westerberg 
775d1ff7024SMika Westerberg 		/*
776d1ff7024SMika Westerberg 		 * Since the properties have been changed, let's update
777d1ff7024SMika Westerberg 		 * the xdomain related to this connection as well in
778d1ff7024SMika Westerberg 		 * case there is a change in services it offers.
779d1ff7024SMika Westerberg 		 */
7808e1de704SMika Westerberg 		if (xd && device_is_registered(&xd->dev))
7818e1de704SMika Westerberg 			queue_delayed_work(tb->wq, &xd->state_work,
7828e1de704SMika Westerberg 					   msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
783d1ff7024SMika Westerberg 		break;
784d1ff7024SMika Westerberg 
7853b4b3235SMika Westerberg 	case UUID_REQUEST_OLD:
7863b4b3235SMika Westerberg 	case UUID_REQUEST:
7878e1de704SMika Westerberg 		tb_dbg(tb, "%llx: received XDomain UUID request\n", route);
7883b4b3235SMika Westerberg 		ret = tb_xdp_uuid_response(ctl, route, sequence, uuid);
789308092d0SMika Westerberg 		/*
790308092d0SMika Westerberg 		 * If we've stopped the discovery with an error such as
791308092d0SMika Westerberg 		 * timing out, we will restart the handshake now that we
792308092d0SMika Westerberg 		 * received UUID request from the remote host.
793308092d0SMika Westerberg 		 */
794308092d0SMika Westerberg 		if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) {
795308092d0SMika Westerberg 			dev_dbg(&xd->dev, "restarting handshake\n");
796308092d0SMika Westerberg 			start_handshake(xd);
797308092d0SMika Westerberg 		}
7983b4b3235SMika Westerberg 		break;
7993b4b3235SMika Westerberg 
8008e1de704SMika Westerberg 	case LINK_STATE_STATUS_REQUEST:
8018e1de704SMika Westerberg 		tb_dbg(tb, "%llx: received XDomain link state status request\n",
8028e1de704SMika Westerberg 		       route);
8038e1de704SMika Westerberg 
8048e1de704SMika Westerberg 		if (xd) {
8058e1de704SMika Westerberg 			ret = tb_xdp_link_state_status_response(tb, ctl, xd,
8068e1de704SMika Westerberg 								sequence);
8078e1de704SMika Westerberg 		} else {
8088e1de704SMika Westerberg 			tb_xdp_error_response(ctl, route, sequence,
8098e1de704SMika Westerberg 					      ERROR_NOT_READY);
8108e1de704SMika Westerberg 		}
8118e1de704SMika Westerberg 		break;
8128e1de704SMika Westerberg 
8138e1de704SMika Westerberg 	case LINK_STATE_CHANGE_REQUEST:
8148e1de704SMika Westerberg 		tb_dbg(tb, "%llx: received XDomain link state change request\n",
8158e1de704SMika Westerberg 		       route);
8168e1de704SMika Westerberg 
8178e1de704SMika Westerberg 		if (xd && xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH) {
8188e1de704SMika Westerberg 			const struct tb_xdp_link_state_change *lsc =
8198e1de704SMika Westerberg 				(const struct tb_xdp_link_state_change *)pkg;
8208e1de704SMika Westerberg 
8218e1de704SMika Westerberg 			ret = tb_xdp_link_state_change_response(ctl, route,
8228e1de704SMika Westerberg 								sequence, 0);
8238e1de704SMika Westerberg 			xd->target_link_width = lsc->tlw;
8248e1de704SMika Westerberg 			queue_delayed_work(tb->wq, &xd->state_work,
8258e1de704SMika Westerberg 					   msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
8268e1de704SMika Westerberg 		} else {
8278e1de704SMika Westerberg 			tb_xdp_error_response(ctl, route, sequence,
8288e1de704SMika Westerberg 					      ERROR_NOT_READY);
8298e1de704SMika Westerberg 		}
8308e1de704SMika Westerberg 		break;
8318e1de704SMika Westerberg 
832d1ff7024SMika Westerberg 	default:
8338e1de704SMika Westerberg 		tb_dbg(tb, "%llx: unknown XDomain request %#x\n", route, pkg->type);
8343b4b3235SMika Westerberg 		tb_xdp_error_response(ctl, route, sequence,
8353b4b3235SMika Westerberg 				      ERROR_NOT_SUPPORTED);
836d1ff7024SMika Westerberg 		break;
837d1ff7024SMika Westerberg 	}
838d1ff7024SMika Westerberg 
83946b494f2SMika Westerberg 	tb_xdomain_put(xd);
84046b494f2SMika Westerberg 
841d1ff7024SMika Westerberg 	if (ret) {
842d1ff7024SMika Westerberg 		tb_warn(tb, "failed to send XDomain response for %#x\n",
843d1ff7024SMika Westerberg 			pkg->type);
844d1ff7024SMika Westerberg 	}
845d1ff7024SMika Westerberg 
846d1ff7024SMika Westerberg out:
847d1ff7024SMika Westerberg 	kfree(xw->pkg);
848d1ff7024SMika Westerberg 	kfree(xw);
849559c1e1eSMika Westerberg 
850559c1e1eSMika Westerberg 	tb_domain_put(tb);
851d1ff7024SMika Westerberg }
852d1ff7024SMika Westerberg 
85348f40b96SAditya Pakki static bool
854d1ff7024SMika Westerberg tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr,
855d1ff7024SMika Westerberg 			size_t size)
856d1ff7024SMika Westerberg {
857d1ff7024SMika Westerberg 	struct xdomain_request_work *xw;
858d1ff7024SMika Westerberg 
859d1ff7024SMika Westerberg 	xw = kmalloc(sizeof(*xw), GFP_KERNEL);
860d1ff7024SMika Westerberg 	if (!xw)
86148f40b96SAditya Pakki 		return false;
862d1ff7024SMika Westerberg 
863d1ff7024SMika Westerberg 	INIT_WORK(&xw->work, tb_xdp_handle_request);
864d1ff7024SMika Westerberg 	xw->pkg = kmemdup(hdr, size, GFP_KERNEL);
86548f40b96SAditya Pakki 	if (!xw->pkg) {
86648f40b96SAditya Pakki 		kfree(xw);
86748f40b96SAditya Pakki 		return false;
86848f40b96SAditya Pakki 	}
869559c1e1eSMika Westerberg 	xw->tb = tb_domain_get(tb);
870d1ff7024SMika Westerberg 
871559c1e1eSMika Westerberg 	schedule_work(&xw->work);
87248f40b96SAditya Pakki 	return true;
873d1ff7024SMika Westerberg }
874d1ff7024SMika Westerberg 
875d1ff7024SMika Westerberg /**
876d1ff7024SMika Westerberg  * tb_register_service_driver() - Register XDomain service driver
877d1ff7024SMika Westerberg  * @drv: Driver to register
878d1ff7024SMika Westerberg  *
879d1ff7024SMika Westerberg  * Registers new service driver from @drv to the bus.
880d1ff7024SMika Westerberg  */
881d1ff7024SMika Westerberg int tb_register_service_driver(struct tb_service_driver *drv)
882d1ff7024SMika Westerberg {
883d1ff7024SMika Westerberg 	drv->driver.bus = &tb_bus_type;
884d1ff7024SMika Westerberg 	return driver_register(&drv->driver);
885d1ff7024SMika Westerberg }
886d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_register_service_driver);
887d1ff7024SMika Westerberg 
888d1ff7024SMika Westerberg /**
889d1ff7024SMika Westerberg  * tb_unregister_service_driver() - Unregister XDomain service driver
890925a2af9SLee Jones  * @drv: Driver to unregister
891d1ff7024SMika Westerberg  *
892d1ff7024SMika Westerberg  * Unregisters XDomain service driver from the bus.
893d1ff7024SMika Westerberg  */
894d1ff7024SMika Westerberg void tb_unregister_service_driver(struct tb_service_driver *drv)
895d1ff7024SMika Westerberg {
896d1ff7024SMika Westerberg 	driver_unregister(&drv->driver);
897d1ff7024SMika Westerberg }
898d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_unregister_service_driver);
899d1ff7024SMika Westerberg 
900d1ff7024SMika Westerberg static ssize_t key_show(struct device *dev, struct device_attribute *attr,
901d1ff7024SMika Westerberg 			char *buf)
902d1ff7024SMika Westerberg {
903d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
904d1ff7024SMika Westerberg 
905d1ff7024SMika Westerberg 	/*
906d1ff7024SMika Westerberg 	 * It should be null terminated but anything else is pretty much
907d1ff7024SMika Westerberg 	 * allowed.
908d1ff7024SMika Westerberg 	 */
9098283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%*pE\n", (int)strlen(svc->key), svc->key);
910d1ff7024SMika Westerberg }
911d1ff7024SMika Westerberg static DEVICE_ATTR_RO(key);
912d1ff7024SMika Westerberg 
913162736b0SGreg Kroah-Hartman static int get_modalias(const struct tb_service *svc, char *buf, size_t size)
914d1ff7024SMika Westerberg {
915d1ff7024SMika Westerberg 	return snprintf(buf, size, "tbsvc:k%sp%08Xv%08Xr%08X", svc->key,
916d1ff7024SMika Westerberg 			svc->prtcid, svc->prtcvers, svc->prtcrevs);
917d1ff7024SMika Westerberg }
918d1ff7024SMika Westerberg 
919d1ff7024SMika Westerberg static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
920d1ff7024SMika Westerberg 			     char *buf)
921d1ff7024SMika Westerberg {
922d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
923d1ff7024SMika Westerberg 
924d1ff7024SMika Westerberg 	/* Full buffer size except new line and null termination */
925d1ff7024SMika Westerberg 	get_modalias(svc, buf, PAGE_SIZE - 2);
9260a0624a2SArnd Bergmann 	return strlen(strcat(buf, "\n"));
927d1ff7024SMika Westerberg }
928d1ff7024SMika Westerberg static DEVICE_ATTR_RO(modalias);
929d1ff7024SMika Westerberg 
930d1ff7024SMika Westerberg static ssize_t prtcid_show(struct device *dev, struct device_attribute *attr,
931d1ff7024SMika Westerberg 			   char *buf)
932d1ff7024SMika Westerberg {
933d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
934d1ff7024SMika Westerberg 
9358283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%u\n", svc->prtcid);
936d1ff7024SMika Westerberg }
937d1ff7024SMika Westerberg static DEVICE_ATTR_RO(prtcid);
938d1ff7024SMika Westerberg 
939d1ff7024SMika Westerberg static ssize_t prtcvers_show(struct device *dev, struct device_attribute *attr,
940d1ff7024SMika Westerberg 			     char *buf)
941d1ff7024SMika Westerberg {
942d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
943d1ff7024SMika Westerberg 
9448283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%u\n", svc->prtcvers);
945d1ff7024SMika Westerberg }
946d1ff7024SMika Westerberg static DEVICE_ATTR_RO(prtcvers);
947d1ff7024SMika Westerberg 
948d1ff7024SMika Westerberg static ssize_t prtcrevs_show(struct device *dev, struct device_attribute *attr,
949d1ff7024SMika Westerberg 			     char *buf)
950d1ff7024SMika Westerberg {
951d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
952d1ff7024SMika Westerberg 
9538283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%u\n", svc->prtcrevs);
954d1ff7024SMika Westerberg }
955d1ff7024SMika Westerberg static DEVICE_ATTR_RO(prtcrevs);
956d1ff7024SMika Westerberg 
957d1ff7024SMika Westerberg static ssize_t prtcstns_show(struct device *dev, struct device_attribute *attr,
958d1ff7024SMika Westerberg 			     char *buf)
959d1ff7024SMika Westerberg {
960d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
961d1ff7024SMika Westerberg 
9628283fb57SAndy Shevchenko 	return sysfs_emit(buf, "0x%08x\n", svc->prtcstns);
963d1ff7024SMika Westerberg }
964d1ff7024SMika Westerberg static DEVICE_ATTR_RO(prtcstns);
965d1ff7024SMika Westerberg 
966d1ff7024SMika Westerberg static struct attribute *tb_service_attrs[] = {
967d1ff7024SMika Westerberg 	&dev_attr_key.attr,
968d1ff7024SMika Westerberg 	&dev_attr_modalias.attr,
969d1ff7024SMika Westerberg 	&dev_attr_prtcid.attr,
970d1ff7024SMika Westerberg 	&dev_attr_prtcvers.attr,
971d1ff7024SMika Westerberg 	&dev_attr_prtcrevs.attr,
972d1ff7024SMika Westerberg 	&dev_attr_prtcstns.attr,
973d1ff7024SMika Westerberg 	NULL,
974d1ff7024SMika Westerberg };
975d1ff7024SMika Westerberg 
9766889e00fSRikard Falkeborn static const struct attribute_group tb_service_attr_group = {
977d1ff7024SMika Westerberg 	.attrs = tb_service_attrs,
978d1ff7024SMika Westerberg };
979d1ff7024SMika Westerberg 
980d1ff7024SMika Westerberg static const struct attribute_group *tb_service_attr_groups[] = {
981d1ff7024SMika Westerberg 	&tb_service_attr_group,
982d1ff7024SMika Westerberg 	NULL,
983d1ff7024SMika Westerberg };
984d1ff7024SMika Westerberg 
985162736b0SGreg Kroah-Hartman static int tb_service_uevent(const struct device *dev, struct kobj_uevent_env *env)
986d1ff7024SMika Westerberg {
987162736b0SGreg Kroah-Hartman 	const struct tb_service *svc = container_of_const(dev, struct tb_service, dev);
988d1ff7024SMika Westerberg 	char modalias[64];
989d1ff7024SMika Westerberg 
990d1ff7024SMika Westerberg 	get_modalias(svc, modalias, sizeof(modalias));
991d1ff7024SMika Westerberg 	return add_uevent_var(env, "MODALIAS=%s", modalias);
992d1ff7024SMika Westerberg }
993d1ff7024SMika Westerberg 
994d1ff7024SMika Westerberg static void tb_service_release(struct device *dev)
995d1ff7024SMika Westerberg {
996d1ff7024SMika Westerberg 	struct tb_service *svc = container_of(dev, struct tb_service, dev);
997d1ff7024SMika Westerberg 	struct tb_xdomain *xd = tb_service_parent(svc);
998d1ff7024SMika Westerberg 
999407ac931SMika Westerberg 	tb_service_debugfs_remove(svc);
1000dec6a613SChristophe JAILLET 	ida_free(&xd->service_ids, svc->id);
1001d1ff7024SMika Westerberg 	kfree(svc->key);
1002d1ff7024SMika Westerberg 	kfree(svc);
1003d1ff7024SMika Westerberg }
1004d1ff7024SMika Westerberg 
1005b8a73083SRicardo B. Marliere const struct device_type tb_service_type = {
1006d1ff7024SMika Westerberg 	.name = "thunderbolt_service",
1007d1ff7024SMika Westerberg 	.groups = tb_service_attr_groups,
1008d1ff7024SMika Westerberg 	.uevent = tb_service_uevent,
1009d1ff7024SMika Westerberg 	.release = tb_service_release,
1010d1ff7024SMika Westerberg };
1011d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_service_type);
1012d1ff7024SMika Westerberg 
1013d1ff7024SMika Westerberg static int remove_missing_service(struct device *dev, void *data)
1014d1ff7024SMika Westerberg {
1015d1ff7024SMika Westerberg 	struct tb_xdomain *xd = data;
1016d1ff7024SMika Westerberg 	struct tb_service *svc;
1017d1ff7024SMika Westerberg 
1018d1ff7024SMika Westerberg 	svc = tb_to_service(dev);
1019d1ff7024SMika Westerberg 	if (!svc)
1020d1ff7024SMika Westerberg 		return 0;
1021d1ff7024SMika Westerberg 
102246b494f2SMika Westerberg 	if (!tb_property_find(xd->remote_properties, svc->key,
1023d1ff7024SMika Westerberg 			      TB_PROPERTY_TYPE_DIRECTORY))
1024d1ff7024SMika Westerberg 		device_unregister(dev);
1025d1ff7024SMika Westerberg 
1026d1ff7024SMika Westerberg 	return 0;
1027d1ff7024SMika Westerberg }
1028d1ff7024SMika Westerberg 
1029d1ff7024SMika Westerberg static int find_service(struct device *dev, void *data)
1030d1ff7024SMika Westerberg {
1031d1ff7024SMika Westerberg 	const struct tb_property *p = data;
1032d1ff7024SMika Westerberg 	struct tb_service *svc;
1033d1ff7024SMika Westerberg 
1034d1ff7024SMika Westerberg 	svc = tb_to_service(dev);
1035d1ff7024SMika Westerberg 	if (!svc)
1036d1ff7024SMika Westerberg 		return 0;
1037d1ff7024SMika Westerberg 
1038d1ff7024SMika Westerberg 	return !strcmp(svc->key, p->key);
1039d1ff7024SMika Westerberg }
1040d1ff7024SMika Westerberg 
1041d1ff7024SMika Westerberg static int populate_service(struct tb_service *svc,
1042d1ff7024SMika Westerberg 			    struct tb_property *property)
1043d1ff7024SMika Westerberg {
1044d1ff7024SMika Westerberg 	struct tb_property_dir *dir = property->value.dir;
1045d1ff7024SMika Westerberg 	struct tb_property *p;
1046d1ff7024SMika Westerberg 
1047d1ff7024SMika Westerberg 	/* Fill in standard properties */
1048d1ff7024SMika Westerberg 	p = tb_property_find(dir, "prtcid", TB_PROPERTY_TYPE_VALUE);
1049d1ff7024SMika Westerberg 	if (p)
1050d1ff7024SMika Westerberg 		svc->prtcid = p->value.immediate;
1051d1ff7024SMika Westerberg 	p = tb_property_find(dir, "prtcvers", TB_PROPERTY_TYPE_VALUE);
1052d1ff7024SMika Westerberg 	if (p)
1053d1ff7024SMika Westerberg 		svc->prtcvers = p->value.immediate;
1054d1ff7024SMika Westerberg 	p = tb_property_find(dir, "prtcrevs", TB_PROPERTY_TYPE_VALUE);
1055d1ff7024SMika Westerberg 	if (p)
1056d1ff7024SMika Westerberg 		svc->prtcrevs = p->value.immediate;
1057d1ff7024SMika Westerberg 	p = tb_property_find(dir, "prtcstns", TB_PROPERTY_TYPE_VALUE);
1058d1ff7024SMika Westerberg 	if (p)
1059d1ff7024SMika Westerberg 		svc->prtcstns = p->value.immediate;
1060d1ff7024SMika Westerberg 
1061d1ff7024SMika Westerberg 	svc->key = kstrdup(property->key, GFP_KERNEL);
1062d1ff7024SMika Westerberg 	if (!svc->key)
1063d1ff7024SMika Westerberg 		return -ENOMEM;
1064d1ff7024SMika Westerberg 
1065d1ff7024SMika Westerberg 	return 0;
1066d1ff7024SMika Westerberg }
1067d1ff7024SMika Westerberg 
1068d1ff7024SMika Westerberg static void enumerate_services(struct tb_xdomain *xd)
1069d1ff7024SMika Westerberg {
1070d1ff7024SMika Westerberg 	struct tb_service *svc;
1071d1ff7024SMika Westerberg 	struct tb_property *p;
1072d1ff7024SMika Westerberg 	struct device *dev;
10739aabb685SAditya Pakki 	int id;
1074d1ff7024SMika Westerberg 
1075d1ff7024SMika Westerberg 	/*
1076d1ff7024SMika Westerberg 	 * First remove all services that are not available anymore in
1077d1ff7024SMika Westerberg 	 * the updated property block.
1078d1ff7024SMika Westerberg 	 */
1079d1ff7024SMika Westerberg 	device_for_each_child_reverse(&xd->dev, xd, remove_missing_service);
1080d1ff7024SMika Westerberg 
1081d1ff7024SMika Westerberg 	/* Then re-enumerate properties creating new services as we go */
108246b494f2SMika Westerberg 	tb_property_for_each(xd->remote_properties, p) {
1083d1ff7024SMika Westerberg 		if (p->type != TB_PROPERTY_TYPE_DIRECTORY)
1084d1ff7024SMika Westerberg 			continue;
1085d1ff7024SMika Westerberg 
1086d1ff7024SMika Westerberg 		/* If the service exists already we are fine */
1087d1ff7024SMika Westerberg 		dev = device_find_child(&xd->dev, p, find_service);
1088d1ff7024SMika Westerberg 		if (dev) {
1089d1ff7024SMika Westerberg 			put_device(dev);
1090d1ff7024SMika Westerberg 			continue;
1091d1ff7024SMika Westerberg 		}
1092d1ff7024SMika Westerberg 
1093d1ff7024SMika Westerberg 		svc = kzalloc(sizeof(*svc), GFP_KERNEL);
1094d1ff7024SMika Westerberg 		if (!svc)
1095d1ff7024SMika Westerberg 			break;
1096d1ff7024SMika Westerberg 
1097d1ff7024SMika Westerberg 		if (populate_service(svc, p)) {
1098d1ff7024SMika Westerberg 			kfree(svc);
1099d1ff7024SMika Westerberg 			break;
1100d1ff7024SMika Westerberg 		}
1101d1ff7024SMika Westerberg 
1102dec6a613SChristophe JAILLET 		id = ida_alloc(&xd->service_ids, GFP_KERNEL);
11039aabb685SAditya Pakki 		if (id < 0) {
1104a663e0dfSMika Westerberg 			kfree(svc->key);
11059aabb685SAditya Pakki 			kfree(svc);
11069aabb685SAditya Pakki 			break;
11079aabb685SAditya Pakki 		}
11089aabb685SAditya Pakki 		svc->id = id;
1109d1ff7024SMika Westerberg 		svc->dev.bus = &tb_bus_type;
1110d1ff7024SMika Westerberg 		svc->dev.type = &tb_service_type;
1111d1ff7024SMika Westerberg 		svc->dev.parent = &xd->dev;
1112d1ff7024SMika Westerberg 		dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
1113d1ff7024SMika Westerberg 
1114407ac931SMika Westerberg 		tb_service_debugfs_init(svc);
1115407ac931SMika Westerberg 
1116d1ff7024SMika Westerberg 		if (device_register(&svc->dev)) {
1117d1ff7024SMika Westerberg 			put_device(&svc->dev);
1118d1ff7024SMika Westerberg 			break;
1119d1ff7024SMika Westerberg 		}
1120d1ff7024SMika Westerberg 	}
1121d1ff7024SMika Westerberg }
1122d1ff7024SMika Westerberg 
1123d1ff7024SMika Westerberg static int populate_properties(struct tb_xdomain *xd,
1124d1ff7024SMika Westerberg 			       struct tb_property_dir *dir)
1125d1ff7024SMika Westerberg {
1126d1ff7024SMika Westerberg 	const struct tb_property *p;
1127d1ff7024SMika Westerberg 
1128d1ff7024SMika Westerberg 	/* Required properties */
1129d1ff7024SMika Westerberg 	p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_VALUE);
1130d1ff7024SMika Westerberg 	if (!p)
1131d1ff7024SMika Westerberg 		return -EINVAL;
1132d1ff7024SMika Westerberg 	xd->device = p->value.immediate;
1133d1ff7024SMika Westerberg 
1134d1ff7024SMika Westerberg 	p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_VALUE);
1135d1ff7024SMika Westerberg 	if (!p)
1136d1ff7024SMika Westerberg 		return -EINVAL;
1137d1ff7024SMika Westerberg 	xd->vendor = p->value.immediate;
1138d1ff7024SMika Westerberg 
113946b494f2SMika Westerberg 	p = tb_property_find(dir, "maxhopid", TB_PROPERTY_TYPE_VALUE);
114046b494f2SMika Westerberg 	/*
114146b494f2SMika Westerberg 	 * USB4 inter-domain spec suggests using 15 as HopID if the
114246b494f2SMika Westerberg 	 * other end does not announce it in a property. This is for
114346b494f2SMika Westerberg 	 * TBT3 compatibility.
114446b494f2SMika Westerberg 	 */
114546b494f2SMika Westerberg 	xd->remote_max_hopid = p ? p->value.immediate : XDOMAIN_DEFAULT_MAX_HOPID;
114646b494f2SMika Westerberg 
1147d1ff7024SMika Westerberg 	kfree(xd->device_name);
1148d1ff7024SMika Westerberg 	xd->device_name = NULL;
1149d1ff7024SMika Westerberg 	kfree(xd->vendor_name);
1150d1ff7024SMika Westerberg 	xd->vendor_name = NULL;
1151d1ff7024SMika Westerberg 
1152d1ff7024SMika Westerberg 	/* Optional properties */
1153d1ff7024SMika Westerberg 	p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_TEXT);
1154d1ff7024SMika Westerberg 	if (p)
1155d1ff7024SMika Westerberg 		xd->device_name = kstrdup(p->value.text, GFP_KERNEL);
1156d1ff7024SMika Westerberg 	p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_TEXT);
1157d1ff7024SMika Westerberg 	if (p)
1158d1ff7024SMika Westerberg 		xd->vendor_name = kstrdup(p->value.text, GFP_KERNEL);
1159d1ff7024SMika Westerberg 
1160d1ff7024SMika Westerberg 	return 0;
1161d1ff7024SMika Westerberg }
1162d1ff7024SMika Westerberg 
11634210d50fSIsaac Hazan static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd)
11644210d50fSIsaac Hazan {
11654210d50fSIsaac Hazan 	bool change = false;
11664210d50fSIsaac Hazan 	struct tb_port *port;
11674210d50fSIsaac Hazan 	int ret;
11684210d50fSIsaac Hazan 
116917fb1a3dSMika Westerberg 	port = tb_xdomain_downstream_port(xd);
11704210d50fSIsaac Hazan 
11714210d50fSIsaac Hazan 	ret = tb_port_get_link_speed(port);
11724210d50fSIsaac Hazan 	if (ret < 0)
11734210d50fSIsaac Hazan 		return ret;
11744210d50fSIsaac Hazan 
11754210d50fSIsaac Hazan 	if (xd->link_speed != ret)
11764210d50fSIsaac Hazan 		change = true;
11774210d50fSIsaac Hazan 
11784210d50fSIsaac Hazan 	xd->link_speed = ret;
11794210d50fSIsaac Hazan 
11804210d50fSIsaac Hazan 	ret = tb_port_get_link_width(port);
11814210d50fSIsaac Hazan 	if (ret < 0)
11824210d50fSIsaac Hazan 		return ret;
11834210d50fSIsaac Hazan 
11844210d50fSIsaac Hazan 	if (xd->link_width != ret)
11854210d50fSIsaac Hazan 		change = true;
11864210d50fSIsaac Hazan 
11874210d50fSIsaac Hazan 	xd->link_width = ret;
11884210d50fSIsaac Hazan 
11894210d50fSIsaac Hazan 	if (change)
11904210d50fSIsaac Hazan 		kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE);
11914210d50fSIsaac Hazan 
11924210d50fSIsaac Hazan 	return 0;
11934210d50fSIsaac Hazan }
11944210d50fSIsaac Hazan 
11958e1de704SMika Westerberg static int tb_xdomain_get_uuid(struct tb_xdomain *xd)
11963b4b3235SMika Westerberg {
11973b4b3235SMika Westerberg 	struct tb *tb = xd->tb;
11983b4b3235SMika Westerberg 	uuid_t uuid;
11998e1de704SMika Westerberg 	u64 route;
12003b4b3235SMika Westerberg 	int ret;
12013b4b3235SMika Westerberg 
1202d29c59b1SMika Westerberg 	dev_dbg(&xd->dev, "requesting remote UUID\n");
1203d29c59b1SMika Westerberg 
12048e1de704SMika Westerberg 	ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->state_retries, &uuid,
12058e1de704SMika Westerberg 				  &route);
12063b4b3235SMika Westerberg 	if (ret < 0) {
12078e1de704SMika Westerberg 		if (xd->state_retries-- > 0) {
1208d29c59b1SMika Westerberg 			dev_dbg(&xd->dev, "failed to request UUID, retrying\n");
12098e1de704SMika Westerberg 			return -EAGAIN;
12103b4b3235SMika Westerberg 		}
12114e99c98eSAndy Shevchenko 		dev_dbg(&xd->dev, "failed to read remote UUID\n");
12128e1de704SMika Westerberg 		return ret;
12133b4b3235SMika Westerberg 	}
12143b4b3235SMika Westerberg 
1215d29c59b1SMika Westerberg 	dev_dbg(&xd->dev, "got remote UUID %pUb\n", &uuid);
1216d29c59b1SMika Westerberg 
12178e1de704SMika Westerberg 	if (uuid_equal(&uuid, xd->local_uuid)) {
12188e1de704SMika Westerberg 		if (route == xd->route)
12198e1de704SMika Westerberg 			dev_dbg(&xd->dev, "loop back detected\n");
12208e1de704SMika Westerberg 		else
12213b4b3235SMika Westerberg 			dev_dbg(&xd->dev, "intra-domain loop detected\n");
12223b4b3235SMika Westerberg 
12238e1de704SMika Westerberg 		/* Don't bond lanes automatically for loops */
12248e1de704SMika Westerberg 		xd->bonding_possible = false;
12258e1de704SMika Westerberg 	}
12268e1de704SMika Westerberg 
12273b4b3235SMika Westerberg 	/*
12283b4b3235SMika Westerberg 	 * If the UUID is different, there is another domain connected
12293b4b3235SMika Westerberg 	 * so mark this one unplugged and wait for the connection
12303b4b3235SMika Westerberg 	 * manager to replace it.
12313b4b3235SMika Westerberg 	 */
12323b4b3235SMika Westerberg 	if (xd->remote_uuid && !uuid_equal(&uuid, xd->remote_uuid)) {
12333b4b3235SMika Westerberg 		dev_dbg(&xd->dev, "remote UUID is different, unplugging\n");
12343b4b3235SMika Westerberg 		xd->is_unplugged = true;
12358e1de704SMika Westerberg 		return -ENODEV;
12363b4b3235SMika Westerberg 	}
12373b4b3235SMika Westerberg 
12383b4b3235SMika Westerberg 	/* First time fill in the missing UUID */
12393b4b3235SMika Westerberg 	if (!xd->remote_uuid) {
12403b4b3235SMika Westerberg 		xd->remote_uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL);
12413b4b3235SMika Westerberg 		if (!xd->remote_uuid)
12428e1de704SMika Westerberg 			return -ENOMEM;
12433b4b3235SMika Westerberg 	}
12443b4b3235SMika Westerberg 
12458e1de704SMika Westerberg 	return 0;
12463b4b3235SMika Westerberg }
12473b4b3235SMika Westerberg 
12488e1de704SMika Westerberg static int tb_xdomain_get_link_status(struct tb_xdomain *xd)
1249d1ff7024SMika Westerberg {
12508e1de704SMika Westerberg 	struct tb *tb = xd->tb;
12518e1de704SMika Westerberg 	u8 slw, tlw, sls, tls;
12528e1de704SMika Westerberg 	int ret;
12538e1de704SMika Westerberg 
12548e1de704SMika Westerberg 	dev_dbg(&xd->dev, "sending link state status request to %pUb\n",
12558e1de704SMika Westerberg 		xd->remote_uuid);
12568e1de704SMika Westerberg 
12578e1de704SMika Westerberg 	ret = tb_xdp_link_state_status_request(tb->ctl, xd->route,
12588e1de704SMika Westerberg 					       xd->state_retries, &slw, &tlw, &sls,
12598e1de704SMika Westerberg 					       &tls);
12608e1de704SMika Westerberg 	if (ret) {
12618e1de704SMika Westerberg 		if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) {
12628e1de704SMika Westerberg 			dev_dbg(&xd->dev,
12638e1de704SMika Westerberg 				"failed to request remote link status, retrying\n");
12648e1de704SMika Westerberg 			return -EAGAIN;
12658e1de704SMika Westerberg 		}
12668e1de704SMika Westerberg 		dev_dbg(&xd->dev, "failed to receive remote link status\n");
12678e1de704SMika Westerberg 		return ret;
12688e1de704SMika Westerberg 	}
12698e1de704SMika Westerberg 
12708e1de704SMika Westerberg 	dev_dbg(&xd->dev, "remote link supports width %#x speed %#x\n", slw, sls);
12718e1de704SMika Westerberg 
12728e1de704SMika Westerberg 	if (slw < LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL) {
12738e1de704SMika Westerberg 		dev_dbg(&xd->dev, "remote adapter is single lane only\n");
12748e1de704SMika Westerberg 		return -EOPNOTSUPP;
12758e1de704SMika Westerberg 	}
12768e1de704SMika Westerberg 
12778e1de704SMika Westerberg 	return 0;
12788e1de704SMika Westerberg }
12798e1de704SMika Westerberg 
12808e1de704SMika Westerberg static int tb_xdomain_link_state_change(struct tb_xdomain *xd,
12818e1de704SMika Westerberg 					unsigned int width)
12828e1de704SMika Westerberg {
128317fb1a3dSMika Westerberg 	struct tb_port *port = tb_xdomain_downstream_port(xd);
12848e1de704SMika Westerberg 	struct tb *tb = xd->tb;
12858e1de704SMika Westerberg 	u8 tlw, tls;
12868e1de704SMika Westerberg 	u32 val;
12878e1de704SMika Westerberg 	int ret;
12888e1de704SMika Westerberg 
12898e1de704SMika Westerberg 	if (width == 2)
12908e1de704SMika Westerberg 		tlw = LANE_ADP_CS_1_TARGET_WIDTH_DUAL;
12918e1de704SMika Westerberg 	else if (width == 1)
12928e1de704SMika Westerberg 		tlw = LANE_ADP_CS_1_TARGET_WIDTH_SINGLE;
12938e1de704SMika Westerberg 	else
12948e1de704SMika Westerberg 		return -EINVAL;
12958e1de704SMika Westerberg 
12968e1de704SMika Westerberg 	/* Use the current target speed */
12978e1de704SMika Westerberg 	ret = tb_port_read(port, &val, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1);
12988e1de704SMika Westerberg 	if (ret)
12998e1de704SMika Westerberg 		return ret;
13008e1de704SMika Westerberg 	tls = val & LANE_ADP_CS_1_TARGET_SPEED_MASK;
13018e1de704SMika Westerberg 
13028e1de704SMika Westerberg 	dev_dbg(&xd->dev, "sending link state change request with width %#x speed %#x\n",
13038e1de704SMika Westerberg 		tlw, tls);
13048e1de704SMika Westerberg 
13058e1de704SMika Westerberg 	ret = tb_xdp_link_state_change_request(tb->ctl, xd->route,
13068e1de704SMika Westerberg 					       xd->state_retries, tlw, tls);
13078e1de704SMika Westerberg 	if (ret) {
13088e1de704SMika Westerberg 		if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) {
13098e1de704SMika Westerberg 			dev_dbg(&xd->dev,
13108e1de704SMika Westerberg 				"failed to change remote link state, retrying\n");
13118e1de704SMika Westerberg 			return -EAGAIN;
13128e1de704SMika Westerberg 		}
13138e1de704SMika Westerberg 		dev_err(&xd->dev, "failed request link state change, aborting\n");
13148e1de704SMika Westerberg 		return ret;
13158e1de704SMika Westerberg 	}
13168e1de704SMika Westerberg 
13178e1de704SMika Westerberg 	dev_dbg(&xd->dev, "received link state change response\n");
13188e1de704SMika Westerberg 	return 0;
13198e1de704SMika Westerberg }
13208e1de704SMika Westerberg 
13218e1de704SMika Westerberg static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd)
13228e1de704SMika Westerberg {
1323e111fb92SGil Fine 	unsigned int width, width_mask;
13248e1de704SMika Westerberg 	struct tb_port *port;
1325e111fb92SGil Fine 	int ret;
13268e1de704SMika Westerberg 
13278e1de704SMika Westerberg 	if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_SINGLE) {
1328e111fb92SGil Fine 		width = TB_LINK_WIDTH_SINGLE;
1329e111fb92SGil Fine 		width_mask = width;
13308e1de704SMika Westerberg 	} else if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_DUAL) {
1331e111fb92SGil Fine 		width = TB_LINK_WIDTH_DUAL;
1332e111fb92SGil Fine 		width_mask = width | TB_LINK_WIDTH_ASYM_TX | TB_LINK_WIDTH_ASYM_RX;
13338e1de704SMika Westerberg 	} else {
13348e1de704SMika Westerberg 		if (xd->state_retries-- > 0) {
13358e1de704SMika Westerberg 			dev_dbg(&xd->dev,
13368e1de704SMika Westerberg 				"link state change request not received yet, retrying\n");
13378e1de704SMika Westerberg 			return -EAGAIN;
13388e1de704SMika Westerberg 		}
13398e1de704SMika Westerberg 		dev_dbg(&xd->dev, "timeout waiting for link change request\n");
13408e1de704SMika Westerberg 		return -ETIMEDOUT;
13418e1de704SMika Westerberg 	}
13428e1de704SMika Westerberg 
134317fb1a3dSMika Westerberg 	port = tb_xdomain_downstream_port(xd);
13448e1de704SMika Westerberg 
13458e1de704SMika Westerberg 	/*
13468e1de704SMika Westerberg 	 * We can't use tb_xdomain_lane_bonding_enable() here because it
13478e1de704SMika Westerberg 	 * is the other side that initiates lane bonding. So here we
13488e1de704SMika Westerberg 	 * just set the width to both lane adapters and wait for the
13498e1de704SMika Westerberg 	 * link to transition bonded.
13508e1de704SMika Westerberg 	 */
13518e1de704SMika Westerberg 	ret = tb_port_set_link_width(port->dual_link_port, width);
13528e1de704SMika Westerberg 	if (ret) {
13538e1de704SMika Westerberg 		tb_port_warn(port->dual_link_port,
13548e1de704SMika Westerberg 			     "failed to set link width to %d\n", width);
13558e1de704SMika Westerberg 		return ret;
13568e1de704SMika Westerberg 	}
13578e1de704SMika Westerberg 
13588e1de704SMika Westerberg 	ret = tb_port_set_link_width(port, width);
13598e1de704SMika Westerberg 	if (ret) {
13608e1de704SMika Westerberg 		tb_port_warn(port, "failed to set link width to %d\n", width);
13618e1de704SMika Westerberg 		return ret;
13628e1de704SMika Westerberg 	}
13638e1de704SMika Westerberg 
1364e111fb92SGil Fine 	ret = tb_port_wait_for_link_width(port, width_mask,
1365e111fb92SGil Fine 					  XDOMAIN_BONDING_TIMEOUT);
13668e1de704SMika Westerberg 	if (ret) {
13678e1de704SMika Westerberg 		dev_warn(&xd->dev, "error waiting for link width to become %d\n",
1368e111fb92SGil Fine 			 width_mask);
13698e1de704SMika Westerberg 		return ret;
13708e1de704SMika Westerberg 	}
13718e1de704SMika Westerberg 
1372e111fb92SGil Fine 	port->bonded = width > TB_LINK_WIDTH_SINGLE;
1373e111fb92SGil Fine 	port->dual_link_port->bonded = width > TB_LINK_WIDTH_SINGLE;
13748e1de704SMika Westerberg 
13758e1de704SMika Westerberg 	tb_port_update_credits(port);
13768e1de704SMika Westerberg 	tb_xdomain_update_link_attributes(xd);
13778e1de704SMika Westerberg 
137887fa05b6SAndy Shevchenko 	dev_dbg(&xd->dev, "lane bonding %s\n", str_enabled_disabled(width == 2));
13798e1de704SMika Westerberg 	return 0;
13808e1de704SMika Westerberg }
13818e1de704SMika Westerberg 
13828e1de704SMika Westerberg static int tb_xdomain_get_properties(struct tb_xdomain *xd)
13838e1de704SMika Westerberg {
1384d1ff7024SMika Westerberg 	struct tb_property_dir *dir;
1385d1ff7024SMika Westerberg 	struct tb *tb = xd->tb;
1386d1ff7024SMika Westerberg 	bool update = false;
1387d1ff7024SMika Westerberg 	u32 *block = NULL;
1388d1ff7024SMika Westerberg 	u32 gen = 0;
1389d1ff7024SMika Westerberg 	int ret;
1390d1ff7024SMika Westerberg 
1391d29c59b1SMika Westerberg 	dev_dbg(&xd->dev, "requesting remote properties\n");
1392d29c59b1SMika Westerberg 
1393d1ff7024SMika Westerberg 	ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid,
13948e1de704SMika Westerberg 					xd->remote_uuid, xd->state_retries,
1395d1ff7024SMika Westerberg 					&block, &gen);
1396d1ff7024SMika Westerberg 	if (ret < 0) {
13978e1de704SMika Westerberg 		if (xd->state_retries-- > 0) {
1398d29c59b1SMika Westerberg 			dev_dbg(&xd->dev,
1399d29c59b1SMika Westerberg 				"failed to request remote properties, retrying\n");
14008e1de704SMika Westerberg 			return -EAGAIN;
1401d1ff7024SMika Westerberg 		}
14024e99c98eSAndy Shevchenko 		/* Give up now */
14034e99c98eSAndy Shevchenko 		dev_err(&xd->dev, "failed read XDomain properties from %pUb\n",
14044e99c98eSAndy Shevchenko 			xd->remote_uuid);
1405d1ff7024SMika Westerberg 
14068e1de704SMika Westerberg 		return ret;
14078e1de704SMika Westerberg 	}
1408d1ff7024SMika Westerberg 
1409d1ff7024SMika Westerberg 	mutex_lock(&xd->lock);
1410d1ff7024SMika Westerberg 
1411d1ff7024SMika Westerberg 	/* Only accept newer generation properties */
14128e1de704SMika Westerberg 	if (xd->remote_properties && gen <= xd->remote_property_block_gen) {
14138e1de704SMika Westerberg 		ret = 0;
1414d1ff7024SMika Westerberg 		goto err_free_block;
14158e1de704SMika Westerberg 	}
1416d1ff7024SMika Westerberg 
1417d1ff7024SMika Westerberg 	dir = tb_property_parse_dir(block, ret);
1418d1ff7024SMika Westerberg 	if (!dir) {
1419d1ff7024SMika Westerberg 		dev_err(&xd->dev, "failed to parse XDomain properties\n");
14208e1de704SMika Westerberg 		ret = -ENOMEM;
1421d1ff7024SMika Westerberg 		goto err_free_block;
1422d1ff7024SMika Westerberg 	}
1423d1ff7024SMika Westerberg 
1424d1ff7024SMika Westerberg 	ret = populate_properties(xd, dir);
1425d1ff7024SMika Westerberg 	if (ret) {
1426d1ff7024SMika Westerberg 		dev_err(&xd->dev, "missing XDomain properties in response\n");
1427d1ff7024SMika Westerberg 		goto err_free_dir;
1428d1ff7024SMika Westerberg 	}
1429d1ff7024SMika Westerberg 
1430d1ff7024SMika Westerberg 	/* Release the existing one */
143146b494f2SMika Westerberg 	if (xd->remote_properties) {
143246b494f2SMika Westerberg 		tb_property_free_dir(xd->remote_properties);
1433d1ff7024SMika Westerberg 		update = true;
1434d1ff7024SMika Westerberg 	}
1435d1ff7024SMika Westerberg 
143646b494f2SMika Westerberg 	xd->remote_properties = dir;
143746b494f2SMika Westerberg 	xd->remote_property_block_gen = gen;
1438d1ff7024SMika Westerberg 
14394210d50fSIsaac Hazan 	tb_xdomain_update_link_attributes(xd);
14404210d50fSIsaac Hazan 
1441d1ff7024SMika Westerberg 	mutex_unlock(&xd->lock);
1442d1ff7024SMika Westerberg 
1443d1ff7024SMika Westerberg 	kfree(block);
1444d1ff7024SMika Westerberg 
1445d1ff7024SMika Westerberg 	/*
1446d1ff7024SMika Westerberg 	 * Now the device should be ready enough so we can add it to the
1447d1ff7024SMika Westerberg 	 * bus and let userspace know about it. If the device is already
1448d1ff7024SMika Westerberg 	 * registered, we notify the userspace that it has changed.
1449d1ff7024SMika Westerberg 	 */
1450d1ff7024SMika Westerberg 	if (!update) {
145184ee211cSMika Westerberg 		/*
145284ee211cSMika Westerberg 		 * Now disable lane 1 if bonding was not enabled. Do
145384ee211cSMika Westerberg 		 * this only if bonding was possible at the beginning
145484ee211cSMika Westerberg 		 * (that is we are the connection manager and there are
145584ee211cSMika Westerberg 		 * two lanes).
145684ee211cSMika Westerberg 		 */
145784ee211cSMika Westerberg 		if (xd->bonding_possible) {
14588e1de704SMika Westerberg 			struct tb_port *port;
14598e1de704SMika Westerberg 
146017fb1a3dSMika Westerberg 			port = tb_xdomain_downstream_port(xd);
14618e1de704SMika Westerberg 			if (!port->bonded)
14628e1de704SMika Westerberg 				tb_port_disable(port->dual_link_port);
146384ee211cSMika Westerberg 		}
14648e1de704SMika Westerberg 
1465ea20adddSGil Fine 		dev_dbg(&xd->dev, "current link speed %u.0 Gb/s\n",
1466ea20adddSGil Fine 			xd->link_speed);
1467ea20adddSGil Fine 		dev_dbg(&xd->dev, "current link width %s\n",
1468ea20adddSGil Fine 			tb_width_name(xd->link_width));
1469ea20adddSGil Fine 
1470d1ff7024SMika Westerberg 		if (device_add(&xd->dev)) {
1471d1ff7024SMika Westerberg 			dev_err(&xd->dev, "failed to add XDomain device\n");
14728e1de704SMika Westerberg 			return -ENODEV;
1473d1ff7024SMika Westerberg 		}
1474d29c59b1SMika Westerberg 		dev_info(&xd->dev, "new host found, vendor=%#x device=%#x\n",
1475d29c59b1SMika Westerberg 			 xd->vendor, xd->device);
1476d29c59b1SMika Westerberg 		if (xd->vendor_name && xd->device_name)
1477d29c59b1SMika Westerberg 			dev_info(&xd->dev, "%s %s\n", xd->vendor_name,
1478d29c59b1SMika Westerberg 				 xd->device_name);
1479d0f1e0c2SMika Westerberg 
1480d0f1e0c2SMika Westerberg 		tb_xdomain_debugfs_init(xd);
1481d1ff7024SMika Westerberg 	} else {
1482d1ff7024SMika Westerberg 		kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE);
1483d1ff7024SMika Westerberg 	}
1484d1ff7024SMika Westerberg 
1485d1ff7024SMika Westerberg 	enumerate_services(xd);
14868e1de704SMika Westerberg 	return 0;
1487d1ff7024SMika Westerberg 
1488d1ff7024SMika Westerberg err_free_dir:
1489d1ff7024SMika Westerberg 	tb_property_free_dir(dir);
1490d1ff7024SMika Westerberg err_free_block:
1491d1ff7024SMika Westerberg 	kfree(block);
1492d1ff7024SMika Westerberg 	mutex_unlock(&xd->lock);
14938e1de704SMika Westerberg 
14948e1de704SMika Westerberg 	return ret;
14958e1de704SMika Westerberg }
14968e1de704SMika Westerberg 
14978e1de704SMika Westerberg static void tb_xdomain_queue_uuid(struct tb_xdomain *xd)
14988e1de704SMika Westerberg {
14998e1de704SMika Westerberg 	xd->state = XDOMAIN_STATE_UUID;
15008e1de704SMika Westerberg 	xd->state_retries = XDOMAIN_RETRIES;
15018e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
15028e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
15038e1de704SMika Westerberg }
15048e1de704SMika Westerberg 
15058e1de704SMika Westerberg static void tb_xdomain_queue_link_status(struct tb_xdomain *xd)
15068e1de704SMika Westerberg {
15078e1de704SMika Westerberg 	xd->state = XDOMAIN_STATE_LINK_STATUS;
15088e1de704SMika Westerberg 	xd->state_retries = XDOMAIN_RETRIES;
15098e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
15108e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
15118e1de704SMika Westerberg }
15128e1de704SMika Westerberg 
15138e1de704SMika Westerberg static void tb_xdomain_queue_link_status2(struct tb_xdomain *xd)
15148e1de704SMika Westerberg {
15158e1de704SMika Westerberg 	xd->state = XDOMAIN_STATE_LINK_STATUS2;
15168e1de704SMika Westerberg 	xd->state_retries = XDOMAIN_RETRIES;
15178e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
15188e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
15198e1de704SMika Westerberg }
15208e1de704SMika Westerberg 
15218e1de704SMika Westerberg static void tb_xdomain_queue_bonding(struct tb_xdomain *xd)
15228e1de704SMika Westerberg {
15238e1de704SMika Westerberg 	if (memcmp(xd->local_uuid, xd->remote_uuid, UUID_SIZE) > 0) {
15248e1de704SMika Westerberg 		dev_dbg(&xd->dev, "we have higher UUID, other side bonds the lanes\n");
15258e1de704SMika Westerberg 		xd->state = XDOMAIN_STATE_BONDING_UUID_HIGH;
15268e1de704SMika Westerberg 	} else {
15278e1de704SMika Westerberg 		dev_dbg(&xd->dev, "we have lower UUID, bonding lanes\n");
15288e1de704SMika Westerberg 		xd->state = XDOMAIN_STATE_LINK_STATE_CHANGE;
15298e1de704SMika Westerberg 	}
15308e1de704SMika Westerberg 
15318e1de704SMika Westerberg 	xd->state_retries = XDOMAIN_RETRIES;
15328e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
15338e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
15348e1de704SMika Westerberg }
15358e1de704SMika Westerberg 
15368e1de704SMika Westerberg static void tb_xdomain_queue_bonding_uuid_low(struct tb_xdomain *xd)
15378e1de704SMika Westerberg {
15388e1de704SMika Westerberg 	xd->state = XDOMAIN_STATE_BONDING_UUID_LOW;
15398e1de704SMika Westerberg 	xd->state_retries = XDOMAIN_RETRIES;
15408e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
15418e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
15428e1de704SMika Westerberg }
15438e1de704SMika Westerberg 
15448e1de704SMika Westerberg static void tb_xdomain_queue_properties(struct tb_xdomain *xd)
15458e1de704SMika Westerberg {
15468e1de704SMika Westerberg 	xd->state = XDOMAIN_STATE_PROPERTIES;
15478e1de704SMika Westerberg 	xd->state_retries = XDOMAIN_RETRIES;
15488e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
15498e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
15508e1de704SMika Westerberg }
15518e1de704SMika Westerberg 
15528e1de704SMika Westerberg static void tb_xdomain_queue_properties_changed(struct tb_xdomain *xd)
15538e1de704SMika Westerberg {
15548e1de704SMika Westerberg 	xd->properties_changed_retries = XDOMAIN_RETRIES;
15558e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
15568e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
15578e1de704SMika Westerberg }
15588e1de704SMika Westerberg 
1559308092d0SMika Westerberg static void tb_xdomain_failed(struct tb_xdomain *xd)
1560308092d0SMika Westerberg {
1561308092d0SMika Westerberg 	xd->state = XDOMAIN_STATE_ERROR;
1562308092d0SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
1563308092d0SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
1564308092d0SMika Westerberg }
1565308092d0SMika Westerberg 
15668e1de704SMika Westerberg static void tb_xdomain_state_work(struct work_struct *work)
15678e1de704SMika Westerberg {
15688e1de704SMika Westerberg 	struct tb_xdomain *xd = container_of(work, typeof(*xd), state_work.work);
15698e1de704SMika Westerberg 	int ret, state = xd->state;
15708e1de704SMika Westerberg 
15718e1de704SMika Westerberg 	if (WARN_ON_ONCE(state < XDOMAIN_STATE_INIT ||
15728e1de704SMika Westerberg 			 state > XDOMAIN_STATE_ERROR))
15738e1de704SMika Westerberg 		return;
15748e1de704SMika Westerberg 
15758e1de704SMika Westerberg 	dev_dbg(&xd->dev, "running state %s\n", state_names[state]);
15768e1de704SMika Westerberg 
15778e1de704SMika Westerberg 	switch (state) {
15788e1de704SMika Westerberg 	case XDOMAIN_STATE_INIT:
15798e1de704SMika Westerberg 		if (xd->needs_uuid) {
15808e1de704SMika Westerberg 			tb_xdomain_queue_uuid(xd);
15818e1de704SMika Westerberg 		} else {
15828e1de704SMika Westerberg 			tb_xdomain_queue_properties_changed(xd);
15838e1de704SMika Westerberg 			tb_xdomain_queue_properties(xd);
15848e1de704SMika Westerberg 		}
15858e1de704SMika Westerberg 		break;
15868e1de704SMika Westerberg 
15878e1de704SMika Westerberg 	case XDOMAIN_STATE_UUID:
15888e1de704SMika Westerberg 		ret = tb_xdomain_get_uuid(xd);
15898e1de704SMika Westerberg 		if (ret) {
15908e1de704SMika Westerberg 			if (ret == -EAGAIN)
15918e1de704SMika Westerberg 				goto retry_state;
1592308092d0SMika Westerberg 			tb_xdomain_failed(xd);
15938e1de704SMika Westerberg 		} else {
15948e1de704SMika Westerberg 			tb_xdomain_queue_properties_changed(xd);
15958e1de704SMika Westerberg 			if (xd->bonding_possible)
15968e1de704SMika Westerberg 				tb_xdomain_queue_link_status(xd);
15978e1de704SMika Westerberg 			else
15988e1de704SMika Westerberg 				tb_xdomain_queue_properties(xd);
15998e1de704SMika Westerberg 		}
16008e1de704SMika Westerberg 		break;
16018e1de704SMika Westerberg 
16028e1de704SMika Westerberg 	case XDOMAIN_STATE_LINK_STATUS:
16038e1de704SMika Westerberg 		ret = tb_xdomain_get_link_status(xd);
16048e1de704SMika Westerberg 		if (ret) {
16058e1de704SMika Westerberg 			if (ret == -EAGAIN)
16068e1de704SMika Westerberg 				goto retry_state;
16078e1de704SMika Westerberg 
16088e1de704SMika Westerberg 			/*
16098e1de704SMika Westerberg 			 * If any of the lane bonding states fail we skip
16108e1de704SMika Westerberg 			 * bonding completely and try to continue from
16118e1de704SMika Westerberg 			 * reading properties.
16128e1de704SMika Westerberg 			 */
16138e1de704SMika Westerberg 			tb_xdomain_queue_properties(xd);
16148e1de704SMika Westerberg 		} else {
16158e1de704SMika Westerberg 			tb_xdomain_queue_bonding(xd);
16168e1de704SMika Westerberg 		}
16178e1de704SMika Westerberg 		break;
16188e1de704SMika Westerberg 
16198e1de704SMika Westerberg 	case XDOMAIN_STATE_LINK_STATE_CHANGE:
16208e1de704SMika Westerberg 		ret = tb_xdomain_link_state_change(xd, 2);
16218e1de704SMika Westerberg 		if (ret) {
16228e1de704SMika Westerberg 			if (ret == -EAGAIN)
16238e1de704SMika Westerberg 				goto retry_state;
16248e1de704SMika Westerberg 			tb_xdomain_queue_properties(xd);
16258e1de704SMika Westerberg 		} else {
16268e1de704SMika Westerberg 			tb_xdomain_queue_link_status2(xd);
16278e1de704SMika Westerberg 		}
16288e1de704SMika Westerberg 		break;
16298e1de704SMika Westerberg 
16308e1de704SMika Westerberg 	case XDOMAIN_STATE_LINK_STATUS2:
16318e1de704SMika Westerberg 		ret = tb_xdomain_get_link_status(xd);
16328e1de704SMika Westerberg 		if (ret) {
16338e1de704SMika Westerberg 			if (ret == -EAGAIN)
16348e1de704SMika Westerberg 				goto retry_state;
16358e1de704SMika Westerberg 			tb_xdomain_queue_properties(xd);
16368e1de704SMika Westerberg 		} else {
16378e1de704SMika Westerberg 			tb_xdomain_queue_bonding_uuid_low(xd);
16388e1de704SMika Westerberg 		}
16398e1de704SMika Westerberg 		break;
16408e1de704SMika Westerberg 
16418e1de704SMika Westerberg 	case XDOMAIN_STATE_BONDING_UUID_LOW:
16428e1de704SMika Westerberg 		tb_xdomain_lane_bonding_enable(xd);
16438e1de704SMika Westerberg 		tb_xdomain_queue_properties(xd);
16448e1de704SMika Westerberg 		break;
16458e1de704SMika Westerberg 
16468e1de704SMika Westerberg 	case XDOMAIN_STATE_BONDING_UUID_HIGH:
16478e1de704SMika Westerberg 		if (tb_xdomain_bond_lanes_uuid_high(xd) == -EAGAIN)
16488e1de704SMika Westerberg 			goto retry_state;
16498e1de704SMika Westerberg 		tb_xdomain_queue_properties(xd);
16508e1de704SMika Westerberg 		break;
16518e1de704SMika Westerberg 
16528e1de704SMika Westerberg 	case XDOMAIN_STATE_PROPERTIES:
16538e1de704SMika Westerberg 		ret = tb_xdomain_get_properties(xd);
16548e1de704SMika Westerberg 		if (ret) {
16558e1de704SMika Westerberg 			if (ret == -EAGAIN)
16568e1de704SMika Westerberg 				goto retry_state;
1657308092d0SMika Westerberg 			tb_xdomain_failed(xd);
16588e1de704SMika Westerberg 		} else {
16598e1de704SMika Westerberg 			xd->state = XDOMAIN_STATE_ENUMERATED;
16608e1de704SMika Westerberg 		}
16618e1de704SMika Westerberg 		break;
16628e1de704SMika Westerberg 
16638e1de704SMika Westerberg 	case XDOMAIN_STATE_ENUMERATED:
16648e1de704SMika Westerberg 		tb_xdomain_queue_properties(xd);
16658e1de704SMika Westerberg 		break;
16668e1de704SMika Westerberg 
16678e1de704SMika Westerberg 	case XDOMAIN_STATE_ERROR:
1668308092d0SMika Westerberg 		dev_dbg(&xd->dev, "discovery failed, stopping handshake\n");
1669308092d0SMika Westerberg 		__stop_handshake(xd);
16708e1de704SMika Westerberg 		break;
16718e1de704SMika Westerberg 
16728e1de704SMika Westerberg 	default:
16738e1de704SMika Westerberg 		dev_warn(&xd->dev, "unexpected state %d\n", state);
16748e1de704SMika Westerberg 		break;
16758e1de704SMika Westerberg 	}
16768e1de704SMika Westerberg 
16778e1de704SMika Westerberg 	return;
16788e1de704SMika Westerberg 
16798e1de704SMika Westerberg retry_state:
16808e1de704SMika Westerberg 	queue_delayed_work(xd->tb->wq, &xd->state_work,
16818e1de704SMika Westerberg 			   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
1682d1ff7024SMika Westerberg }
1683d1ff7024SMika Westerberg 
1684d1ff7024SMika Westerberg static void tb_xdomain_properties_changed(struct work_struct *work)
1685d1ff7024SMika Westerberg {
1686d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(work, typeof(*xd),
1687d1ff7024SMika Westerberg 					     properties_changed_work.work);
1688d1ff7024SMika Westerberg 	int ret;
1689d1ff7024SMika Westerberg 
1690d29c59b1SMika Westerberg 	dev_dbg(&xd->dev, "sending properties changed notification\n");
1691d29c59b1SMika Westerberg 
1692d1ff7024SMika Westerberg 	ret = tb_xdp_properties_changed_request(xd->tb->ctl, xd->route,
1693d1ff7024SMika Westerberg 				xd->properties_changed_retries, xd->local_uuid);
1694d1ff7024SMika Westerberg 	if (ret) {
1695d29c59b1SMika Westerberg 		if (xd->properties_changed_retries-- > 0) {
1696d29c59b1SMika Westerberg 			dev_dbg(&xd->dev,
1697d29c59b1SMika Westerberg 				"failed to send properties changed notification, retrying\n");
1698d1ff7024SMika Westerberg 			queue_delayed_work(xd->tb->wq,
1699d1ff7024SMika Westerberg 					   &xd->properties_changed_work,
17008e1de704SMika Westerberg 					   msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
1701d29c59b1SMika Westerberg 		}
1702d29c59b1SMika Westerberg 		dev_err(&xd->dev, "failed to send properties changed notification\n");
1703d1ff7024SMika Westerberg 		return;
1704d1ff7024SMika Westerberg 	}
1705d1ff7024SMika Westerberg 
17068e1de704SMika Westerberg 	xd->properties_changed_retries = XDOMAIN_RETRIES;
1707d1ff7024SMika Westerberg }
1708d1ff7024SMika Westerberg 
1709d1ff7024SMika Westerberg static ssize_t device_show(struct device *dev, struct device_attribute *attr,
1710d1ff7024SMika Westerberg 			   char *buf)
1711d1ff7024SMika Westerberg {
1712d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1713d1ff7024SMika Westerberg 
17148283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%#x\n", xd->device);
1715d1ff7024SMika Westerberg }
1716d1ff7024SMika Westerberg static DEVICE_ATTR_RO(device);
1717d1ff7024SMika Westerberg 
1718d1ff7024SMika Westerberg static ssize_t
1719d1ff7024SMika Westerberg device_name_show(struct device *dev, struct device_attribute *attr, char *buf)
1720d1ff7024SMika Westerberg {
1721d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1722d1ff7024SMika Westerberg 	int ret;
1723d1ff7024SMika Westerberg 
1724d1ff7024SMika Westerberg 	if (mutex_lock_interruptible(&xd->lock))
1725d1ff7024SMika Westerberg 		return -ERESTARTSYS;
17268283fb57SAndy Shevchenko 	ret = sysfs_emit(buf, "%s\n", xd->device_name ?: "");
1727d1ff7024SMika Westerberg 	mutex_unlock(&xd->lock);
1728d1ff7024SMika Westerberg 
1729d1ff7024SMika Westerberg 	return ret;
1730d1ff7024SMika Westerberg }
1731d1ff7024SMika Westerberg static DEVICE_ATTR_RO(device_name);
1732d1ff7024SMika Westerberg 
173346b494f2SMika Westerberg static ssize_t maxhopid_show(struct device *dev, struct device_attribute *attr,
173446b494f2SMika Westerberg 			     char *buf)
173546b494f2SMika Westerberg {
173646b494f2SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
173746b494f2SMika Westerberg 
17388283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%d\n", xd->remote_max_hopid);
173946b494f2SMika Westerberg }
174046b494f2SMika Westerberg static DEVICE_ATTR_RO(maxhopid);
174146b494f2SMika Westerberg 
1742d1ff7024SMika Westerberg static ssize_t vendor_show(struct device *dev, struct device_attribute *attr,
1743d1ff7024SMika Westerberg 			   char *buf)
1744d1ff7024SMika Westerberg {
1745d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1746d1ff7024SMika Westerberg 
17478283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%#x\n", xd->vendor);
1748d1ff7024SMika Westerberg }
1749d1ff7024SMika Westerberg static DEVICE_ATTR_RO(vendor);
1750d1ff7024SMika Westerberg 
1751d1ff7024SMika Westerberg static ssize_t
1752d1ff7024SMika Westerberg vendor_name_show(struct device *dev, struct device_attribute *attr, char *buf)
1753d1ff7024SMika Westerberg {
1754d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1755d1ff7024SMika Westerberg 	int ret;
1756d1ff7024SMika Westerberg 
1757d1ff7024SMika Westerberg 	if (mutex_lock_interruptible(&xd->lock))
1758d1ff7024SMika Westerberg 		return -ERESTARTSYS;
17598283fb57SAndy Shevchenko 	ret = sysfs_emit(buf, "%s\n", xd->vendor_name ?: "");
1760d1ff7024SMika Westerberg 	mutex_unlock(&xd->lock);
1761d1ff7024SMika Westerberg 
1762d1ff7024SMika Westerberg 	return ret;
1763d1ff7024SMika Westerberg }
1764d1ff7024SMika Westerberg static DEVICE_ATTR_RO(vendor_name);
1765d1ff7024SMika Westerberg 
1766d1ff7024SMika Westerberg static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr,
1767d1ff7024SMika Westerberg 			      char *buf)
1768d1ff7024SMika Westerberg {
1769d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1770d1ff7024SMika Westerberg 
17718283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%pUb\n", xd->remote_uuid);
1772d1ff7024SMika Westerberg }
1773d1ff7024SMika Westerberg static DEVICE_ATTR_RO(unique_id);
1774d1ff7024SMika Westerberg 
17754210d50fSIsaac Hazan static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
17764210d50fSIsaac Hazan 			  char *buf)
17774210d50fSIsaac Hazan {
17784210d50fSIsaac Hazan 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
17794210d50fSIsaac Hazan 
17808283fb57SAndy Shevchenko 	return sysfs_emit(buf, "%u.0 Gb/s\n", xd->link_speed);
17814210d50fSIsaac Hazan }
17824210d50fSIsaac Hazan 
17834210d50fSIsaac Hazan static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL);
17844210d50fSIsaac Hazan static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL);
17854210d50fSIsaac Hazan 
1786e111fb92SGil Fine static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr,
17874210d50fSIsaac Hazan 			     char *buf)
17884210d50fSIsaac Hazan {
17894210d50fSIsaac Hazan 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1790e111fb92SGil Fine 	unsigned int width;
17914210d50fSIsaac Hazan 
1792e111fb92SGil Fine 	switch (xd->link_width) {
1793e111fb92SGil Fine 	case TB_LINK_WIDTH_SINGLE:
17947ebe52f3SMohammad Rahimi 	case TB_LINK_WIDTH_ASYM_TX:
1795e111fb92SGil Fine 		width = 1;
1796e111fb92SGil Fine 		break;
1797e111fb92SGil Fine 	case TB_LINK_WIDTH_DUAL:
1798e111fb92SGil Fine 		width = 2;
1799e111fb92SGil Fine 		break;
18007ebe52f3SMohammad Rahimi 	case TB_LINK_WIDTH_ASYM_RX:
1801e111fb92SGil Fine 		width = 3;
1802e111fb92SGil Fine 		break;
1803e111fb92SGil Fine 	default:
1804e111fb92SGil Fine 		WARN_ON_ONCE(1);
1805e111fb92SGil Fine 		return -EINVAL;
18064210d50fSIsaac Hazan 	}
18074210d50fSIsaac Hazan 
1808e111fb92SGil Fine 	return sysfs_emit(buf, "%u\n", width);
1809e111fb92SGil Fine }
1810e111fb92SGil Fine static DEVICE_ATTR(rx_lanes, 0444, rx_lanes_show, NULL);
1811e111fb92SGil Fine 
1812e111fb92SGil Fine static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr,
1813e111fb92SGil Fine 			     char *buf)
1814e111fb92SGil Fine {
1815e111fb92SGil Fine 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1816e111fb92SGil Fine 	unsigned int width;
1817e111fb92SGil Fine 
1818e111fb92SGil Fine 	switch (xd->link_width) {
1819e111fb92SGil Fine 	case TB_LINK_WIDTH_SINGLE:
18207ebe52f3SMohammad Rahimi 	case TB_LINK_WIDTH_ASYM_RX:
1821e111fb92SGil Fine 		width = 1;
1822e111fb92SGil Fine 		break;
1823e111fb92SGil Fine 	case TB_LINK_WIDTH_DUAL:
1824e111fb92SGil Fine 		width = 2;
1825e111fb92SGil Fine 		break;
18267ebe52f3SMohammad Rahimi 	case TB_LINK_WIDTH_ASYM_TX:
1827e111fb92SGil Fine 		width = 3;
1828e111fb92SGil Fine 		break;
1829e111fb92SGil Fine 	default:
1830e111fb92SGil Fine 		WARN_ON_ONCE(1);
1831e111fb92SGil Fine 		return -EINVAL;
1832e111fb92SGil Fine 	}
1833e111fb92SGil Fine 
1834e111fb92SGil Fine 	return sysfs_emit(buf, "%u\n", width);
1835e111fb92SGil Fine }
1836e111fb92SGil Fine static DEVICE_ATTR(tx_lanes, 0444, tx_lanes_show, NULL);
18374210d50fSIsaac Hazan 
1838d1ff7024SMika Westerberg static struct attribute *xdomain_attrs[] = {
1839d1ff7024SMika Westerberg 	&dev_attr_device.attr,
1840d1ff7024SMika Westerberg 	&dev_attr_device_name.attr,
184146b494f2SMika Westerberg 	&dev_attr_maxhopid.attr,
18424210d50fSIsaac Hazan 	&dev_attr_rx_lanes.attr,
18434210d50fSIsaac Hazan 	&dev_attr_rx_speed.attr,
18444210d50fSIsaac Hazan 	&dev_attr_tx_lanes.attr,
18454210d50fSIsaac Hazan 	&dev_attr_tx_speed.attr,
1846d1ff7024SMika Westerberg 	&dev_attr_unique_id.attr,
1847d1ff7024SMika Westerberg 	&dev_attr_vendor.attr,
1848d1ff7024SMika Westerberg 	&dev_attr_vendor_name.attr,
1849d1ff7024SMika Westerberg 	NULL,
1850d1ff7024SMika Westerberg };
1851d1ff7024SMika Westerberg 
18526889e00fSRikard Falkeborn static const struct attribute_group xdomain_attr_group = {
1853d1ff7024SMika Westerberg 	.attrs = xdomain_attrs,
1854d1ff7024SMika Westerberg };
1855d1ff7024SMika Westerberg 
1856d1ff7024SMika Westerberg static const struct attribute_group *xdomain_attr_groups[] = {
1857d1ff7024SMika Westerberg 	&xdomain_attr_group,
1858d1ff7024SMika Westerberg 	NULL,
1859d1ff7024SMika Westerberg };
1860d1ff7024SMika Westerberg 
1861d1ff7024SMika Westerberg static void tb_xdomain_release(struct device *dev)
1862d1ff7024SMika Westerberg {
1863d1ff7024SMika Westerberg 	struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
1864d1ff7024SMika Westerberg 
1865d1ff7024SMika Westerberg 	put_device(xd->dev.parent);
1866d1ff7024SMika Westerberg 
186746b494f2SMika Westerberg 	kfree(xd->local_property_block);
186846b494f2SMika Westerberg 	tb_property_free_dir(xd->remote_properties);
1869180b0689SMika Westerberg 	ida_destroy(&xd->out_hopids);
1870180b0689SMika Westerberg 	ida_destroy(&xd->in_hopids);
1871d1ff7024SMika Westerberg 	ida_destroy(&xd->service_ids);
1872d1ff7024SMika Westerberg 
1873d1ff7024SMika Westerberg 	kfree(xd->local_uuid);
1874d1ff7024SMika Westerberg 	kfree(xd->remote_uuid);
1875d1ff7024SMika Westerberg 	kfree(xd->device_name);
1876d1ff7024SMika Westerberg 	kfree(xd->vendor_name);
1877d1ff7024SMika Westerberg 	kfree(xd);
1878d1ff7024SMika Westerberg }
1879d1ff7024SMika Westerberg 
1880d1ff7024SMika Westerberg static int __maybe_unused tb_xdomain_suspend(struct device *dev)
1881d1ff7024SMika Westerberg {
1882d1ff7024SMika Westerberg 	stop_handshake(tb_to_xdomain(dev));
1883d1ff7024SMika Westerberg 	return 0;
1884d1ff7024SMika Westerberg }
1885d1ff7024SMika Westerberg 
1886d1ff7024SMika Westerberg static int __maybe_unused tb_xdomain_resume(struct device *dev)
1887d1ff7024SMika Westerberg {
18888ccbed24SMika Westerberg 	start_handshake(tb_to_xdomain(dev));
1889d1ff7024SMika Westerberg 	return 0;
1890d1ff7024SMika Westerberg }
1891d1ff7024SMika Westerberg 
1892d1ff7024SMika Westerberg static const struct dev_pm_ops tb_xdomain_pm_ops = {
1893d1ff7024SMika Westerberg 	SET_SYSTEM_SLEEP_PM_OPS(tb_xdomain_suspend, tb_xdomain_resume)
1894d1ff7024SMika Westerberg };
1895d1ff7024SMika Westerberg 
1896b8a73083SRicardo B. Marliere const struct device_type tb_xdomain_type = {
1897d1ff7024SMika Westerberg 	.name = "thunderbolt_xdomain",
1898d1ff7024SMika Westerberg 	.release = tb_xdomain_release,
1899d1ff7024SMika Westerberg 	.pm = &tb_xdomain_pm_ops,
1900d1ff7024SMika Westerberg };
1901d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_type);
1902d1ff7024SMika Westerberg 
190336b6ad6aSGil Fine static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down)
190436b6ad6aSGil Fine {
190536b6ad6aSGil Fine 	if (!down->dual_link_port)
190636b6ad6aSGil Fine 		return;
190736b6ad6aSGil Fine 
190836b6ad6aSGil Fine 	/*
190936b6ad6aSGil Fine 	 * Gen 4 links come up already as bonded so only update the port
191036b6ad6aSGil Fine 	 * structures here.
191136b6ad6aSGil Fine 	 */
191236b6ad6aSGil Fine 	if (tb_port_get_link_generation(down) >= 4) {
191336b6ad6aSGil Fine 		down->bonded = true;
191436b6ad6aSGil Fine 		down->dual_link_port->bonded = true;
191536b6ad6aSGil Fine 	} else {
191636b6ad6aSGil Fine 		xd->bonding_possible = true;
191736b6ad6aSGil Fine 	}
191836b6ad6aSGil Fine }
191936b6ad6aSGil Fine 
192036b6ad6aSGil Fine static void tb_xdomain_link_exit(struct tb_xdomain *xd)
192136b6ad6aSGil Fine {
192236b6ad6aSGil Fine 	struct tb_port *down = tb_xdomain_downstream_port(xd);
192336b6ad6aSGil Fine 
192436b6ad6aSGil Fine 	if (!down->dual_link_port)
192536b6ad6aSGil Fine 		return;
192636b6ad6aSGil Fine 
192736b6ad6aSGil Fine 	if (tb_port_get_link_generation(down) >= 4) {
192836b6ad6aSGil Fine 		down->bonded = false;
192936b6ad6aSGil Fine 		down->dual_link_port->bonded = false;
193036b6ad6aSGil Fine 	} else if (xd->link_width > TB_LINK_WIDTH_SINGLE) {
193136b6ad6aSGil Fine 		/*
193236b6ad6aSGil Fine 		 * Just return port structures back to way they were and
193336b6ad6aSGil Fine 		 * update credits. No need to update userspace because
193436b6ad6aSGil Fine 		 * the XDomain is removed soon anyway.
193536b6ad6aSGil Fine 		 */
193636b6ad6aSGil Fine 		tb_port_lane_bonding_disable(down);
193736b6ad6aSGil Fine 		tb_port_update_credits(down);
193836b6ad6aSGil Fine 	} else if (down->dual_link_port) {
193936b6ad6aSGil Fine 		/*
194036b6ad6aSGil Fine 		 * Re-enable the lane 1 adapter we disabled at the end
194136b6ad6aSGil Fine 		 * of tb_xdomain_get_properties().
194236b6ad6aSGil Fine 		 */
194336b6ad6aSGil Fine 		tb_port_enable(down->dual_link_port);
194436b6ad6aSGil Fine 	}
194536b6ad6aSGil Fine }
194636b6ad6aSGil Fine 
1947d1ff7024SMika Westerberg /**
1948d1ff7024SMika Westerberg  * tb_xdomain_alloc() - Allocate new XDomain object
1949d1ff7024SMika Westerberg  * @tb: Domain where the XDomain belongs
1950d1ff7024SMika Westerberg  * @parent: Parent device (the switch through the connection to the
1951d1ff7024SMika Westerberg  *	    other domain is reached).
1952d1ff7024SMika Westerberg  * @route: Route string used to reach the other domain
1953d1ff7024SMika Westerberg  * @local_uuid: Our local domain UUID
19543b4b3235SMika Westerberg  * @remote_uuid: UUID of the other domain (optional)
1955d1ff7024SMika Westerberg  *
1956d1ff7024SMika Westerberg  * Allocates new XDomain structure and returns pointer to that. The
1957d1ff7024SMika Westerberg  * object must be released by calling tb_xdomain_put().
1958d1ff7024SMika Westerberg  */
1959d1ff7024SMika Westerberg struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent,
1960d1ff7024SMika Westerberg 				    u64 route, const uuid_t *local_uuid,
1961d1ff7024SMika Westerberg 				    const uuid_t *remote_uuid)
1962d1ff7024SMika Westerberg {
1963b0407983SMika Westerberg 	struct tb_switch *parent_sw = tb_to_switch(parent);
1964d1ff7024SMika Westerberg 	struct tb_xdomain *xd;
1965b0407983SMika Westerberg 	struct tb_port *down;
1966b0407983SMika Westerberg 
1967b0407983SMika Westerberg 	/* Make sure the downstream domain is accessible */
1968b0407983SMika Westerberg 	down = tb_port_at(route, parent_sw);
1969b0407983SMika Westerberg 	tb_port_unlock(down);
1970d1ff7024SMika Westerberg 
1971d1ff7024SMika Westerberg 	xd = kzalloc(sizeof(*xd), GFP_KERNEL);
1972d1ff7024SMika Westerberg 	if (!xd)
1973d1ff7024SMika Westerberg 		return NULL;
1974d1ff7024SMika Westerberg 
1975d1ff7024SMika Westerberg 	xd->tb = tb;
1976d1ff7024SMika Westerberg 	xd->route = route;
197746b494f2SMika Westerberg 	xd->local_max_hopid = down->config.max_in_hop_id;
1978d1ff7024SMika Westerberg 	ida_init(&xd->service_ids);
1979180b0689SMika Westerberg 	ida_init(&xd->in_hopids);
1980180b0689SMika Westerberg 	ida_init(&xd->out_hopids);
1981d1ff7024SMika Westerberg 	mutex_init(&xd->lock);
19828e1de704SMika Westerberg 	INIT_DELAYED_WORK(&xd->state_work, tb_xdomain_state_work);
1983d1ff7024SMika Westerberg 	INIT_DELAYED_WORK(&xd->properties_changed_work,
1984d1ff7024SMika Westerberg 			  tb_xdomain_properties_changed);
1985d1ff7024SMika Westerberg 
1986d1ff7024SMika Westerberg 	xd->local_uuid = kmemdup(local_uuid, sizeof(uuid_t), GFP_KERNEL);
1987d1ff7024SMika Westerberg 	if (!xd->local_uuid)
1988d1ff7024SMika Westerberg 		goto err_free;
1989d1ff7024SMika Westerberg 
19903b4b3235SMika Westerberg 	if (remote_uuid) {
19913b4b3235SMika Westerberg 		xd->remote_uuid = kmemdup(remote_uuid, sizeof(uuid_t),
19923b4b3235SMika Westerberg 					  GFP_KERNEL);
1993d1ff7024SMika Westerberg 		if (!xd->remote_uuid)
1994d1ff7024SMika Westerberg 			goto err_free_local_uuid;
19953b4b3235SMika Westerberg 	} else {
19963b4b3235SMika Westerberg 		xd->needs_uuid = true;
199736b6ad6aSGil Fine 
199836b6ad6aSGil Fine 		tb_xdomain_link_init(xd, down);
19993b4b3235SMika Westerberg 	}
2000d1ff7024SMika Westerberg 
2001d1ff7024SMika Westerberg 	device_initialize(&xd->dev);
2002d1ff7024SMika Westerberg 	xd->dev.parent = get_device(parent);
2003d1ff7024SMika Westerberg 	xd->dev.bus = &tb_bus_type;
2004d1ff7024SMika Westerberg 	xd->dev.type = &tb_xdomain_type;
2005d1ff7024SMika Westerberg 	xd->dev.groups = xdomain_attr_groups;
2006d1ff7024SMika Westerberg 	dev_set_name(&xd->dev, "%u-%llx", tb->index, route);
2007d1ff7024SMika Westerberg 
2008d29c59b1SMika Westerberg 	dev_dbg(&xd->dev, "local UUID %pUb\n", local_uuid);
2009d29c59b1SMika Westerberg 	if (remote_uuid)
2010d29c59b1SMika Westerberg 		dev_dbg(&xd->dev, "remote UUID %pUb\n", remote_uuid);
2011d29c59b1SMika Westerberg 
20122d8ff0b5SMika Westerberg 	/*
20132d8ff0b5SMika Westerberg 	 * This keeps the DMA powered on as long as we have active
20142d8ff0b5SMika Westerberg 	 * connection to another host.
20152d8ff0b5SMika Westerberg 	 */
20162d8ff0b5SMika Westerberg 	pm_runtime_set_active(&xd->dev);
20172d8ff0b5SMika Westerberg 	pm_runtime_get_noresume(&xd->dev);
20182d8ff0b5SMika Westerberg 	pm_runtime_enable(&xd->dev);
20192d8ff0b5SMika Westerberg 
2020d1ff7024SMika Westerberg 	return xd;
2021d1ff7024SMika Westerberg 
2022d1ff7024SMika Westerberg err_free_local_uuid:
2023d1ff7024SMika Westerberg 	kfree(xd->local_uuid);
2024d1ff7024SMika Westerberg err_free:
2025d1ff7024SMika Westerberg 	kfree(xd);
2026d1ff7024SMika Westerberg 
2027d1ff7024SMika Westerberg 	return NULL;
2028d1ff7024SMika Westerberg }
2029d1ff7024SMika Westerberg 
2030d1ff7024SMika Westerberg /**
2031d1ff7024SMika Westerberg  * tb_xdomain_add() - Add XDomain to the bus
2032d1ff7024SMika Westerberg  * @xd: XDomain to add
2033d1ff7024SMika Westerberg  *
2034d1ff7024SMika Westerberg  * This function starts XDomain discovery protocol handshake and
2035d1ff7024SMika Westerberg  * eventually adds the XDomain to the bus. After calling this function
2036d1ff7024SMika Westerberg  * the caller needs to call tb_xdomain_remove() in order to remove and
2037d1ff7024SMika Westerberg  * release the object regardless whether the handshake succeeded or not.
2038d1ff7024SMika Westerberg  */
2039d1ff7024SMika Westerberg void tb_xdomain_add(struct tb_xdomain *xd)
2040d1ff7024SMika Westerberg {
2041d1ff7024SMika Westerberg 	/* Start exchanging properties with the other host */
2042d1ff7024SMika Westerberg 	start_handshake(xd);
2043d1ff7024SMika Westerberg }
2044d1ff7024SMika Westerberg 
2045d1ff7024SMika Westerberg static int unregister_service(struct device *dev, void *data)
2046d1ff7024SMika Westerberg {
2047d1ff7024SMika Westerberg 	device_unregister(dev);
2048d1ff7024SMika Westerberg 	return 0;
2049d1ff7024SMika Westerberg }
2050d1ff7024SMika Westerberg 
2051d1ff7024SMika Westerberg /**
2052d1ff7024SMika Westerberg  * tb_xdomain_remove() - Remove XDomain from the bus
2053d1ff7024SMika Westerberg  * @xd: XDomain to remove
2054d1ff7024SMika Westerberg  *
2055d1ff7024SMika Westerberg  * This will stop all ongoing configuration work and remove the XDomain
2056d1ff7024SMika Westerberg  * along with any services from the bus. When the last reference to @xd
2057d1ff7024SMika Westerberg  * is released the object will be released as well.
2058d1ff7024SMika Westerberg  */
2059d1ff7024SMika Westerberg void tb_xdomain_remove(struct tb_xdomain *xd)
2060d1ff7024SMika Westerberg {
2061d0f1e0c2SMika Westerberg 	tb_xdomain_debugfs_remove(xd);
2062d0f1e0c2SMika Westerberg 
2063d1ff7024SMika Westerberg 	stop_handshake(xd);
2064d1ff7024SMika Westerberg 
2065d1ff7024SMika Westerberg 	device_for_each_child_reverse(&xd->dev, xd, unregister_service);
2066d1ff7024SMika Westerberg 
206736b6ad6aSGil Fine 	tb_xdomain_link_exit(xd);
206836b6ad6aSGil Fine 
20692d8ff0b5SMika Westerberg 	/*
20702d8ff0b5SMika Westerberg 	 * Undo runtime PM here explicitly because it is possible that
20712d8ff0b5SMika Westerberg 	 * the XDomain was never added to the bus and thus device_del()
20722d8ff0b5SMika Westerberg 	 * is not called for it (device_del() would handle this otherwise).
20732d8ff0b5SMika Westerberg 	 */
20742d8ff0b5SMika Westerberg 	pm_runtime_disable(&xd->dev);
20752d8ff0b5SMika Westerberg 	pm_runtime_put_noidle(&xd->dev);
20762d8ff0b5SMika Westerberg 	pm_runtime_set_suspended(&xd->dev);
20772d8ff0b5SMika Westerberg 
2078d29c59b1SMika Westerberg 	if (!device_is_registered(&xd->dev)) {
2079d1ff7024SMika Westerberg 		put_device(&xd->dev);
2080d29c59b1SMika Westerberg 	} else {
2081d29c59b1SMika Westerberg 		dev_info(&xd->dev, "host disconnected\n");
2082d1ff7024SMika Westerberg 		device_unregister(&xd->dev);
2083d1ff7024SMika Westerberg 	}
2084d29c59b1SMika Westerberg }
2085d1ff7024SMika Westerberg 
2086d1ff7024SMika Westerberg /**
20875cc0df9cSIsaac Hazan  * tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
20885cc0df9cSIsaac Hazan  * @xd: XDomain connection
20895cc0df9cSIsaac Hazan  *
20905cc0df9cSIsaac Hazan  * Lane bonding is disabled by default for XDomains. This function tries
20915cc0df9cSIsaac Hazan  * to enable bonding by first enabling the port and waiting for the CL0
20925cc0df9cSIsaac Hazan  * state.
20935cc0df9cSIsaac Hazan  *
20945cc0df9cSIsaac Hazan  * Return: %0 in case of success and negative errno in case of error.
20955cc0df9cSIsaac Hazan  */
20965cc0df9cSIsaac Hazan int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
20975cc0df9cSIsaac Hazan {
2098e111fb92SGil Fine 	unsigned int width_mask;
20995cc0df9cSIsaac Hazan 	struct tb_port *port;
21005cc0df9cSIsaac Hazan 	int ret;
21015cc0df9cSIsaac Hazan 
210217fb1a3dSMika Westerberg 	port = tb_xdomain_downstream_port(xd);
21035cc0df9cSIsaac Hazan 	if (!port->dual_link_port)
21045cc0df9cSIsaac Hazan 		return -ENODEV;
21055cc0df9cSIsaac Hazan 
21065cc0df9cSIsaac Hazan 	ret = tb_port_enable(port->dual_link_port);
21075cc0df9cSIsaac Hazan 	if (ret)
21085cc0df9cSIsaac Hazan 		return ret;
21095cc0df9cSIsaac Hazan 
21105cc0df9cSIsaac Hazan 	ret = tb_wait_for_port(port->dual_link_port, true);
21115cc0df9cSIsaac Hazan 	if (ret < 0)
21125cc0df9cSIsaac Hazan 		return ret;
21135cc0df9cSIsaac Hazan 	if (!ret)
21145cc0df9cSIsaac Hazan 		return -ENOTCONN;
21155cc0df9cSIsaac Hazan 
21165cc0df9cSIsaac Hazan 	ret = tb_port_lane_bonding_enable(port);
21175cc0df9cSIsaac Hazan 	if (ret) {
21185cc0df9cSIsaac Hazan 		tb_port_warn(port, "failed to enable lane bonding\n");
21195cc0df9cSIsaac Hazan 		return ret;
21205cc0df9cSIsaac Hazan 	}
21215cc0df9cSIsaac Hazan 
2122e111fb92SGil Fine 	/* Any of the widths are all bonded */
2123e111fb92SGil Fine 	width_mask = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX |
2124e111fb92SGil Fine 		     TB_LINK_WIDTH_ASYM_RX;
2125e111fb92SGil Fine 
2126e111fb92SGil Fine 	ret = tb_port_wait_for_link_width(port, width_mask,
2127e111fb92SGil Fine 					  XDOMAIN_BONDING_TIMEOUT);
2128e7051beaSMika Westerberg 	if (ret) {
21298e1de704SMika Westerberg 		tb_port_warn(port, "failed to enable lane bonding\n");
2130e7051beaSMika Westerberg 		return ret;
2131e7051beaSMika Westerberg 	}
2132e7051beaSMika Westerberg 
213369fea377SMika Westerberg 	tb_port_update_credits(port);
21345cc0df9cSIsaac Hazan 	tb_xdomain_update_link_attributes(xd);
21355cc0df9cSIsaac Hazan 
21365cc0df9cSIsaac Hazan 	dev_dbg(&xd->dev, "lane bonding enabled\n");
21375cc0df9cSIsaac Hazan 	return 0;
21385cc0df9cSIsaac Hazan }
21395cc0df9cSIsaac Hazan EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_enable);
21405cc0df9cSIsaac Hazan 
21415cc0df9cSIsaac Hazan /**
21425cc0df9cSIsaac Hazan  * tb_xdomain_lane_bonding_disable() - Disable lane bonding
21435cc0df9cSIsaac Hazan  * @xd: XDomain connection
21445cc0df9cSIsaac Hazan  *
21455cc0df9cSIsaac Hazan  * Lane bonding is disabled by default for XDomains. If bonding has been
21465cc0df9cSIsaac Hazan  * enabled, this function can be used to disable it.
21475cc0df9cSIsaac Hazan  */
21485cc0df9cSIsaac Hazan void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
21495cc0df9cSIsaac Hazan {
21505cc0df9cSIsaac Hazan 	struct tb_port *port;
21515cc0df9cSIsaac Hazan 
215217fb1a3dSMika Westerberg 	port = tb_xdomain_downstream_port(xd);
21535cc0df9cSIsaac Hazan 	if (port->dual_link_port) {
2154e111fb92SGil Fine 		int ret;
2155e111fb92SGil Fine 
21565cc0df9cSIsaac Hazan 		tb_port_lane_bonding_disable(port);
2157e111fb92SGil Fine 		ret = tb_port_wait_for_link_width(port, TB_LINK_WIDTH_SINGLE, 100);
2158e111fb92SGil Fine 		if (ret == -ETIMEDOUT)
2159e7051beaSMika Westerberg 			tb_port_warn(port, "timeout disabling lane bonding\n");
21605cc0df9cSIsaac Hazan 		tb_port_disable(port->dual_link_port);
216169fea377SMika Westerberg 		tb_port_update_credits(port);
21625cc0df9cSIsaac Hazan 		tb_xdomain_update_link_attributes(xd);
21635cc0df9cSIsaac Hazan 
21645cc0df9cSIsaac Hazan 		dev_dbg(&xd->dev, "lane bonding disabled\n");
21655cc0df9cSIsaac Hazan 	}
21665cc0df9cSIsaac Hazan }
21675cc0df9cSIsaac Hazan EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_disable);
21685cc0df9cSIsaac Hazan 
21695cc0df9cSIsaac Hazan /**
2170180b0689SMika Westerberg  * tb_xdomain_alloc_in_hopid() - Allocate input HopID for tunneling
2171180b0689SMika Westerberg  * @xd: XDomain connection
2172180b0689SMika Westerberg  * @hopid: Preferred HopID or %-1 for next available
2173180b0689SMika Westerberg  *
2174180b0689SMika Westerberg  * Returns allocated HopID or negative errno. Specifically returns
2175180b0689SMika Westerberg  * %-ENOSPC if there are no more available HopIDs. Returned HopID is
2176180b0689SMika Westerberg  * guaranteed to be within range supported by the input lane adapter.
2177180b0689SMika Westerberg  * Call tb_xdomain_release_in_hopid() to release the allocated HopID.
2178180b0689SMika Westerberg  */
2179180b0689SMika Westerberg int tb_xdomain_alloc_in_hopid(struct tb_xdomain *xd, int hopid)
2180180b0689SMika Westerberg {
2181180b0689SMika Westerberg 	if (hopid < 0)
2182180b0689SMika Westerberg 		hopid = TB_PATH_MIN_HOPID;
2183180b0689SMika Westerberg 	if (hopid < TB_PATH_MIN_HOPID || hopid > xd->local_max_hopid)
2184180b0689SMika Westerberg 		return -EINVAL;
2185180b0689SMika Westerberg 
2186180b0689SMika Westerberg 	return ida_alloc_range(&xd->in_hopids, hopid, xd->local_max_hopid,
2187180b0689SMika Westerberg 			       GFP_KERNEL);
2188180b0689SMika Westerberg }
2189180b0689SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_alloc_in_hopid);
2190180b0689SMika Westerberg 
2191180b0689SMika Westerberg /**
2192180b0689SMika Westerberg  * tb_xdomain_alloc_out_hopid() - Allocate output HopID for tunneling
2193180b0689SMika Westerberg  * @xd: XDomain connection
2194180b0689SMika Westerberg  * @hopid: Preferred HopID or %-1 for next available
2195180b0689SMika Westerberg  *
2196180b0689SMika Westerberg  * Returns allocated HopID or negative errno. Specifically returns
2197180b0689SMika Westerberg  * %-ENOSPC if there are no more available HopIDs. Returned HopID is
2198180b0689SMika Westerberg  * guaranteed to be within range supported by the output lane adapter.
2199180b0689SMika Westerberg  * Call tb_xdomain_release_in_hopid() to release the allocated HopID.
2200180b0689SMika Westerberg  */
2201180b0689SMika Westerberg int tb_xdomain_alloc_out_hopid(struct tb_xdomain *xd, int hopid)
2202180b0689SMika Westerberg {
2203180b0689SMika Westerberg 	if (hopid < 0)
2204180b0689SMika Westerberg 		hopid = TB_PATH_MIN_HOPID;
2205180b0689SMika Westerberg 	if (hopid < TB_PATH_MIN_HOPID || hopid > xd->remote_max_hopid)
2206180b0689SMika Westerberg 		return -EINVAL;
2207180b0689SMika Westerberg 
2208180b0689SMika Westerberg 	return ida_alloc_range(&xd->out_hopids, hopid, xd->remote_max_hopid,
2209180b0689SMika Westerberg 			       GFP_KERNEL);
2210180b0689SMika Westerberg }
2211180b0689SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_alloc_out_hopid);
2212180b0689SMika Westerberg 
2213180b0689SMika Westerberg /**
2214180b0689SMika Westerberg  * tb_xdomain_release_in_hopid() - Release input HopID
2215180b0689SMika Westerberg  * @xd: XDomain connection
2216180b0689SMika Westerberg  * @hopid: HopID to release
2217180b0689SMika Westerberg  */
2218180b0689SMika Westerberg void tb_xdomain_release_in_hopid(struct tb_xdomain *xd, int hopid)
2219180b0689SMika Westerberg {
2220180b0689SMika Westerberg 	ida_free(&xd->in_hopids, hopid);
2221180b0689SMika Westerberg }
2222180b0689SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_release_in_hopid);
2223180b0689SMika Westerberg 
2224180b0689SMika Westerberg /**
2225180b0689SMika Westerberg  * tb_xdomain_release_out_hopid() - Release output HopID
2226180b0689SMika Westerberg  * @xd: XDomain connection
2227180b0689SMika Westerberg  * @hopid: HopID to release
2228180b0689SMika Westerberg  */
2229180b0689SMika Westerberg void tb_xdomain_release_out_hopid(struct tb_xdomain *xd, int hopid)
2230180b0689SMika Westerberg {
2231180b0689SMika Westerberg 	ida_free(&xd->out_hopids, hopid);
2232180b0689SMika Westerberg }
2233180b0689SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_release_out_hopid);
2234180b0689SMika Westerberg 
2235180b0689SMika Westerberg /**
2236d1ff7024SMika Westerberg  * tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection
2237d1ff7024SMika Westerberg  * @xd: XDomain connection
2238180b0689SMika Westerberg  * @transmit_path: HopID we are using to send out packets
2239180b0689SMika Westerberg  * @transmit_ring: DMA ring used to send out packets
2240180b0689SMika Westerberg  * @receive_path: HopID the other end is using to send packets to us
2241180b0689SMika Westerberg  * @receive_ring: DMA ring used to receive packets from @receive_path
2242d1ff7024SMika Westerberg  *
2243d1ff7024SMika Westerberg  * The function enables DMA paths accordingly so that after successful
2244d1ff7024SMika Westerberg  * return the caller can send and receive packets using high-speed DMA
2245180b0689SMika Westerberg  * path. If a transmit or receive path is not needed, pass %-1 for those
2246180b0689SMika Westerberg  * parameters.
2247d1ff7024SMika Westerberg  *
2248d1ff7024SMika Westerberg  * Return: %0 in case of success and negative errno in case of error
2249d1ff7024SMika Westerberg  */
2250180b0689SMika Westerberg int tb_xdomain_enable_paths(struct tb_xdomain *xd, int transmit_path,
2251180b0689SMika Westerberg 			    int transmit_ring, int receive_path,
2252180b0689SMika Westerberg 			    int receive_ring)
2253d1ff7024SMika Westerberg {
2254180b0689SMika Westerberg 	return tb_domain_approve_xdomain_paths(xd->tb, xd, transmit_path,
2255180b0689SMika Westerberg 					       transmit_ring, receive_path,
2256180b0689SMika Westerberg 					       receive_ring);
2257d1ff7024SMika Westerberg }
2258d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_enable_paths);
2259d1ff7024SMika Westerberg 
2260d1ff7024SMika Westerberg /**
2261d1ff7024SMika Westerberg  * tb_xdomain_disable_paths() - Disable DMA paths for XDomain connection
2262d1ff7024SMika Westerberg  * @xd: XDomain connection
2263180b0689SMika Westerberg  * @transmit_path: HopID we are using to send out packets
2264180b0689SMika Westerberg  * @transmit_ring: DMA ring used to send out packets
2265180b0689SMika Westerberg  * @receive_path: HopID the other end is using to send packets to us
2266180b0689SMika Westerberg  * @receive_ring: DMA ring used to receive packets from @receive_path
2267d1ff7024SMika Westerberg  *
2268d1ff7024SMika Westerberg  * This does the opposite of tb_xdomain_enable_paths(). After call to
2269180b0689SMika Westerberg  * this the caller is not expected to use the rings anymore. Passing %-1
2270180b0689SMika Westerberg  * as path/ring parameter means don't care. Normally the callers should
2271180b0689SMika Westerberg  * pass the same values here as they do when paths are enabled.
2272d1ff7024SMika Westerberg  *
2273d1ff7024SMika Westerberg  * Return: %0 in case of success and negative errno in case of error
2274d1ff7024SMika Westerberg  */
2275180b0689SMika Westerberg int tb_xdomain_disable_paths(struct tb_xdomain *xd, int transmit_path,
2276180b0689SMika Westerberg 			     int transmit_ring, int receive_path,
2277180b0689SMika Westerberg 			     int receive_ring)
2278d1ff7024SMika Westerberg {
2279180b0689SMika Westerberg 	return tb_domain_disconnect_xdomain_paths(xd->tb, xd, transmit_path,
2280180b0689SMika Westerberg 						  transmit_ring, receive_path,
2281180b0689SMika Westerberg 						  receive_ring);
2282d1ff7024SMika Westerberg }
2283d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_disable_paths);
2284d1ff7024SMika Westerberg 
2285d1ff7024SMika Westerberg struct tb_xdomain_lookup {
2286d1ff7024SMika Westerberg 	const uuid_t *uuid;
2287d1ff7024SMika Westerberg 	u8 link;
2288d1ff7024SMika Westerberg 	u8 depth;
2289484cb153SRadion Mirchevsky 	u64 route;
2290d1ff7024SMika Westerberg };
2291d1ff7024SMika Westerberg 
2292d1ff7024SMika Westerberg static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
2293d1ff7024SMika Westerberg 	const struct tb_xdomain_lookup *lookup)
2294d1ff7024SMika Westerberg {
2295b433d010SMika Westerberg 	struct tb_port *port;
2296d1ff7024SMika Westerberg 
2297b433d010SMika Westerberg 	tb_switch_for_each_port(sw, port) {
2298d1ff7024SMika Westerberg 		struct tb_xdomain *xd;
2299d1ff7024SMika Westerberg 
2300d1ff7024SMika Westerberg 		if (port->xdomain) {
2301d1ff7024SMika Westerberg 			xd = port->xdomain;
2302d1ff7024SMika Westerberg 
2303d1ff7024SMika Westerberg 			if (lookup->uuid) {
23043b4b3235SMika Westerberg 				if (xd->remote_uuid &&
23053b4b3235SMika Westerberg 				    uuid_equal(xd->remote_uuid, lookup->uuid))
2306d1ff7024SMika Westerberg 					return xd;
23074e99c98eSAndy Shevchenko 			} else {
23084e99c98eSAndy Shevchenko 				if (lookup->link && lookup->link == xd->link &&
23094e99c98eSAndy Shevchenko 				    lookup->depth == xd->depth)
2310d1ff7024SMika Westerberg 					return xd;
23114e99c98eSAndy Shevchenko 				if (lookup->route && lookup->route == xd->route)
2312484cb153SRadion Mirchevsky 					return xd;
2313d1ff7024SMika Westerberg 			}
2314dfe40ca4SMika Westerberg 		} else if (tb_port_has_remote(port)) {
2315d1ff7024SMika Westerberg 			xd = switch_find_xdomain(port->remote->sw, lookup);
2316d1ff7024SMika Westerberg 			if (xd)
2317d1ff7024SMika Westerberg 				return xd;
2318d1ff7024SMika Westerberg 		}
2319d1ff7024SMika Westerberg 	}
2320d1ff7024SMika Westerberg 
2321d1ff7024SMika Westerberg 	return NULL;
2322d1ff7024SMika Westerberg }
2323d1ff7024SMika Westerberg 
2324d1ff7024SMika Westerberg /**
2325d1ff7024SMika Westerberg  * tb_xdomain_find_by_uuid() - Find an XDomain by UUID
2326d1ff7024SMika Westerberg  * @tb: Domain where the XDomain belongs to
2327d1ff7024SMika Westerberg  * @uuid: UUID to look for
2328d1ff7024SMika Westerberg  *
2329d1ff7024SMika Westerberg  * Finds XDomain by walking through the Thunderbolt topology below @tb.
2330d1ff7024SMika Westerberg  * The returned XDomain will have its reference count increased so the
2331d1ff7024SMika Westerberg  * caller needs to call tb_xdomain_put() when it is done with the
2332d1ff7024SMika Westerberg  * object.
2333d1ff7024SMika Westerberg  *
2334d1ff7024SMika Westerberg  * This will find all XDomains including the ones that are not yet added
2335d1ff7024SMika Westerberg  * to the bus (handshake is still in progress).
2336d1ff7024SMika Westerberg  *
2337d1ff7024SMika Westerberg  * The caller needs to hold @tb->lock.
2338d1ff7024SMika Westerberg  */
2339d1ff7024SMika Westerberg struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid)
2340d1ff7024SMika Westerberg {
2341d1ff7024SMika Westerberg 	struct tb_xdomain_lookup lookup;
2342d1ff7024SMika Westerberg 	struct tb_xdomain *xd;
2343d1ff7024SMika Westerberg 
2344d1ff7024SMika Westerberg 	memset(&lookup, 0, sizeof(lookup));
2345d1ff7024SMika Westerberg 	lookup.uuid = uuid;
2346d1ff7024SMika Westerberg 
2347d1ff7024SMika Westerberg 	xd = switch_find_xdomain(tb->root_switch, &lookup);
2348484cb153SRadion Mirchevsky 	return tb_xdomain_get(xd);
2349d1ff7024SMika Westerberg }
2350d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid);
2351d1ff7024SMika Westerberg 
2352d1ff7024SMika Westerberg /**
2353d1ff7024SMika Westerberg  * tb_xdomain_find_by_link_depth() - Find an XDomain by link and depth
2354d1ff7024SMika Westerberg  * @tb: Domain where the XDomain belongs to
2355d1ff7024SMika Westerberg  * @link: Root switch link number
2356d1ff7024SMika Westerberg  * @depth: Depth in the link
2357d1ff7024SMika Westerberg  *
2358d1ff7024SMika Westerberg  * Finds XDomain by walking through the Thunderbolt topology below @tb.
2359d1ff7024SMika Westerberg  * The returned XDomain will have its reference count increased so the
2360d1ff7024SMika Westerberg  * caller needs to call tb_xdomain_put() when it is done with the
2361d1ff7024SMika Westerberg  * object.
2362d1ff7024SMika Westerberg  *
2363d1ff7024SMika Westerberg  * This will find all XDomains including the ones that are not yet added
2364d1ff7024SMika Westerberg  * to the bus (handshake is still in progress).
2365d1ff7024SMika Westerberg  *
2366d1ff7024SMika Westerberg  * The caller needs to hold @tb->lock.
2367d1ff7024SMika Westerberg  */
2368d1ff7024SMika Westerberg struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
2369d1ff7024SMika Westerberg 						 u8 depth)
2370d1ff7024SMika Westerberg {
2371d1ff7024SMika Westerberg 	struct tb_xdomain_lookup lookup;
2372d1ff7024SMika Westerberg 	struct tb_xdomain *xd;
2373d1ff7024SMika Westerberg 
2374d1ff7024SMika Westerberg 	memset(&lookup, 0, sizeof(lookup));
2375d1ff7024SMika Westerberg 	lookup.link = link;
2376d1ff7024SMika Westerberg 	lookup.depth = depth;
2377d1ff7024SMika Westerberg 
2378d1ff7024SMika Westerberg 	xd = switch_find_xdomain(tb->root_switch, &lookup);
2379484cb153SRadion Mirchevsky 	return tb_xdomain_get(xd);
2380d1ff7024SMika Westerberg }
2381d1ff7024SMika Westerberg 
2382484cb153SRadion Mirchevsky /**
2383484cb153SRadion Mirchevsky  * tb_xdomain_find_by_route() - Find an XDomain by route string
2384484cb153SRadion Mirchevsky  * @tb: Domain where the XDomain belongs to
2385484cb153SRadion Mirchevsky  * @route: XDomain route string
2386484cb153SRadion Mirchevsky  *
2387484cb153SRadion Mirchevsky  * Finds XDomain by walking through the Thunderbolt topology below @tb.
2388484cb153SRadion Mirchevsky  * The returned XDomain will have its reference count increased so the
2389484cb153SRadion Mirchevsky  * caller needs to call tb_xdomain_put() when it is done with the
2390484cb153SRadion Mirchevsky  * object.
2391484cb153SRadion Mirchevsky  *
2392484cb153SRadion Mirchevsky  * This will find all XDomains including the ones that are not yet added
2393484cb153SRadion Mirchevsky  * to the bus (handshake is still in progress).
2394484cb153SRadion Mirchevsky  *
2395484cb153SRadion Mirchevsky  * The caller needs to hold @tb->lock.
2396484cb153SRadion Mirchevsky  */
2397484cb153SRadion Mirchevsky struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route)
2398484cb153SRadion Mirchevsky {
2399484cb153SRadion Mirchevsky 	struct tb_xdomain_lookup lookup;
2400484cb153SRadion Mirchevsky 	struct tb_xdomain *xd;
2401484cb153SRadion Mirchevsky 
2402484cb153SRadion Mirchevsky 	memset(&lookup, 0, sizeof(lookup));
2403484cb153SRadion Mirchevsky 	lookup.route = route;
2404484cb153SRadion Mirchevsky 
2405484cb153SRadion Mirchevsky 	xd = switch_find_xdomain(tb->root_switch, &lookup);
2406484cb153SRadion Mirchevsky 	return tb_xdomain_get(xd);
2407d1ff7024SMika Westerberg }
2408484cb153SRadion Mirchevsky EXPORT_SYMBOL_GPL(tb_xdomain_find_by_route);
2409d1ff7024SMika Westerberg 
2410d1ff7024SMika Westerberg bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type,
2411d1ff7024SMika Westerberg 			       const void *buf, size_t size)
2412d1ff7024SMika Westerberg {
2413d1ff7024SMika Westerberg 	const struct tb_protocol_handler *handler, *tmp;
2414d1ff7024SMika Westerberg 	const struct tb_xdp_header *hdr = buf;
2415d1ff7024SMika Westerberg 	unsigned int length;
2416d1ff7024SMika Westerberg 	int ret = 0;
2417d1ff7024SMika Westerberg 
2418d1ff7024SMika Westerberg 	/* We expect the packet is at least size of the header */
2419d1ff7024SMika Westerberg 	length = hdr->xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK;
2420d1ff7024SMika Westerberg 	if (length != size / 4 - sizeof(hdr->xd_hdr) / 4)
2421d1ff7024SMika Westerberg 		return true;
2422d1ff7024SMika Westerberg 	if (length < sizeof(*hdr) / 4 - sizeof(hdr->xd_hdr) / 4)
2423d1ff7024SMika Westerberg 		return true;
2424d1ff7024SMika Westerberg 
2425d1ff7024SMika Westerberg 	/*
2426d1ff7024SMika Westerberg 	 * Handle XDomain discovery protocol packets directly here. For
2427d1ff7024SMika Westerberg 	 * other protocols (based on their UUID) we call registered
2428d1ff7024SMika Westerberg 	 * handlers in turn.
2429d1ff7024SMika Westerberg 	 */
2430d1ff7024SMika Westerberg 	if (uuid_equal(&hdr->uuid, &tb_xdp_uuid)) {
243148f40b96SAditya Pakki 		if (type == TB_CFG_PKG_XDOMAIN_REQ)
243248f40b96SAditya Pakki 			return tb_xdp_schedule_request(tb, hdr, size);
2433d1ff7024SMika Westerberg 		return false;
2434d1ff7024SMika Westerberg 	}
2435d1ff7024SMika Westerberg 
2436d1ff7024SMika Westerberg 	mutex_lock(&xdomain_lock);
2437d1ff7024SMika Westerberg 	list_for_each_entry_safe(handler, tmp, &protocol_handlers, list) {
2438d1ff7024SMika Westerberg 		if (!uuid_equal(&hdr->uuid, handler->uuid))
2439d1ff7024SMika Westerberg 			continue;
2440d1ff7024SMika Westerberg 
2441d1ff7024SMika Westerberg 		mutex_unlock(&xdomain_lock);
2442d1ff7024SMika Westerberg 		ret = handler->callback(buf, size, handler->data);
2443d1ff7024SMika Westerberg 		mutex_lock(&xdomain_lock);
2444d1ff7024SMika Westerberg 
2445d1ff7024SMika Westerberg 		if (ret)
2446d1ff7024SMika Westerberg 			break;
2447d1ff7024SMika Westerberg 	}
2448d1ff7024SMika Westerberg 	mutex_unlock(&xdomain_lock);
2449d1ff7024SMika Westerberg 
2450d1ff7024SMika Westerberg 	return ret > 0;
2451d1ff7024SMika Westerberg }
2452d1ff7024SMika Westerberg 
2453d1ff7024SMika Westerberg static int update_xdomain(struct device *dev, void *data)
2454d1ff7024SMika Westerberg {
2455d1ff7024SMika Westerberg 	struct tb_xdomain *xd;
2456d1ff7024SMika Westerberg 
2457d1ff7024SMika Westerberg 	xd = tb_to_xdomain(dev);
2458d1ff7024SMika Westerberg 	if (xd) {
2459d1ff7024SMika Westerberg 		queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
2460d1ff7024SMika Westerberg 				   msecs_to_jiffies(50));
2461d1ff7024SMika Westerberg 	}
2462d1ff7024SMika Westerberg 
2463d1ff7024SMika Westerberg 	return 0;
2464d1ff7024SMika Westerberg }
2465d1ff7024SMika Westerberg 
2466d1ff7024SMika Westerberg static void update_all_xdomains(void)
2467d1ff7024SMika Westerberg {
2468d1ff7024SMika Westerberg 	bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain);
2469d1ff7024SMika Westerberg }
2470d1ff7024SMika Westerberg 
2471d1ff7024SMika Westerberg static bool remove_directory(const char *key, const struct tb_property_dir *dir)
2472d1ff7024SMika Westerberg {
2473d1ff7024SMika Westerberg 	struct tb_property *p;
2474d1ff7024SMika Westerberg 
2475d1ff7024SMika Westerberg 	p = tb_property_find(xdomain_property_dir, key,
2476d1ff7024SMika Westerberg 			     TB_PROPERTY_TYPE_DIRECTORY);
2477d1ff7024SMika Westerberg 	if (p && p->value.dir == dir) {
2478d1ff7024SMika Westerberg 		tb_property_remove(p);
2479d1ff7024SMika Westerberg 		return true;
2480d1ff7024SMika Westerberg 	}
2481d1ff7024SMika Westerberg 	return false;
2482d1ff7024SMika Westerberg }
2483d1ff7024SMika Westerberg 
2484d1ff7024SMika Westerberg /**
2485d1ff7024SMika Westerberg  * tb_register_property_dir() - Register property directory to the host
2486d1ff7024SMika Westerberg  * @key: Key (name) of the directory to add
2487d1ff7024SMika Westerberg  * @dir: Directory to add
2488d1ff7024SMika Westerberg  *
2489d1ff7024SMika Westerberg  * Service drivers can use this function to add new property directory
2490d1ff7024SMika Westerberg  * to the host available properties. The other connected hosts are
2491d1ff7024SMika Westerberg  * notified so they can re-read properties of this host if they are
2492d1ff7024SMika Westerberg  * interested.
2493d1ff7024SMika Westerberg  *
2494d1ff7024SMika Westerberg  * Return: %0 on success and negative errno on failure
2495d1ff7024SMika Westerberg  */
2496d1ff7024SMika Westerberg int tb_register_property_dir(const char *key, struct tb_property_dir *dir)
2497d1ff7024SMika Westerberg {
2498d1ff7024SMika Westerberg 	int ret;
2499d1ff7024SMika Westerberg 
2500acb40d84SMika Westerberg 	if (WARN_ON(!xdomain_property_dir))
2501acb40d84SMika Westerberg 		return -EAGAIN;
2502acb40d84SMika Westerberg 
2503d1ff7024SMika Westerberg 	if (!key || strlen(key) > 8)
2504d1ff7024SMika Westerberg 		return -EINVAL;
2505d1ff7024SMika Westerberg 
2506d1ff7024SMika Westerberg 	mutex_lock(&xdomain_lock);
2507d1ff7024SMika Westerberg 	if (tb_property_find(xdomain_property_dir, key,
2508d1ff7024SMika Westerberg 			     TB_PROPERTY_TYPE_DIRECTORY)) {
2509d1ff7024SMika Westerberg 		ret = -EEXIST;
2510d1ff7024SMika Westerberg 		goto err_unlock;
2511d1ff7024SMika Westerberg 	}
2512d1ff7024SMika Westerberg 
2513d1ff7024SMika Westerberg 	ret = tb_property_add_dir(xdomain_property_dir, key, dir);
2514d1ff7024SMika Westerberg 	if (ret)
2515d1ff7024SMika Westerberg 		goto err_unlock;
2516d1ff7024SMika Westerberg 
251746b494f2SMika Westerberg 	xdomain_property_block_gen++;
2518d1ff7024SMika Westerberg 
2519d1ff7024SMika Westerberg 	mutex_unlock(&xdomain_lock);
2520d1ff7024SMika Westerberg 	update_all_xdomains();
2521d1ff7024SMika Westerberg 	return 0;
2522d1ff7024SMika Westerberg 
2523d1ff7024SMika Westerberg err_unlock:
2524d1ff7024SMika Westerberg 	mutex_unlock(&xdomain_lock);
2525d1ff7024SMika Westerberg 	return ret;
2526d1ff7024SMika Westerberg }
2527d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_register_property_dir);
2528d1ff7024SMika Westerberg 
2529d1ff7024SMika Westerberg /**
2530d1ff7024SMika Westerberg  * tb_unregister_property_dir() - Removes property directory from host
2531d1ff7024SMika Westerberg  * @key: Key (name) of the directory
2532d1ff7024SMika Westerberg  * @dir: Directory to remove
2533d1ff7024SMika Westerberg  *
2534d1ff7024SMika Westerberg  * This will remove the existing directory from this host and notify the
2535d1ff7024SMika Westerberg  * connected hosts about the change.
2536d1ff7024SMika Westerberg  */
2537d1ff7024SMika Westerberg void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir)
2538d1ff7024SMika Westerberg {
2539d1ff7024SMika Westerberg 	int ret = 0;
2540d1ff7024SMika Westerberg 
2541d1ff7024SMika Westerberg 	mutex_lock(&xdomain_lock);
2542d1ff7024SMika Westerberg 	if (remove_directory(key, dir))
254346b494f2SMika Westerberg 		xdomain_property_block_gen++;
2544d1ff7024SMika Westerberg 	mutex_unlock(&xdomain_lock);
2545d1ff7024SMika Westerberg 
2546d1ff7024SMika Westerberg 	if (!ret)
2547d1ff7024SMika Westerberg 		update_all_xdomains();
2548d1ff7024SMika Westerberg }
2549d1ff7024SMika Westerberg EXPORT_SYMBOL_GPL(tb_unregister_property_dir);
2550d1ff7024SMika Westerberg 
2551d1ff7024SMika Westerberg int tb_xdomain_init(void)
2552d1ff7024SMika Westerberg {
2553d1ff7024SMika Westerberg 	xdomain_property_dir = tb_property_create_dir(NULL);
2554d1ff7024SMika Westerberg 	if (!xdomain_property_dir)
2555d1ff7024SMika Westerberg 		return -ENOMEM;
2556d1ff7024SMika Westerberg 
2557d1ff7024SMika Westerberg 	/*
2558d1ff7024SMika Westerberg 	 * Initialize standard set of properties without any service
2559d1ff7024SMika Westerberg 	 * directories. Those will be added by service drivers
2560d1ff7024SMika Westerberg 	 * themselves when they are loaded.
25618a00c67eSMika Westerberg 	 *
256246b494f2SMika Westerberg 	 * Rest of the properties are filled dynamically based on these
256346b494f2SMika Westerberg 	 * when the P2P connection is made.
2564d1ff7024SMika Westerberg 	 */
2565d1ff7024SMika Westerberg 	tb_property_add_immediate(xdomain_property_dir, "vendorid",
2566d1ff7024SMika Westerberg 				  PCI_VENDOR_ID_INTEL);
2567d1ff7024SMika Westerberg 	tb_property_add_text(xdomain_property_dir, "vendorid", "Intel Corp.");
2568d1ff7024SMika Westerberg 	tb_property_add_immediate(xdomain_property_dir, "deviceid", 0x1);
2569d1ff7024SMika Westerberg 	tb_property_add_immediate(xdomain_property_dir, "devicerv", 0x80000100);
2570d1ff7024SMika Westerberg 
2571a251c17aSJason A. Donenfeld 	xdomain_property_block_gen = get_random_u32();
25728a00c67eSMika Westerberg 	return 0;
2573d1ff7024SMika Westerberg }
2574d1ff7024SMika Westerberg 
2575d1ff7024SMika Westerberg void tb_xdomain_exit(void)
2576d1ff7024SMika Westerberg {
2577d1ff7024SMika Westerberg 	tb_property_free_dir(xdomain_property_dir);
2578d1ff7024SMika Westerberg }
2579