1f67cf491SMika Westerberg /* 2f67cf491SMika Westerberg * Internal Thunderbolt Connection Manager. This is a firmware running on 3f67cf491SMika Westerberg * the Thunderbolt host controller performing most of the low-level 4f67cf491SMika Westerberg * handling. 5f67cf491SMika Westerberg * 6f67cf491SMika Westerberg * Copyright (C) 2017, Intel Corporation 7f67cf491SMika Westerberg * Authors: Michael Jamet <michael.jamet@intel.com> 8f67cf491SMika Westerberg * Mika Westerberg <mika.westerberg@linux.intel.com> 9f67cf491SMika Westerberg * 10f67cf491SMika Westerberg * This program is free software; you can redistribute it and/or modify 11f67cf491SMika Westerberg * it under the terms of the GNU General Public License version 2 as 12f67cf491SMika Westerberg * published by the Free Software Foundation. 13f67cf491SMika Westerberg */ 14f67cf491SMika Westerberg 15f67cf491SMika Westerberg #include <linux/delay.h> 16f67cf491SMika Westerberg #include <linux/mutex.h> 17f67cf491SMika Westerberg #include <linux/pci.h> 18630b3affSLukas Wunner #include <linux/platform_data/x86/apple.h> 19f67cf491SMika Westerberg #include <linux/sizes.h> 20f67cf491SMika Westerberg #include <linux/slab.h> 21f67cf491SMika Westerberg #include <linux/workqueue.h> 22f67cf491SMika Westerberg 23f67cf491SMika Westerberg #include "ctl.h" 24f67cf491SMika Westerberg #include "nhi_regs.h" 25f67cf491SMika Westerberg #include "tb.h" 26f67cf491SMika Westerberg 27f67cf491SMika Westerberg #define PCIE2CIO_CMD 0x30 28f67cf491SMika Westerberg #define PCIE2CIO_CMD_TIMEOUT BIT(31) 29f67cf491SMika Westerberg #define PCIE2CIO_CMD_START BIT(30) 30f67cf491SMika Westerberg #define PCIE2CIO_CMD_WRITE BIT(21) 31f67cf491SMika Westerberg #define PCIE2CIO_CMD_CS_MASK GENMASK(20, 19) 32f67cf491SMika Westerberg #define PCIE2CIO_CMD_CS_SHIFT 19 33f67cf491SMika Westerberg #define PCIE2CIO_CMD_PORT_MASK GENMASK(18, 13) 34f67cf491SMika Westerberg #define PCIE2CIO_CMD_PORT_SHIFT 13 35f67cf491SMika Westerberg 36f67cf491SMika Westerberg #define PCIE2CIO_WRDATA 0x34 37f67cf491SMika Westerberg #define PCIE2CIO_RDDATA 0x38 38f67cf491SMika Westerberg 39f67cf491SMika Westerberg #define PHY_PORT_CS1 0x37 40f67cf491SMika Westerberg #define PHY_PORT_CS1_LINK_DISABLE BIT(14) 41f67cf491SMika Westerberg #define PHY_PORT_CS1_LINK_STATE_MASK GENMASK(29, 26) 42f67cf491SMika Westerberg #define PHY_PORT_CS1_LINK_STATE_SHIFT 26 43f67cf491SMika Westerberg 44f67cf491SMika Westerberg #define ICM_TIMEOUT 5000 /* ms */ 450b0a0bd0SMika Westerberg #define ICM_APPROVE_TIMEOUT 10000 /* ms */ 46f67cf491SMika Westerberg #define ICM_MAX_LINK 4 47f67cf491SMika Westerberg #define ICM_MAX_DEPTH 6 48f67cf491SMika Westerberg 49f67cf491SMika Westerberg /** 50f67cf491SMika Westerberg * struct icm - Internal connection manager private data 51f67cf491SMika Westerberg * @request_lock: Makes sure only one message is send to ICM at time 52f67cf491SMika Westerberg * @rescan_work: Work used to rescan the surviving switches after resume 53f67cf491SMika Westerberg * @upstream_port: Pointer to the PCIe upstream port this host 54f67cf491SMika Westerberg * controller is connected. This is only set for systems 55f67cf491SMika Westerberg * where ICM needs to be started manually 56f67cf491SMika Westerberg * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides 57f67cf491SMika Westerberg * (only set when @upstream_port is not %NULL) 58e6b245ccSMika Westerberg * @safe_mode: ICM is in safe mode 59f67cf491SMika Westerberg * @is_supported: Checks if we can support ICM on this controller 60f67cf491SMika Westerberg * @get_mode: Read and return the ICM firmware mode (optional) 61f67cf491SMika Westerberg * @get_route: Find a route string for given switch 623080e197SMika Westerberg * @driver_ready: Send driver ready message to ICM 63f67cf491SMika Westerberg * @device_connected: Handle device connected ICM message 64f67cf491SMika Westerberg * @device_disconnected: Handle device disconnected ICM message 65d1ff7024SMika Westerberg * @xdomain_connected - Handle XDomain connected ICM message 66d1ff7024SMika Westerberg * @xdomain_disconnected - Handle XDomain disconnected ICM message 67f67cf491SMika Westerberg */ 68f67cf491SMika Westerberg struct icm { 69f67cf491SMika Westerberg struct mutex request_lock; 70f67cf491SMika Westerberg struct delayed_work rescan_work; 71f67cf491SMika Westerberg struct pci_dev *upstream_port; 72f67cf491SMika Westerberg int vnd_cap; 73e6b245ccSMika Westerberg bool safe_mode; 74f67cf491SMika Westerberg bool (*is_supported)(struct tb *tb); 75f67cf491SMika Westerberg int (*get_mode)(struct tb *tb); 76f67cf491SMika Westerberg int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); 773080e197SMika Westerberg int (*driver_ready)(struct tb *tb, 783080e197SMika Westerberg enum tb_security_level *security_level); 79f67cf491SMika Westerberg void (*device_connected)(struct tb *tb, 80f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 81f67cf491SMika Westerberg void (*device_disconnected)(struct tb *tb, 82f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 83d1ff7024SMika Westerberg void (*xdomain_connected)(struct tb *tb, 84d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 85d1ff7024SMika Westerberg void (*xdomain_disconnected)(struct tb *tb, 86d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 87f67cf491SMika Westerberg }; 88f67cf491SMika Westerberg 89f67cf491SMika Westerberg struct icm_notification { 90f67cf491SMika Westerberg struct work_struct work; 91f67cf491SMika Westerberg struct icm_pkg_header *pkg; 92f67cf491SMika Westerberg struct tb *tb; 93f67cf491SMika Westerberg }; 94f67cf491SMika Westerberg 95f67cf491SMika Westerberg static inline struct tb *icm_to_tb(struct icm *icm) 96f67cf491SMika Westerberg { 97f67cf491SMika Westerberg return ((void *)icm - sizeof(struct tb)); 98f67cf491SMika Westerberg } 99f67cf491SMika Westerberg 100f67cf491SMika Westerberg static inline u8 phy_port_from_route(u64 route, u8 depth) 101f67cf491SMika Westerberg { 102d1ff7024SMika Westerberg u8 link; 103d1ff7024SMika Westerberg 104d1ff7024SMika Westerberg link = depth ? route >> ((depth - 1) * 8) : route; 105d1ff7024SMika Westerberg return tb_phy_port_from_link(link); 106f67cf491SMika Westerberg } 107f67cf491SMika Westerberg 108f67cf491SMika Westerberg static inline u8 dual_link_from_link(u8 link) 109f67cf491SMika Westerberg { 110f67cf491SMika Westerberg return link ? ((link - 1) ^ 0x01) + 1 : 0; 111f67cf491SMika Westerberg } 112f67cf491SMika Westerberg 113f67cf491SMika Westerberg static inline u64 get_route(u32 route_hi, u32 route_lo) 114f67cf491SMika Westerberg { 115f67cf491SMika Westerberg return (u64)route_hi << 32 | route_lo; 116f67cf491SMika Westerberg } 117f67cf491SMika Westerberg 118f67cf491SMika Westerberg static bool icm_match(const struct tb_cfg_request *req, 119f67cf491SMika Westerberg const struct ctl_pkg *pkg) 120f67cf491SMika Westerberg { 121f67cf491SMika Westerberg const struct icm_pkg_header *res_hdr = pkg->buffer; 122f67cf491SMika Westerberg const struct icm_pkg_header *req_hdr = req->request; 123f67cf491SMika Westerberg 124f67cf491SMika Westerberg if (pkg->frame.eof != req->response_type) 125f67cf491SMika Westerberg return false; 126f67cf491SMika Westerberg if (res_hdr->code != req_hdr->code) 127f67cf491SMika Westerberg return false; 128f67cf491SMika Westerberg 129f67cf491SMika Westerberg return true; 130f67cf491SMika Westerberg } 131f67cf491SMika Westerberg 132f67cf491SMika Westerberg static bool icm_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) 133f67cf491SMika Westerberg { 134f67cf491SMika Westerberg const struct icm_pkg_header *hdr = pkg->buffer; 135f67cf491SMika Westerberg 136f67cf491SMika Westerberg if (hdr->packet_id < req->npackets) { 137f67cf491SMika Westerberg size_t offset = hdr->packet_id * req->response_size; 138f67cf491SMika Westerberg 139f67cf491SMika Westerberg memcpy(req->response + offset, pkg->buffer, req->response_size); 140f67cf491SMika Westerberg } 141f67cf491SMika Westerberg 142f67cf491SMika Westerberg return hdr->packet_id == hdr->total_packets - 1; 143f67cf491SMika Westerberg } 144f67cf491SMika Westerberg 145f67cf491SMika Westerberg static int icm_request(struct tb *tb, const void *request, size_t request_size, 146f67cf491SMika Westerberg void *response, size_t response_size, size_t npackets, 147f67cf491SMika Westerberg unsigned int timeout_msec) 148f67cf491SMika Westerberg { 149f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 150f67cf491SMika Westerberg int retries = 3; 151f67cf491SMika Westerberg 152f67cf491SMika Westerberg do { 153f67cf491SMika Westerberg struct tb_cfg_request *req; 154f67cf491SMika Westerberg struct tb_cfg_result res; 155f67cf491SMika Westerberg 156f67cf491SMika Westerberg req = tb_cfg_request_alloc(); 157f67cf491SMika Westerberg if (!req) 158f67cf491SMika Westerberg return -ENOMEM; 159f67cf491SMika Westerberg 160f67cf491SMika Westerberg req->match = icm_match; 161f67cf491SMika Westerberg req->copy = icm_copy; 162f67cf491SMika Westerberg req->request = request; 163f67cf491SMika Westerberg req->request_size = request_size; 164f67cf491SMika Westerberg req->request_type = TB_CFG_PKG_ICM_CMD; 165f67cf491SMika Westerberg req->response = response; 166f67cf491SMika Westerberg req->npackets = npackets; 167f67cf491SMika Westerberg req->response_size = response_size; 168f67cf491SMika Westerberg req->response_type = TB_CFG_PKG_ICM_RESP; 169f67cf491SMika Westerberg 170f67cf491SMika Westerberg mutex_lock(&icm->request_lock); 171f67cf491SMika Westerberg res = tb_cfg_request_sync(tb->ctl, req, timeout_msec); 172f67cf491SMika Westerberg mutex_unlock(&icm->request_lock); 173f67cf491SMika Westerberg 174f67cf491SMika Westerberg tb_cfg_request_put(req); 175f67cf491SMika Westerberg 176f67cf491SMika Westerberg if (res.err != -ETIMEDOUT) 177f67cf491SMika Westerberg return res.err == 1 ? -EIO : res.err; 178f67cf491SMika Westerberg 179f67cf491SMika Westerberg usleep_range(20, 50); 180f67cf491SMika Westerberg } while (retries--); 181f67cf491SMika Westerberg 182f67cf491SMika Westerberg return -ETIMEDOUT; 183f67cf491SMika Westerberg } 184f67cf491SMika Westerberg 185f67cf491SMika Westerberg static bool icm_fr_is_supported(struct tb *tb) 186f67cf491SMika Westerberg { 187630b3affSLukas Wunner return !x86_apple_machine; 188f67cf491SMika Westerberg } 189f67cf491SMika Westerberg 190f67cf491SMika Westerberg static inline int icm_fr_get_switch_index(u32 port) 191f67cf491SMika Westerberg { 192f67cf491SMika Westerberg int index; 193f67cf491SMika Westerberg 194f67cf491SMika Westerberg if ((port & ICM_PORT_TYPE_MASK) != TB_TYPE_PORT) 195f67cf491SMika Westerberg return 0; 196f67cf491SMika Westerberg 197f67cf491SMika Westerberg index = port >> ICM_PORT_INDEX_SHIFT; 198f67cf491SMika Westerberg return index != 0xff ? index : 0; 199f67cf491SMika Westerberg } 200f67cf491SMika Westerberg 201f67cf491SMika Westerberg static int icm_fr_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 202f67cf491SMika Westerberg { 203f67cf491SMika Westerberg struct icm_fr_pkg_get_topology_response *switches, *sw; 204f67cf491SMika Westerberg struct icm_fr_pkg_get_topology request = { 205f67cf491SMika Westerberg .hdr = { .code = ICM_GET_TOPOLOGY }, 206f67cf491SMika Westerberg }; 207f67cf491SMika Westerberg size_t npackets = ICM_GET_TOPOLOGY_PACKETS; 208f67cf491SMika Westerberg int ret, index; 209f67cf491SMika Westerberg u8 i; 210f67cf491SMika Westerberg 211f67cf491SMika Westerberg switches = kcalloc(npackets, sizeof(*switches), GFP_KERNEL); 212f67cf491SMika Westerberg if (!switches) 213f67cf491SMika Westerberg return -ENOMEM; 214f67cf491SMika Westerberg 215f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), switches, 216f67cf491SMika Westerberg sizeof(*switches), npackets, ICM_TIMEOUT); 217f67cf491SMika Westerberg if (ret) 218f67cf491SMika Westerberg goto err_free; 219f67cf491SMika Westerberg 220f67cf491SMika Westerberg sw = &switches[0]; 221f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[link]); 222f67cf491SMika Westerberg if (!index) { 223f67cf491SMika Westerberg ret = -ENODEV; 224f67cf491SMika Westerberg goto err_free; 225f67cf491SMika Westerberg } 226f67cf491SMika Westerberg 227f67cf491SMika Westerberg sw = &switches[index]; 228f67cf491SMika Westerberg for (i = 1; i < depth; i++) { 229f67cf491SMika Westerberg unsigned int j; 230f67cf491SMika Westerberg 231f67cf491SMika Westerberg if (!(sw->first_data & ICM_SWITCH_USED)) { 232f67cf491SMika Westerberg ret = -ENODEV; 233f67cf491SMika Westerberg goto err_free; 234f67cf491SMika Westerberg } 235f67cf491SMika Westerberg 236f67cf491SMika Westerberg for (j = 0; j < ARRAY_SIZE(sw->ports); j++) { 237f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[j]); 238f67cf491SMika Westerberg if (index > sw->switch_index) { 239f67cf491SMika Westerberg sw = &switches[index]; 240f67cf491SMika Westerberg break; 241f67cf491SMika Westerberg } 242f67cf491SMika Westerberg } 243f67cf491SMika Westerberg } 244f67cf491SMika Westerberg 245f67cf491SMika Westerberg *route = get_route(sw->route_hi, sw->route_lo); 246f67cf491SMika Westerberg 247f67cf491SMika Westerberg err_free: 248f67cf491SMika Westerberg kfree(switches); 249f67cf491SMika Westerberg return ret; 250f67cf491SMika Westerberg } 251f67cf491SMika Westerberg 2523080e197SMika Westerberg static int 2533080e197SMika Westerberg icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level) 2543080e197SMika Westerberg { 2553080e197SMika Westerberg struct icm_fr_pkg_driver_ready_response reply; 2563080e197SMika Westerberg struct icm_pkg_driver_ready request = { 2573080e197SMika Westerberg .hdr.code = ICM_DRIVER_READY, 2583080e197SMika Westerberg }; 2593080e197SMika Westerberg int ret; 2603080e197SMika Westerberg 2613080e197SMika Westerberg memset(&reply, 0, sizeof(reply)); 2623080e197SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 2633080e197SMika Westerberg 1, ICM_TIMEOUT); 2643080e197SMika Westerberg if (ret) 2653080e197SMika Westerberg return ret; 2663080e197SMika Westerberg 2673080e197SMika Westerberg if (security_level) 2683080e197SMika Westerberg *security_level = reply.security_level & ICM_FR_SLEVEL_MASK; 2693080e197SMika Westerberg 2703080e197SMika Westerberg return 0; 2713080e197SMika Westerberg } 2723080e197SMika Westerberg 273f67cf491SMika Westerberg static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) 274f67cf491SMika Westerberg { 275f67cf491SMika Westerberg struct icm_fr_pkg_approve_device request; 276f67cf491SMika Westerberg struct icm_fr_pkg_approve_device reply; 277f67cf491SMika Westerberg int ret; 278f67cf491SMika Westerberg 279f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 280f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 281f67cf491SMika Westerberg request.hdr.code = ICM_APPROVE_DEVICE; 282f67cf491SMika Westerberg request.connection_id = sw->connection_id; 283f67cf491SMika Westerberg request.connection_key = sw->connection_key; 284f67cf491SMika Westerberg 285f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 286f67cf491SMika Westerberg /* Use larger timeout as establishing tunnels can take some time */ 287f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 2880b0a0bd0SMika Westerberg 1, ICM_APPROVE_TIMEOUT); 289f67cf491SMika Westerberg if (ret) 290f67cf491SMika Westerberg return ret; 291f67cf491SMika Westerberg 292f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 293f67cf491SMika Westerberg tb_warn(tb, "PCIe tunnel creation failed\n"); 294f67cf491SMika Westerberg return -EIO; 295f67cf491SMika Westerberg } 296f67cf491SMika Westerberg 297f67cf491SMika Westerberg return 0; 298f67cf491SMika Westerberg } 299f67cf491SMika Westerberg 300f67cf491SMika Westerberg static int icm_fr_add_switch_key(struct tb *tb, struct tb_switch *sw) 301f67cf491SMika Westerberg { 302f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key request; 303f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key_response reply; 304f67cf491SMika Westerberg int ret; 305f67cf491SMika Westerberg 306f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 307f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 308f67cf491SMika Westerberg request.hdr.code = ICM_ADD_DEVICE_KEY; 309f67cf491SMika Westerberg request.connection_id = sw->connection_id; 310f67cf491SMika Westerberg request.connection_key = sw->connection_key; 311f67cf491SMika Westerberg memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 312f67cf491SMika Westerberg 313f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 314f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 315f67cf491SMika Westerberg 1, ICM_TIMEOUT); 316f67cf491SMika Westerberg if (ret) 317f67cf491SMika Westerberg return ret; 318f67cf491SMika Westerberg 319f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 320f67cf491SMika Westerberg tb_warn(tb, "Adding key to switch failed\n"); 321f67cf491SMika Westerberg return -EIO; 322f67cf491SMika Westerberg } 323f67cf491SMika Westerberg 324f67cf491SMika Westerberg return 0; 325f67cf491SMika Westerberg } 326f67cf491SMika Westerberg 327f67cf491SMika Westerberg static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 328f67cf491SMika Westerberg const u8 *challenge, u8 *response) 329f67cf491SMika Westerberg { 330f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device request; 331f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device_response reply; 332f67cf491SMika Westerberg int ret; 333f67cf491SMika Westerberg 334f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 335f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 336f67cf491SMika Westerberg request.hdr.code = ICM_CHALLENGE_DEVICE; 337f67cf491SMika Westerberg request.connection_id = sw->connection_id; 338f67cf491SMika Westerberg request.connection_key = sw->connection_key; 339f67cf491SMika Westerberg memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 340f67cf491SMika Westerberg 341f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 342f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 343f67cf491SMika Westerberg 1, ICM_TIMEOUT); 344f67cf491SMika Westerberg if (ret) 345f67cf491SMika Westerberg return ret; 346f67cf491SMika Westerberg 347f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 348f67cf491SMika Westerberg return -EKEYREJECTED; 349f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 350f67cf491SMika Westerberg return -ENOKEY; 351f67cf491SMika Westerberg 352f67cf491SMika Westerberg memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 353f67cf491SMika Westerberg 354f67cf491SMika Westerberg return 0; 355f67cf491SMika Westerberg } 356f67cf491SMika Westerberg 357d1ff7024SMika Westerberg static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 358d1ff7024SMika Westerberg { 359d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain_response reply; 360d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain request; 361d1ff7024SMika Westerberg int ret; 362d1ff7024SMika Westerberg 363d1ff7024SMika Westerberg memset(&request, 0, sizeof(request)); 364d1ff7024SMika Westerberg request.hdr.code = ICM_APPROVE_XDOMAIN; 365d1ff7024SMika Westerberg request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; 366d1ff7024SMika Westerberg memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 367d1ff7024SMika Westerberg 368d1ff7024SMika Westerberg request.transmit_path = xd->transmit_path; 369d1ff7024SMika Westerberg request.transmit_ring = xd->transmit_ring; 370d1ff7024SMika Westerberg request.receive_path = xd->receive_path; 371d1ff7024SMika Westerberg request.receive_ring = xd->receive_ring; 372d1ff7024SMika Westerberg 373d1ff7024SMika Westerberg memset(&reply, 0, sizeof(reply)); 374d1ff7024SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 375d1ff7024SMika Westerberg 1, ICM_TIMEOUT); 376d1ff7024SMika Westerberg if (ret) 377d1ff7024SMika Westerberg return ret; 378d1ff7024SMika Westerberg 379d1ff7024SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 380d1ff7024SMika Westerberg return -EIO; 381d1ff7024SMika Westerberg 382d1ff7024SMika Westerberg return 0; 383d1ff7024SMika Westerberg } 384d1ff7024SMika Westerberg 385d1ff7024SMika Westerberg static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 386d1ff7024SMika Westerberg { 387d1ff7024SMika Westerberg u8 phy_port; 388d1ff7024SMika Westerberg u8 cmd; 389d1ff7024SMika Westerberg 390d1ff7024SMika Westerberg phy_port = tb_phy_port_from_link(xd->link); 391d1ff7024SMika Westerberg if (phy_port == 0) 392d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PA; 393d1ff7024SMika Westerberg else 394d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PB; 395d1ff7024SMika Westerberg 396d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 1); 397d1ff7024SMika Westerberg usleep_range(10, 50); 398d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 2); 399d1ff7024SMika Westerberg return 0; 400d1ff7024SMika Westerberg } 401d1ff7024SMika Westerberg 402ee487dd2SMika Westerberg static void add_switch(struct tb_switch *parent_sw, u64 route, 403ee487dd2SMika Westerberg const uuid_t *uuid, u8 connection_id, u8 connection_key, 404ee487dd2SMika Westerberg u8 link, u8 depth, enum tb_security_level security_level, 40514862ee3SYehezkel Bernat bool authorized, bool boot) 406ee487dd2SMika Westerberg { 407ee487dd2SMika Westerberg struct tb_switch *sw; 408ee487dd2SMika Westerberg 409ee487dd2SMika Westerberg sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route); 410ee487dd2SMika Westerberg if (!sw) 411ee487dd2SMika Westerberg return; 412ee487dd2SMika Westerberg 413ee487dd2SMika Westerberg sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); 414ee487dd2SMika Westerberg sw->connection_id = connection_id; 415ee487dd2SMika Westerberg sw->connection_key = connection_key; 416ee487dd2SMika Westerberg sw->link = link; 417ee487dd2SMika Westerberg sw->depth = depth; 418ee487dd2SMika Westerberg sw->authorized = authorized; 419ee487dd2SMika Westerberg sw->security_level = security_level; 42014862ee3SYehezkel Bernat sw->boot = boot; 421ee487dd2SMika Westerberg 422ee487dd2SMika Westerberg /* Link the two switches now */ 423ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 424ee487dd2SMika Westerberg tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); 425ee487dd2SMika Westerberg 426ee487dd2SMika Westerberg if (tb_switch_add(sw)) { 427ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 428ee487dd2SMika Westerberg tb_switch_put(sw); 429ee487dd2SMika Westerberg return; 430ee487dd2SMika Westerberg } 431ee487dd2SMika Westerberg } 432ee487dd2SMika Westerberg 433ee487dd2SMika Westerberg static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, 434ee487dd2SMika Westerberg u64 route, u8 connection_id, u8 connection_key, 43514862ee3SYehezkel Bernat u8 link, u8 depth, bool boot) 436ee487dd2SMika Westerberg { 437ee487dd2SMika Westerberg /* Disconnect from parent */ 438ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 439ee487dd2SMika Westerberg /* Re-connect via updated port*/ 440ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 441ee487dd2SMika Westerberg 442ee487dd2SMika Westerberg /* Update with the new addressing information */ 443ee487dd2SMika Westerberg sw->config.route_hi = upper_32_bits(route); 444ee487dd2SMika Westerberg sw->config.route_lo = lower_32_bits(route); 445ee487dd2SMika Westerberg sw->connection_id = connection_id; 446ee487dd2SMika Westerberg sw->connection_key = connection_key; 447ee487dd2SMika Westerberg sw->link = link; 448ee487dd2SMika Westerberg sw->depth = depth; 44914862ee3SYehezkel Bernat sw->boot = boot; 450ee487dd2SMika Westerberg 451ee487dd2SMika Westerberg /* This switch still exists */ 452ee487dd2SMika Westerberg sw->is_unplugged = false; 453ee487dd2SMika Westerberg } 454ee487dd2SMika Westerberg 455f67cf491SMika Westerberg static void remove_switch(struct tb_switch *sw) 456f67cf491SMika Westerberg { 457f67cf491SMika Westerberg struct tb_switch *parent_sw; 458f67cf491SMika Westerberg 459f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 460f67cf491SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 461f67cf491SMika Westerberg tb_switch_remove(sw); 462f67cf491SMika Westerberg } 463f67cf491SMika Westerberg 464ee487dd2SMika Westerberg static void add_xdomain(struct tb_switch *sw, u64 route, 465ee487dd2SMika Westerberg const uuid_t *local_uuid, const uuid_t *remote_uuid, 466ee487dd2SMika Westerberg u8 link, u8 depth) 467ee487dd2SMika Westerberg { 468ee487dd2SMika Westerberg struct tb_xdomain *xd; 469ee487dd2SMika Westerberg 470ee487dd2SMika Westerberg xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid); 471ee487dd2SMika Westerberg if (!xd) 472ee487dd2SMika Westerberg return; 473ee487dd2SMika Westerberg 474ee487dd2SMika Westerberg xd->link = link; 475ee487dd2SMika Westerberg xd->depth = depth; 476ee487dd2SMika Westerberg 477ee487dd2SMika Westerberg tb_port_at(route, sw)->xdomain = xd; 478ee487dd2SMika Westerberg 479ee487dd2SMika Westerberg tb_xdomain_add(xd); 480ee487dd2SMika Westerberg } 481ee487dd2SMika Westerberg 482ee487dd2SMika Westerberg static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link) 483ee487dd2SMika Westerberg { 484ee487dd2SMika Westerberg xd->link = link; 485ee487dd2SMika Westerberg xd->route = route; 486ee487dd2SMika Westerberg xd->is_unplugged = false; 487ee487dd2SMika Westerberg } 488ee487dd2SMika Westerberg 48979fae987SMika Westerberg static void remove_xdomain(struct tb_xdomain *xd) 49079fae987SMika Westerberg { 49179fae987SMika Westerberg struct tb_switch *sw; 49279fae987SMika Westerberg 49379fae987SMika Westerberg sw = tb_to_switch(xd->dev.parent); 49479fae987SMika Westerberg tb_port_at(xd->route, sw)->xdomain = NULL; 49579fae987SMika Westerberg tb_xdomain_remove(xd); 49679fae987SMika Westerberg } 49779fae987SMika Westerberg 498f67cf491SMika Westerberg static void 499f67cf491SMika Westerberg icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 500f67cf491SMika Westerberg { 501f67cf491SMika Westerberg const struct icm_fr_event_device_connected *pkg = 502f67cf491SMika Westerberg (const struct icm_fr_event_device_connected *)hdr; 503ee487dd2SMika Westerberg enum tb_security_level security_level; 504f67cf491SMika Westerberg struct tb_switch *sw, *parent_sw; 505f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 506f67cf491SMika Westerberg bool authorized = false; 50779fae987SMika Westerberg struct tb_xdomain *xd; 508f67cf491SMika Westerberg u8 link, depth; 50914862ee3SYehezkel Bernat bool boot; 510f67cf491SMika Westerberg u64 route; 511f67cf491SMika Westerberg int ret; 512f67cf491SMika Westerberg 513f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 514f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 515f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 516f67cf491SMika Westerberg authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 517ee487dd2SMika Westerberg security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 518ee487dd2SMika Westerberg ICM_FLAGS_SLEVEL_SHIFT; 51914862ee3SYehezkel Bernat boot = pkg->link_info & ICM_LINK_INFO_BOOT; 520f67cf491SMika Westerberg 521cb653eecSMika Westerberg if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 522cb653eecSMika Westerberg tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", 523cb653eecSMika Westerberg link, depth); 524cb653eecSMika Westerberg return; 525cb653eecSMika Westerberg } 526cb653eecSMika Westerberg 527f67cf491SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 528f67cf491SMika Westerberg if (ret) { 529f67cf491SMika Westerberg tb_err(tb, "failed to find route string for switch at %u.%u\n", 530f67cf491SMika Westerberg link, depth); 531f67cf491SMika Westerberg return; 532f67cf491SMika Westerberg } 533f67cf491SMika Westerberg 534f67cf491SMika Westerberg sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 535f67cf491SMika Westerberg if (sw) { 536f67cf491SMika Westerberg u8 phy_port, sw_phy_port; 537f67cf491SMika Westerberg 538f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 539f67cf491SMika Westerberg sw_phy_port = phy_port_from_route(tb_route(sw), sw->depth); 540f67cf491SMika Westerberg phy_port = phy_port_from_route(route, depth); 541f67cf491SMika Westerberg 542f67cf491SMika Westerberg /* 543f67cf491SMika Westerberg * On resume ICM will send us connected events for the 544f67cf491SMika Westerberg * devices that still are present. However, that 545f67cf491SMika Westerberg * information might have changed for example by the 546f67cf491SMika Westerberg * fact that a switch on a dual-link connection might 547f67cf491SMika Westerberg * have been enumerated using the other link now. Make 548f67cf491SMika Westerberg * sure our book keeping matches that. 549f67cf491SMika Westerberg */ 550f67cf491SMika Westerberg if (sw->depth == depth && sw_phy_port == phy_port && 551f67cf491SMika Westerberg !!sw->authorized == authorized) { 552ee487dd2SMika Westerberg update_switch(parent_sw, sw, route, pkg->connection_id, 55314862ee3SYehezkel Bernat pkg->connection_key, link, depth, boot); 554f67cf491SMika Westerberg tb_switch_put(sw); 555f67cf491SMika Westerberg return; 556f67cf491SMika Westerberg } 557f67cf491SMika Westerberg 558f67cf491SMika Westerberg /* 559f67cf491SMika Westerberg * User connected the same switch to another physical 560f67cf491SMika Westerberg * port or to another part of the topology. Remove the 561f67cf491SMika Westerberg * existing switch now before adding the new one. 562f67cf491SMika Westerberg */ 563f67cf491SMika Westerberg remove_switch(sw); 564f67cf491SMika Westerberg tb_switch_put(sw); 565f67cf491SMika Westerberg } 566f67cf491SMika Westerberg 567f67cf491SMika Westerberg /* 568f67cf491SMika Westerberg * If the switch was not found by UUID, look for a switch on 569f67cf491SMika Westerberg * same physical port (taking possible link aggregation into 570f67cf491SMika Westerberg * account) and depth. If we found one it is definitely a stale 571f67cf491SMika Westerberg * one so remove it first. 572f67cf491SMika Westerberg */ 573f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 574f67cf491SMika Westerberg if (!sw) { 575f67cf491SMika Westerberg u8 dual_link; 576f67cf491SMika Westerberg 577f67cf491SMika Westerberg dual_link = dual_link_from_link(link); 578f67cf491SMika Westerberg if (dual_link) 579f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, dual_link, depth); 580f67cf491SMika Westerberg } 581f67cf491SMika Westerberg if (sw) { 582f67cf491SMika Westerberg remove_switch(sw); 583f67cf491SMika Westerberg tb_switch_put(sw); 584f67cf491SMika Westerberg } 585f67cf491SMika Westerberg 58679fae987SMika Westerberg /* Remove existing XDomain connection if found */ 58779fae987SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 58879fae987SMika Westerberg if (xd) { 58979fae987SMika Westerberg remove_xdomain(xd); 59079fae987SMika Westerberg tb_xdomain_put(xd); 59179fae987SMika Westerberg } 59279fae987SMika Westerberg 593f67cf491SMika Westerberg parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); 594f67cf491SMika Westerberg if (!parent_sw) { 595f67cf491SMika Westerberg tb_err(tb, "failed to find parent switch for %u.%u\n", 596f67cf491SMika Westerberg link, depth); 597f67cf491SMika Westerberg return; 598f67cf491SMika Westerberg } 599f67cf491SMika Westerberg 600ee487dd2SMika Westerberg add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id, 601ee487dd2SMika Westerberg pkg->connection_key, link, depth, security_level, 60214862ee3SYehezkel Bernat authorized, boot); 603f67cf491SMika Westerberg 604f67cf491SMika Westerberg tb_switch_put(parent_sw); 605f67cf491SMika Westerberg } 606f67cf491SMika Westerberg 607f67cf491SMika Westerberg static void 608f67cf491SMika Westerberg icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 609f67cf491SMika Westerberg { 610f67cf491SMika Westerberg const struct icm_fr_event_device_disconnected *pkg = 611f67cf491SMika Westerberg (const struct icm_fr_event_device_disconnected *)hdr; 612f67cf491SMika Westerberg struct tb_switch *sw; 613f67cf491SMika Westerberg u8 link, depth; 614f67cf491SMika Westerberg 615f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 616f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 617f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 618f67cf491SMika Westerberg 619f67cf491SMika Westerberg if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { 620f67cf491SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 621f67cf491SMika Westerberg return; 622f67cf491SMika Westerberg } 623f67cf491SMika Westerberg 624f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 625f67cf491SMika Westerberg if (!sw) { 626f67cf491SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 627f67cf491SMika Westerberg depth); 628f67cf491SMika Westerberg return; 629f67cf491SMika Westerberg } 630f67cf491SMika Westerberg 631f67cf491SMika Westerberg remove_switch(sw); 632f67cf491SMika Westerberg tb_switch_put(sw); 633f67cf491SMika Westerberg } 634f67cf491SMika Westerberg 635d1ff7024SMika Westerberg static void 636d1ff7024SMika Westerberg icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 637d1ff7024SMika Westerberg { 638d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_connected *pkg = 639d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_connected *)hdr; 640d1ff7024SMika Westerberg struct tb_xdomain *xd; 641d1ff7024SMika Westerberg struct tb_switch *sw; 642d1ff7024SMika Westerberg u8 link, depth; 643d1ff7024SMika Westerberg bool approved; 644d1ff7024SMika Westerberg u64 route; 645d1ff7024SMika Westerberg 646d1ff7024SMika Westerberg /* 647d1ff7024SMika Westerberg * After NVM upgrade adding root switch device fails because we 648d1ff7024SMika Westerberg * initiated reset. During that time ICM might still send 649d1ff7024SMika Westerberg * XDomain connected message which we ignore here. 650d1ff7024SMika Westerberg */ 651d1ff7024SMika Westerberg if (!tb->root_switch) 652d1ff7024SMika Westerberg return; 653d1ff7024SMika Westerberg 654d1ff7024SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 655d1ff7024SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 656d1ff7024SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 657d1ff7024SMika Westerberg approved = pkg->link_info & ICM_LINK_INFO_APPROVED; 658d1ff7024SMika Westerberg 659d1ff7024SMika Westerberg if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { 660d1ff7024SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 661d1ff7024SMika Westerberg return; 662d1ff7024SMika Westerberg } 663d1ff7024SMika Westerberg 664d1ff7024SMika Westerberg route = get_route(pkg->local_route_hi, pkg->local_route_lo); 665d1ff7024SMika Westerberg 666d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 667d1ff7024SMika Westerberg if (xd) { 668d1ff7024SMika Westerberg u8 xd_phy_port, phy_port; 669d1ff7024SMika Westerberg 670d1ff7024SMika Westerberg xd_phy_port = phy_port_from_route(xd->route, xd->depth); 671d1ff7024SMika Westerberg phy_port = phy_port_from_route(route, depth); 672d1ff7024SMika Westerberg 673d1ff7024SMika Westerberg if (xd->depth == depth && xd_phy_port == phy_port) { 674ee487dd2SMika Westerberg update_xdomain(xd, route, link); 675d1ff7024SMika Westerberg tb_xdomain_put(xd); 676d1ff7024SMika Westerberg return; 677d1ff7024SMika Westerberg } 678d1ff7024SMika Westerberg 679d1ff7024SMika Westerberg /* 680d1ff7024SMika Westerberg * If we find an existing XDomain connection remove it 681d1ff7024SMika Westerberg * now. We need to go through login handshake and 682d1ff7024SMika Westerberg * everything anyway to be able to re-establish the 683d1ff7024SMika Westerberg * connection. 684d1ff7024SMika Westerberg */ 685d1ff7024SMika Westerberg remove_xdomain(xd); 686d1ff7024SMika Westerberg tb_xdomain_put(xd); 687d1ff7024SMika Westerberg } 688d1ff7024SMika Westerberg 689d1ff7024SMika Westerberg /* 690d1ff7024SMika Westerberg * Look if there already exists an XDomain in the same place 691d1ff7024SMika Westerberg * than the new one and in that case remove it because it is 692d1ff7024SMika Westerberg * most likely another host that got disconnected. 693d1ff7024SMika Westerberg */ 694d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 695d1ff7024SMika Westerberg if (!xd) { 696d1ff7024SMika Westerberg u8 dual_link; 697d1ff7024SMika Westerberg 698d1ff7024SMika Westerberg dual_link = dual_link_from_link(link); 699d1ff7024SMika Westerberg if (dual_link) 700d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, dual_link, 701d1ff7024SMika Westerberg depth); 702d1ff7024SMika Westerberg } 703d1ff7024SMika Westerberg if (xd) { 704d1ff7024SMika Westerberg remove_xdomain(xd); 705d1ff7024SMika Westerberg tb_xdomain_put(xd); 706d1ff7024SMika Westerberg } 707d1ff7024SMika Westerberg 708d1ff7024SMika Westerberg /* 709d1ff7024SMika Westerberg * If the user disconnected a switch during suspend and 710d1ff7024SMika Westerberg * connected another host to the same port, remove the switch 711d1ff7024SMika Westerberg * first. 712d1ff7024SMika Westerberg */ 713d1ff7024SMika Westerberg sw = get_switch_at_route(tb->root_switch, route); 714d1ff7024SMika Westerberg if (sw) 715d1ff7024SMika Westerberg remove_switch(sw); 716d1ff7024SMika Westerberg 717d1ff7024SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 718d1ff7024SMika Westerberg if (!sw) { 719d1ff7024SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 720d1ff7024SMika Westerberg depth); 721d1ff7024SMika Westerberg return; 722d1ff7024SMika Westerberg } 723d1ff7024SMika Westerberg 724ee487dd2SMika Westerberg add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link, 725ee487dd2SMika Westerberg depth); 726d1ff7024SMika Westerberg tb_switch_put(sw); 727d1ff7024SMika Westerberg } 728d1ff7024SMika Westerberg 729d1ff7024SMika Westerberg static void 730d1ff7024SMika Westerberg icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 731d1ff7024SMika Westerberg { 732d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_disconnected *pkg = 733d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_disconnected *)hdr; 734d1ff7024SMika Westerberg struct tb_xdomain *xd; 735d1ff7024SMika Westerberg 736d1ff7024SMika Westerberg /* 737d1ff7024SMika Westerberg * If the connection is through one or multiple devices, the 738d1ff7024SMika Westerberg * XDomain device is removed along with them so it is fine if we 739d1ff7024SMika Westerberg * cannot find it here. 740d1ff7024SMika Westerberg */ 741d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 742d1ff7024SMika Westerberg if (xd) { 743d1ff7024SMika Westerberg remove_xdomain(xd); 744d1ff7024SMika Westerberg tb_xdomain_put(xd); 745d1ff7024SMika Westerberg } 746d1ff7024SMika Westerberg } 747d1ff7024SMika Westerberg 748f67cf491SMika Westerberg static struct pci_dev *get_upstream_port(struct pci_dev *pdev) 749f67cf491SMika Westerberg { 750f67cf491SMika Westerberg struct pci_dev *parent; 751f67cf491SMika Westerberg 752f67cf491SMika Westerberg parent = pci_upstream_bridge(pdev); 753f67cf491SMika Westerberg while (parent) { 754f67cf491SMika Westerberg if (!pci_is_pcie(parent)) 755f67cf491SMika Westerberg return NULL; 756f67cf491SMika Westerberg if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) 757f67cf491SMika Westerberg break; 758f67cf491SMika Westerberg parent = pci_upstream_bridge(parent); 759f67cf491SMika Westerberg } 760f67cf491SMika Westerberg 761f67cf491SMika Westerberg if (!parent) 762f67cf491SMika Westerberg return NULL; 763f67cf491SMika Westerberg 764f67cf491SMika Westerberg switch (parent->device) { 765f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: 766f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: 767f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: 768f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: 769f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: 770f67cf491SMika Westerberg return parent; 771f67cf491SMika Westerberg } 772f67cf491SMika Westerberg 773f67cf491SMika Westerberg return NULL; 774f67cf491SMika Westerberg } 775f67cf491SMika Westerberg 776f67cf491SMika Westerberg static bool icm_ar_is_supported(struct tb *tb) 777f67cf491SMika Westerberg { 778f67cf491SMika Westerberg struct pci_dev *upstream_port; 779f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 780f67cf491SMika Westerberg 781f67cf491SMika Westerberg /* 782f67cf491SMika Westerberg * Starting from Alpine Ridge we can use ICM on Apple machines 783f67cf491SMika Westerberg * as well. We just need to reset and re-enable it first. 784f67cf491SMika Westerberg */ 785630b3affSLukas Wunner if (!x86_apple_machine) 786f67cf491SMika Westerberg return true; 787f67cf491SMika Westerberg 788f67cf491SMika Westerberg /* 789f67cf491SMika Westerberg * Find the upstream PCIe port in case we need to do reset 790f67cf491SMika Westerberg * through its vendor specific registers. 791f67cf491SMika Westerberg */ 792f67cf491SMika Westerberg upstream_port = get_upstream_port(tb->nhi->pdev); 793f67cf491SMika Westerberg if (upstream_port) { 794f67cf491SMika Westerberg int cap; 795f67cf491SMika Westerberg 796f67cf491SMika Westerberg cap = pci_find_ext_capability(upstream_port, 797f67cf491SMika Westerberg PCI_EXT_CAP_ID_VNDR); 798f67cf491SMika Westerberg if (cap > 0) { 799f67cf491SMika Westerberg icm->upstream_port = upstream_port; 800f67cf491SMika Westerberg icm->vnd_cap = cap; 801f67cf491SMika Westerberg 802f67cf491SMika Westerberg return true; 803f67cf491SMika Westerberg } 804f67cf491SMika Westerberg } 805f67cf491SMika Westerberg 806f67cf491SMika Westerberg return false; 807f67cf491SMika Westerberg } 808f67cf491SMika Westerberg 809f67cf491SMika Westerberg static int icm_ar_get_mode(struct tb *tb) 810f67cf491SMika Westerberg { 811f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 812e4be8c9bSMika Westerberg int retries = 60; 813f67cf491SMika Westerberg u32 val; 814f67cf491SMika Westerberg 815f67cf491SMika Westerberg do { 816f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 817f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 818f67cf491SMika Westerberg break; 819e4be8c9bSMika Westerberg msleep(50); 820f67cf491SMika Westerberg } while (--retries); 821f67cf491SMika Westerberg 822f67cf491SMika Westerberg if (!retries) { 823f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "ICM firmware not authenticated\n"); 824f67cf491SMika Westerberg return -ENODEV; 825f67cf491SMika Westerberg } 826f67cf491SMika Westerberg 827f67cf491SMika Westerberg return nhi_mailbox_mode(nhi); 828f67cf491SMika Westerberg } 829f67cf491SMika Westerberg 830f67cf491SMika Westerberg static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 831f67cf491SMika Westerberg { 832f67cf491SMika Westerberg struct icm_ar_pkg_get_route_response reply; 833f67cf491SMika Westerberg struct icm_ar_pkg_get_route request = { 834f67cf491SMika Westerberg .hdr = { .code = ICM_GET_ROUTE }, 835f67cf491SMika Westerberg .link_info = depth << ICM_LINK_INFO_DEPTH_SHIFT | link, 836f67cf491SMika Westerberg }; 837f67cf491SMika Westerberg int ret; 838f67cf491SMika Westerberg 839f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 840f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 841f67cf491SMika Westerberg 1, ICM_TIMEOUT); 842f67cf491SMika Westerberg if (ret) 843f67cf491SMika Westerberg return ret; 844f67cf491SMika Westerberg 845f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 846f67cf491SMika Westerberg return -EIO; 847f67cf491SMika Westerberg 848f67cf491SMika Westerberg *route = get_route(reply.route_hi, reply.route_lo); 849f67cf491SMika Westerberg return 0; 850f67cf491SMika Westerberg } 851f67cf491SMika Westerberg 852f67cf491SMika Westerberg static void icm_handle_notification(struct work_struct *work) 853f67cf491SMika Westerberg { 854f67cf491SMika Westerberg struct icm_notification *n = container_of(work, typeof(*n), work); 855f67cf491SMika Westerberg struct tb *tb = n->tb; 856f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 857f67cf491SMika Westerberg 858f67cf491SMika Westerberg mutex_lock(&tb->lock); 859f67cf491SMika Westerberg 860f67cf491SMika Westerberg switch (n->pkg->code) { 861f67cf491SMika Westerberg case ICM_EVENT_DEVICE_CONNECTED: 862f67cf491SMika Westerberg icm->device_connected(tb, n->pkg); 863f67cf491SMika Westerberg break; 864f67cf491SMika Westerberg case ICM_EVENT_DEVICE_DISCONNECTED: 865f67cf491SMika Westerberg icm->device_disconnected(tb, n->pkg); 866f67cf491SMika Westerberg break; 867d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_CONNECTED: 868d1ff7024SMika Westerberg icm->xdomain_connected(tb, n->pkg); 869d1ff7024SMika Westerberg break; 870d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_DISCONNECTED: 871d1ff7024SMika Westerberg icm->xdomain_disconnected(tb, n->pkg); 872d1ff7024SMika Westerberg break; 873f67cf491SMika Westerberg } 874f67cf491SMika Westerberg 875f67cf491SMika Westerberg mutex_unlock(&tb->lock); 876f67cf491SMika Westerberg 877f67cf491SMika Westerberg kfree(n->pkg); 878f67cf491SMika Westerberg kfree(n); 879f67cf491SMika Westerberg } 880f67cf491SMika Westerberg 881f67cf491SMika Westerberg static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, 882f67cf491SMika Westerberg const void *buf, size_t size) 883f67cf491SMika Westerberg { 884f67cf491SMika Westerberg struct icm_notification *n; 885f67cf491SMika Westerberg 886f67cf491SMika Westerberg n = kmalloc(sizeof(*n), GFP_KERNEL); 887f67cf491SMika Westerberg if (!n) 888f67cf491SMika Westerberg return; 889f67cf491SMika Westerberg 890f67cf491SMika Westerberg INIT_WORK(&n->work, icm_handle_notification); 891f67cf491SMika Westerberg n->pkg = kmemdup(buf, size, GFP_KERNEL); 892f67cf491SMika Westerberg n->tb = tb; 893f67cf491SMika Westerberg 894f67cf491SMika Westerberg queue_work(tb->wq, &n->work); 895f67cf491SMika Westerberg } 896f67cf491SMika Westerberg 897f67cf491SMika Westerberg static int 898f67cf491SMika Westerberg __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level) 899f67cf491SMika Westerberg { 9003080e197SMika Westerberg struct icm *icm = tb_priv(tb); 90144b51bbbSMika Westerberg unsigned int retries = 50; 902f67cf491SMika Westerberg int ret; 903f67cf491SMika Westerberg 9043080e197SMika Westerberg ret = icm->driver_ready(tb, security_level); 9053080e197SMika Westerberg if (ret) { 9063080e197SMika Westerberg tb_err(tb, "failed to send driver ready to ICM\n"); 907f67cf491SMika Westerberg return ret; 9083080e197SMika Westerberg } 909f67cf491SMika Westerberg 910f67cf491SMika Westerberg /* 911f67cf491SMika Westerberg * Hold on here until the switch config space is accessible so 912f67cf491SMika Westerberg * that we can read root switch config successfully. 913f67cf491SMika Westerberg */ 914f67cf491SMika Westerberg do { 915f67cf491SMika Westerberg struct tb_cfg_result res; 916f67cf491SMika Westerberg u32 tmp; 917f67cf491SMika Westerberg 918f67cf491SMika Westerberg res = tb_cfg_read_raw(tb->ctl, &tmp, 0, 0, TB_CFG_SWITCH, 919f67cf491SMika Westerberg 0, 1, 100); 920f67cf491SMika Westerberg if (!res.err) 921f67cf491SMika Westerberg return 0; 922f67cf491SMika Westerberg 923f67cf491SMika Westerberg msleep(50); 924f67cf491SMika Westerberg } while (--retries); 925f67cf491SMika Westerberg 92644b51bbbSMika Westerberg tb_err(tb, "failed to read root switch config space, giving up\n"); 927f67cf491SMika Westerberg return -ETIMEDOUT; 928f67cf491SMika Westerberg } 929f67cf491SMika Westerberg 930f67cf491SMika Westerberg static int pci2cio_wait_completion(struct icm *icm, unsigned long timeout_msec) 931f67cf491SMika Westerberg { 932f67cf491SMika Westerberg unsigned long end = jiffies + msecs_to_jiffies(timeout_msec); 933f67cf491SMika Westerberg u32 cmd; 934f67cf491SMika Westerberg 935f67cf491SMika Westerberg do { 936f67cf491SMika Westerberg pci_read_config_dword(icm->upstream_port, 937f67cf491SMika Westerberg icm->vnd_cap + PCIE2CIO_CMD, &cmd); 938f67cf491SMika Westerberg if (!(cmd & PCIE2CIO_CMD_START)) { 939f67cf491SMika Westerberg if (cmd & PCIE2CIO_CMD_TIMEOUT) 940f67cf491SMika Westerberg break; 941f67cf491SMika Westerberg return 0; 942f67cf491SMika Westerberg } 943f67cf491SMika Westerberg 944f67cf491SMika Westerberg msleep(50); 945f67cf491SMika Westerberg } while (time_before(jiffies, end)); 946f67cf491SMika Westerberg 947f67cf491SMika Westerberg return -ETIMEDOUT; 948f67cf491SMika Westerberg } 949f67cf491SMika Westerberg 950f67cf491SMika Westerberg static int pcie2cio_read(struct icm *icm, enum tb_cfg_space cs, 951f67cf491SMika Westerberg unsigned int port, unsigned int index, u32 *data) 952f67cf491SMika Westerberg { 953f67cf491SMika Westerberg struct pci_dev *pdev = icm->upstream_port; 954f67cf491SMika Westerberg int ret, vnd_cap = icm->vnd_cap; 955f67cf491SMika Westerberg u32 cmd; 956f67cf491SMika Westerberg 957f67cf491SMika Westerberg cmd = index; 958f67cf491SMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 959f67cf491SMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 960f67cf491SMika Westerberg cmd |= PCIE2CIO_CMD_START; 961f67cf491SMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 962f67cf491SMika Westerberg 963f67cf491SMika Westerberg ret = pci2cio_wait_completion(icm, 5000); 964f67cf491SMika Westerberg if (ret) 965f67cf491SMika Westerberg return ret; 966f67cf491SMika Westerberg 967f67cf491SMika Westerberg pci_read_config_dword(pdev, vnd_cap + PCIE2CIO_RDDATA, data); 968f67cf491SMika Westerberg return 0; 969f67cf491SMika Westerberg } 970f67cf491SMika Westerberg 971f67cf491SMika Westerberg static int pcie2cio_write(struct icm *icm, enum tb_cfg_space cs, 972f67cf491SMika Westerberg unsigned int port, unsigned int index, u32 data) 973f67cf491SMika Westerberg { 974f67cf491SMika Westerberg struct pci_dev *pdev = icm->upstream_port; 975f67cf491SMika Westerberg int vnd_cap = icm->vnd_cap; 976f67cf491SMika Westerberg u32 cmd; 977f67cf491SMika Westerberg 978f67cf491SMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_WRDATA, data); 979f67cf491SMika Westerberg 980f67cf491SMika Westerberg cmd = index; 981f67cf491SMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 982f67cf491SMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 983f67cf491SMika Westerberg cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START; 984f67cf491SMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 985f67cf491SMika Westerberg 986f67cf491SMika Westerberg return pci2cio_wait_completion(icm, 5000); 987f67cf491SMika Westerberg } 988f67cf491SMika Westerberg 989f67cf491SMika Westerberg static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) 990f67cf491SMika Westerberg { 991f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 992f67cf491SMika Westerberg u32 val; 993f67cf491SMika Westerberg 994f67cf491SMika Westerberg /* Put ARC to wait for CIO reset event to happen */ 995f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 996f67cf491SMika Westerberg val |= REG_FW_STS_CIO_RESET_REQ; 997f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 998f67cf491SMika Westerberg 999f67cf491SMika Westerberg /* Re-start ARC */ 1000f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1001f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_INVERT; 1002f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_CPU; 1003f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1004f67cf491SMika Westerberg 1005f67cf491SMika Westerberg /* Trigger CIO reset now */ 1006f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_SWITCH, 0, 0x50, BIT(9)); 1007f67cf491SMika Westerberg } 1008f67cf491SMika Westerberg 1009f67cf491SMika Westerberg static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) 1010f67cf491SMika Westerberg { 1011f67cf491SMika Westerberg unsigned int retries = 10; 1012f67cf491SMika Westerberg int ret; 1013f67cf491SMika Westerberg u32 val; 1014f67cf491SMika Westerberg 1015f67cf491SMika Westerberg /* Check if the ICM firmware is already running */ 1016f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1017f67cf491SMika Westerberg if (val & REG_FW_STS_ICM_EN) 1018f67cf491SMika Westerberg return 0; 1019f67cf491SMika Westerberg 1020f67cf491SMika Westerberg dev_info(&nhi->pdev->dev, "starting ICM firmware\n"); 1021f67cf491SMika Westerberg 1022f67cf491SMika Westerberg ret = icm_firmware_reset(tb, nhi); 1023f67cf491SMika Westerberg if (ret) 1024f67cf491SMika Westerberg return ret; 1025f67cf491SMika Westerberg 1026f67cf491SMika Westerberg /* Wait until the ICM firmware tells us it is up and running */ 1027f67cf491SMika Westerberg do { 1028f67cf491SMika Westerberg /* Check that the ICM firmware is running */ 1029f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1030f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1031f67cf491SMika Westerberg return 0; 1032f67cf491SMika Westerberg 1033f67cf491SMika Westerberg msleep(300); 1034f67cf491SMika Westerberg } while (--retries); 1035f67cf491SMika Westerberg 1036f67cf491SMika Westerberg return -ETIMEDOUT; 1037f67cf491SMika Westerberg } 1038f67cf491SMika Westerberg 1039f67cf491SMika Westerberg static int icm_reset_phy_port(struct tb *tb, int phy_port) 1040f67cf491SMika Westerberg { 1041f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1042f67cf491SMika Westerberg u32 state0, state1; 1043f67cf491SMika Westerberg int port0, port1; 1044f67cf491SMika Westerberg u32 val0, val1; 1045f67cf491SMika Westerberg int ret; 1046f67cf491SMika Westerberg 1047f67cf491SMika Westerberg if (!icm->upstream_port) 1048f67cf491SMika Westerberg return 0; 1049f67cf491SMika Westerberg 1050f67cf491SMika Westerberg if (phy_port) { 1051f67cf491SMika Westerberg port0 = 3; 1052f67cf491SMika Westerberg port1 = 4; 1053f67cf491SMika Westerberg } else { 1054f67cf491SMika Westerberg port0 = 1; 1055f67cf491SMika Westerberg port1 = 2; 1056f67cf491SMika Westerberg } 1057f67cf491SMika Westerberg 1058f67cf491SMika Westerberg /* 1059f67cf491SMika Westerberg * Read link status of both null ports belonging to a single 1060f67cf491SMika Westerberg * physical port. 1061f67cf491SMika Westerberg */ 1062f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1063f67cf491SMika Westerberg if (ret) 1064f67cf491SMika Westerberg return ret; 1065f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1066f67cf491SMika Westerberg if (ret) 1067f67cf491SMika Westerberg return ret; 1068f67cf491SMika Westerberg 1069f67cf491SMika Westerberg state0 = val0 & PHY_PORT_CS1_LINK_STATE_MASK; 1070f67cf491SMika Westerberg state0 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1071f67cf491SMika Westerberg state1 = val1 & PHY_PORT_CS1_LINK_STATE_MASK; 1072f67cf491SMika Westerberg state1 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1073f67cf491SMika Westerberg 1074f67cf491SMika Westerberg /* If they are both up we need to reset them now */ 1075f67cf491SMika Westerberg if (state0 != TB_PORT_UP || state1 != TB_PORT_UP) 1076f67cf491SMika Westerberg return 0; 1077f67cf491SMika Westerberg 1078f67cf491SMika Westerberg val0 |= PHY_PORT_CS1_LINK_DISABLE; 1079f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1080f67cf491SMika Westerberg if (ret) 1081f67cf491SMika Westerberg return ret; 1082f67cf491SMika Westerberg 1083f67cf491SMika Westerberg val1 |= PHY_PORT_CS1_LINK_DISABLE; 1084f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1085f67cf491SMika Westerberg if (ret) 1086f67cf491SMika Westerberg return ret; 1087f67cf491SMika Westerberg 1088f67cf491SMika Westerberg /* Wait a bit and then re-enable both ports */ 1089f67cf491SMika Westerberg usleep_range(10, 100); 1090f67cf491SMika Westerberg 1091f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1092f67cf491SMika Westerberg if (ret) 1093f67cf491SMika Westerberg return ret; 1094f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1095f67cf491SMika Westerberg if (ret) 1096f67cf491SMika Westerberg return ret; 1097f67cf491SMika Westerberg 1098f67cf491SMika Westerberg val0 &= ~PHY_PORT_CS1_LINK_DISABLE; 1099f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1100f67cf491SMika Westerberg if (ret) 1101f67cf491SMika Westerberg return ret; 1102f67cf491SMika Westerberg 1103f67cf491SMika Westerberg val1 &= ~PHY_PORT_CS1_LINK_DISABLE; 1104f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1105f67cf491SMika Westerberg } 1106f67cf491SMika Westerberg 1107f67cf491SMika Westerberg static int icm_firmware_init(struct tb *tb) 1108f67cf491SMika Westerberg { 1109f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1110f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1111f67cf491SMika Westerberg int ret; 1112f67cf491SMika Westerberg 1113f67cf491SMika Westerberg ret = icm_firmware_start(tb, nhi); 1114f67cf491SMika Westerberg if (ret) { 1115f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "could not start ICM firmware\n"); 1116f67cf491SMika Westerberg return ret; 1117f67cf491SMika Westerberg } 1118f67cf491SMika Westerberg 1119f67cf491SMika Westerberg if (icm->get_mode) { 1120f67cf491SMika Westerberg ret = icm->get_mode(tb); 1121f67cf491SMika Westerberg 1122f67cf491SMika Westerberg switch (ret) { 1123e6b245ccSMika Westerberg case NHI_FW_SAFE_MODE: 1124e6b245ccSMika Westerberg icm->safe_mode = true; 1125e6b245ccSMika Westerberg break; 1126e6b245ccSMika Westerberg 1127f67cf491SMika Westerberg case NHI_FW_CM_MODE: 1128f67cf491SMika Westerberg /* Ask ICM to accept all Thunderbolt devices */ 1129f67cf491SMika Westerberg nhi_mailbox_cmd(nhi, NHI_MAILBOX_ALLOW_ALL_DEVS, 0); 1130f67cf491SMika Westerberg break; 1131f67cf491SMika Westerberg 1132f67cf491SMika Westerberg default: 1133e4be8c9bSMika Westerberg if (ret < 0) 1134e4be8c9bSMika Westerberg return ret; 1135e4be8c9bSMika Westerberg 1136f67cf491SMika Westerberg tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); 1137f67cf491SMika Westerberg return -ENODEV; 1138f67cf491SMika Westerberg } 1139f67cf491SMika Westerberg } 1140f67cf491SMika Westerberg 1141f67cf491SMika Westerberg /* 1142f67cf491SMika Westerberg * Reset both physical ports if there is anything connected to 1143f67cf491SMika Westerberg * them already. 1144f67cf491SMika Westerberg */ 1145f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 0); 1146f67cf491SMika Westerberg if (ret) 1147f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port0\n"); 1148f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 1); 1149f67cf491SMika Westerberg if (ret) 1150f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port1\n"); 1151f67cf491SMika Westerberg 1152f67cf491SMika Westerberg return 0; 1153f67cf491SMika Westerberg } 1154f67cf491SMika Westerberg 1155f67cf491SMika Westerberg static int icm_driver_ready(struct tb *tb) 1156f67cf491SMika Westerberg { 1157e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1158f67cf491SMika Westerberg int ret; 1159f67cf491SMika Westerberg 1160f67cf491SMika Westerberg ret = icm_firmware_init(tb); 1161f67cf491SMika Westerberg if (ret) 1162f67cf491SMika Westerberg return ret; 1163f67cf491SMika Westerberg 1164e6b245ccSMika Westerberg if (icm->safe_mode) { 1165e6b245ccSMika Westerberg tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); 1166e6b245ccSMika Westerberg tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); 1167e6b245ccSMika Westerberg tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); 1168e6b245ccSMika Westerberg return 0; 1169e6b245ccSMika Westerberg } 1170e6b245ccSMika Westerberg 1171f67cf491SMika Westerberg return __icm_driver_ready(tb, &tb->security_level); 1172f67cf491SMika Westerberg } 1173f67cf491SMika Westerberg 1174f67cf491SMika Westerberg static int icm_suspend(struct tb *tb) 1175f67cf491SMika Westerberg { 1176a684c5b1SRafael J. Wysocki int ret; 1177a684c5b1SRafael J. Wysocki 1178a684c5b1SRafael J. Wysocki ret = nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_SAVE_DEVS, 0); 1179a684c5b1SRafael J. Wysocki if (ret) 1180a684c5b1SRafael J. Wysocki tb_info(tb, "Ignoring mailbox command error (%d) in %s\n", 1181a684c5b1SRafael J. Wysocki ret, __func__); 1182a684c5b1SRafael J. Wysocki 1183a684c5b1SRafael J. Wysocki return 0; 1184f67cf491SMika Westerberg } 1185f67cf491SMika Westerberg 1186f67cf491SMika Westerberg /* 1187f67cf491SMika Westerberg * Mark all switches (except root switch) below this one unplugged. ICM 1188f67cf491SMika Westerberg * firmware will send us an updated list of switches after we have send 1189f67cf491SMika Westerberg * it driver ready command. If a switch is not in that list it will be 1190f67cf491SMika Westerberg * removed when we perform rescan. 1191f67cf491SMika Westerberg */ 1192f67cf491SMika Westerberg static void icm_unplug_children(struct tb_switch *sw) 1193f67cf491SMika Westerberg { 1194f67cf491SMika Westerberg unsigned int i; 1195f67cf491SMika Westerberg 1196f67cf491SMika Westerberg if (tb_route(sw)) 1197f67cf491SMika Westerberg sw->is_unplugged = true; 1198f67cf491SMika Westerberg 1199f67cf491SMika Westerberg for (i = 1; i <= sw->config.max_port_number; i++) { 1200f67cf491SMika Westerberg struct tb_port *port = &sw->ports[i]; 1201f67cf491SMika Westerberg 1202f67cf491SMika Westerberg if (tb_is_upstream_port(port)) 1203f67cf491SMika Westerberg continue; 1204d1ff7024SMika Westerberg if (port->xdomain) { 1205d1ff7024SMika Westerberg port->xdomain->is_unplugged = true; 1206d1ff7024SMika Westerberg continue; 1207d1ff7024SMika Westerberg } 1208f67cf491SMika Westerberg if (!port->remote) 1209f67cf491SMika Westerberg continue; 1210f67cf491SMika Westerberg 1211f67cf491SMika Westerberg icm_unplug_children(port->remote->sw); 1212f67cf491SMika Westerberg } 1213f67cf491SMika Westerberg } 1214f67cf491SMika Westerberg 1215f67cf491SMika Westerberg static void icm_free_unplugged_children(struct tb_switch *sw) 1216f67cf491SMika Westerberg { 1217f67cf491SMika Westerberg unsigned int i; 1218f67cf491SMika Westerberg 1219f67cf491SMika Westerberg for (i = 1; i <= sw->config.max_port_number; i++) { 1220f67cf491SMika Westerberg struct tb_port *port = &sw->ports[i]; 1221f67cf491SMika Westerberg 1222f67cf491SMika Westerberg if (tb_is_upstream_port(port)) 1223f67cf491SMika Westerberg continue; 1224d1ff7024SMika Westerberg 1225d1ff7024SMika Westerberg if (port->xdomain && port->xdomain->is_unplugged) { 1226d1ff7024SMika Westerberg tb_xdomain_remove(port->xdomain); 1227d1ff7024SMika Westerberg port->xdomain = NULL; 1228d1ff7024SMika Westerberg continue; 1229d1ff7024SMika Westerberg } 1230d1ff7024SMika Westerberg 1231f67cf491SMika Westerberg if (!port->remote) 1232f67cf491SMika Westerberg continue; 1233f67cf491SMika Westerberg 1234f67cf491SMika Westerberg if (port->remote->sw->is_unplugged) { 1235f67cf491SMika Westerberg tb_switch_remove(port->remote->sw); 1236f67cf491SMika Westerberg port->remote = NULL; 1237f67cf491SMika Westerberg } else { 1238f67cf491SMika Westerberg icm_free_unplugged_children(port->remote->sw); 1239f67cf491SMika Westerberg } 1240f67cf491SMika Westerberg } 1241f67cf491SMika Westerberg } 1242f67cf491SMika Westerberg 1243f67cf491SMika Westerberg static void icm_rescan_work(struct work_struct *work) 1244f67cf491SMika Westerberg { 1245f67cf491SMika Westerberg struct icm *icm = container_of(work, struct icm, rescan_work.work); 1246f67cf491SMika Westerberg struct tb *tb = icm_to_tb(icm); 1247f67cf491SMika Westerberg 1248f67cf491SMika Westerberg mutex_lock(&tb->lock); 1249f67cf491SMika Westerberg if (tb->root_switch) 1250f67cf491SMika Westerberg icm_free_unplugged_children(tb->root_switch); 1251f67cf491SMika Westerberg mutex_unlock(&tb->lock); 1252f67cf491SMika Westerberg } 1253f67cf491SMika Westerberg 1254f67cf491SMika Westerberg static void icm_complete(struct tb *tb) 1255f67cf491SMika Westerberg { 1256f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1257f67cf491SMika Westerberg 1258f67cf491SMika Westerberg if (tb->nhi->going_away) 1259f67cf491SMika Westerberg return; 1260f67cf491SMika Westerberg 1261f67cf491SMika Westerberg icm_unplug_children(tb->root_switch); 1262f67cf491SMika Westerberg 1263f67cf491SMika Westerberg /* 1264f67cf491SMika Westerberg * Now all existing children should be resumed, start events 1265f67cf491SMika Westerberg * from ICM to get updated status. 1266f67cf491SMika Westerberg */ 1267f67cf491SMika Westerberg __icm_driver_ready(tb, NULL); 1268f67cf491SMika Westerberg 1269f67cf491SMika Westerberg /* 1270f67cf491SMika Westerberg * We do not get notifications of devices that have been 1271f67cf491SMika Westerberg * unplugged during suspend so schedule rescan to clean them up 1272f67cf491SMika Westerberg * if any. 1273f67cf491SMika Westerberg */ 1274f67cf491SMika Westerberg queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500)); 1275f67cf491SMika Westerberg } 1276f67cf491SMika Westerberg 1277f67cf491SMika Westerberg static int icm_start(struct tb *tb) 1278f67cf491SMika Westerberg { 1279e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1280f67cf491SMika Westerberg int ret; 1281f67cf491SMika Westerberg 1282e6b245ccSMika Westerberg if (icm->safe_mode) 1283e6b245ccSMika Westerberg tb->root_switch = tb_switch_alloc_safe_mode(tb, &tb->dev, 0); 1284e6b245ccSMika Westerberg else 1285f67cf491SMika Westerberg tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); 1286f67cf491SMika Westerberg if (!tb->root_switch) 1287f67cf491SMika Westerberg return -ENODEV; 1288f67cf491SMika Westerberg 1289e6b245ccSMika Westerberg /* 1290e6b245ccSMika Westerberg * NVM upgrade has not been tested on Apple systems and they 1291e6b245ccSMika Westerberg * don't provide images publicly either. To be on the safe side 1292e6b245ccSMika Westerberg * prevent root switch NVM upgrade on Macs for now. 1293e6b245ccSMika Westerberg */ 1294630b3affSLukas Wunner tb->root_switch->no_nvm_upgrade = x86_apple_machine; 1295e6b245ccSMika Westerberg 1296f67cf491SMika Westerberg ret = tb_switch_add(tb->root_switch); 1297d1ff7024SMika Westerberg if (ret) { 1298f67cf491SMika Westerberg tb_switch_put(tb->root_switch); 1299d1ff7024SMika Westerberg tb->root_switch = NULL; 1300d1ff7024SMika Westerberg } 1301f67cf491SMika Westerberg 1302f67cf491SMika Westerberg return ret; 1303f67cf491SMika Westerberg } 1304f67cf491SMika Westerberg 1305f67cf491SMika Westerberg static void icm_stop(struct tb *tb) 1306f67cf491SMika Westerberg { 1307f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1308f67cf491SMika Westerberg 1309f67cf491SMika Westerberg cancel_delayed_work(&icm->rescan_work); 1310f67cf491SMika Westerberg tb_switch_remove(tb->root_switch); 1311f67cf491SMika Westerberg tb->root_switch = NULL; 1312f67cf491SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 1313f67cf491SMika Westerberg } 1314f67cf491SMika Westerberg 1315e6b245ccSMika Westerberg static int icm_disconnect_pcie_paths(struct tb *tb) 1316e6b245ccSMika Westerberg { 1317e6b245ccSMika Westerberg return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); 1318e6b245ccSMika Westerberg } 1319e6b245ccSMika Westerberg 1320f67cf491SMika Westerberg /* Falcon Ridge and Alpine Ridge */ 1321f67cf491SMika Westerberg static const struct tb_cm_ops icm_fr_ops = { 1322f67cf491SMika Westerberg .driver_ready = icm_driver_ready, 1323f67cf491SMika Westerberg .start = icm_start, 1324f67cf491SMika Westerberg .stop = icm_stop, 1325f67cf491SMika Westerberg .suspend = icm_suspend, 1326f67cf491SMika Westerberg .complete = icm_complete, 1327f67cf491SMika Westerberg .handle_event = icm_handle_event, 1328f67cf491SMika Westerberg .approve_switch = icm_fr_approve_switch, 1329f67cf491SMika Westerberg .add_switch_key = icm_fr_add_switch_key, 1330f67cf491SMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 1331e6b245ccSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 1332d1ff7024SMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 1333d1ff7024SMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 1334f67cf491SMika Westerberg }; 1335f67cf491SMika Westerberg 1336f67cf491SMika Westerberg struct tb *icm_probe(struct tb_nhi *nhi) 1337f67cf491SMika Westerberg { 1338f67cf491SMika Westerberg struct icm *icm; 1339f67cf491SMika Westerberg struct tb *tb; 1340f67cf491SMika Westerberg 1341f67cf491SMika Westerberg tb = tb_domain_alloc(nhi, sizeof(struct icm)); 1342f67cf491SMika Westerberg if (!tb) 1343f67cf491SMika Westerberg return NULL; 1344f67cf491SMika Westerberg 1345f67cf491SMika Westerberg icm = tb_priv(tb); 1346f67cf491SMika Westerberg INIT_DELAYED_WORK(&icm->rescan_work, icm_rescan_work); 1347f67cf491SMika Westerberg mutex_init(&icm->request_lock); 1348f67cf491SMika Westerberg 1349f67cf491SMika Westerberg switch (nhi->pdev->device) { 1350f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: 1351f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: 1352f67cf491SMika Westerberg icm->is_supported = icm_fr_is_supported; 1353f67cf491SMika Westerberg icm->get_route = icm_fr_get_route; 13543080e197SMika Westerberg icm->driver_ready = icm_fr_driver_ready; 1355f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 1356f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 1357d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 1358d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 1359f67cf491SMika Westerberg tb->cm_ops = &icm_fr_ops; 1360f67cf491SMika Westerberg break; 1361f67cf491SMika Westerberg 1362f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI: 1363f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI: 1364f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: 1365f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: 1366f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: 1367f67cf491SMika Westerberg icm->is_supported = icm_ar_is_supported; 1368f67cf491SMika Westerberg icm->get_mode = icm_ar_get_mode; 1369f67cf491SMika Westerberg icm->get_route = icm_ar_get_route; 13703080e197SMika Westerberg icm->driver_ready = icm_fr_driver_ready; 1371f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 1372f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 1373d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 1374d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 1375f67cf491SMika Westerberg tb->cm_ops = &icm_fr_ops; 1376f67cf491SMika Westerberg break; 1377f67cf491SMika Westerberg } 1378f67cf491SMika Westerberg 1379f67cf491SMika Westerberg if (!icm->is_supported || !icm->is_supported(tb)) { 1380f67cf491SMika Westerberg dev_dbg(&nhi->pdev->dev, "ICM not supported on this controller\n"); 1381f67cf491SMika Westerberg tb_domain_put(tb); 1382f67cf491SMika Westerberg return NULL; 1383f67cf491SMika Westerberg } 1384f67cf491SMika Westerberg 1385f67cf491SMika Westerberg return tb; 1386f67cf491SMika Westerberg } 1387