162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Thunderbolt driver - control channel and configuration commands
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
662306a36Sopenharmony_ci * Copyright (C) 2018, Intel Corporation
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/crc32.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include <linux/dmapool.h>
1462306a36Sopenharmony_ci#include <linux/workqueue.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "ctl.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define TB_CTL_RX_PKG_COUNT	10
2062306a36Sopenharmony_ci#define TB_CTL_RETRIES		4
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/**
2362306a36Sopenharmony_ci * struct tb_ctl - Thunderbolt control channel
2462306a36Sopenharmony_ci * @nhi: Pointer to the NHI structure
2562306a36Sopenharmony_ci * @tx: Transmit ring
2662306a36Sopenharmony_ci * @rx: Receive ring
2762306a36Sopenharmony_ci * @frame_pool: DMA pool for control messages
2862306a36Sopenharmony_ci * @rx_packets: Received control messages
2962306a36Sopenharmony_ci * @request_queue_lock: Lock protecting @request_queue
3062306a36Sopenharmony_ci * @request_queue: List of outstanding requests
3162306a36Sopenharmony_ci * @running: Is the control channel running at the moment
3262306a36Sopenharmony_ci * @timeout_msec: Default timeout for non-raw control messages
3362306a36Sopenharmony_ci * @callback: Callback called when hotplug message is received
3462306a36Sopenharmony_ci * @callback_data: Data passed to @callback
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistruct tb_ctl {
3762306a36Sopenharmony_ci	struct tb_nhi *nhi;
3862306a36Sopenharmony_ci	struct tb_ring *tx;
3962306a36Sopenharmony_ci	struct tb_ring *rx;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	struct dma_pool *frame_pool;
4262306a36Sopenharmony_ci	struct ctl_pkg *rx_packets[TB_CTL_RX_PKG_COUNT];
4362306a36Sopenharmony_ci	struct mutex request_queue_lock;
4462306a36Sopenharmony_ci	struct list_head request_queue;
4562306a36Sopenharmony_ci	bool running;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	int timeout_msec;
4862306a36Sopenharmony_ci	event_cb callback;
4962306a36Sopenharmony_ci	void *callback_data;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define tb_ctl_WARN(ctl, format, arg...) \
5462306a36Sopenharmony_ci	dev_WARN(&(ctl)->nhi->pdev->dev, format, ## arg)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define tb_ctl_err(ctl, format, arg...) \
5762306a36Sopenharmony_ci	dev_err(&(ctl)->nhi->pdev->dev, format, ## arg)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define tb_ctl_warn(ctl, format, arg...) \
6062306a36Sopenharmony_ci	dev_warn(&(ctl)->nhi->pdev->dev, format, ## arg)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define tb_ctl_info(ctl, format, arg...) \
6362306a36Sopenharmony_ci	dev_info(&(ctl)->nhi->pdev->dev, format, ## arg)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define tb_ctl_dbg(ctl, format, arg...) \
6662306a36Sopenharmony_ci	dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue);
6962306a36Sopenharmony_ci/* Serializes access to request kref_get/put */
7062306a36Sopenharmony_cistatic DEFINE_MUTEX(tb_cfg_request_lock);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/**
7362306a36Sopenharmony_ci * tb_cfg_request_alloc() - Allocates a new config request
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * This is refcounted object so when you are done with this, call
7662306a36Sopenharmony_ci * tb_cfg_request_put() to it.
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistruct tb_cfg_request *tb_cfg_request_alloc(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct tb_cfg_request *req;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
8362306a36Sopenharmony_ci	if (!req)
8462306a36Sopenharmony_ci		return NULL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	kref_init(&req->kref);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return req;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/**
9262306a36Sopenharmony_ci * tb_cfg_request_get() - Increase refcount of a request
9362306a36Sopenharmony_ci * @req: Request whose refcount is increased
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_civoid tb_cfg_request_get(struct tb_cfg_request *req)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	mutex_lock(&tb_cfg_request_lock);
9862306a36Sopenharmony_ci	kref_get(&req->kref);
9962306a36Sopenharmony_ci	mutex_unlock(&tb_cfg_request_lock);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void tb_cfg_request_destroy(struct kref *kref)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct tb_cfg_request *req = container_of(kref, typeof(*req), kref);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	kfree(req);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * tb_cfg_request_put() - Decrease refcount and possibly release the request
11162306a36Sopenharmony_ci * @req: Request whose refcount is decreased
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * Call this function when you are done with the request. When refcount
11462306a36Sopenharmony_ci * goes to %0 the object is released.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_civoid tb_cfg_request_put(struct tb_cfg_request *req)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	mutex_lock(&tb_cfg_request_lock);
11962306a36Sopenharmony_ci	kref_put(&req->kref, tb_cfg_request_destroy);
12062306a36Sopenharmony_ci	mutex_unlock(&tb_cfg_request_lock);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int tb_cfg_request_enqueue(struct tb_ctl *ctl,
12462306a36Sopenharmony_ci				  struct tb_cfg_request *req)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags));
12762306a36Sopenharmony_ci	WARN_ON(req->ctl);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	mutex_lock(&ctl->request_queue_lock);
13062306a36Sopenharmony_ci	if (!ctl->running) {
13162306a36Sopenharmony_ci		mutex_unlock(&ctl->request_queue_lock);
13262306a36Sopenharmony_ci		return -ENOTCONN;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	req->ctl = ctl;
13562306a36Sopenharmony_ci	list_add_tail(&req->list, &ctl->request_queue);
13662306a36Sopenharmony_ci	set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
13762306a36Sopenharmony_ci	mutex_unlock(&ctl->request_queue_lock);
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void tb_cfg_request_dequeue(struct tb_cfg_request *req)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct tb_ctl *ctl = req->ctl;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	mutex_lock(&ctl->request_queue_lock);
14662306a36Sopenharmony_ci	list_del(&req->list);
14762306a36Sopenharmony_ci	clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
14862306a36Sopenharmony_ci	if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
14962306a36Sopenharmony_ci		wake_up(&tb_cfg_request_cancel_queue);
15062306a36Sopenharmony_ci	mutex_unlock(&ctl->request_queue_lock);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic bool tb_cfg_request_is_active(struct tb_cfg_request *req)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct tb_cfg_request *
15962306a36Sopenharmony_citb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct tb_cfg_request *req = NULL, *iter;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	mutex_lock(&pkg->ctl->request_queue_lock);
16462306a36Sopenharmony_ci	list_for_each_entry(iter, &pkg->ctl->request_queue, list) {
16562306a36Sopenharmony_ci		tb_cfg_request_get(iter);
16662306a36Sopenharmony_ci		if (iter->match(iter, pkg)) {
16762306a36Sopenharmony_ci			req = iter;
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci		tb_cfg_request_put(iter);
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	mutex_unlock(&pkg->ctl->request_queue_lock);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return req;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* utility functions */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int check_header(const struct ctl_pkg *pkg, u32 len,
18162306a36Sopenharmony_ci			enum tb_cfg_pkg_type type, u64 route)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct tb_cfg_header *header = pkg->buffer;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* check frame, TODO: frame flags */
18662306a36Sopenharmony_ci	if (WARN(len != pkg->frame.size,
18762306a36Sopenharmony_ci			"wrong framesize (expected %#x, got %#x)\n",
18862306a36Sopenharmony_ci			len, pkg->frame.size))
18962306a36Sopenharmony_ci		return -EIO;
19062306a36Sopenharmony_ci	if (WARN(type != pkg->frame.eof, "wrong eof (expected %#x, got %#x)\n",
19162306a36Sopenharmony_ci			type, pkg->frame.eof))
19262306a36Sopenharmony_ci		return -EIO;
19362306a36Sopenharmony_ci	if (WARN(pkg->frame.sof, "wrong sof (expected 0x0, got %#x)\n",
19462306a36Sopenharmony_ci			pkg->frame.sof))
19562306a36Sopenharmony_ci		return -EIO;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* check header */
19862306a36Sopenharmony_ci	if (WARN(header->unknown != 1 << 9,
19962306a36Sopenharmony_ci			"header->unknown is %#x\n", header->unknown))
20062306a36Sopenharmony_ci		return -EIO;
20162306a36Sopenharmony_ci	if (WARN(route != tb_cfg_get_route(header),
20262306a36Sopenharmony_ci			"wrong route (expected %llx, got %llx)",
20362306a36Sopenharmony_ci			route, tb_cfg_get_route(header)))
20462306a36Sopenharmony_ci		return -EIO;
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int check_config_address(struct tb_cfg_address addr,
20962306a36Sopenharmony_ci				enum tb_cfg_space space, u32 offset,
21062306a36Sopenharmony_ci				u32 length)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	if (WARN(addr.zero, "addr.zero is %#x\n", addr.zero))
21362306a36Sopenharmony_ci		return -EIO;
21462306a36Sopenharmony_ci	if (WARN(space != addr.space, "wrong space (expected %x, got %x\n)",
21562306a36Sopenharmony_ci			space, addr.space))
21662306a36Sopenharmony_ci		return -EIO;
21762306a36Sopenharmony_ci	if (WARN(offset != addr.offset, "wrong offset (expected %x, got %x\n)",
21862306a36Sopenharmony_ci			offset, addr.offset))
21962306a36Sopenharmony_ci		return -EIO;
22062306a36Sopenharmony_ci	if (WARN(length != addr.length, "wrong space (expected %x, got %x\n)",
22162306a36Sopenharmony_ci			length, addr.length))
22262306a36Sopenharmony_ci		return -EIO;
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * We cannot check addr->port as it is set to the upstream port of the
22562306a36Sopenharmony_ci	 * sender.
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic struct tb_cfg_result decode_error(const struct ctl_pkg *response)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct cfg_error_pkg *pkg = response->buffer;
23362306a36Sopenharmony_ci	struct tb_cfg_result res = { 0 };
23462306a36Sopenharmony_ci	res.response_route = tb_cfg_get_route(&pkg->header);
23562306a36Sopenharmony_ci	res.response_port = 0;
23662306a36Sopenharmony_ci	res.err = check_header(response, sizeof(*pkg), TB_CFG_PKG_ERROR,
23762306a36Sopenharmony_ci			       tb_cfg_get_route(&pkg->header));
23862306a36Sopenharmony_ci	if (res.err)
23962306a36Sopenharmony_ci		return res;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	res.err = 1;
24262306a36Sopenharmony_ci	res.tb_error = pkg->error;
24362306a36Sopenharmony_ci	res.response_port = pkg->port;
24462306a36Sopenharmony_ci	return res;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic struct tb_cfg_result parse_header(const struct ctl_pkg *pkg, u32 len,
24962306a36Sopenharmony_ci					 enum tb_cfg_pkg_type type, u64 route)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct tb_cfg_header *header = pkg->buffer;
25262306a36Sopenharmony_ci	struct tb_cfg_result res = { 0 };
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (pkg->frame.eof == TB_CFG_PKG_ERROR)
25562306a36Sopenharmony_ci		return decode_error(pkg);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	res.response_port = 0; /* will be updated later for cfg_read/write */
25862306a36Sopenharmony_ci	res.response_route = tb_cfg_get_route(header);
25962306a36Sopenharmony_ci	res.err = check_header(pkg, len, type, route);
26062306a36Sopenharmony_ci	return res;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void tb_cfg_print_error(struct tb_ctl *ctl,
26462306a36Sopenharmony_ci			       const struct tb_cfg_result *res)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	WARN_ON(res->err != 1);
26762306a36Sopenharmony_ci	switch (res->tb_error) {
26862306a36Sopenharmony_ci	case TB_CFG_ERROR_PORT_NOT_CONNECTED:
26962306a36Sopenharmony_ci		/* Port is not connected. This can happen during surprise
27062306a36Sopenharmony_ci		 * removal. Do not warn. */
27162306a36Sopenharmony_ci		return;
27262306a36Sopenharmony_ci	case TB_CFG_ERROR_INVALID_CONFIG_SPACE:
27362306a36Sopenharmony_ci		/*
27462306a36Sopenharmony_ci		 * Invalid cfg_space/offset/length combination in
27562306a36Sopenharmony_ci		 * cfg_read/cfg_write.
27662306a36Sopenharmony_ci		 */
27762306a36Sopenharmony_ci		tb_ctl_dbg(ctl, "%llx:%x: invalid config space or offset\n",
27862306a36Sopenharmony_ci			   res->response_route, res->response_port);
27962306a36Sopenharmony_ci		return;
28062306a36Sopenharmony_ci	case TB_CFG_ERROR_NO_SUCH_PORT:
28162306a36Sopenharmony_ci		/*
28262306a36Sopenharmony_ci		 * - The route contains a non-existent port.
28362306a36Sopenharmony_ci		 * - The route contains a non-PHY port (e.g. PCIe).
28462306a36Sopenharmony_ci		 * - The port in cfg_read/cfg_write does not exist.
28562306a36Sopenharmony_ci		 */
28662306a36Sopenharmony_ci		tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Invalid port\n",
28762306a36Sopenharmony_ci			res->response_route, res->response_port);
28862306a36Sopenharmony_ci		return;
28962306a36Sopenharmony_ci	case TB_CFG_ERROR_LOOP:
29062306a36Sopenharmony_ci		tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Route contains a loop\n",
29162306a36Sopenharmony_ci			res->response_route, res->response_port);
29262306a36Sopenharmony_ci		return;
29362306a36Sopenharmony_ci	case TB_CFG_ERROR_LOCK:
29462306a36Sopenharmony_ci		tb_ctl_warn(ctl, "%llx:%x: downstream port is locked\n",
29562306a36Sopenharmony_ci			    res->response_route, res->response_port);
29662306a36Sopenharmony_ci		return;
29762306a36Sopenharmony_ci	default:
29862306a36Sopenharmony_ci		/* 5,6,7,9 and 11 are also valid error codes */
29962306a36Sopenharmony_ci		tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Unknown error\n",
30062306a36Sopenharmony_ci			res->response_route, res->response_port);
30162306a36Sopenharmony_ci		return;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic __be32 tb_crc(const void *data, size_t len)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	return cpu_to_be32(~__crc32c_le(~0, data, len));
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void tb_ctl_pkg_free(struct ctl_pkg *pkg)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	if (pkg) {
31362306a36Sopenharmony_ci		dma_pool_free(pkg->ctl->frame_pool,
31462306a36Sopenharmony_ci			      pkg->buffer, pkg->frame.buffer_phy);
31562306a36Sopenharmony_ci		kfree(pkg);
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct ctl_pkg *tb_ctl_pkg_alloc(struct tb_ctl *ctl)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct ctl_pkg *pkg = kzalloc(sizeof(*pkg), GFP_KERNEL);
32262306a36Sopenharmony_ci	if (!pkg)
32362306a36Sopenharmony_ci		return NULL;
32462306a36Sopenharmony_ci	pkg->ctl = ctl;
32562306a36Sopenharmony_ci	pkg->buffer = dma_pool_alloc(ctl->frame_pool, GFP_KERNEL,
32662306a36Sopenharmony_ci				     &pkg->frame.buffer_phy);
32762306a36Sopenharmony_ci	if (!pkg->buffer) {
32862306a36Sopenharmony_ci		kfree(pkg);
32962306a36Sopenharmony_ci		return NULL;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	return pkg;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci/* RX/TX handling */
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void tb_ctl_tx_callback(struct tb_ring *ring, struct ring_frame *frame,
33862306a36Sopenharmony_ci			       bool canceled)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame);
34162306a36Sopenharmony_ci	tb_ctl_pkg_free(pkg);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci/*
34562306a36Sopenharmony_ci * tb_cfg_tx() - transmit a packet on the control channel
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * len must be a multiple of four.
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * Return: Returns 0 on success or an error code on failure.
35062306a36Sopenharmony_ci */
35162306a36Sopenharmony_cistatic int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len,
35262306a36Sopenharmony_ci		     enum tb_cfg_pkg_type type)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	int res;
35562306a36Sopenharmony_ci	struct ctl_pkg *pkg;
35662306a36Sopenharmony_ci	if (len % 4 != 0) { /* required for le->be conversion */
35762306a36Sopenharmony_ci		tb_ctl_WARN(ctl, "TX: invalid size: %zu\n", len);
35862306a36Sopenharmony_ci		return -EINVAL;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci	if (len > TB_FRAME_SIZE - 4) { /* checksum is 4 bytes */
36162306a36Sopenharmony_ci		tb_ctl_WARN(ctl, "TX: packet too large: %zu/%d\n",
36262306a36Sopenharmony_ci			    len, TB_FRAME_SIZE - 4);
36362306a36Sopenharmony_ci		return -EINVAL;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci	pkg = tb_ctl_pkg_alloc(ctl);
36662306a36Sopenharmony_ci	if (!pkg)
36762306a36Sopenharmony_ci		return -ENOMEM;
36862306a36Sopenharmony_ci	pkg->frame.callback = tb_ctl_tx_callback;
36962306a36Sopenharmony_ci	pkg->frame.size = len + 4;
37062306a36Sopenharmony_ci	pkg->frame.sof = type;
37162306a36Sopenharmony_ci	pkg->frame.eof = type;
37262306a36Sopenharmony_ci	cpu_to_be32_array(pkg->buffer, data, len / 4);
37362306a36Sopenharmony_ci	*(__be32 *) (pkg->buffer + len) = tb_crc(pkg->buffer, len);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	res = tb_ring_tx(ctl->tx, &pkg->frame);
37662306a36Sopenharmony_ci	if (res) /* ring is stopped */
37762306a36Sopenharmony_ci		tb_ctl_pkg_free(pkg);
37862306a36Sopenharmony_ci	return res;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/*
38262306a36Sopenharmony_ci * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistatic bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type,
38562306a36Sopenharmony_ci				struct ctl_pkg *pkg, size_t size)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	return ctl->callback(ctl->callback_data, type, pkg->buffer, size);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void tb_ctl_rx_submit(struct ctl_pkg *pkg)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	tb_ring_rx(pkg->ctl->rx, &pkg->frame); /*
39362306a36Sopenharmony_ci					     * We ignore failures during stop.
39462306a36Sopenharmony_ci					     * All rx packets are referenced
39562306a36Sopenharmony_ci					     * from ctl->rx_packets, so we do
39662306a36Sopenharmony_ci					     * not loose them.
39762306a36Sopenharmony_ci					     */
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int tb_async_error(const struct ctl_pkg *pkg)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	const struct cfg_error_pkg *error = pkg->buffer;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (pkg->frame.eof != TB_CFG_PKG_ERROR)
40562306a36Sopenharmony_ci		return false;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	switch (error->error) {
40862306a36Sopenharmony_ci	case TB_CFG_ERROR_LINK_ERROR:
40962306a36Sopenharmony_ci	case TB_CFG_ERROR_HEC_ERROR_DETECTED:
41062306a36Sopenharmony_ci	case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
41162306a36Sopenharmony_ci	case TB_CFG_ERROR_DP_BW:
41262306a36Sopenharmony_ci	case TB_CFG_ERROR_ROP_CMPLT:
41362306a36Sopenharmony_ci	case TB_CFG_ERROR_POP_CMPLT:
41462306a36Sopenharmony_ci	case TB_CFG_ERROR_PCIE_WAKE:
41562306a36Sopenharmony_ci	case TB_CFG_ERROR_DP_CON_CHANGE:
41662306a36Sopenharmony_ci	case TB_CFG_ERROR_DPTX_DISCOVERY:
41762306a36Sopenharmony_ci	case TB_CFG_ERROR_LINK_RECOVERY:
41862306a36Sopenharmony_ci	case TB_CFG_ERROR_ASYM_LINK:
41962306a36Sopenharmony_ci		return true;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	default:
42262306a36Sopenharmony_ci		return false;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
42762306a36Sopenharmony_ci			       bool canceled)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame);
43062306a36Sopenharmony_ci	struct tb_cfg_request *req;
43162306a36Sopenharmony_ci	__be32 crc32;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (canceled)
43462306a36Sopenharmony_ci		return; /*
43562306a36Sopenharmony_ci			 * ring is stopped, packet is referenced from
43662306a36Sopenharmony_ci			 * ctl->rx_packets.
43762306a36Sopenharmony_ci			 */
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (frame->size < 4 || frame->size % 4 != 0) {
44062306a36Sopenharmony_ci		tb_ctl_err(pkg->ctl, "RX: invalid size %#x, dropping packet\n",
44162306a36Sopenharmony_ci			   frame->size);
44262306a36Sopenharmony_ci		goto rx;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	frame->size -= 4; /* remove checksum */
44662306a36Sopenharmony_ci	crc32 = tb_crc(pkg->buffer, frame->size);
44762306a36Sopenharmony_ci	be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	switch (frame->eof) {
45062306a36Sopenharmony_ci	case TB_CFG_PKG_READ:
45162306a36Sopenharmony_ci	case TB_CFG_PKG_WRITE:
45262306a36Sopenharmony_ci	case TB_CFG_PKG_ERROR:
45362306a36Sopenharmony_ci	case TB_CFG_PKG_OVERRIDE:
45462306a36Sopenharmony_ci	case TB_CFG_PKG_RESET:
45562306a36Sopenharmony_ci		if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
45662306a36Sopenharmony_ci			tb_ctl_err(pkg->ctl,
45762306a36Sopenharmony_ci				   "RX: checksum mismatch, dropping packet\n");
45862306a36Sopenharmony_ci			goto rx;
45962306a36Sopenharmony_ci		}
46062306a36Sopenharmony_ci		if (tb_async_error(pkg)) {
46162306a36Sopenharmony_ci			tb_ctl_handle_event(pkg->ctl, frame->eof,
46262306a36Sopenharmony_ci					    pkg, frame->size);
46362306a36Sopenharmony_ci			goto rx;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci		break;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	case TB_CFG_PKG_EVENT:
46862306a36Sopenharmony_ci	case TB_CFG_PKG_XDOMAIN_RESP:
46962306a36Sopenharmony_ci	case TB_CFG_PKG_XDOMAIN_REQ:
47062306a36Sopenharmony_ci		if (*(__be32 *)(pkg->buffer + frame->size) != crc32) {
47162306a36Sopenharmony_ci			tb_ctl_err(pkg->ctl,
47262306a36Sopenharmony_ci				   "RX: checksum mismatch, dropping packet\n");
47362306a36Sopenharmony_ci			goto rx;
47462306a36Sopenharmony_ci		}
47562306a36Sopenharmony_ci		fallthrough;
47662306a36Sopenharmony_ci	case TB_CFG_PKG_ICM_EVENT:
47762306a36Sopenharmony_ci		if (tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size))
47862306a36Sopenharmony_ci			goto rx;
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	default:
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * The received packet will be processed only if there is an
48762306a36Sopenharmony_ci	 * active request and that the packet is what is expected. This
48862306a36Sopenharmony_ci	 * prevents packets such as replies coming after timeout has
48962306a36Sopenharmony_ci	 * triggered from messing with the active requests.
49062306a36Sopenharmony_ci	 */
49162306a36Sopenharmony_ci	req = tb_cfg_request_find(pkg->ctl, pkg);
49262306a36Sopenharmony_ci	if (req) {
49362306a36Sopenharmony_ci		if (req->copy(req, pkg))
49462306a36Sopenharmony_ci			schedule_work(&req->work);
49562306a36Sopenharmony_ci		tb_cfg_request_put(req);
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cirx:
49962306a36Sopenharmony_ci	tb_ctl_rx_submit(pkg);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void tb_cfg_request_work(struct work_struct *work)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct tb_cfg_request *req = container_of(work, typeof(*req), work);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
50762306a36Sopenharmony_ci		req->callback(req->callback_data);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	tb_cfg_request_dequeue(req);
51062306a36Sopenharmony_ci	tb_cfg_request_put(req);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/**
51462306a36Sopenharmony_ci * tb_cfg_request() - Start control request not waiting for it to complete
51562306a36Sopenharmony_ci * @ctl: Control channel to use
51662306a36Sopenharmony_ci * @req: Request to start
51762306a36Sopenharmony_ci * @callback: Callback called when the request is completed
51862306a36Sopenharmony_ci * @callback_data: Data to be passed to @callback
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci * This queues @req on the given control channel without waiting for it
52162306a36Sopenharmony_ci * to complete. When the request completes @callback is called.
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_ciint tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req,
52462306a36Sopenharmony_ci		   void (*callback)(void *), void *callback_data)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	int ret;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	req->flags = 0;
52962306a36Sopenharmony_ci	req->callback = callback;
53062306a36Sopenharmony_ci	req->callback_data = callback_data;
53162306a36Sopenharmony_ci	INIT_WORK(&req->work, tb_cfg_request_work);
53262306a36Sopenharmony_ci	INIT_LIST_HEAD(&req->list);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	tb_cfg_request_get(req);
53562306a36Sopenharmony_ci	ret = tb_cfg_request_enqueue(ctl, req);
53662306a36Sopenharmony_ci	if (ret)
53762306a36Sopenharmony_ci		goto err_put;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = tb_ctl_tx(ctl, req->request, req->request_size,
54062306a36Sopenharmony_ci			req->request_type);
54162306a36Sopenharmony_ci	if (ret)
54262306a36Sopenharmony_ci		goto err_dequeue;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (!req->response)
54562306a36Sopenharmony_ci		schedule_work(&req->work);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cierr_dequeue:
55062306a36Sopenharmony_ci	tb_cfg_request_dequeue(req);
55162306a36Sopenharmony_cierr_put:
55262306a36Sopenharmony_ci	tb_cfg_request_put(req);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return ret;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci/**
55862306a36Sopenharmony_ci * tb_cfg_request_cancel() - Cancel a control request
55962306a36Sopenharmony_ci * @req: Request to cancel
56062306a36Sopenharmony_ci * @err: Error to assign to the request
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * This function can be used to cancel ongoing request. It will wait
56362306a36Sopenharmony_ci * until the request is not active anymore.
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_civoid tb_cfg_request_cancel(struct tb_cfg_request *req, int err)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
56862306a36Sopenharmony_ci	schedule_work(&req->work);
56962306a36Sopenharmony_ci	wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req));
57062306a36Sopenharmony_ci	req->result.err = err;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void tb_cfg_request_complete(void *data)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	complete(data);
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/**
57962306a36Sopenharmony_ci * tb_cfg_request_sync() - Start control request and wait until it completes
58062306a36Sopenharmony_ci * @ctl: Control channel to use
58162306a36Sopenharmony_ci * @req: Request to start
58262306a36Sopenharmony_ci * @timeout_msec: Timeout how long to wait @req to complete
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * Starts a control request and waits until it completes. If timeout
58562306a36Sopenharmony_ci * triggers the request is canceled before function returns. Note the
58662306a36Sopenharmony_ci * caller needs to make sure only one message for given switch is active
58762306a36Sopenharmony_ci * at a time.
58862306a36Sopenharmony_ci */
58962306a36Sopenharmony_cistruct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl,
59062306a36Sopenharmony_ci					 struct tb_cfg_request *req,
59162306a36Sopenharmony_ci					 int timeout_msec)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	unsigned long timeout = msecs_to_jiffies(timeout_msec);
59462306a36Sopenharmony_ci	struct tb_cfg_result res = { 0 };
59562306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(done);
59662306a36Sopenharmony_ci	int ret;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	ret = tb_cfg_request(ctl, req, tb_cfg_request_complete, &done);
59962306a36Sopenharmony_ci	if (ret) {
60062306a36Sopenharmony_ci		res.err = ret;
60162306a36Sopenharmony_ci		return res;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&done, timeout))
60562306a36Sopenharmony_ci		tb_cfg_request_cancel(req, -ETIMEDOUT);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	flush_work(&req->work);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return req->result;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/* public interface, alloc/start/stop/free */
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/**
61562306a36Sopenharmony_ci * tb_ctl_alloc() - allocate a control channel
61662306a36Sopenharmony_ci * @nhi: Pointer to NHI
61762306a36Sopenharmony_ci * @timeout_msec: Default timeout used with non-raw control messages
61862306a36Sopenharmony_ci * @cb: Callback called for plug events
61962306a36Sopenharmony_ci * @cb_data: Data passed to @cb
62062306a36Sopenharmony_ci *
62162306a36Sopenharmony_ci * cb will be invoked once for every hot plug event.
62262306a36Sopenharmony_ci *
62362306a36Sopenharmony_ci * Return: Returns a pointer on success or NULL on failure.
62462306a36Sopenharmony_ci */
62562306a36Sopenharmony_cistruct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int timeout_msec, event_cb cb,
62662306a36Sopenharmony_ci			    void *cb_data)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	int i;
62962306a36Sopenharmony_ci	struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
63062306a36Sopenharmony_ci	if (!ctl)
63162306a36Sopenharmony_ci		return NULL;
63262306a36Sopenharmony_ci	ctl->nhi = nhi;
63362306a36Sopenharmony_ci	ctl->timeout_msec = timeout_msec;
63462306a36Sopenharmony_ci	ctl->callback = cb;
63562306a36Sopenharmony_ci	ctl->callback_data = cb_data;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	mutex_init(&ctl->request_queue_lock);
63862306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctl->request_queue);
63962306a36Sopenharmony_ci	ctl->frame_pool = dma_pool_create("thunderbolt_ctl", &nhi->pdev->dev,
64062306a36Sopenharmony_ci					 TB_FRAME_SIZE, 4, 0);
64162306a36Sopenharmony_ci	if (!ctl->frame_pool)
64262306a36Sopenharmony_ci		goto err;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	ctl->tx = tb_ring_alloc_tx(nhi, 0, 10, RING_FLAG_NO_SUSPEND);
64562306a36Sopenharmony_ci	if (!ctl->tx)
64662306a36Sopenharmony_ci		goto err;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff,
64962306a36Sopenharmony_ci				   0xffff, NULL, NULL);
65062306a36Sopenharmony_ci	if (!ctl->rx)
65162306a36Sopenharmony_ci		goto err;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) {
65462306a36Sopenharmony_ci		ctl->rx_packets[i] = tb_ctl_pkg_alloc(ctl);
65562306a36Sopenharmony_ci		if (!ctl->rx_packets[i])
65662306a36Sopenharmony_ci			goto err;
65762306a36Sopenharmony_ci		ctl->rx_packets[i]->frame.callback = tb_ctl_rx_callback;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	tb_ctl_dbg(ctl, "control channel created\n");
66162306a36Sopenharmony_ci	return ctl;
66262306a36Sopenharmony_cierr:
66362306a36Sopenharmony_ci	tb_ctl_free(ctl);
66462306a36Sopenharmony_ci	return NULL;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/**
66862306a36Sopenharmony_ci * tb_ctl_free() - free a control channel
66962306a36Sopenharmony_ci * @ctl: Control channel to free
67062306a36Sopenharmony_ci *
67162306a36Sopenharmony_ci * Must be called after tb_ctl_stop.
67262306a36Sopenharmony_ci *
67362306a36Sopenharmony_ci * Must NOT be called from ctl->callback.
67462306a36Sopenharmony_ci */
67562306a36Sopenharmony_civoid tb_ctl_free(struct tb_ctl *ctl)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	int i;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (!ctl)
68062306a36Sopenharmony_ci		return;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (ctl->rx)
68362306a36Sopenharmony_ci		tb_ring_free(ctl->rx);
68462306a36Sopenharmony_ci	if (ctl->tx)
68562306a36Sopenharmony_ci		tb_ring_free(ctl->tx);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/* free RX packets */
68862306a36Sopenharmony_ci	for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++)
68962306a36Sopenharmony_ci		tb_ctl_pkg_free(ctl->rx_packets[i]);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	dma_pool_destroy(ctl->frame_pool);
69362306a36Sopenharmony_ci	kfree(ctl);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci/**
69762306a36Sopenharmony_ci * tb_ctl_start() - start/resume the control channel
69862306a36Sopenharmony_ci * @ctl: Control channel to start
69962306a36Sopenharmony_ci */
70062306a36Sopenharmony_civoid tb_ctl_start(struct tb_ctl *ctl)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	int i;
70362306a36Sopenharmony_ci	tb_ctl_dbg(ctl, "control channel starting...\n");
70462306a36Sopenharmony_ci	tb_ring_start(ctl->tx); /* is used to ack hotplug packets, start first */
70562306a36Sopenharmony_ci	tb_ring_start(ctl->rx);
70662306a36Sopenharmony_ci	for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++)
70762306a36Sopenharmony_ci		tb_ctl_rx_submit(ctl->rx_packets[i]);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	ctl->running = true;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci/**
71362306a36Sopenharmony_ci * tb_ctl_stop() - pause the control channel
71462306a36Sopenharmony_ci * @ctl: Control channel to stop
71562306a36Sopenharmony_ci *
71662306a36Sopenharmony_ci * All invocations of ctl->callback will have finished after this method
71762306a36Sopenharmony_ci * returns.
71862306a36Sopenharmony_ci *
71962306a36Sopenharmony_ci * Must NOT be called from ctl->callback.
72062306a36Sopenharmony_ci */
72162306a36Sopenharmony_civoid tb_ctl_stop(struct tb_ctl *ctl)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	mutex_lock(&ctl->request_queue_lock);
72462306a36Sopenharmony_ci	ctl->running = false;
72562306a36Sopenharmony_ci	mutex_unlock(&ctl->request_queue_lock);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	tb_ring_stop(ctl->rx);
72862306a36Sopenharmony_ci	tb_ring_stop(ctl->tx);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (!list_empty(&ctl->request_queue))
73162306a36Sopenharmony_ci		tb_ctl_WARN(ctl, "dangling request in request_queue\n");
73262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctl->request_queue);
73362306a36Sopenharmony_ci	tb_ctl_dbg(ctl, "control channel stopped\n");
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/* public interface, commands */
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci/**
73962306a36Sopenharmony_ci * tb_cfg_ack_notification() - Ack notification
74062306a36Sopenharmony_ci * @ctl: Control channel to use
74162306a36Sopenharmony_ci * @route: Router that originated the event
74262306a36Sopenharmony_ci * @error: Pointer to the notification package
74362306a36Sopenharmony_ci *
74462306a36Sopenharmony_ci * Call this as response for non-plug notification to ack it. Returns
74562306a36Sopenharmony_ci * %0 on success or an error code on failure.
74662306a36Sopenharmony_ci */
74762306a36Sopenharmony_ciint tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
74862306a36Sopenharmony_ci			    const struct cfg_error_pkg *error)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct cfg_ack_pkg pkg = {
75162306a36Sopenharmony_ci		.header = tb_cfg_make_header(route),
75262306a36Sopenharmony_ci	};
75362306a36Sopenharmony_ci	const char *name;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	switch (error->error) {
75662306a36Sopenharmony_ci	case TB_CFG_ERROR_LINK_ERROR:
75762306a36Sopenharmony_ci		name = "link error";
75862306a36Sopenharmony_ci		break;
75962306a36Sopenharmony_ci	case TB_CFG_ERROR_HEC_ERROR_DETECTED:
76062306a36Sopenharmony_ci		name = "HEC error";
76162306a36Sopenharmony_ci		break;
76262306a36Sopenharmony_ci	case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
76362306a36Sopenharmony_ci		name = "flow control error";
76462306a36Sopenharmony_ci		break;
76562306a36Sopenharmony_ci	case TB_CFG_ERROR_DP_BW:
76662306a36Sopenharmony_ci		name = "DP_BW";
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	case TB_CFG_ERROR_ROP_CMPLT:
76962306a36Sopenharmony_ci		name = "router operation completion";
77062306a36Sopenharmony_ci		break;
77162306a36Sopenharmony_ci	case TB_CFG_ERROR_POP_CMPLT:
77262306a36Sopenharmony_ci		name = "port operation completion";
77362306a36Sopenharmony_ci		break;
77462306a36Sopenharmony_ci	case TB_CFG_ERROR_PCIE_WAKE:
77562306a36Sopenharmony_ci		name = "PCIe wake";
77662306a36Sopenharmony_ci		break;
77762306a36Sopenharmony_ci	case TB_CFG_ERROR_DP_CON_CHANGE:
77862306a36Sopenharmony_ci		name = "DP connector change";
77962306a36Sopenharmony_ci		break;
78062306a36Sopenharmony_ci	case TB_CFG_ERROR_DPTX_DISCOVERY:
78162306a36Sopenharmony_ci		name = "DPTX discovery";
78262306a36Sopenharmony_ci		break;
78362306a36Sopenharmony_ci	case TB_CFG_ERROR_LINK_RECOVERY:
78462306a36Sopenharmony_ci		name = "link recovery";
78562306a36Sopenharmony_ci		break;
78662306a36Sopenharmony_ci	case TB_CFG_ERROR_ASYM_LINK:
78762306a36Sopenharmony_ci		name = "asymmetric link";
78862306a36Sopenharmony_ci		break;
78962306a36Sopenharmony_ci	default:
79062306a36Sopenharmony_ci		name = "unknown";
79162306a36Sopenharmony_ci		break;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	tb_ctl_dbg(ctl, "acking %s (%#x) notification on %llx\n", name,
79562306a36Sopenharmony_ci		   error->error, route);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_NOTIFY_ACK);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/**
80162306a36Sopenharmony_ci * tb_cfg_ack_plug() - Ack hot plug/unplug event
80262306a36Sopenharmony_ci * @ctl: Control channel to use
80362306a36Sopenharmony_ci * @route: Router that originated the event
80462306a36Sopenharmony_ci * @port: Port where the hot plug/unplug happened
80562306a36Sopenharmony_ci * @unplug: Ack hot plug or unplug
80662306a36Sopenharmony_ci *
80762306a36Sopenharmony_ci * Call this as response for hot plug/unplug event to ack it.
80862306a36Sopenharmony_ci * Returns %0 on success or an error code on failure.
80962306a36Sopenharmony_ci */
81062306a36Sopenharmony_ciint tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct cfg_error_pkg pkg = {
81362306a36Sopenharmony_ci		.header = tb_cfg_make_header(route),
81462306a36Sopenharmony_ci		.port = port,
81562306a36Sopenharmony_ci		.error = TB_CFG_ERROR_ACK_PLUG_EVENT,
81662306a36Sopenharmony_ci		.pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG
81762306a36Sopenharmony_ci			     : TB_CFG_ERROR_PG_HOT_PLUG,
81862306a36Sopenharmony_ci	};
81962306a36Sopenharmony_ci	tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%u\n",
82062306a36Sopenharmony_ci		   unplug ? "un" : "", route, port);
82162306a36Sopenharmony_ci	return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic bool tb_cfg_match(const struct tb_cfg_request *req,
82562306a36Sopenharmony_ci			 const struct ctl_pkg *pkg)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	u64 route = tb_cfg_get_route(pkg->buffer) & ~BIT_ULL(63);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	if (pkg->frame.eof == TB_CFG_PKG_ERROR)
83062306a36Sopenharmony_ci		return true;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (pkg->frame.eof != req->response_type)
83362306a36Sopenharmony_ci		return false;
83462306a36Sopenharmony_ci	if (route != tb_cfg_get_route(req->request))
83562306a36Sopenharmony_ci		return false;
83662306a36Sopenharmony_ci	if (pkg->frame.size != req->response_size)
83762306a36Sopenharmony_ci		return false;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (pkg->frame.eof == TB_CFG_PKG_READ ||
84062306a36Sopenharmony_ci	    pkg->frame.eof == TB_CFG_PKG_WRITE) {
84162306a36Sopenharmony_ci		const struct cfg_read_pkg *req_hdr = req->request;
84262306a36Sopenharmony_ci		const struct cfg_read_pkg *res_hdr = pkg->buffer;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		if (req_hdr->addr.seq != res_hdr->addr.seq)
84562306a36Sopenharmony_ci			return false;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return true;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic bool tb_cfg_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct tb_cfg_result res;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	/* Now make sure it is in expected format */
85662306a36Sopenharmony_ci	res = parse_header(pkg, req->response_size, req->response_type,
85762306a36Sopenharmony_ci			   tb_cfg_get_route(req->request));
85862306a36Sopenharmony_ci	if (!res.err)
85962306a36Sopenharmony_ci		memcpy(req->response, pkg->buffer, req->response_size);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	req->result = res;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* Always complete when first response is received */
86462306a36Sopenharmony_ci	return true;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/**
86862306a36Sopenharmony_ci * tb_cfg_reset() - send a reset packet and wait for a response
86962306a36Sopenharmony_ci * @ctl: Control channel pointer
87062306a36Sopenharmony_ci * @route: Router string for the router to send reset
87162306a36Sopenharmony_ci *
87262306a36Sopenharmony_ci * If the switch at route is incorrectly configured then we will not receive a
87362306a36Sopenharmony_ci * reply (even though the switch will reset). The caller should check for
87462306a36Sopenharmony_ci * -ETIMEDOUT and attempt to reconfigure the switch.
87562306a36Sopenharmony_ci */
87662306a36Sopenharmony_cistruct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	struct cfg_reset_pkg request = { .header = tb_cfg_make_header(route) };
87962306a36Sopenharmony_ci	struct tb_cfg_result res = { 0 };
88062306a36Sopenharmony_ci	struct tb_cfg_header reply;
88162306a36Sopenharmony_ci	struct tb_cfg_request *req;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	req = tb_cfg_request_alloc();
88462306a36Sopenharmony_ci	if (!req) {
88562306a36Sopenharmony_ci		res.err = -ENOMEM;
88662306a36Sopenharmony_ci		return res;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	req->match = tb_cfg_match;
89062306a36Sopenharmony_ci	req->copy = tb_cfg_copy;
89162306a36Sopenharmony_ci	req->request = &request;
89262306a36Sopenharmony_ci	req->request_size = sizeof(request);
89362306a36Sopenharmony_ci	req->request_type = TB_CFG_PKG_RESET;
89462306a36Sopenharmony_ci	req->response = &reply;
89562306a36Sopenharmony_ci	req->response_size = sizeof(reply);
89662306a36Sopenharmony_ci	req->response_type = TB_CFG_PKG_RESET;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	res = tb_cfg_request_sync(ctl, req, ctl->timeout_msec);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	tb_cfg_request_put(req);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return res;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci/**
90662306a36Sopenharmony_ci * tb_cfg_read_raw() - read from config space into buffer
90762306a36Sopenharmony_ci * @ctl: Pointer to the control channel
90862306a36Sopenharmony_ci * @buffer: Buffer where the data is read
90962306a36Sopenharmony_ci * @route: Route string of the router
91062306a36Sopenharmony_ci * @port: Port number when reading from %TB_CFG_PORT, %0 otherwise
91162306a36Sopenharmony_ci * @space: Config space selector
91262306a36Sopenharmony_ci * @offset: Dword word offset of the register to start reading
91362306a36Sopenharmony_ci * @length: Number of dwords to read
91462306a36Sopenharmony_ci * @timeout_msec: Timeout in ms how long to wait for the response
91562306a36Sopenharmony_ci *
91662306a36Sopenharmony_ci * Reads from router config space without translating the possible error.
91762306a36Sopenharmony_ci */
91862306a36Sopenharmony_cistruct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
91962306a36Sopenharmony_ci		u64 route, u32 port, enum tb_cfg_space space,
92062306a36Sopenharmony_ci		u32 offset, u32 length, int timeout_msec)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct tb_cfg_result res = { 0 };
92362306a36Sopenharmony_ci	struct cfg_read_pkg request = {
92462306a36Sopenharmony_ci		.header = tb_cfg_make_header(route),
92562306a36Sopenharmony_ci		.addr = {
92662306a36Sopenharmony_ci			.port = port,
92762306a36Sopenharmony_ci			.space = space,
92862306a36Sopenharmony_ci			.offset = offset,
92962306a36Sopenharmony_ci			.length = length,
93062306a36Sopenharmony_ci		},
93162306a36Sopenharmony_ci	};
93262306a36Sopenharmony_ci	struct cfg_write_pkg reply;
93362306a36Sopenharmony_ci	int retries = 0;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	while (retries < TB_CTL_RETRIES) {
93662306a36Sopenharmony_ci		struct tb_cfg_request *req;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		req = tb_cfg_request_alloc();
93962306a36Sopenharmony_ci		if (!req) {
94062306a36Sopenharmony_ci			res.err = -ENOMEM;
94162306a36Sopenharmony_ci			return res;
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		request.addr.seq = retries++;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		req->match = tb_cfg_match;
94762306a36Sopenharmony_ci		req->copy = tb_cfg_copy;
94862306a36Sopenharmony_ci		req->request = &request;
94962306a36Sopenharmony_ci		req->request_size = sizeof(request);
95062306a36Sopenharmony_ci		req->request_type = TB_CFG_PKG_READ;
95162306a36Sopenharmony_ci		req->response = &reply;
95262306a36Sopenharmony_ci		req->response_size = 12 + 4 * length;
95362306a36Sopenharmony_ci		req->response_type = TB_CFG_PKG_READ;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		res = tb_cfg_request_sync(ctl, req, timeout_msec);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		tb_cfg_request_put(req);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci		if (res.err != -ETIMEDOUT)
96062306a36Sopenharmony_ci			break;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		/* Wait a bit (arbitrary time) until we send a retry */
96362306a36Sopenharmony_ci		usleep_range(10, 100);
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (res.err)
96762306a36Sopenharmony_ci		return res;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	res.response_port = reply.addr.port;
97062306a36Sopenharmony_ci	res.err = check_config_address(reply.addr, space, offset, length);
97162306a36Sopenharmony_ci	if (!res.err)
97262306a36Sopenharmony_ci		memcpy(buffer, &reply.data, 4 * length);
97362306a36Sopenharmony_ci	return res;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci/**
97762306a36Sopenharmony_ci * tb_cfg_write_raw() - write from buffer into config space
97862306a36Sopenharmony_ci * @ctl: Pointer to the control channel
97962306a36Sopenharmony_ci * @buffer: Data to write
98062306a36Sopenharmony_ci * @route: Route string of the router
98162306a36Sopenharmony_ci * @port: Port number when writing to %TB_CFG_PORT, %0 otherwise
98262306a36Sopenharmony_ci * @space: Config space selector
98362306a36Sopenharmony_ci * @offset: Dword word offset of the register to start writing
98462306a36Sopenharmony_ci * @length: Number of dwords to write
98562306a36Sopenharmony_ci * @timeout_msec: Timeout in ms how long to wait for the response
98662306a36Sopenharmony_ci *
98762306a36Sopenharmony_ci * Writes to router config space without translating the possible error.
98862306a36Sopenharmony_ci */
98962306a36Sopenharmony_cistruct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer,
99062306a36Sopenharmony_ci		u64 route, u32 port, enum tb_cfg_space space,
99162306a36Sopenharmony_ci		u32 offset, u32 length, int timeout_msec)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	struct tb_cfg_result res = { 0 };
99462306a36Sopenharmony_ci	struct cfg_write_pkg request = {
99562306a36Sopenharmony_ci		.header = tb_cfg_make_header(route),
99662306a36Sopenharmony_ci		.addr = {
99762306a36Sopenharmony_ci			.port = port,
99862306a36Sopenharmony_ci			.space = space,
99962306a36Sopenharmony_ci			.offset = offset,
100062306a36Sopenharmony_ci			.length = length,
100162306a36Sopenharmony_ci		},
100262306a36Sopenharmony_ci	};
100362306a36Sopenharmony_ci	struct cfg_read_pkg reply;
100462306a36Sopenharmony_ci	int retries = 0;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	memcpy(&request.data, buffer, length * 4);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	while (retries < TB_CTL_RETRIES) {
100962306a36Sopenharmony_ci		struct tb_cfg_request *req;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		req = tb_cfg_request_alloc();
101262306a36Sopenharmony_ci		if (!req) {
101362306a36Sopenharmony_ci			res.err = -ENOMEM;
101462306a36Sopenharmony_ci			return res;
101562306a36Sopenharmony_ci		}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		request.addr.seq = retries++;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		req->match = tb_cfg_match;
102062306a36Sopenharmony_ci		req->copy = tb_cfg_copy;
102162306a36Sopenharmony_ci		req->request = &request;
102262306a36Sopenharmony_ci		req->request_size = 12 + 4 * length;
102362306a36Sopenharmony_ci		req->request_type = TB_CFG_PKG_WRITE;
102462306a36Sopenharmony_ci		req->response = &reply;
102562306a36Sopenharmony_ci		req->response_size = sizeof(reply);
102662306a36Sopenharmony_ci		req->response_type = TB_CFG_PKG_WRITE;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		res = tb_cfg_request_sync(ctl, req, timeout_msec);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		tb_cfg_request_put(req);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		if (res.err != -ETIMEDOUT)
103362306a36Sopenharmony_ci			break;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		/* Wait a bit (arbitrary time) until we send a retry */
103662306a36Sopenharmony_ci		usleep_range(10, 100);
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (res.err)
104062306a36Sopenharmony_ci		return res;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	res.response_port = reply.addr.port;
104362306a36Sopenharmony_ci	res.err = check_config_address(reply.addr, space, offset, length);
104462306a36Sopenharmony_ci	return res;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space,
104862306a36Sopenharmony_ci			    const struct tb_cfg_result *res)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	/*
105162306a36Sopenharmony_ci	 * For unimplemented ports access to port config space may return
105262306a36Sopenharmony_ci	 * TB_CFG_ERROR_INVALID_CONFIG_SPACE (alternatively their type is
105362306a36Sopenharmony_ci	 * set to TB_TYPE_INACTIVE). In the former case return -ENODEV so
105462306a36Sopenharmony_ci	 * that the caller can mark the port as disabled.
105562306a36Sopenharmony_ci	 */
105662306a36Sopenharmony_ci	if (space == TB_CFG_PORT &&
105762306a36Sopenharmony_ci	    res->tb_error == TB_CFG_ERROR_INVALID_CONFIG_SPACE)
105862306a36Sopenharmony_ci		return -ENODEV;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	tb_cfg_print_error(ctl, res);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	if (res->tb_error == TB_CFG_ERROR_LOCK)
106362306a36Sopenharmony_ci		return -EACCES;
106462306a36Sopenharmony_ci	if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED)
106562306a36Sopenharmony_ci		return -ENOTCONN;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	return -EIO;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ciint tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,
107162306a36Sopenharmony_ci		enum tb_cfg_space space, u32 offset, u32 length)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct tb_cfg_result res = tb_cfg_read_raw(ctl, buffer, route, port,
107462306a36Sopenharmony_ci			space, offset, length, ctl->timeout_msec);
107562306a36Sopenharmony_ci	switch (res.err) {
107662306a36Sopenharmony_ci	case 0:
107762306a36Sopenharmony_ci		/* Success */
107862306a36Sopenharmony_ci		break;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	case 1:
108162306a36Sopenharmony_ci		/* Thunderbolt error, tb_error holds the actual number */
108262306a36Sopenharmony_ci		return tb_cfg_get_error(ctl, space, &res);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	case -ETIMEDOUT:
108562306a36Sopenharmony_ci		tb_ctl_warn(ctl, "%llx: timeout reading config space %u from %#x\n",
108662306a36Sopenharmony_ci			    route, space, offset);
108762306a36Sopenharmony_ci		break;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	default:
109062306a36Sopenharmony_ci		WARN(1, "tb_cfg_read: %d\n", res.err);
109162306a36Sopenharmony_ci		break;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci	return res.err;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ciint tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,
109762306a36Sopenharmony_ci		 enum tb_cfg_space space, u32 offset, u32 length)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct tb_cfg_result res = tb_cfg_write_raw(ctl, buffer, route, port,
110062306a36Sopenharmony_ci			space, offset, length, ctl->timeout_msec);
110162306a36Sopenharmony_ci	switch (res.err) {
110262306a36Sopenharmony_ci	case 0:
110362306a36Sopenharmony_ci		/* Success */
110462306a36Sopenharmony_ci		break;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	case 1:
110762306a36Sopenharmony_ci		/* Thunderbolt error, tb_error holds the actual number */
110862306a36Sopenharmony_ci		return tb_cfg_get_error(ctl, space, &res);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	case -ETIMEDOUT:
111162306a36Sopenharmony_ci		tb_ctl_warn(ctl, "%llx: timeout writing config space %u to %#x\n",
111262306a36Sopenharmony_ci			    route, space, offset);
111362306a36Sopenharmony_ci		break;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	default:
111662306a36Sopenharmony_ci		WARN(1, "tb_cfg_write: %d\n", res.err);
111762306a36Sopenharmony_ci		break;
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci	return res.err;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/**
112362306a36Sopenharmony_ci * tb_cfg_get_upstream_port() - get upstream port number of switch at route
112462306a36Sopenharmony_ci * @ctl: Pointer to the control channel
112562306a36Sopenharmony_ci * @route: Route string of the router
112662306a36Sopenharmony_ci *
112762306a36Sopenharmony_ci * Reads the first dword from the switches TB_CFG_SWITCH config area and
112862306a36Sopenharmony_ci * returns the port number from which the reply originated.
112962306a36Sopenharmony_ci *
113062306a36Sopenharmony_ci * Return: Returns the upstream port number on success or an error code on
113162306a36Sopenharmony_ci * failure.
113262306a36Sopenharmony_ci */
113362306a36Sopenharmony_ciint tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	u32 dummy;
113662306a36Sopenharmony_ci	struct tb_cfg_result res = tb_cfg_read_raw(ctl, &dummy, route, 0,
113762306a36Sopenharmony_ci						   TB_CFG_SWITCH, 0, 1,
113862306a36Sopenharmony_ci						   ctl->timeout_msec);
113962306a36Sopenharmony_ci	if (res.err == 1)
114062306a36Sopenharmony_ci		return -EIO;
114162306a36Sopenharmony_ci	if (res.err)
114262306a36Sopenharmony_ci		return res.err;
114362306a36Sopenharmony_ci	return res.response_port;
114462306a36Sopenharmony_ci}
1145