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 599aaa3b8bSMika Westerberg * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) 60f67cf491SMika Westerberg * @is_supported: Checks if we can support ICM on this controller 61f67cf491SMika Westerberg * @get_mode: Read and return the ICM firmware mode (optional) 62f67cf491SMika Westerberg * @get_route: Find a route string for given switch 633080e197SMika Westerberg * @driver_ready: Send driver ready message to ICM 64f67cf491SMika Westerberg * @device_connected: Handle device connected ICM message 65f67cf491SMika Westerberg * @device_disconnected: Handle device disconnected ICM message 66d1ff7024SMika Westerberg * @xdomain_connected - Handle XDomain connected ICM message 67d1ff7024SMika Westerberg * @xdomain_disconnected - Handle XDomain disconnected ICM message 68f67cf491SMika Westerberg */ 69f67cf491SMika Westerberg struct icm { 70f67cf491SMika Westerberg struct mutex request_lock; 71f67cf491SMika Westerberg struct delayed_work rescan_work; 72f67cf491SMika Westerberg struct pci_dev *upstream_port; 739aaa3b8bSMika Westerberg size_t max_boot_acl; 74f67cf491SMika Westerberg int vnd_cap; 75e6b245ccSMika Westerberg bool safe_mode; 76f67cf491SMika Westerberg bool (*is_supported)(struct tb *tb); 77f67cf491SMika Westerberg int (*get_mode)(struct tb *tb); 78f67cf491SMika Westerberg int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); 793080e197SMika Westerberg int (*driver_ready)(struct tb *tb, 809aaa3b8bSMika Westerberg enum tb_security_level *security_level, 819aaa3b8bSMika Westerberg size_t *nboot_acl); 82f67cf491SMika Westerberg void (*device_connected)(struct tb *tb, 83f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 84f67cf491SMika Westerberg void (*device_disconnected)(struct tb *tb, 85f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 86d1ff7024SMika Westerberg void (*xdomain_connected)(struct tb *tb, 87d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 88d1ff7024SMika Westerberg void (*xdomain_disconnected)(struct tb *tb, 89d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 90f67cf491SMika Westerberg }; 91f67cf491SMika Westerberg 92f67cf491SMika Westerberg struct icm_notification { 93f67cf491SMika Westerberg struct work_struct work; 94f67cf491SMika Westerberg struct icm_pkg_header *pkg; 95f67cf491SMika Westerberg struct tb *tb; 96f67cf491SMika Westerberg }; 97f67cf491SMika Westerberg 98f67cf491SMika Westerberg static inline struct tb *icm_to_tb(struct icm *icm) 99f67cf491SMika Westerberg { 100f67cf491SMika Westerberg return ((void *)icm - sizeof(struct tb)); 101f67cf491SMika Westerberg } 102f67cf491SMika Westerberg 103f67cf491SMika Westerberg static inline u8 phy_port_from_route(u64 route, u8 depth) 104f67cf491SMika Westerberg { 105d1ff7024SMika Westerberg u8 link; 106d1ff7024SMika Westerberg 107d1ff7024SMika Westerberg link = depth ? route >> ((depth - 1) * 8) : route; 108d1ff7024SMika Westerberg return tb_phy_port_from_link(link); 109f67cf491SMika Westerberg } 110f67cf491SMika Westerberg 111f67cf491SMika Westerberg static inline u8 dual_link_from_link(u8 link) 112f67cf491SMika Westerberg { 113f67cf491SMika Westerberg return link ? ((link - 1) ^ 0x01) + 1 : 0; 114f67cf491SMika Westerberg } 115f67cf491SMika Westerberg 116f67cf491SMika Westerberg static inline u64 get_route(u32 route_hi, u32 route_lo) 117f67cf491SMika Westerberg { 118f67cf491SMika Westerberg return (u64)route_hi << 32 | route_lo; 119f67cf491SMika Westerberg } 120f67cf491SMika Westerberg 121f67cf491SMika Westerberg static bool icm_match(const struct tb_cfg_request *req, 122f67cf491SMika Westerberg const struct ctl_pkg *pkg) 123f67cf491SMika Westerberg { 124f67cf491SMika Westerberg const struct icm_pkg_header *res_hdr = pkg->buffer; 125f67cf491SMika Westerberg const struct icm_pkg_header *req_hdr = req->request; 126f67cf491SMika Westerberg 127f67cf491SMika Westerberg if (pkg->frame.eof != req->response_type) 128f67cf491SMika Westerberg return false; 129f67cf491SMika Westerberg if (res_hdr->code != req_hdr->code) 130f67cf491SMika Westerberg return false; 131f67cf491SMika Westerberg 132f67cf491SMika Westerberg return true; 133f67cf491SMika Westerberg } 134f67cf491SMika Westerberg 135f67cf491SMika Westerberg static bool icm_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) 136f67cf491SMika Westerberg { 137f67cf491SMika Westerberg const struct icm_pkg_header *hdr = pkg->buffer; 138f67cf491SMika Westerberg 139f67cf491SMika Westerberg if (hdr->packet_id < req->npackets) { 140f67cf491SMika Westerberg size_t offset = hdr->packet_id * req->response_size; 141f67cf491SMika Westerberg 142f67cf491SMika Westerberg memcpy(req->response + offset, pkg->buffer, req->response_size); 143f67cf491SMika Westerberg } 144f67cf491SMika Westerberg 145f67cf491SMika Westerberg return hdr->packet_id == hdr->total_packets - 1; 146f67cf491SMika Westerberg } 147f67cf491SMika Westerberg 148f67cf491SMika Westerberg static int icm_request(struct tb *tb, const void *request, size_t request_size, 149f67cf491SMika Westerberg void *response, size_t response_size, size_t npackets, 150f67cf491SMika Westerberg unsigned int timeout_msec) 151f67cf491SMika Westerberg { 152f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 153f67cf491SMika Westerberg int retries = 3; 154f67cf491SMika Westerberg 155f67cf491SMika Westerberg do { 156f67cf491SMika Westerberg struct tb_cfg_request *req; 157f67cf491SMika Westerberg struct tb_cfg_result res; 158f67cf491SMika Westerberg 159f67cf491SMika Westerberg req = tb_cfg_request_alloc(); 160f67cf491SMika Westerberg if (!req) 161f67cf491SMika Westerberg return -ENOMEM; 162f67cf491SMika Westerberg 163f67cf491SMika Westerberg req->match = icm_match; 164f67cf491SMika Westerberg req->copy = icm_copy; 165f67cf491SMika Westerberg req->request = request; 166f67cf491SMika Westerberg req->request_size = request_size; 167f67cf491SMika Westerberg req->request_type = TB_CFG_PKG_ICM_CMD; 168f67cf491SMika Westerberg req->response = response; 169f67cf491SMika Westerberg req->npackets = npackets; 170f67cf491SMika Westerberg req->response_size = response_size; 171f67cf491SMika Westerberg req->response_type = TB_CFG_PKG_ICM_RESP; 172f67cf491SMika Westerberg 173f67cf491SMika Westerberg mutex_lock(&icm->request_lock); 174f67cf491SMika Westerberg res = tb_cfg_request_sync(tb->ctl, req, timeout_msec); 175f67cf491SMika Westerberg mutex_unlock(&icm->request_lock); 176f67cf491SMika Westerberg 177f67cf491SMika Westerberg tb_cfg_request_put(req); 178f67cf491SMika Westerberg 179f67cf491SMika Westerberg if (res.err != -ETIMEDOUT) 180f67cf491SMika Westerberg return res.err == 1 ? -EIO : res.err; 181f67cf491SMika Westerberg 182f67cf491SMika Westerberg usleep_range(20, 50); 183f67cf491SMika Westerberg } while (retries--); 184f67cf491SMika Westerberg 185f67cf491SMika Westerberg return -ETIMEDOUT; 186f67cf491SMika Westerberg } 187f67cf491SMika Westerberg 188f67cf491SMika Westerberg static bool icm_fr_is_supported(struct tb *tb) 189f67cf491SMika Westerberg { 190630b3affSLukas Wunner return !x86_apple_machine; 191f67cf491SMika Westerberg } 192f67cf491SMika Westerberg 193f67cf491SMika Westerberg static inline int icm_fr_get_switch_index(u32 port) 194f67cf491SMika Westerberg { 195f67cf491SMika Westerberg int index; 196f67cf491SMika Westerberg 197f67cf491SMika Westerberg if ((port & ICM_PORT_TYPE_MASK) != TB_TYPE_PORT) 198f67cf491SMika Westerberg return 0; 199f67cf491SMika Westerberg 200f67cf491SMika Westerberg index = port >> ICM_PORT_INDEX_SHIFT; 201f67cf491SMika Westerberg return index != 0xff ? index : 0; 202f67cf491SMika Westerberg } 203f67cf491SMika Westerberg 204f67cf491SMika Westerberg static int icm_fr_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 205f67cf491SMika Westerberg { 206f67cf491SMika Westerberg struct icm_fr_pkg_get_topology_response *switches, *sw; 207f67cf491SMika Westerberg struct icm_fr_pkg_get_topology request = { 208f67cf491SMika Westerberg .hdr = { .code = ICM_GET_TOPOLOGY }, 209f67cf491SMika Westerberg }; 210f67cf491SMika Westerberg size_t npackets = ICM_GET_TOPOLOGY_PACKETS; 211f67cf491SMika Westerberg int ret, index; 212f67cf491SMika Westerberg u8 i; 213f67cf491SMika Westerberg 214f67cf491SMika Westerberg switches = kcalloc(npackets, sizeof(*switches), GFP_KERNEL); 215f67cf491SMika Westerberg if (!switches) 216f67cf491SMika Westerberg return -ENOMEM; 217f67cf491SMika Westerberg 218f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), switches, 219f67cf491SMika Westerberg sizeof(*switches), npackets, ICM_TIMEOUT); 220f67cf491SMika Westerberg if (ret) 221f67cf491SMika Westerberg goto err_free; 222f67cf491SMika Westerberg 223f67cf491SMika Westerberg sw = &switches[0]; 224f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[link]); 225f67cf491SMika Westerberg if (!index) { 226f67cf491SMika Westerberg ret = -ENODEV; 227f67cf491SMika Westerberg goto err_free; 228f67cf491SMika Westerberg } 229f67cf491SMika Westerberg 230f67cf491SMika Westerberg sw = &switches[index]; 231f67cf491SMika Westerberg for (i = 1; i < depth; i++) { 232f67cf491SMika Westerberg unsigned int j; 233f67cf491SMika Westerberg 234f67cf491SMika Westerberg if (!(sw->first_data & ICM_SWITCH_USED)) { 235f67cf491SMika Westerberg ret = -ENODEV; 236f67cf491SMika Westerberg goto err_free; 237f67cf491SMika Westerberg } 238f67cf491SMika Westerberg 239f67cf491SMika Westerberg for (j = 0; j < ARRAY_SIZE(sw->ports); j++) { 240f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[j]); 241f67cf491SMika Westerberg if (index > sw->switch_index) { 242f67cf491SMika Westerberg sw = &switches[index]; 243f67cf491SMika Westerberg break; 244f67cf491SMika Westerberg } 245f67cf491SMika Westerberg } 246f67cf491SMika Westerberg } 247f67cf491SMika Westerberg 248f67cf491SMika Westerberg *route = get_route(sw->route_hi, sw->route_lo); 249f67cf491SMika Westerberg 250f67cf491SMika Westerberg err_free: 251f67cf491SMika Westerberg kfree(switches); 252f67cf491SMika Westerberg return ret; 253f67cf491SMika Westerberg } 254f67cf491SMika Westerberg 2553080e197SMika Westerberg static int 2569aaa3b8bSMika Westerberg icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 2579aaa3b8bSMika Westerberg size_t *nboot_acl) 2583080e197SMika Westerberg { 2593080e197SMika Westerberg struct icm_fr_pkg_driver_ready_response reply; 2603080e197SMika Westerberg struct icm_pkg_driver_ready request = { 2613080e197SMika Westerberg .hdr.code = ICM_DRIVER_READY, 2623080e197SMika Westerberg }; 2633080e197SMika Westerberg int ret; 2643080e197SMika Westerberg 2653080e197SMika Westerberg memset(&reply, 0, sizeof(reply)); 2663080e197SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 2673080e197SMika Westerberg 1, ICM_TIMEOUT); 2683080e197SMika Westerberg if (ret) 2693080e197SMika Westerberg return ret; 2703080e197SMika Westerberg 2713080e197SMika Westerberg if (security_level) 2723080e197SMika Westerberg *security_level = reply.security_level & ICM_FR_SLEVEL_MASK; 2733080e197SMika Westerberg 2743080e197SMika Westerberg return 0; 2753080e197SMika Westerberg } 2763080e197SMika Westerberg 277f67cf491SMika Westerberg static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) 278f67cf491SMika Westerberg { 279f67cf491SMika Westerberg struct icm_fr_pkg_approve_device request; 280f67cf491SMika Westerberg struct icm_fr_pkg_approve_device reply; 281f67cf491SMika Westerberg int ret; 282f67cf491SMika Westerberg 283f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 284f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 285f67cf491SMika Westerberg request.hdr.code = ICM_APPROVE_DEVICE; 286f67cf491SMika Westerberg request.connection_id = sw->connection_id; 287f67cf491SMika Westerberg request.connection_key = sw->connection_key; 288f67cf491SMika Westerberg 289f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 290f67cf491SMika Westerberg /* Use larger timeout as establishing tunnels can take some time */ 291f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 2920b0a0bd0SMika Westerberg 1, ICM_APPROVE_TIMEOUT); 293f67cf491SMika Westerberg if (ret) 294f67cf491SMika Westerberg return ret; 295f67cf491SMika Westerberg 296f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 297f67cf491SMika Westerberg tb_warn(tb, "PCIe tunnel creation failed\n"); 298f67cf491SMika Westerberg return -EIO; 299f67cf491SMika Westerberg } 300f67cf491SMika Westerberg 301f67cf491SMika Westerberg return 0; 302f67cf491SMika Westerberg } 303f67cf491SMika Westerberg 304f67cf491SMika Westerberg static int icm_fr_add_switch_key(struct tb *tb, struct tb_switch *sw) 305f67cf491SMika Westerberg { 306f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key request; 307f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key_response reply; 308f67cf491SMika Westerberg int ret; 309f67cf491SMika Westerberg 310f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 311f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 312f67cf491SMika Westerberg request.hdr.code = ICM_ADD_DEVICE_KEY; 313f67cf491SMika Westerberg request.connection_id = sw->connection_id; 314f67cf491SMika Westerberg request.connection_key = sw->connection_key; 315f67cf491SMika Westerberg memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 316f67cf491SMika Westerberg 317f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 318f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 319f67cf491SMika Westerberg 1, ICM_TIMEOUT); 320f67cf491SMika Westerberg if (ret) 321f67cf491SMika Westerberg return ret; 322f67cf491SMika Westerberg 323f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 324f67cf491SMika Westerberg tb_warn(tb, "Adding key to switch failed\n"); 325f67cf491SMika Westerberg return -EIO; 326f67cf491SMika Westerberg } 327f67cf491SMika Westerberg 328f67cf491SMika Westerberg return 0; 329f67cf491SMika Westerberg } 330f67cf491SMika Westerberg 331f67cf491SMika Westerberg static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 332f67cf491SMika Westerberg const u8 *challenge, u8 *response) 333f67cf491SMika Westerberg { 334f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device request; 335f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device_response reply; 336f67cf491SMika Westerberg int ret; 337f67cf491SMika Westerberg 338f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 339f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 340f67cf491SMika Westerberg request.hdr.code = ICM_CHALLENGE_DEVICE; 341f67cf491SMika Westerberg request.connection_id = sw->connection_id; 342f67cf491SMika Westerberg request.connection_key = sw->connection_key; 343f67cf491SMika Westerberg memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 344f67cf491SMika Westerberg 345f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 346f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 347f67cf491SMika Westerberg 1, ICM_TIMEOUT); 348f67cf491SMika Westerberg if (ret) 349f67cf491SMika Westerberg return ret; 350f67cf491SMika Westerberg 351f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 352f67cf491SMika Westerberg return -EKEYREJECTED; 353f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 354f67cf491SMika Westerberg return -ENOKEY; 355f67cf491SMika Westerberg 356f67cf491SMika Westerberg memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 357f67cf491SMika Westerberg 358f67cf491SMika Westerberg return 0; 359f67cf491SMika Westerberg } 360f67cf491SMika Westerberg 361d1ff7024SMika Westerberg static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 362d1ff7024SMika Westerberg { 363d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain_response reply; 364d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain request; 365d1ff7024SMika Westerberg int ret; 366d1ff7024SMika Westerberg 367d1ff7024SMika Westerberg memset(&request, 0, sizeof(request)); 368d1ff7024SMika Westerberg request.hdr.code = ICM_APPROVE_XDOMAIN; 369d1ff7024SMika Westerberg request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; 370d1ff7024SMika Westerberg memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 371d1ff7024SMika Westerberg 372d1ff7024SMika Westerberg request.transmit_path = xd->transmit_path; 373d1ff7024SMika Westerberg request.transmit_ring = xd->transmit_ring; 374d1ff7024SMika Westerberg request.receive_path = xd->receive_path; 375d1ff7024SMika Westerberg request.receive_ring = xd->receive_ring; 376d1ff7024SMika Westerberg 377d1ff7024SMika Westerberg memset(&reply, 0, sizeof(reply)); 378d1ff7024SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 379d1ff7024SMika Westerberg 1, ICM_TIMEOUT); 380d1ff7024SMika Westerberg if (ret) 381d1ff7024SMika Westerberg return ret; 382d1ff7024SMika Westerberg 383d1ff7024SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 384d1ff7024SMika Westerberg return -EIO; 385d1ff7024SMika Westerberg 386d1ff7024SMika Westerberg return 0; 387d1ff7024SMika Westerberg } 388d1ff7024SMika Westerberg 389d1ff7024SMika Westerberg static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 390d1ff7024SMika Westerberg { 391d1ff7024SMika Westerberg u8 phy_port; 392d1ff7024SMika Westerberg u8 cmd; 393d1ff7024SMika Westerberg 394d1ff7024SMika Westerberg phy_port = tb_phy_port_from_link(xd->link); 395d1ff7024SMika Westerberg if (phy_port == 0) 396d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PA; 397d1ff7024SMika Westerberg else 398d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PB; 399d1ff7024SMika Westerberg 400d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 1); 401d1ff7024SMika Westerberg usleep_range(10, 50); 402d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 2); 403d1ff7024SMika Westerberg return 0; 404d1ff7024SMika Westerberg } 405d1ff7024SMika Westerberg 406ee487dd2SMika Westerberg static void add_switch(struct tb_switch *parent_sw, u64 route, 407ee487dd2SMika Westerberg const uuid_t *uuid, u8 connection_id, u8 connection_key, 408ee487dd2SMika Westerberg u8 link, u8 depth, enum tb_security_level security_level, 40914862ee3SYehezkel Bernat bool authorized, bool boot) 410ee487dd2SMika Westerberg { 411ee487dd2SMika Westerberg struct tb_switch *sw; 412ee487dd2SMika Westerberg 413ee487dd2SMika Westerberg sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route); 414ee487dd2SMika Westerberg if (!sw) 415ee487dd2SMika Westerberg return; 416ee487dd2SMika Westerberg 417ee487dd2SMika Westerberg sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); 418ee487dd2SMika Westerberg sw->connection_id = connection_id; 419ee487dd2SMika Westerberg sw->connection_key = connection_key; 420ee487dd2SMika Westerberg sw->link = link; 421ee487dd2SMika Westerberg sw->depth = depth; 422ee487dd2SMika Westerberg sw->authorized = authorized; 423ee487dd2SMika Westerberg sw->security_level = security_level; 42414862ee3SYehezkel Bernat sw->boot = boot; 425ee487dd2SMika Westerberg 426ee487dd2SMika Westerberg /* Link the two switches now */ 427ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 428ee487dd2SMika Westerberg tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); 429ee487dd2SMika Westerberg 430ee487dd2SMika Westerberg if (tb_switch_add(sw)) { 431ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 432ee487dd2SMika Westerberg tb_switch_put(sw); 433ee487dd2SMika Westerberg return; 434ee487dd2SMika Westerberg } 435ee487dd2SMika Westerberg } 436ee487dd2SMika Westerberg 437ee487dd2SMika Westerberg static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, 438ee487dd2SMika Westerberg u64 route, u8 connection_id, u8 connection_key, 43914862ee3SYehezkel Bernat u8 link, u8 depth, bool boot) 440ee487dd2SMika Westerberg { 441ee487dd2SMika Westerberg /* Disconnect from parent */ 442ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 443ee487dd2SMika Westerberg /* Re-connect via updated port*/ 444ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 445ee487dd2SMika Westerberg 446ee487dd2SMika Westerberg /* Update with the new addressing information */ 447ee487dd2SMika Westerberg sw->config.route_hi = upper_32_bits(route); 448ee487dd2SMika Westerberg sw->config.route_lo = lower_32_bits(route); 449ee487dd2SMika Westerberg sw->connection_id = connection_id; 450ee487dd2SMika Westerberg sw->connection_key = connection_key; 451ee487dd2SMika Westerberg sw->link = link; 452ee487dd2SMika Westerberg sw->depth = depth; 45314862ee3SYehezkel Bernat sw->boot = boot; 454ee487dd2SMika Westerberg 455ee487dd2SMika Westerberg /* This switch still exists */ 456ee487dd2SMika Westerberg sw->is_unplugged = false; 457ee487dd2SMika Westerberg } 458ee487dd2SMika Westerberg 459f67cf491SMika Westerberg static void remove_switch(struct tb_switch *sw) 460f67cf491SMika Westerberg { 461f67cf491SMika Westerberg struct tb_switch *parent_sw; 462f67cf491SMika Westerberg 463f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 464f67cf491SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 465f67cf491SMika Westerberg tb_switch_remove(sw); 466f67cf491SMika Westerberg } 467f67cf491SMika Westerberg 468ee487dd2SMika Westerberg static void add_xdomain(struct tb_switch *sw, u64 route, 469ee487dd2SMika Westerberg const uuid_t *local_uuid, const uuid_t *remote_uuid, 470ee487dd2SMika Westerberg u8 link, u8 depth) 471ee487dd2SMika Westerberg { 472ee487dd2SMika Westerberg struct tb_xdomain *xd; 473ee487dd2SMika Westerberg 474ee487dd2SMika Westerberg xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid); 475ee487dd2SMika Westerberg if (!xd) 476ee487dd2SMika Westerberg return; 477ee487dd2SMika Westerberg 478ee487dd2SMika Westerberg xd->link = link; 479ee487dd2SMika Westerberg xd->depth = depth; 480ee487dd2SMika Westerberg 481ee487dd2SMika Westerberg tb_port_at(route, sw)->xdomain = xd; 482ee487dd2SMika Westerberg 483ee487dd2SMika Westerberg tb_xdomain_add(xd); 484ee487dd2SMika Westerberg } 485ee487dd2SMika Westerberg 486ee487dd2SMika Westerberg static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link) 487ee487dd2SMika Westerberg { 488ee487dd2SMika Westerberg xd->link = link; 489ee487dd2SMika Westerberg xd->route = route; 490ee487dd2SMika Westerberg xd->is_unplugged = false; 491ee487dd2SMika Westerberg } 492ee487dd2SMika Westerberg 49379fae987SMika Westerberg static void remove_xdomain(struct tb_xdomain *xd) 49479fae987SMika Westerberg { 49579fae987SMika Westerberg struct tb_switch *sw; 49679fae987SMika Westerberg 49779fae987SMika Westerberg sw = tb_to_switch(xd->dev.parent); 49879fae987SMika Westerberg tb_port_at(xd->route, sw)->xdomain = NULL; 49979fae987SMika Westerberg tb_xdomain_remove(xd); 50079fae987SMika Westerberg } 50179fae987SMika Westerberg 502f67cf491SMika Westerberg static void 503f67cf491SMika Westerberg icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 504f67cf491SMika Westerberg { 505f67cf491SMika Westerberg const struct icm_fr_event_device_connected *pkg = 506f67cf491SMika Westerberg (const struct icm_fr_event_device_connected *)hdr; 507ee487dd2SMika Westerberg enum tb_security_level security_level; 508f67cf491SMika Westerberg struct tb_switch *sw, *parent_sw; 509f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 510f67cf491SMika Westerberg bool authorized = false; 51179fae987SMika Westerberg struct tb_xdomain *xd; 512f67cf491SMika Westerberg u8 link, depth; 51314862ee3SYehezkel Bernat bool boot; 514f67cf491SMika Westerberg u64 route; 515f67cf491SMika Westerberg int ret; 516f67cf491SMika Westerberg 517f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 518f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 519f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 520f67cf491SMika Westerberg authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 521ee487dd2SMika Westerberg security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 522ee487dd2SMika Westerberg ICM_FLAGS_SLEVEL_SHIFT; 52314862ee3SYehezkel Bernat boot = pkg->link_info & ICM_LINK_INFO_BOOT; 524f67cf491SMika Westerberg 525cb653eecSMika Westerberg if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 526cb653eecSMika Westerberg tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", 527cb653eecSMika Westerberg link, depth); 528cb653eecSMika Westerberg return; 529cb653eecSMika Westerberg } 530cb653eecSMika Westerberg 531f67cf491SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 532f67cf491SMika Westerberg if (ret) { 533f67cf491SMika Westerberg tb_err(tb, "failed to find route string for switch at %u.%u\n", 534f67cf491SMika Westerberg link, depth); 535f67cf491SMika Westerberg return; 536f67cf491SMika Westerberg } 537f67cf491SMika Westerberg 538f67cf491SMika Westerberg sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 539f67cf491SMika Westerberg if (sw) { 540f67cf491SMika Westerberg u8 phy_port, sw_phy_port; 541f67cf491SMika Westerberg 542f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 543f67cf491SMika Westerberg sw_phy_port = phy_port_from_route(tb_route(sw), sw->depth); 544f67cf491SMika Westerberg phy_port = phy_port_from_route(route, depth); 545f67cf491SMika Westerberg 546f67cf491SMika Westerberg /* 547f67cf491SMika Westerberg * On resume ICM will send us connected events for the 548f67cf491SMika Westerberg * devices that still are present. However, that 549f67cf491SMika Westerberg * information might have changed for example by the 550f67cf491SMika Westerberg * fact that a switch on a dual-link connection might 551f67cf491SMika Westerberg * have been enumerated using the other link now. Make 552f67cf491SMika Westerberg * sure our book keeping matches that. 553f67cf491SMika Westerberg */ 554f67cf491SMika Westerberg if (sw->depth == depth && sw_phy_port == phy_port && 555f67cf491SMika Westerberg !!sw->authorized == authorized) { 556ee487dd2SMika Westerberg update_switch(parent_sw, sw, route, pkg->connection_id, 55714862ee3SYehezkel Bernat pkg->connection_key, link, depth, boot); 558f67cf491SMika Westerberg tb_switch_put(sw); 559f67cf491SMika Westerberg return; 560f67cf491SMika Westerberg } 561f67cf491SMika Westerberg 562f67cf491SMika Westerberg /* 563f67cf491SMika Westerberg * User connected the same switch to another physical 564f67cf491SMika Westerberg * port or to another part of the topology. Remove the 565f67cf491SMika Westerberg * existing switch now before adding the new one. 566f67cf491SMika Westerberg */ 567f67cf491SMika Westerberg remove_switch(sw); 568f67cf491SMika Westerberg tb_switch_put(sw); 569f67cf491SMika Westerberg } 570f67cf491SMika Westerberg 571f67cf491SMika Westerberg /* 572f67cf491SMika Westerberg * If the switch was not found by UUID, look for a switch on 573f67cf491SMika Westerberg * same physical port (taking possible link aggregation into 574f67cf491SMika Westerberg * account) and depth. If we found one it is definitely a stale 575f67cf491SMika Westerberg * one so remove it first. 576f67cf491SMika Westerberg */ 577f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 578f67cf491SMika Westerberg if (!sw) { 579f67cf491SMika Westerberg u8 dual_link; 580f67cf491SMika Westerberg 581f67cf491SMika Westerberg dual_link = dual_link_from_link(link); 582f67cf491SMika Westerberg if (dual_link) 583f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, dual_link, depth); 584f67cf491SMika Westerberg } 585f67cf491SMika Westerberg if (sw) { 586f67cf491SMika Westerberg remove_switch(sw); 587f67cf491SMika Westerberg tb_switch_put(sw); 588f67cf491SMika Westerberg } 589f67cf491SMika Westerberg 59079fae987SMika Westerberg /* Remove existing XDomain connection if found */ 59179fae987SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 59279fae987SMika Westerberg if (xd) { 59379fae987SMika Westerberg remove_xdomain(xd); 59479fae987SMika Westerberg tb_xdomain_put(xd); 59579fae987SMika Westerberg } 59679fae987SMika Westerberg 597f67cf491SMika Westerberg parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); 598f67cf491SMika Westerberg if (!parent_sw) { 599f67cf491SMika Westerberg tb_err(tb, "failed to find parent switch for %u.%u\n", 600f67cf491SMika Westerberg link, depth); 601f67cf491SMika Westerberg return; 602f67cf491SMika Westerberg } 603f67cf491SMika Westerberg 604ee487dd2SMika Westerberg add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id, 605ee487dd2SMika Westerberg pkg->connection_key, link, depth, security_level, 60614862ee3SYehezkel Bernat authorized, boot); 607f67cf491SMika Westerberg 608f67cf491SMika Westerberg tb_switch_put(parent_sw); 609f67cf491SMika Westerberg } 610f67cf491SMika Westerberg 611f67cf491SMika Westerberg static void 612f67cf491SMika Westerberg icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 613f67cf491SMika Westerberg { 614f67cf491SMika Westerberg const struct icm_fr_event_device_disconnected *pkg = 615f67cf491SMika Westerberg (const struct icm_fr_event_device_disconnected *)hdr; 616f67cf491SMika Westerberg struct tb_switch *sw; 617f67cf491SMika Westerberg u8 link, depth; 618f67cf491SMika Westerberg 619f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 620f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 621f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 622f67cf491SMika Westerberg 623f67cf491SMika Westerberg if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { 624f67cf491SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 625f67cf491SMika Westerberg return; 626f67cf491SMika Westerberg } 627f67cf491SMika Westerberg 628f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 629f67cf491SMika Westerberg if (!sw) { 630f67cf491SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 631f67cf491SMika Westerberg depth); 632f67cf491SMika Westerberg return; 633f67cf491SMika Westerberg } 634f67cf491SMika Westerberg 635f67cf491SMika Westerberg remove_switch(sw); 636f67cf491SMika Westerberg tb_switch_put(sw); 637f67cf491SMika Westerberg } 638f67cf491SMika Westerberg 639d1ff7024SMika Westerberg static void 640d1ff7024SMika Westerberg icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 641d1ff7024SMika Westerberg { 642d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_connected *pkg = 643d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_connected *)hdr; 644d1ff7024SMika Westerberg struct tb_xdomain *xd; 645d1ff7024SMika Westerberg struct tb_switch *sw; 646d1ff7024SMika Westerberg u8 link, depth; 647d1ff7024SMika Westerberg bool approved; 648d1ff7024SMika Westerberg u64 route; 649d1ff7024SMika Westerberg 650d1ff7024SMika Westerberg /* 651d1ff7024SMika Westerberg * After NVM upgrade adding root switch device fails because we 652d1ff7024SMika Westerberg * initiated reset. During that time ICM might still send 653d1ff7024SMika Westerberg * XDomain connected message which we ignore here. 654d1ff7024SMika Westerberg */ 655d1ff7024SMika Westerberg if (!tb->root_switch) 656d1ff7024SMika Westerberg return; 657d1ff7024SMika Westerberg 658d1ff7024SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 659d1ff7024SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 660d1ff7024SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 661d1ff7024SMika Westerberg approved = pkg->link_info & ICM_LINK_INFO_APPROVED; 662d1ff7024SMika Westerberg 663d1ff7024SMika Westerberg if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { 664d1ff7024SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 665d1ff7024SMika Westerberg return; 666d1ff7024SMika Westerberg } 667d1ff7024SMika Westerberg 668d1ff7024SMika Westerberg route = get_route(pkg->local_route_hi, pkg->local_route_lo); 669d1ff7024SMika Westerberg 670d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 671d1ff7024SMika Westerberg if (xd) { 672d1ff7024SMika Westerberg u8 xd_phy_port, phy_port; 673d1ff7024SMika Westerberg 674d1ff7024SMika Westerberg xd_phy_port = phy_port_from_route(xd->route, xd->depth); 675d1ff7024SMika Westerberg phy_port = phy_port_from_route(route, depth); 676d1ff7024SMika Westerberg 677d1ff7024SMika Westerberg if (xd->depth == depth && xd_phy_port == phy_port) { 678ee487dd2SMika Westerberg update_xdomain(xd, route, link); 679d1ff7024SMika Westerberg tb_xdomain_put(xd); 680d1ff7024SMika Westerberg return; 681d1ff7024SMika Westerberg } 682d1ff7024SMika Westerberg 683d1ff7024SMika Westerberg /* 684d1ff7024SMika Westerberg * If we find an existing XDomain connection remove it 685d1ff7024SMika Westerberg * now. We need to go through login handshake and 686d1ff7024SMika Westerberg * everything anyway to be able to re-establish the 687d1ff7024SMika Westerberg * connection. 688d1ff7024SMika Westerberg */ 689d1ff7024SMika Westerberg remove_xdomain(xd); 690d1ff7024SMika Westerberg tb_xdomain_put(xd); 691d1ff7024SMika Westerberg } 692d1ff7024SMika Westerberg 693d1ff7024SMika Westerberg /* 694d1ff7024SMika Westerberg * Look if there already exists an XDomain in the same place 695d1ff7024SMika Westerberg * than the new one and in that case remove it because it is 696d1ff7024SMika Westerberg * most likely another host that got disconnected. 697d1ff7024SMika Westerberg */ 698d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 699d1ff7024SMika Westerberg if (!xd) { 700d1ff7024SMika Westerberg u8 dual_link; 701d1ff7024SMika Westerberg 702d1ff7024SMika Westerberg dual_link = dual_link_from_link(link); 703d1ff7024SMika Westerberg if (dual_link) 704d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, dual_link, 705d1ff7024SMika Westerberg depth); 706d1ff7024SMika Westerberg } 707d1ff7024SMika Westerberg if (xd) { 708d1ff7024SMika Westerberg remove_xdomain(xd); 709d1ff7024SMika Westerberg tb_xdomain_put(xd); 710d1ff7024SMika Westerberg } 711d1ff7024SMika Westerberg 712d1ff7024SMika Westerberg /* 713d1ff7024SMika Westerberg * If the user disconnected a switch during suspend and 714d1ff7024SMika Westerberg * connected another host to the same port, remove the switch 715d1ff7024SMika Westerberg * first. 716d1ff7024SMika Westerberg */ 717d1ff7024SMika Westerberg sw = get_switch_at_route(tb->root_switch, route); 718d1ff7024SMika Westerberg if (sw) 719d1ff7024SMika Westerberg remove_switch(sw); 720d1ff7024SMika Westerberg 721d1ff7024SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 722d1ff7024SMika Westerberg if (!sw) { 723d1ff7024SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 724d1ff7024SMika Westerberg depth); 725d1ff7024SMika Westerberg return; 726d1ff7024SMika Westerberg } 727d1ff7024SMika Westerberg 728ee487dd2SMika Westerberg add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link, 729ee487dd2SMika Westerberg depth); 730d1ff7024SMika Westerberg tb_switch_put(sw); 731d1ff7024SMika Westerberg } 732d1ff7024SMika Westerberg 733d1ff7024SMika Westerberg static void 734d1ff7024SMika Westerberg icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 735d1ff7024SMika Westerberg { 736d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_disconnected *pkg = 737d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_disconnected *)hdr; 738d1ff7024SMika Westerberg struct tb_xdomain *xd; 739d1ff7024SMika Westerberg 740d1ff7024SMika Westerberg /* 741d1ff7024SMika Westerberg * If the connection is through one or multiple devices, the 742d1ff7024SMika Westerberg * XDomain device is removed along with them so it is fine if we 743d1ff7024SMika Westerberg * cannot find it here. 744d1ff7024SMika Westerberg */ 745d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 746d1ff7024SMika Westerberg if (xd) { 747d1ff7024SMika Westerberg remove_xdomain(xd); 748d1ff7024SMika Westerberg tb_xdomain_put(xd); 749d1ff7024SMika Westerberg } 750d1ff7024SMika Westerberg } 751d1ff7024SMika Westerberg 752f67cf491SMika Westerberg static struct pci_dev *get_upstream_port(struct pci_dev *pdev) 753f67cf491SMika Westerberg { 754f67cf491SMika Westerberg struct pci_dev *parent; 755f67cf491SMika Westerberg 756f67cf491SMika Westerberg parent = pci_upstream_bridge(pdev); 757f67cf491SMika Westerberg while (parent) { 758f67cf491SMika Westerberg if (!pci_is_pcie(parent)) 759f67cf491SMika Westerberg return NULL; 760f67cf491SMika Westerberg if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) 761f67cf491SMika Westerberg break; 762f67cf491SMika Westerberg parent = pci_upstream_bridge(parent); 763f67cf491SMika Westerberg } 764f67cf491SMika Westerberg 765f67cf491SMika Westerberg if (!parent) 766f67cf491SMika Westerberg return NULL; 767f67cf491SMika Westerberg 768f67cf491SMika Westerberg switch (parent->device) { 769f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: 770f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: 771f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: 772f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: 773f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: 774f67cf491SMika Westerberg return parent; 775f67cf491SMika Westerberg } 776f67cf491SMika Westerberg 777f67cf491SMika Westerberg return NULL; 778f67cf491SMika Westerberg } 779f67cf491SMika Westerberg 780f67cf491SMika Westerberg static bool icm_ar_is_supported(struct tb *tb) 781f67cf491SMika Westerberg { 782f67cf491SMika Westerberg struct pci_dev *upstream_port; 783f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 784f67cf491SMika Westerberg 785f67cf491SMika Westerberg /* 786f67cf491SMika Westerberg * Starting from Alpine Ridge we can use ICM on Apple machines 787f67cf491SMika Westerberg * as well. We just need to reset and re-enable it first. 788f67cf491SMika Westerberg */ 789630b3affSLukas Wunner if (!x86_apple_machine) 790f67cf491SMika Westerberg return true; 791f67cf491SMika Westerberg 792f67cf491SMika Westerberg /* 793f67cf491SMika Westerberg * Find the upstream PCIe port in case we need to do reset 794f67cf491SMika Westerberg * through its vendor specific registers. 795f67cf491SMika Westerberg */ 796f67cf491SMika Westerberg upstream_port = get_upstream_port(tb->nhi->pdev); 797f67cf491SMika Westerberg if (upstream_port) { 798f67cf491SMika Westerberg int cap; 799f67cf491SMika Westerberg 800f67cf491SMika Westerberg cap = pci_find_ext_capability(upstream_port, 801f67cf491SMika Westerberg PCI_EXT_CAP_ID_VNDR); 802f67cf491SMika Westerberg if (cap > 0) { 803f67cf491SMika Westerberg icm->upstream_port = upstream_port; 804f67cf491SMika Westerberg icm->vnd_cap = cap; 805f67cf491SMika Westerberg 806f67cf491SMika Westerberg return true; 807f67cf491SMika Westerberg } 808f67cf491SMika Westerberg } 809f67cf491SMika Westerberg 810f67cf491SMika Westerberg return false; 811f67cf491SMika Westerberg } 812f67cf491SMika Westerberg 813f67cf491SMika Westerberg static int icm_ar_get_mode(struct tb *tb) 814f67cf491SMika Westerberg { 815f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 816e4be8c9bSMika Westerberg int retries = 60; 817f67cf491SMika Westerberg u32 val; 818f67cf491SMika Westerberg 819f67cf491SMika Westerberg do { 820f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 821f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 822f67cf491SMika Westerberg break; 823e4be8c9bSMika Westerberg msleep(50); 824f67cf491SMika Westerberg } while (--retries); 825f67cf491SMika Westerberg 826f67cf491SMika Westerberg if (!retries) { 827f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "ICM firmware not authenticated\n"); 828f67cf491SMika Westerberg return -ENODEV; 829f67cf491SMika Westerberg } 830f67cf491SMika Westerberg 831f67cf491SMika Westerberg return nhi_mailbox_mode(nhi); 832f67cf491SMika Westerberg } 833f67cf491SMika Westerberg 8349aaa3b8bSMika Westerberg static int 8359aaa3b8bSMika Westerberg icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, 8369aaa3b8bSMika Westerberg size_t *nboot_acl) 8379aaa3b8bSMika Westerberg { 8389aaa3b8bSMika Westerberg struct icm_ar_pkg_driver_ready_response reply; 8399aaa3b8bSMika Westerberg struct icm_pkg_driver_ready request = { 8409aaa3b8bSMika Westerberg .hdr.code = ICM_DRIVER_READY, 8419aaa3b8bSMika Westerberg }; 8429aaa3b8bSMika Westerberg int ret; 8439aaa3b8bSMika Westerberg 8449aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 8459aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 8469aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 8479aaa3b8bSMika Westerberg if (ret) 8489aaa3b8bSMika Westerberg return ret; 8499aaa3b8bSMika Westerberg 8509aaa3b8bSMika Westerberg if (security_level) 8519aaa3b8bSMika Westerberg *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK; 8529aaa3b8bSMika Westerberg if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED)) 8539aaa3b8bSMika Westerberg *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >> 8549aaa3b8bSMika Westerberg ICM_AR_INFO_BOOT_ACL_SHIFT; 8559aaa3b8bSMika Westerberg return 0; 8569aaa3b8bSMika Westerberg } 8579aaa3b8bSMika Westerberg 858f67cf491SMika Westerberg static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 859f67cf491SMika Westerberg { 860f67cf491SMika Westerberg struct icm_ar_pkg_get_route_response reply; 861f67cf491SMika Westerberg struct icm_ar_pkg_get_route request = { 862f67cf491SMika Westerberg .hdr = { .code = ICM_GET_ROUTE }, 863f67cf491SMika Westerberg .link_info = depth << ICM_LINK_INFO_DEPTH_SHIFT | link, 864f67cf491SMika Westerberg }; 865f67cf491SMika Westerberg int ret; 866f67cf491SMika Westerberg 867f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 868f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 869f67cf491SMika Westerberg 1, ICM_TIMEOUT); 870f67cf491SMika Westerberg if (ret) 871f67cf491SMika Westerberg return ret; 872f67cf491SMika Westerberg 873f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 874f67cf491SMika Westerberg return -EIO; 875f67cf491SMika Westerberg 876f67cf491SMika Westerberg *route = get_route(reply.route_hi, reply.route_lo); 877f67cf491SMika Westerberg return 0; 878f67cf491SMika Westerberg } 879f67cf491SMika Westerberg 8809aaa3b8bSMika Westerberg static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) 8819aaa3b8bSMika Westerberg { 8829aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 8839aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 8849aaa3b8bSMika Westerberg .hdr = { .code = ICM_PREBOOT_ACL }, 8859aaa3b8bSMika Westerberg }; 8869aaa3b8bSMika Westerberg int ret, i; 8879aaa3b8bSMika Westerberg 8889aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 8899aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 8909aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 8919aaa3b8bSMika Westerberg if (ret) 8929aaa3b8bSMika Westerberg return ret; 8939aaa3b8bSMika Westerberg 8949aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 8959aaa3b8bSMika Westerberg return -EIO; 8969aaa3b8bSMika Westerberg 8979aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 8989aaa3b8bSMika Westerberg u32 *uuid = (u32 *)&uuids[i]; 8999aaa3b8bSMika Westerberg 9009aaa3b8bSMika Westerberg uuid[0] = reply.acl[i].uuid_lo; 9019aaa3b8bSMika Westerberg uuid[1] = reply.acl[i].uuid_hi; 9029aaa3b8bSMika Westerberg 9039aaa3b8bSMika Westerberg if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) { 9049aaa3b8bSMika Westerberg /* Map empty entries to null UUID */ 9059aaa3b8bSMika Westerberg uuid[0] = 0; 9069aaa3b8bSMika Westerberg uuid[1] = 0; 9079aaa3b8bSMika Westerberg } else { 9089aaa3b8bSMika Westerberg /* Upper two DWs are always one's */ 9099aaa3b8bSMika Westerberg uuid[2] = 0xffffffff; 9109aaa3b8bSMika Westerberg uuid[3] = 0xffffffff; 9119aaa3b8bSMika Westerberg } 9129aaa3b8bSMika Westerberg } 9139aaa3b8bSMika Westerberg 9149aaa3b8bSMika Westerberg return ret; 9159aaa3b8bSMika Westerberg } 9169aaa3b8bSMika Westerberg 9179aaa3b8bSMika Westerberg static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, 9189aaa3b8bSMika Westerberg size_t nuuids) 9199aaa3b8bSMika Westerberg { 9209aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 9219aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 9229aaa3b8bSMika Westerberg .hdr = { 9239aaa3b8bSMika Westerberg .code = ICM_PREBOOT_ACL, 9249aaa3b8bSMika Westerberg .flags = ICM_FLAGS_WRITE, 9259aaa3b8bSMika Westerberg }, 9269aaa3b8bSMika Westerberg }; 9279aaa3b8bSMika Westerberg int ret, i; 9289aaa3b8bSMika Westerberg 9299aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 9309aaa3b8bSMika Westerberg const u32 *uuid = (const u32 *)&uuids[i]; 9319aaa3b8bSMika Westerberg 9329aaa3b8bSMika Westerberg if (uuid_is_null(&uuids[i])) { 9339aaa3b8bSMika Westerberg /* 9349aaa3b8bSMika Westerberg * Map null UUID to the empty (all one) entries 9359aaa3b8bSMika Westerberg * for ICM. 9369aaa3b8bSMika Westerberg */ 9379aaa3b8bSMika Westerberg request.acl[i].uuid_lo = 0xffffffff; 9389aaa3b8bSMika Westerberg request.acl[i].uuid_hi = 0xffffffff; 9399aaa3b8bSMika Westerberg } else { 9409aaa3b8bSMika Westerberg /* Two high DWs need to be set to all one */ 9419aaa3b8bSMika Westerberg if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff) 9429aaa3b8bSMika Westerberg return -EINVAL; 9439aaa3b8bSMika Westerberg 9449aaa3b8bSMika Westerberg request.acl[i].uuid_lo = uuid[0]; 9459aaa3b8bSMika Westerberg request.acl[i].uuid_hi = uuid[1]; 9469aaa3b8bSMika Westerberg } 9479aaa3b8bSMika Westerberg } 9489aaa3b8bSMika Westerberg 9499aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 9509aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 9519aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 9529aaa3b8bSMika Westerberg if (ret) 9539aaa3b8bSMika Westerberg return ret; 9549aaa3b8bSMika Westerberg 9559aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 9569aaa3b8bSMika Westerberg return -EIO; 9579aaa3b8bSMika Westerberg 9589aaa3b8bSMika Westerberg return 0; 9599aaa3b8bSMika Westerberg } 9609aaa3b8bSMika Westerberg 961f67cf491SMika Westerberg static void icm_handle_notification(struct work_struct *work) 962f67cf491SMika Westerberg { 963f67cf491SMika Westerberg struct icm_notification *n = container_of(work, typeof(*n), work); 964f67cf491SMika Westerberg struct tb *tb = n->tb; 965f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 966f67cf491SMika Westerberg 967f67cf491SMika Westerberg mutex_lock(&tb->lock); 968f67cf491SMika Westerberg 969f67cf491SMika Westerberg switch (n->pkg->code) { 970f67cf491SMika Westerberg case ICM_EVENT_DEVICE_CONNECTED: 971f67cf491SMika Westerberg icm->device_connected(tb, n->pkg); 972f67cf491SMika Westerberg break; 973f67cf491SMika Westerberg case ICM_EVENT_DEVICE_DISCONNECTED: 974f67cf491SMika Westerberg icm->device_disconnected(tb, n->pkg); 975f67cf491SMika Westerberg break; 976d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_CONNECTED: 977d1ff7024SMika Westerberg icm->xdomain_connected(tb, n->pkg); 978d1ff7024SMika Westerberg break; 979d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_DISCONNECTED: 980d1ff7024SMika Westerberg icm->xdomain_disconnected(tb, n->pkg); 981d1ff7024SMika Westerberg break; 982f67cf491SMika Westerberg } 983f67cf491SMika Westerberg 984f67cf491SMika Westerberg mutex_unlock(&tb->lock); 985f67cf491SMika Westerberg 986f67cf491SMika Westerberg kfree(n->pkg); 987f67cf491SMika Westerberg kfree(n); 988f67cf491SMika Westerberg } 989f67cf491SMika Westerberg 990f67cf491SMika Westerberg static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, 991f67cf491SMika Westerberg const void *buf, size_t size) 992f67cf491SMika Westerberg { 993f67cf491SMika Westerberg struct icm_notification *n; 994f67cf491SMika Westerberg 995f67cf491SMika Westerberg n = kmalloc(sizeof(*n), GFP_KERNEL); 996f67cf491SMika Westerberg if (!n) 997f67cf491SMika Westerberg return; 998f67cf491SMika Westerberg 999f67cf491SMika Westerberg INIT_WORK(&n->work, icm_handle_notification); 1000f67cf491SMika Westerberg n->pkg = kmemdup(buf, size, GFP_KERNEL); 1001f67cf491SMika Westerberg n->tb = tb; 1002f67cf491SMika Westerberg 1003f67cf491SMika Westerberg queue_work(tb->wq, &n->work); 1004f67cf491SMika Westerberg } 1005f67cf491SMika Westerberg 1006f67cf491SMika Westerberg static int 10079aaa3b8bSMika Westerberg __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, 10089aaa3b8bSMika Westerberg size_t *nboot_acl) 1009f67cf491SMika Westerberg { 10103080e197SMika Westerberg struct icm *icm = tb_priv(tb); 101144b51bbbSMika Westerberg unsigned int retries = 50; 1012f67cf491SMika Westerberg int ret; 1013f67cf491SMika Westerberg 10149aaa3b8bSMika Westerberg ret = icm->driver_ready(tb, security_level, nboot_acl); 10153080e197SMika Westerberg if (ret) { 10163080e197SMika Westerberg tb_err(tb, "failed to send driver ready to ICM\n"); 1017f67cf491SMika Westerberg return ret; 10183080e197SMika Westerberg } 1019f67cf491SMika Westerberg 1020f67cf491SMika Westerberg /* 1021f67cf491SMika Westerberg * Hold on here until the switch config space is accessible so 1022f67cf491SMika Westerberg * that we can read root switch config successfully. 1023f67cf491SMika Westerberg */ 1024f67cf491SMika Westerberg do { 1025f67cf491SMika Westerberg struct tb_cfg_result res; 1026f67cf491SMika Westerberg u32 tmp; 1027f67cf491SMika Westerberg 1028f67cf491SMika Westerberg res = tb_cfg_read_raw(tb->ctl, &tmp, 0, 0, TB_CFG_SWITCH, 1029f67cf491SMika Westerberg 0, 1, 100); 1030f67cf491SMika Westerberg if (!res.err) 1031f67cf491SMika Westerberg return 0; 1032f67cf491SMika Westerberg 1033f67cf491SMika Westerberg msleep(50); 1034f67cf491SMika Westerberg } while (--retries); 1035f67cf491SMika Westerberg 103644b51bbbSMika Westerberg tb_err(tb, "failed to read root switch config space, giving up\n"); 1037f67cf491SMika Westerberg return -ETIMEDOUT; 1038f67cf491SMika Westerberg } 1039f67cf491SMika Westerberg 1040f67cf491SMika Westerberg static int pci2cio_wait_completion(struct icm *icm, unsigned long timeout_msec) 1041f67cf491SMika Westerberg { 1042f67cf491SMika Westerberg unsigned long end = jiffies + msecs_to_jiffies(timeout_msec); 1043f67cf491SMika Westerberg u32 cmd; 1044f67cf491SMika Westerberg 1045f67cf491SMika Westerberg do { 1046f67cf491SMika Westerberg pci_read_config_dword(icm->upstream_port, 1047f67cf491SMika Westerberg icm->vnd_cap + PCIE2CIO_CMD, &cmd); 1048f67cf491SMika Westerberg if (!(cmd & PCIE2CIO_CMD_START)) { 1049f67cf491SMika Westerberg if (cmd & PCIE2CIO_CMD_TIMEOUT) 1050f67cf491SMika Westerberg break; 1051f67cf491SMika Westerberg return 0; 1052f67cf491SMika Westerberg } 1053f67cf491SMika Westerberg 1054f67cf491SMika Westerberg msleep(50); 1055f67cf491SMika Westerberg } while (time_before(jiffies, end)); 1056f67cf491SMika Westerberg 1057f67cf491SMika Westerberg return -ETIMEDOUT; 1058f67cf491SMika Westerberg } 1059f67cf491SMika Westerberg 1060f67cf491SMika Westerberg static int pcie2cio_read(struct icm *icm, enum tb_cfg_space cs, 1061f67cf491SMika Westerberg unsigned int port, unsigned int index, u32 *data) 1062f67cf491SMika Westerberg { 1063f67cf491SMika Westerberg struct pci_dev *pdev = icm->upstream_port; 1064f67cf491SMika Westerberg int ret, vnd_cap = icm->vnd_cap; 1065f67cf491SMika Westerberg u32 cmd; 1066f67cf491SMika Westerberg 1067f67cf491SMika Westerberg cmd = index; 1068f67cf491SMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 1069f67cf491SMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 1070f67cf491SMika Westerberg cmd |= PCIE2CIO_CMD_START; 1071f67cf491SMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 1072f67cf491SMika Westerberg 1073f67cf491SMika Westerberg ret = pci2cio_wait_completion(icm, 5000); 1074f67cf491SMika Westerberg if (ret) 1075f67cf491SMika Westerberg return ret; 1076f67cf491SMika Westerberg 1077f67cf491SMika Westerberg pci_read_config_dword(pdev, vnd_cap + PCIE2CIO_RDDATA, data); 1078f67cf491SMika Westerberg return 0; 1079f67cf491SMika Westerberg } 1080f67cf491SMika Westerberg 1081f67cf491SMika Westerberg static int pcie2cio_write(struct icm *icm, enum tb_cfg_space cs, 1082f67cf491SMika Westerberg unsigned int port, unsigned int index, u32 data) 1083f67cf491SMika Westerberg { 1084f67cf491SMika Westerberg struct pci_dev *pdev = icm->upstream_port; 1085f67cf491SMika Westerberg int vnd_cap = icm->vnd_cap; 1086f67cf491SMika Westerberg u32 cmd; 1087f67cf491SMika Westerberg 1088f67cf491SMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_WRDATA, data); 1089f67cf491SMika Westerberg 1090f67cf491SMika Westerberg cmd = index; 1091f67cf491SMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 1092f67cf491SMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 1093f67cf491SMika Westerberg cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START; 1094f67cf491SMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 1095f67cf491SMika Westerberg 1096f67cf491SMika Westerberg return pci2cio_wait_completion(icm, 5000); 1097f67cf491SMika Westerberg } 1098f67cf491SMika Westerberg 1099f67cf491SMika Westerberg static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) 1100f67cf491SMika Westerberg { 1101f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1102f67cf491SMika Westerberg u32 val; 1103f67cf491SMika Westerberg 1104f67cf491SMika Westerberg /* Put ARC to wait for CIO reset event to happen */ 1105f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1106f67cf491SMika Westerberg val |= REG_FW_STS_CIO_RESET_REQ; 1107f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1108f67cf491SMika Westerberg 1109f67cf491SMika Westerberg /* Re-start ARC */ 1110f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1111f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_INVERT; 1112f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_CPU; 1113f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1114f67cf491SMika Westerberg 1115f67cf491SMika Westerberg /* Trigger CIO reset now */ 1116f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_SWITCH, 0, 0x50, BIT(9)); 1117f67cf491SMika Westerberg } 1118f67cf491SMika Westerberg 1119f67cf491SMika Westerberg static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) 1120f67cf491SMika Westerberg { 1121f67cf491SMika Westerberg unsigned int retries = 10; 1122f67cf491SMika Westerberg int ret; 1123f67cf491SMika Westerberg u32 val; 1124f67cf491SMika Westerberg 1125f67cf491SMika Westerberg /* Check if the ICM firmware is already running */ 1126f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1127f67cf491SMika Westerberg if (val & REG_FW_STS_ICM_EN) 1128f67cf491SMika Westerberg return 0; 1129f67cf491SMika Westerberg 1130f67cf491SMika Westerberg dev_info(&nhi->pdev->dev, "starting ICM firmware\n"); 1131f67cf491SMika Westerberg 1132f67cf491SMika Westerberg ret = icm_firmware_reset(tb, nhi); 1133f67cf491SMika Westerberg if (ret) 1134f67cf491SMika Westerberg return ret; 1135f67cf491SMika Westerberg 1136f67cf491SMika Westerberg /* Wait until the ICM firmware tells us it is up and running */ 1137f67cf491SMika Westerberg do { 1138f67cf491SMika Westerberg /* Check that the ICM firmware is running */ 1139f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1140f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1141f67cf491SMika Westerberg return 0; 1142f67cf491SMika Westerberg 1143f67cf491SMika Westerberg msleep(300); 1144f67cf491SMika Westerberg } while (--retries); 1145f67cf491SMika Westerberg 1146f67cf491SMika Westerberg return -ETIMEDOUT; 1147f67cf491SMika Westerberg } 1148f67cf491SMika Westerberg 1149f67cf491SMika Westerberg static int icm_reset_phy_port(struct tb *tb, int phy_port) 1150f67cf491SMika Westerberg { 1151f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1152f67cf491SMika Westerberg u32 state0, state1; 1153f67cf491SMika Westerberg int port0, port1; 1154f67cf491SMika Westerberg u32 val0, val1; 1155f67cf491SMika Westerberg int ret; 1156f67cf491SMika Westerberg 1157f67cf491SMika Westerberg if (!icm->upstream_port) 1158f67cf491SMika Westerberg return 0; 1159f67cf491SMika Westerberg 1160f67cf491SMika Westerberg if (phy_port) { 1161f67cf491SMika Westerberg port0 = 3; 1162f67cf491SMika Westerberg port1 = 4; 1163f67cf491SMika Westerberg } else { 1164f67cf491SMika Westerberg port0 = 1; 1165f67cf491SMika Westerberg port1 = 2; 1166f67cf491SMika Westerberg } 1167f67cf491SMika Westerberg 1168f67cf491SMika Westerberg /* 1169f67cf491SMika Westerberg * Read link status of both null ports belonging to a single 1170f67cf491SMika Westerberg * physical port. 1171f67cf491SMika Westerberg */ 1172f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1173f67cf491SMika Westerberg if (ret) 1174f67cf491SMika Westerberg return ret; 1175f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1176f67cf491SMika Westerberg if (ret) 1177f67cf491SMika Westerberg return ret; 1178f67cf491SMika Westerberg 1179f67cf491SMika Westerberg state0 = val0 & PHY_PORT_CS1_LINK_STATE_MASK; 1180f67cf491SMika Westerberg state0 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1181f67cf491SMika Westerberg state1 = val1 & PHY_PORT_CS1_LINK_STATE_MASK; 1182f67cf491SMika Westerberg state1 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1183f67cf491SMika Westerberg 1184f67cf491SMika Westerberg /* If they are both up we need to reset them now */ 1185f67cf491SMika Westerberg if (state0 != TB_PORT_UP || state1 != TB_PORT_UP) 1186f67cf491SMika Westerberg return 0; 1187f67cf491SMika Westerberg 1188f67cf491SMika Westerberg val0 |= PHY_PORT_CS1_LINK_DISABLE; 1189f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1190f67cf491SMika Westerberg if (ret) 1191f67cf491SMika Westerberg return ret; 1192f67cf491SMika Westerberg 1193f67cf491SMika Westerberg val1 |= PHY_PORT_CS1_LINK_DISABLE; 1194f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1195f67cf491SMika Westerberg if (ret) 1196f67cf491SMika Westerberg return ret; 1197f67cf491SMika Westerberg 1198f67cf491SMika Westerberg /* Wait a bit and then re-enable both ports */ 1199f67cf491SMika Westerberg usleep_range(10, 100); 1200f67cf491SMika Westerberg 1201f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1202f67cf491SMika Westerberg if (ret) 1203f67cf491SMika Westerberg return ret; 1204f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1205f67cf491SMika Westerberg if (ret) 1206f67cf491SMika Westerberg return ret; 1207f67cf491SMika Westerberg 1208f67cf491SMika Westerberg val0 &= ~PHY_PORT_CS1_LINK_DISABLE; 1209f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1210f67cf491SMika Westerberg if (ret) 1211f67cf491SMika Westerberg return ret; 1212f67cf491SMika Westerberg 1213f67cf491SMika Westerberg val1 &= ~PHY_PORT_CS1_LINK_DISABLE; 1214f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1215f67cf491SMika Westerberg } 1216f67cf491SMika Westerberg 1217f67cf491SMika Westerberg static int icm_firmware_init(struct tb *tb) 1218f67cf491SMika Westerberg { 1219f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1220f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1221f67cf491SMika Westerberg int ret; 1222f67cf491SMika Westerberg 1223f67cf491SMika Westerberg ret = icm_firmware_start(tb, nhi); 1224f67cf491SMika Westerberg if (ret) { 1225f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "could not start ICM firmware\n"); 1226f67cf491SMika Westerberg return ret; 1227f67cf491SMika Westerberg } 1228f67cf491SMika Westerberg 1229f67cf491SMika Westerberg if (icm->get_mode) { 1230f67cf491SMika Westerberg ret = icm->get_mode(tb); 1231f67cf491SMika Westerberg 1232f67cf491SMika Westerberg switch (ret) { 1233e6b245ccSMika Westerberg case NHI_FW_SAFE_MODE: 1234e6b245ccSMika Westerberg icm->safe_mode = true; 1235e6b245ccSMika Westerberg break; 1236e6b245ccSMika Westerberg 1237f67cf491SMika Westerberg case NHI_FW_CM_MODE: 1238f67cf491SMika Westerberg /* Ask ICM to accept all Thunderbolt devices */ 1239f67cf491SMika Westerberg nhi_mailbox_cmd(nhi, NHI_MAILBOX_ALLOW_ALL_DEVS, 0); 1240f67cf491SMika Westerberg break; 1241f67cf491SMika Westerberg 1242f67cf491SMika Westerberg default: 1243e4be8c9bSMika Westerberg if (ret < 0) 1244e4be8c9bSMika Westerberg return ret; 1245e4be8c9bSMika Westerberg 1246f67cf491SMika Westerberg tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); 1247f67cf491SMika Westerberg return -ENODEV; 1248f67cf491SMika Westerberg } 1249f67cf491SMika Westerberg } 1250f67cf491SMika Westerberg 1251f67cf491SMika Westerberg /* 1252f67cf491SMika Westerberg * Reset both physical ports if there is anything connected to 1253f67cf491SMika Westerberg * them already. 1254f67cf491SMika Westerberg */ 1255f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 0); 1256f67cf491SMika Westerberg if (ret) 1257f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port0\n"); 1258f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 1); 1259f67cf491SMika Westerberg if (ret) 1260f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port1\n"); 1261f67cf491SMika Westerberg 1262f67cf491SMika Westerberg return 0; 1263f67cf491SMika Westerberg } 1264f67cf491SMika Westerberg 1265f67cf491SMika Westerberg static int icm_driver_ready(struct tb *tb) 1266f67cf491SMika Westerberg { 1267e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1268f67cf491SMika Westerberg int ret; 1269f67cf491SMika Westerberg 1270f67cf491SMika Westerberg ret = icm_firmware_init(tb); 1271f67cf491SMika Westerberg if (ret) 1272f67cf491SMika Westerberg return ret; 1273f67cf491SMika Westerberg 1274e6b245ccSMika Westerberg if (icm->safe_mode) { 1275e6b245ccSMika Westerberg tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); 1276e6b245ccSMika Westerberg tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); 1277e6b245ccSMika Westerberg tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); 1278e6b245ccSMika Westerberg return 0; 1279e6b245ccSMika Westerberg } 1280e6b245ccSMika Westerberg 12819aaa3b8bSMika Westerberg ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl); 12829aaa3b8bSMika Westerberg if (ret) 12839aaa3b8bSMika Westerberg return ret; 12849aaa3b8bSMika Westerberg 12859aaa3b8bSMika Westerberg /* 12869aaa3b8bSMika Westerberg * Make sure the number of supported preboot ACL matches what we 12879aaa3b8bSMika Westerberg * expect or disable the whole feature. 12889aaa3b8bSMika Westerberg */ 12899aaa3b8bSMika Westerberg if (tb->nboot_acl > icm->max_boot_acl) 12909aaa3b8bSMika Westerberg tb->nboot_acl = 0; 12919aaa3b8bSMika Westerberg 12929aaa3b8bSMika Westerberg return 0; 1293f67cf491SMika Westerberg } 1294f67cf491SMika Westerberg 1295f67cf491SMika Westerberg static int icm_suspend(struct tb *tb) 1296f67cf491SMika Westerberg { 1297a684c5b1SRafael J. Wysocki int ret; 1298a684c5b1SRafael J. Wysocki 1299a684c5b1SRafael J. Wysocki ret = nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_SAVE_DEVS, 0); 1300a684c5b1SRafael J. Wysocki if (ret) 1301a684c5b1SRafael J. Wysocki tb_info(tb, "Ignoring mailbox command error (%d) in %s\n", 1302a684c5b1SRafael J. Wysocki ret, __func__); 1303a684c5b1SRafael J. Wysocki 1304a684c5b1SRafael J. Wysocki return 0; 1305f67cf491SMika Westerberg } 1306f67cf491SMika Westerberg 1307f67cf491SMika Westerberg /* 1308f67cf491SMika Westerberg * Mark all switches (except root switch) below this one unplugged. ICM 1309f67cf491SMika Westerberg * firmware will send us an updated list of switches after we have send 1310f67cf491SMika Westerberg * it driver ready command. If a switch is not in that list it will be 1311f67cf491SMika Westerberg * removed when we perform rescan. 1312f67cf491SMika Westerberg */ 1313f67cf491SMika Westerberg static void icm_unplug_children(struct tb_switch *sw) 1314f67cf491SMika Westerberg { 1315f67cf491SMika Westerberg unsigned int i; 1316f67cf491SMika Westerberg 1317f67cf491SMika Westerberg if (tb_route(sw)) 1318f67cf491SMika Westerberg sw->is_unplugged = true; 1319f67cf491SMika Westerberg 1320f67cf491SMika Westerberg for (i = 1; i <= sw->config.max_port_number; i++) { 1321f67cf491SMika Westerberg struct tb_port *port = &sw->ports[i]; 1322f67cf491SMika Westerberg 1323f67cf491SMika Westerberg if (tb_is_upstream_port(port)) 1324f67cf491SMika Westerberg continue; 1325d1ff7024SMika Westerberg if (port->xdomain) { 1326d1ff7024SMika Westerberg port->xdomain->is_unplugged = true; 1327d1ff7024SMika Westerberg continue; 1328d1ff7024SMika Westerberg } 1329f67cf491SMika Westerberg if (!port->remote) 1330f67cf491SMika Westerberg continue; 1331f67cf491SMika Westerberg 1332f67cf491SMika Westerberg icm_unplug_children(port->remote->sw); 1333f67cf491SMika Westerberg } 1334f67cf491SMika Westerberg } 1335f67cf491SMika Westerberg 1336f67cf491SMika Westerberg static void icm_free_unplugged_children(struct tb_switch *sw) 1337f67cf491SMika Westerberg { 1338f67cf491SMika Westerberg unsigned int i; 1339f67cf491SMika Westerberg 1340f67cf491SMika Westerberg for (i = 1; i <= sw->config.max_port_number; i++) { 1341f67cf491SMika Westerberg struct tb_port *port = &sw->ports[i]; 1342f67cf491SMika Westerberg 1343f67cf491SMika Westerberg if (tb_is_upstream_port(port)) 1344f67cf491SMika Westerberg continue; 1345d1ff7024SMika Westerberg 1346d1ff7024SMika Westerberg if (port->xdomain && port->xdomain->is_unplugged) { 1347d1ff7024SMika Westerberg tb_xdomain_remove(port->xdomain); 1348d1ff7024SMika Westerberg port->xdomain = NULL; 1349d1ff7024SMika Westerberg continue; 1350d1ff7024SMika Westerberg } 1351d1ff7024SMika Westerberg 1352f67cf491SMika Westerberg if (!port->remote) 1353f67cf491SMika Westerberg continue; 1354f67cf491SMika Westerberg 1355f67cf491SMika Westerberg if (port->remote->sw->is_unplugged) { 1356f67cf491SMika Westerberg tb_switch_remove(port->remote->sw); 1357f67cf491SMika Westerberg port->remote = NULL; 1358f67cf491SMika Westerberg } else { 1359f67cf491SMika Westerberg icm_free_unplugged_children(port->remote->sw); 1360f67cf491SMika Westerberg } 1361f67cf491SMika Westerberg } 1362f67cf491SMika Westerberg } 1363f67cf491SMika Westerberg 1364f67cf491SMika Westerberg static void icm_rescan_work(struct work_struct *work) 1365f67cf491SMika Westerberg { 1366f67cf491SMika Westerberg struct icm *icm = container_of(work, struct icm, rescan_work.work); 1367f67cf491SMika Westerberg struct tb *tb = icm_to_tb(icm); 1368f67cf491SMika Westerberg 1369f67cf491SMika Westerberg mutex_lock(&tb->lock); 1370f67cf491SMika Westerberg if (tb->root_switch) 1371f67cf491SMika Westerberg icm_free_unplugged_children(tb->root_switch); 1372f67cf491SMika Westerberg mutex_unlock(&tb->lock); 1373f67cf491SMika Westerberg } 1374f67cf491SMika Westerberg 1375f67cf491SMika Westerberg static void icm_complete(struct tb *tb) 1376f67cf491SMika Westerberg { 1377f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1378f67cf491SMika Westerberg 1379f67cf491SMika Westerberg if (tb->nhi->going_away) 1380f67cf491SMika Westerberg return; 1381f67cf491SMika Westerberg 1382f67cf491SMika Westerberg icm_unplug_children(tb->root_switch); 1383f67cf491SMika Westerberg 1384f67cf491SMika Westerberg /* 1385f67cf491SMika Westerberg * Now all existing children should be resumed, start events 1386f67cf491SMika Westerberg * from ICM to get updated status. 1387f67cf491SMika Westerberg */ 13889aaa3b8bSMika Westerberg __icm_driver_ready(tb, NULL, NULL); 1389f67cf491SMika Westerberg 1390f67cf491SMika Westerberg /* 1391f67cf491SMika Westerberg * We do not get notifications of devices that have been 1392f67cf491SMika Westerberg * unplugged during suspend so schedule rescan to clean them up 1393f67cf491SMika Westerberg * if any. 1394f67cf491SMika Westerberg */ 1395f67cf491SMika Westerberg queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500)); 1396f67cf491SMika Westerberg } 1397f67cf491SMika Westerberg 1398f67cf491SMika Westerberg static int icm_start(struct tb *tb) 1399f67cf491SMika Westerberg { 1400e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1401f67cf491SMika Westerberg int ret; 1402f67cf491SMika Westerberg 1403e6b245ccSMika Westerberg if (icm->safe_mode) 1404e6b245ccSMika Westerberg tb->root_switch = tb_switch_alloc_safe_mode(tb, &tb->dev, 0); 1405e6b245ccSMika Westerberg else 1406f67cf491SMika Westerberg tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); 1407f67cf491SMika Westerberg if (!tb->root_switch) 1408f67cf491SMika Westerberg return -ENODEV; 1409f67cf491SMika Westerberg 1410e6b245ccSMika Westerberg /* 1411e6b245ccSMika Westerberg * NVM upgrade has not been tested on Apple systems and they 1412e6b245ccSMika Westerberg * don't provide images publicly either. To be on the safe side 1413e6b245ccSMika Westerberg * prevent root switch NVM upgrade on Macs for now. 1414e6b245ccSMika Westerberg */ 1415630b3affSLukas Wunner tb->root_switch->no_nvm_upgrade = x86_apple_machine; 1416e6b245ccSMika Westerberg 1417f67cf491SMika Westerberg ret = tb_switch_add(tb->root_switch); 1418d1ff7024SMika Westerberg if (ret) { 1419f67cf491SMika Westerberg tb_switch_put(tb->root_switch); 1420d1ff7024SMika Westerberg tb->root_switch = NULL; 1421d1ff7024SMika Westerberg } 1422f67cf491SMika Westerberg 1423f67cf491SMika Westerberg return ret; 1424f67cf491SMika Westerberg } 1425f67cf491SMika Westerberg 1426f67cf491SMika Westerberg static void icm_stop(struct tb *tb) 1427f67cf491SMika Westerberg { 1428f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1429f67cf491SMika Westerberg 1430f67cf491SMika Westerberg cancel_delayed_work(&icm->rescan_work); 1431f67cf491SMika Westerberg tb_switch_remove(tb->root_switch); 1432f67cf491SMika Westerberg tb->root_switch = NULL; 1433f67cf491SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 1434f67cf491SMika Westerberg } 1435f67cf491SMika Westerberg 1436e6b245ccSMika Westerberg static int icm_disconnect_pcie_paths(struct tb *tb) 1437e6b245ccSMika Westerberg { 1438e6b245ccSMika Westerberg return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); 1439e6b245ccSMika Westerberg } 1440e6b245ccSMika Westerberg 14419aaa3b8bSMika Westerberg /* Falcon Ridge */ 1442f67cf491SMika Westerberg static const struct tb_cm_ops icm_fr_ops = { 1443f67cf491SMika Westerberg .driver_ready = icm_driver_ready, 1444f67cf491SMika Westerberg .start = icm_start, 1445f67cf491SMika Westerberg .stop = icm_stop, 1446f67cf491SMika Westerberg .suspend = icm_suspend, 1447f67cf491SMika Westerberg .complete = icm_complete, 1448f67cf491SMika Westerberg .handle_event = icm_handle_event, 1449f67cf491SMika Westerberg .approve_switch = icm_fr_approve_switch, 1450f67cf491SMika Westerberg .add_switch_key = icm_fr_add_switch_key, 1451f67cf491SMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 1452e6b245ccSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 1453d1ff7024SMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 1454d1ff7024SMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 1455f67cf491SMika Westerberg }; 1456f67cf491SMika Westerberg 14579aaa3b8bSMika Westerberg /* Alpine Ridge */ 14589aaa3b8bSMika Westerberg static const struct tb_cm_ops icm_ar_ops = { 14599aaa3b8bSMika Westerberg .driver_ready = icm_driver_ready, 14609aaa3b8bSMika Westerberg .start = icm_start, 14619aaa3b8bSMika Westerberg .stop = icm_stop, 14629aaa3b8bSMika Westerberg .suspend = icm_suspend, 14639aaa3b8bSMika Westerberg .complete = icm_complete, 14649aaa3b8bSMika Westerberg .handle_event = icm_handle_event, 14659aaa3b8bSMika Westerberg .get_boot_acl = icm_ar_get_boot_acl, 14669aaa3b8bSMika Westerberg .set_boot_acl = icm_ar_set_boot_acl, 14679aaa3b8bSMika Westerberg .approve_switch = icm_fr_approve_switch, 14689aaa3b8bSMika Westerberg .add_switch_key = icm_fr_add_switch_key, 14699aaa3b8bSMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 14709aaa3b8bSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 14719aaa3b8bSMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 14729aaa3b8bSMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 14739aaa3b8bSMika Westerberg }; 14749aaa3b8bSMika Westerberg 1475f67cf491SMika Westerberg struct tb *icm_probe(struct tb_nhi *nhi) 1476f67cf491SMika Westerberg { 1477f67cf491SMika Westerberg struct icm *icm; 1478f67cf491SMika Westerberg struct tb *tb; 1479f67cf491SMika Westerberg 1480f67cf491SMika Westerberg tb = tb_domain_alloc(nhi, sizeof(struct icm)); 1481f67cf491SMika Westerberg if (!tb) 1482f67cf491SMika Westerberg return NULL; 1483f67cf491SMika Westerberg 1484f67cf491SMika Westerberg icm = tb_priv(tb); 1485f67cf491SMika Westerberg INIT_DELAYED_WORK(&icm->rescan_work, icm_rescan_work); 1486f67cf491SMika Westerberg mutex_init(&icm->request_lock); 1487f67cf491SMika Westerberg 1488f67cf491SMika Westerberg switch (nhi->pdev->device) { 1489f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: 1490f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: 1491f67cf491SMika Westerberg icm->is_supported = icm_fr_is_supported; 1492f67cf491SMika Westerberg icm->get_route = icm_fr_get_route; 14933080e197SMika Westerberg icm->driver_ready = icm_fr_driver_ready; 1494f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 1495f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 1496d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 1497d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 1498f67cf491SMika Westerberg tb->cm_ops = &icm_fr_ops; 1499f67cf491SMika Westerberg break; 1500f67cf491SMika Westerberg 1501f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI: 1502f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI: 1503f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: 1504f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: 1505f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: 15069aaa3b8bSMika Westerberg icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 1507f67cf491SMika Westerberg icm->is_supported = icm_ar_is_supported; 1508f67cf491SMika Westerberg icm->get_mode = icm_ar_get_mode; 1509f67cf491SMika Westerberg icm->get_route = icm_ar_get_route; 15109aaa3b8bSMika Westerberg icm->driver_ready = icm_ar_driver_ready; 1511f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 1512f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 1513d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 1514d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 15159aaa3b8bSMika Westerberg tb->cm_ops = &icm_ar_ops; 1516f67cf491SMika Westerberg break; 1517f67cf491SMika Westerberg } 1518f67cf491SMika Westerberg 1519f67cf491SMika Westerberg if (!icm->is_supported || !icm->is_supported(tb)) { 1520f67cf491SMika Westerberg dev_dbg(&nhi->pdev->dev, "ICM not supported on this controller\n"); 1521f67cf491SMika Westerberg tb_domain_put(tb); 1522f67cf491SMika Westerberg return NULL; 1523f67cf491SMika Westerberg } 1524f67cf491SMika Westerberg 1525f67cf491SMika Westerberg return tb; 1526f67cf491SMika Westerberg } 1527