diff options
Diffstat (limited to 'drivers/thunderbolt/ctl.c')
-rw-r--r-- | drivers/thunderbolt/ctl.c | 110 |
1 files changed, 84 insertions, 26 deletions
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 2ec1af8f7968..f21d05373f52 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -20,7 +20,17 @@ #define TB_CTL_RETRIES 4 /** - * struct tb_cfg - thunderbolt control channel + * struct tb_ctl - Thunderbolt control channel + * @nhi: Pointer to the NHI structure + * @tx: Transmit ring + * @rx: Receive ring + * @frame_pool: DMA pool for control messages + * @rx_packets: Received control messages + * @request_queue_lock: Lock protecting @request_queue + * @request_queue: List of outstanding requests + * @running: Is the control channel running at the moment + * @callback: Callback called when hotplug message is received + * @callback_data: Data passed to @callback */ struct tb_ctl { struct tb_nhi *nhi; @@ -219,6 +229,7 @@ static int check_config_address(struct tb_cfg_address addr, static struct tb_cfg_result decode_error(const struct ctl_pkg *response) { struct cfg_error_pkg *pkg = response->buffer; + struct tb_ctl *ctl = response->ctl; struct tb_cfg_result res = { 0 }; res.response_route = tb_cfg_get_route(&pkg->header); res.response_port = 0; @@ -227,9 +238,13 @@ static struct tb_cfg_result decode_error(const struct ctl_pkg *response) if (res.err) return res; - WARN(pkg->zero1, "pkg->zero1 is %#x\n", pkg->zero1); - WARN(pkg->zero2, "pkg->zero1 is %#x\n", pkg->zero1); - WARN(pkg->zero3, "pkg->zero1 is %#x\n", pkg->zero1); + if (pkg->zero1) + tb_ctl_warn(ctl, "pkg->zero1 is %#x\n", pkg->zero1); + if (pkg->zero2) + tb_ctl_warn(ctl, "pkg->zero2 is %#x\n", pkg->zero2); + if (pkg->zero3) + tb_ctl_warn(ctl, "pkg->zero3 is %#x\n", pkg->zero3); + res.err = 1; res.tb_error = pkg->error; res.response_port = pkg->port; @@ -266,9 +281,8 @@ static void tb_cfg_print_error(struct tb_ctl *ctl, * Invalid cfg_space/offset/length combination in * cfg_read/cfg_write. */ - tb_ctl_WARN(ctl, - "CFG_ERROR(%llx:%x): Invalid config space or offset\n", - res->response_route, res->response_port); + tb_ctl_dbg(ctl, "%llx:%x: invalid config space or offset\n", + res->response_route, res->response_port); return; case TB_CFG_ERROR_NO_SUCH_PORT: /* @@ -283,6 +297,10 @@ static void tb_cfg_print_error(struct tb_ctl *ctl, tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Route contains a loop\n", res->response_route, res->response_port); return; + case TB_CFG_ERROR_LOCK: + tb_ctl_warn(ctl, "%llx:%x: downstream port is locked\n", + res->response_route, res->response_port); + return; default: /* 5,6,7,9 and 11 are also valid error codes */ tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Unknown error\n", @@ -330,7 +348,7 @@ static void tb_ctl_tx_callback(struct tb_ring *ring, struct ring_frame *frame, tb_ctl_pkg_free(pkg); } -/** +/* * tb_cfg_tx() - transmit a packet on the control channel * * len must be a multiple of four. @@ -367,7 +385,7 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len, return res; } -/** +/* * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback */ static bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, @@ -388,7 +406,7 @@ static void tb_ctl_rx_submit(struct ctl_pkg *pkg) static int tb_async_error(const struct ctl_pkg *pkg) { - const struct cfg_error_pkg *error = (const struct cfg_error_pkg *)pkg; + const struct cfg_error_pkg *error = pkg->buffer; if (pkg->frame.eof != TB_CFG_PKG_ERROR) return false; @@ -594,6 +612,9 @@ struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl, /** * tb_ctl_alloc() - allocate a control channel + * @nhi: Pointer to NHI + * @cb: Callback called for plug events + * @cb_data: Data passed to @cb * * cb will be invoked once for every hot plug event. * @@ -620,8 +641,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) if (!ctl->tx) goto err; - ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, - 0xffff, NULL, NULL); + ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff, + 0xffff, NULL, NULL); if (!ctl->rx) goto err; @@ -641,6 +662,7 @@ err: /** * tb_ctl_free() - free a control channel + * @ctl: Control channel to free * * Must be called after tb_ctl_stop. * @@ -669,6 +691,7 @@ void tb_ctl_free(struct tb_ctl *ctl) /** * tb_cfg_start() - start/resume the control channel + * @ctl: Control channel to start */ void tb_ctl_start(struct tb_ctl *ctl) { @@ -683,7 +706,8 @@ void tb_ctl_start(struct tb_ctl *ctl) } /** - * control() - pause the control channel + * tb_ctrl_stop() - pause the control channel + * @ctl: Control channel to stop * * All invocations of ctl->callback will have finished after this method * returns. @@ -708,19 +732,26 @@ void tb_ctl_stop(struct tb_ctl *ctl) /* public interface, commands */ /** - * tb_cfg_error() - send error packet + * tb_cfg_ack_plug() - Ack hot plug/unplug event + * @ctl: Control channel to use + * @route: Router that originated the event + * @port: Port where the hot plug/unplug happened + * @unplug: Ack hot plug or unplug * - * Return: Returns 0 on success or an error code on failure. + * Call this as response for hot plug/unplug event to ack it. + * Returns %0 on success or an error code on failure. */ -int tb_cfg_error(struct tb_ctl *ctl, u64 route, u32 port, - enum tb_cfg_error error) +int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug) { struct cfg_error_pkg pkg = { .header = tb_cfg_make_header(route), .port = port, - .error = error, + .error = TB_CFG_ERROR_ACK_PLUG_EVENT, + .pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG + : TB_CFG_ERROR_PG_HOT_PLUG, }; - tb_ctl_dbg(ctl, "resetting error on %llx:%x.\n", route, port); + tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%x\n", + unplug ? "un" : "", route, port); return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR); } @@ -769,6 +800,9 @@ static bool tb_cfg_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) /** * tb_cfg_reset() - send a reset packet and wait for a response + * @ctl: Control channel pointer + * @route: Router string for the router to send reset + * @timeout_msec: Timeout in ms how long to wait for the response * * If the switch at route is incorrectly configured then we will not receive a * reply (even though the switch will reset). The caller should check for @@ -805,9 +839,17 @@ struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, } /** - * tb_cfg_read() - read from config space into buffer + * tb_cfg_read_raw() - read from config space into buffer + * @ctl: Pointer to the control channel + * @buffer: Buffer where the data is read + * @route: Route string of the router + * @port: Port number when reading from %TB_CFG_PORT, %0 otherwise + * @space: Config space selector + * @offset: Dword word offset of the register to start reading + * @length: Number of dwords to read + * @timeout_msec: Timeout in ms how long to wait for the response * - * Offset and length are in dwords. + * Reads from router config space without translating the possible error. */ struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, enum tb_cfg_space space, @@ -869,8 +911,16 @@ struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, /** * tb_cfg_write() - write from buffer into config space + * @ctl: Pointer to the control channel + * @buffer: Data to write + * @route: Route string of the router + * @port: Port number when writing to %TB_CFG_PORT, %0 otherwise + * @space: Config space selector + * @offset: Dword word offset of the register to start writing + * @length: Number of dwords to write + * @timeout_msec: Timeout in ms how long to wait for the response * - * Offset and length are in dwords. + * Writes to router config space without translating the possible error. */ struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, enum tb_cfg_space space, @@ -944,6 +994,12 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space, return -ENODEV; tb_cfg_print_error(ctl, res); + + if (res->tb_error == TB_CFG_ERROR_LOCK) + return -EACCES; + else if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED) + return -ENOTCONN; + return -EIO; } @@ -962,8 +1018,8 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, return tb_cfg_get_error(ctl, space, &res); case -ETIMEDOUT: - tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n", - space, offset); + tb_ctl_warn(ctl, "%llx: timeout reading config space %u from %#x\n", + route, space, offset); break; default: @@ -988,8 +1044,8 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, return tb_cfg_get_error(ctl, space, &res); case -ETIMEDOUT: - tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n", - space, offset); + tb_ctl_warn(ctl, "%llx: timeout writing config space %u to %#x\n", + route, space, offset); break; default: @@ -1001,6 +1057,8 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, /** * tb_cfg_get_upstream_port() - get upstream port number of switch at route + * @ctl: Pointer to the control channel + * @route: Route string of the router * * Reads the first dword from the switches TB_CFG_SWITCH config area and * returns the port number from which the reply originated. |