| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | 2 | /* |
|---|
| 3 | | - * Thunderbolt Cactus Ridge driver - control channel and configuration commands |
|---|
| 3 | + * Thunderbolt driver - control channel and configuration commands |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> |
|---|
| 6 | + * Copyright (C) 2018, Intel Corporation |
|---|
| 6 | 7 | */ |
|---|
| 7 | 8 | |
|---|
| 8 | 9 | #include <linux/crc32.h> |
|---|
| .. | .. |
|---|
| 218 | 219 | static struct tb_cfg_result decode_error(const struct ctl_pkg *response) |
|---|
| 219 | 220 | { |
|---|
| 220 | 221 | struct cfg_error_pkg *pkg = response->buffer; |
|---|
| 222 | + struct tb_ctl *ctl = response->ctl; |
|---|
| 221 | 223 | struct tb_cfg_result res = { 0 }; |
|---|
| 222 | 224 | res.response_route = tb_cfg_get_route(&pkg->header); |
|---|
| 223 | 225 | res.response_port = 0; |
|---|
| .. | .. |
|---|
| 226 | 228 | if (res.err) |
|---|
| 227 | 229 | return res; |
|---|
| 228 | 230 | |
|---|
| 229 | | - WARN(pkg->zero1, "pkg->zero1 is %#x\n", pkg->zero1); |
|---|
| 230 | | - WARN(pkg->zero2, "pkg->zero1 is %#x\n", pkg->zero1); |
|---|
| 231 | | - WARN(pkg->zero3, "pkg->zero1 is %#x\n", pkg->zero1); |
|---|
| 231 | + if (pkg->zero1) |
|---|
| 232 | + tb_ctl_warn(ctl, "pkg->zero1 is %#x\n", pkg->zero1); |
|---|
| 233 | + if (pkg->zero2) |
|---|
| 234 | + tb_ctl_warn(ctl, "pkg->zero2 is %#x\n", pkg->zero2); |
|---|
| 235 | + if (pkg->zero3) |
|---|
| 236 | + tb_ctl_warn(ctl, "pkg->zero3 is %#x\n", pkg->zero3); |
|---|
| 237 | + |
|---|
| 232 | 238 | res.err = 1; |
|---|
| 233 | 239 | res.tb_error = pkg->error; |
|---|
| 234 | 240 | res.response_port = pkg->port; |
|---|
| .. | .. |
|---|
| 265 | 271 | * Invalid cfg_space/offset/length combination in |
|---|
| 266 | 272 | * cfg_read/cfg_write. |
|---|
| 267 | 273 | */ |
|---|
| 268 | | - tb_ctl_WARN(ctl, |
|---|
| 269 | | - "CFG_ERROR(%llx:%x): Invalid config space or offset\n", |
|---|
| 270 | | - res->response_route, res->response_port); |
|---|
| 274 | + tb_ctl_dbg(ctl, "%llx:%x: invalid config space or offset\n", |
|---|
| 275 | + res->response_route, res->response_port); |
|---|
| 271 | 276 | return; |
|---|
| 272 | 277 | case TB_CFG_ERROR_NO_SUCH_PORT: |
|---|
| 273 | 278 | /* |
|---|
| .. | .. |
|---|
| 281 | 286 | case TB_CFG_ERROR_LOOP: |
|---|
| 282 | 287 | tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Route contains a loop\n", |
|---|
| 283 | 288 | res->response_route, res->response_port); |
|---|
| 289 | + return; |
|---|
| 290 | + case TB_CFG_ERROR_LOCK: |
|---|
| 291 | + tb_ctl_warn(ctl, "%llx:%x: downstream port is locked\n", |
|---|
| 292 | + res->response_route, res->response_port); |
|---|
| 284 | 293 | return; |
|---|
| 285 | 294 | default: |
|---|
| 286 | 295 | /* 5,6,7,9 and 11 are also valid error codes */ |
|---|
| .. | .. |
|---|
| 387 | 396 | |
|---|
| 388 | 397 | static int tb_async_error(const struct ctl_pkg *pkg) |
|---|
| 389 | 398 | { |
|---|
| 390 | | - const struct cfg_error_pkg *error = (const struct cfg_error_pkg *)pkg; |
|---|
| 399 | + const struct cfg_error_pkg *error = pkg->buffer; |
|---|
| 391 | 400 | |
|---|
| 392 | 401 | if (pkg->frame.eof != TB_CFG_PKG_ERROR) |
|---|
| 393 | 402 | return false; |
|---|
| .. | .. |
|---|
| 452 | 461 | "RX: checksum mismatch, dropping packet\n"); |
|---|
| 453 | 462 | goto rx; |
|---|
| 454 | 463 | } |
|---|
| 455 | | - /* Fall through */ |
|---|
| 464 | + fallthrough; |
|---|
| 456 | 465 | case TB_CFG_PKG_ICM_EVENT: |
|---|
| 457 | 466 | if (tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size)) |
|---|
| 458 | 467 | goto rx; |
|---|
| .. | .. |
|---|
| 631 | 640 | ctl->rx_packets[i]->frame.callback = tb_ctl_rx_callback; |
|---|
| 632 | 641 | } |
|---|
| 633 | 642 | |
|---|
| 634 | | - tb_ctl_info(ctl, "control channel created\n"); |
|---|
| 643 | + tb_ctl_dbg(ctl, "control channel created\n"); |
|---|
| 635 | 644 | return ctl; |
|---|
| 636 | 645 | err: |
|---|
| 637 | 646 | tb_ctl_free(ctl); |
|---|
| .. | .. |
|---|
| 662 | 671 | tb_ctl_pkg_free(ctl->rx_packets[i]); |
|---|
| 663 | 672 | |
|---|
| 664 | 673 | |
|---|
| 665 | | - if (ctl->frame_pool) |
|---|
| 666 | | - dma_pool_destroy(ctl->frame_pool); |
|---|
| 674 | + dma_pool_destroy(ctl->frame_pool); |
|---|
| 667 | 675 | kfree(ctl); |
|---|
| 668 | 676 | } |
|---|
| 669 | 677 | |
|---|
| .. | .. |
|---|
| 673 | 681 | void tb_ctl_start(struct tb_ctl *ctl) |
|---|
| 674 | 682 | { |
|---|
| 675 | 683 | int i; |
|---|
| 676 | | - tb_ctl_info(ctl, "control channel starting...\n"); |
|---|
| 684 | + tb_ctl_dbg(ctl, "control channel starting...\n"); |
|---|
| 677 | 685 | tb_ring_start(ctl->tx); /* is used to ack hotplug packets, start first */ |
|---|
| 678 | 686 | tb_ring_start(ctl->rx); |
|---|
| 679 | 687 | for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) |
|---|
| .. | .. |
|---|
| 702 | 710 | if (!list_empty(&ctl->request_queue)) |
|---|
| 703 | 711 | tb_ctl_WARN(ctl, "dangling request in request_queue\n"); |
|---|
| 704 | 712 | INIT_LIST_HEAD(&ctl->request_queue); |
|---|
| 705 | | - tb_ctl_info(ctl, "control channel stopped\n"); |
|---|
| 713 | + tb_ctl_dbg(ctl, "control channel stopped\n"); |
|---|
| 706 | 714 | } |
|---|
| 707 | 715 | |
|---|
| 708 | 716 | /* public interface, commands */ |
|---|
| 709 | 717 | |
|---|
| 710 | 718 | /** |
|---|
| 711 | | - * tb_cfg_error() - send error packet |
|---|
| 719 | + * tb_cfg_ack_plug() - Ack hot plug/unplug event |
|---|
| 720 | + * @ctl: Control channel to use |
|---|
| 721 | + * @route: Router that originated the event |
|---|
| 722 | + * @port: Port where the hot plug/unplug happened |
|---|
| 723 | + * @unplug: Ack hot plug or unplug |
|---|
| 712 | 724 | * |
|---|
| 713 | | - * Return: Returns 0 on success or an error code on failure. |
|---|
| 725 | + * Call this as response for hot plug/unplug event to ack it. |
|---|
| 726 | + * Returns %0 on success or an error code on failure. |
|---|
| 714 | 727 | */ |
|---|
| 715 | | -int tb_cfg_error(struct tb_ctl *ctl, u64 route, u32 port, |
|---|
| 716 | | - enum tb_cfg_error error) |
|---|
| 728 | +int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug) |
|---|
| 717 | 729 | { |
|---|
| 718 | 730 | struct cfg_error_pkg pkg = { |
|---|
| 719 | 731 | .header = tb_cfg_make_header(route), |
|---|
| 720 | 732 | .port = port, |
|---|
| 721 | | - .error = error, |
|---|
| 733 | + .error = TB_CFG_ERROR_ACK_PLUG_EVENT, |
|---|
| 734 | + .pg = unplug ? TB_CFG_ERROR_PG_HOT_UNPLUG |
|---|
| 735 | + : TB_CFG_ERROR_PG_HOT_PLUG, |
|---|
| 722 | 736 | }; |
|---|
| 723 | | - tb_ctl_info(ctl, "resetting error on %llx:%x.\n", route, port); |
|---|
| 737 | + tb_ctl_dbg(ctl, "acking hot %splug event on %llx:%x\n", |
|---|
| 738 | + unplug ? "un" : "", route, port); |
|---|
| 724 | 739 | return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR); |
|---|
| 725 | 740 | } |
|---|
| 726 | 741 | |
|---|
| .. | .. |
|---|
| 930 | 945 | return res; |
|---|
| 931 | 946 | } |
|---|
| 932 | 947 | |
|---|
| 948 | +static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space, |
|---|
| 949 | + const struct tb_cfg_result *res) |
|---|
| 950 | +{ |
|---|
| 951 | + /* |
|---|
| 952 | + * For unimplemented ports access to port config space may return |
|---|
| 953 | + * TB_CFG_ERROR_INVALID_CONFIG_SPACE (alternatively their type is |
|---|
| 954 | + * set to TB_TYPE_INACTIVE). In the former case return -ENODEV so |
|---|
| 955 | + * that the caller can mark the port as disabled. |
|---|
| 956 | + */ |
|---|
| 957 | + if (space == TB_CFG_PORT && |
|---|
| 958 | + res->tb_error == TB_CFG_ERROR_INVALID_CONFIG_SPACE) |
|---|
| 959 | + return -ENODEV; |
|---|
| 960 | + |
|---|
| 961 | + tb_cfg_print_error(ctl, res); |
|---|
| 962 | + |
|---|
| 963 | + if (res->tb_error == TB_CFG_ERROR_LOCK) |
|---|
| 964 | + return -EACCES; |
|---|
| 965 | + return -EIO; |
|---|
| 966 | +} |
|---|
| 967 | + |
|---|
| 933 | 968 | int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, |
|---|
| 934 | 969 | enum tb_cfg_space space, u32 offset, u32 length) |
|---|
| 935 | 970 | { |
|---|
| .. | .. |
|---|
| 942 | 977 | |
|---|
| 943 | 978 | case 1: |
|---|
| 944 | 979 | /* Thunderbolt error, tb_error holds the actual number */ |
|---|
| 945 | | - tb_cfg_print_error(ctl, &res); |
|---|
| 946 | | - return -EIO; |
|---|
| 980 | + return tb_cfg_get_error(ctl, space, &res); |
|---|
| 947 | 981 | |
|---|
| 948 | 982 | case -ETIMEDOUT: |
|---|
| 949 | | - tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n", |
|---|
| 950 | | - space, offset); |
|---|
| 983 | + tb_ctl_warn(ctl, "%llx: timeout reading config space %u from %#x\n", |
|---|
| 984 | + route, space, offset); |
|---|
| 951 | 985 | break; |
|---|
| 952 | 986 | |
|---|
| 953 | 987 | default: |
|---|
| .. | .. |
|---|
| 969 | 1003 | |
|---|
| 970 | 1004 | case 1: |
|---|
| 971 | 1005 | /* Thunderbolt error, tb_error holds the actual number */ |
|---|
| 972 | | - tb_cfg_print_error(ctl, &res); |
|---|
| 973 | | - return -EIO; |
|---|
| 1006 | + return tb_cfg_get_error(ctl, space, &res); |
|---|
| 974 | 1007 | |
|---|
| 975 | 1008 | case -ETIMEDOUT: |
|---|
| 976 | | - tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n", |
|---|
| 977 | | - space, offset); |
|---|
| 1009 | + tb_ctl_warn(ctl, "%llx: timeout writing config space %u to %#x\n", |
|---|
| 1010 | + route, space, offset); |
|---|
| 978 | 1011 | break; |
|---|
| 979 | 1012 | |
|---|
| 980 | 1013 | default: |
|---|