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