1fd3b339cSMika Westerberg // SPDX-License-Identifier: GPL-2.0 2f67cf491SMika Westerberg /* 3f67cf491SMika Westerberg * Internal Thunderbolt Connection Manager. This is a firmware running on 4f67cf491SMika Westerberg * the Thunderbolt host controller performing most of the low-level 5f67cf491SMika Westerberg * handling. 6f67cf491SMika Westerberg * 7f67cf491SMika Westerberg * Copyright (C) 2017, Intel Corporation 8f67cf491SMika Westerberg * Authors: Michael Jamet <michael.jamet@intel.com> 9f67cf491SMika Westerberg * Mika Westerberg <mika.westerberg@linux.intel.com> 10f67cf491SMika Westerberg */ 11f67cf491SMika Westerberg 12f67cf491SMika Westerberg #include <linux/delay.h> 13f67cf491SMika Westerberg #include <linux/mutex.h> 14354a7a77SMika Westerberg #include <linux/moduleparam.h> 15f67cf491SMika Westerberg #include <linux/pci.h> 162d8ff0b5SMika Westerberg #include <linux/pm_runtime.h> 17630b3affSLukas Wunner #include <linux/platform_data/x86/apple.h> 18f67cf491SMika Westerberg #include <linux/sizes.h> 19f67cf491SMika Westerberg #include <linux/slab.h> 20f67cf491SMika Westerberg #include <linux/workqueue.h> 21f67cf491SMika Westerberg 22f67cf491SMika Westerberg #include "ctl.h" 23f67cf491SMika Westerberg #include "nhi_regs.h" 24f67cf491SMika Westerberg #include "tb.h" 25f67cf491SMika Westerberg 26f67cf491SMika Westerberg #define PCIE2CIO_CMD 0x30 27f67cf491SMika Westerberg #define PCIE2CIO_CMD_TIMEOUT BIT(31) 28f67cf491SMika Westerberg #define PCIE2CIO_CMD_START BIT(30) 29f67cf491SMika Westerberg #define PCIE2CIO_CMD_WRITE BIT(21) 30f67cf491SMika Westerberg #define PCIE2CIO_CMD_CS_MASK GENMASK(20, 19) 31f67cf491SMika Westerberg #define PCIE2CIO_CMD_CS_SHIFT 19 32f67cf491SMika Westerberg #define PCIE2CIO_CMD_PORT_MASK GENMASK(18, 13) 33f67cf491SMika Westerberg #define PCIE2CIO_CMD_PORT_SHIFT 13 34f67cf491SMika Westerberg 35f67cf491SMika Westerberg #define PCIE2CIO_WRDATA 0x34 36f67cf491SMika Westerberg #define PCIE2CIO_RDDATA 0x38 37f67cf491SMika Westerberg 38f67cf491SMika Westerberg #define PHY_PORT_CS1 0x37 39f67cf491SMika Westerberg #define PHY_PORT_CS1_LINK_DISABLE BIT(14) 40f67cf491SMika Westerberg #define PHY_PORT_CS1_LINK_STATE_MASK GENMASK(29, 26) 41f67cf491SMika Westerberg #define PHY_PORT_CS1_LINK_STATE_SHIFT 26 42f67cf491SMika Westerberg 43f67cf491SMika Westerberg #define ICM_TIMEOUT 5000 /* ms */ 440b0a0bd0SMika Westerberg #define ICM_APPROVE_TIMEOUT 10000 /* ms */ 45f67cf491SMika Westerberg #define ICM_MAX_LINK 4 46f67cf491SMika Westerberg 47354a7a77SMika Westerberg static bool start_icm; 48354a7a77SMika Westerberg module_param(start_icm, bool, 0444); 49354a7a77SMika Westerberg MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: false)"); 50354a7a77SMika Westerberg 51f67cf491SMika Westerberg /** 529039387eSMika Westerberg * struct usb4_switch_nvm_auth - Holds USB4 NVM_AUTH status 539039387eSMika Westerberg * @reply: Reply from ICM firmware is placed here 549039387eSMika Westerberg * @request: Request that is sent to ICM firmware 559039387eSMika Westerberg * @icm: Pointer to ICM private data 569039387eSMika Westerberg */ 579039387eSMika Westerberg struct usb4_switch_nvm_auth { 589039387eSMika Westerberg struct icm_usb4_switch_op_response reply; 599039387eSMika Westerberg struct icm_usb4_switch_op request; 609039387eSMika Westerberg struct icm *icm; 619039387eSMika Westerberg }; 629039387eSMika Westerberg 639039387eSMika Westerberg /** 64f67cf491SMika Westerberg * struct icm - Internal connection manager private data 65f67cf491SMika Westerberg * @request_lock: Makes sure only one message is send to ICM at time 66f67cf491SMika Westerberg * @rescan_work: Work used to rescan the surviving switches after resume 67f67cf491SMika Westerberg * @upstream_port: Pointer to the PCIe upstream port this host 68f67cf491SMika Westerberg * controller is connected. This is only set for systems 69f67cf491SMika Westerberg * where ICM needs to be started manually 70f67cf491SMika Westerberg * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides 71f67cf491SMika Westerberg * (only set when @upstream_port is not %NULL) 72e6b245ccSMika Westerberg * @safe_mode: ICM is in safe mode 739aaa3b8bSMika Westerberg * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) 742d8ff0b5SMika Westerberg * @rpm: Does the controller support runtime PM (RTD3) 75f437c24bSMika Westerberg * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller 769039387eSMika Westerberg * @proto_version: Firmware protocol version 779039387eSMika Westerberg * @last_nvm_auth: Last USB4 router NVM_AUTH result (or %NULL if not set) 783cdb9446SMika Westerberg * @veto: Is RTD3 veto in effect 79f67cf491SMika Westerberg * @is_supported: Checks if we can support ICM on this controller 800d53827dSMika Westerberg * @cio_reset: Trigger CIO reset 81f67cf491SMika Westerberg * @get_mode: Read and return the ICM firmware mode (optional) 82f67cf491SMika Westerberg * @get_route: Find a route string for given switch 83d04522faSMika Westerberg * @save_devices: Ask ICM to save devices to ACL when suspending (optional) 843080e197SMika Westerberg * @driver_ready: Send driver ready message to ICM 853cdb9446SMika Westerberg * @set_uuid: Set UUID for the root switch (optional) 86f67cf491SMika Westerberg * @device_connected: Handle device connected ICM message 87f67cf491SMika Westerberg * @device_disconnected: Handle device disconnected ICM message 88af6bd59eSLee Jones * @xdomain_connected: Handle XDomain connected ICM message 89af6bd59eSLee Jones * @xdomain_disconnected: Handle XDomain disconnected ICM message 903cdb9446SMika Westerberg * @rtd3_veto: Handle RTD3 veto notification ICM message 91f67cf491SMika Westerberg */ 92f67cf491SMika Westerberg struct icm { 93f67cf491SMika Westerberg struct mutex request_lock; 94f67cf491SMika Westerberg struct delayed_work rescan_work; 95f67cf491SMika Westerberg struct pci_dev *upstream_port; 96f67cf491SMika Westerberg int vnd_cap; 97e6b245ccSMika Westerberg bool safe_mode; 9845ef561aSMika Westerberg size_t max_boot_acl; 992d8ff0b5SMika Westerberg bool rpm; 100f437c24bSMika Westerberg bool can_upgrade_nvm; 1019039387eSMika Westerberg u8 proto_version; 1029039387eSMika Westerberg struct usb4_switch_nvm_auth *last_nvm_auth; 1033cdb9446SMika Westerberg bool veto; 104f67cf491SMika Westerberg bool (*is_supported)(struct tb *tb); 1050d53827dSMika Westerberg int (*cio_reset)(struct tb *tb); 106f67cf491SMika Westerberg int (*get_mode)(struct tb *tb); 107f67cf491SMika Westerberg int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); 108d04522faSMika Westerberg void (*save_devices)(struct tb *tb); 1093080e197SMika Westerberg int (*driver_ready)(struct tb *tb, 1109aaa3b8bSMika Westerberg enum tb_security_level *security_level, 1119039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm); 1123cdb9446SMika Westerberg void (*set_uuid)(struct tb *tb); 113f67cf491SMika Westerberg void (*device_connected)(struct tb *tb, 114f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 115f67cf491SMika Westerberg void (*device_disconnected)(struct tb *tb, 116f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 117d1ff7024SMika Westerberg void (*xdomain_connected)(struct tb *tb, 118d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 119d1ff7024SMika Westerberg void (*xdomain_disconnected)(struct tb *tb, 120d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 1213cdb9446SMika Westerberg void (*rtd3_veto)(struct tb *tb, const struct icm_pkg_header *hdr); 122f67cf491SMika Westerberg }; 123f67cf491SMika Westerberg 124f67cf491SMika Westerberg struct icm_notification { 125f67cf491SMika Westerberg struct work_struct work; 126f67cf491SMika Westerberg struct icm_pkg_header *pkg; 127f67cf491SMika Westerberg struct tb *tb; 128f67cf491SMika Westerberg }; 129f67cf491SMika Westerberg 1302d8ff0b5SMika Westerberg struct ep_name_entry { 1312d8ff0b5SMika Westerberg u8 len; 1322d8ff0b5SMika Westerberg u8 type; 1333084ea9eSGustavo A. R. Silva u8 data[]; 1342d8ff0b5SMika Westerberg }; 1352d8ff0b5SMika Westerberg 1362d8ff0b5SMika Westerberg #define EP_NAME_INTEL_VSS 0x10 1372d8ff0b5SMika Westerberg 1382d8ff0b5SMika Westerberg /* Intel Vendor specific structure */ 1392d8ff0b5SMika Westerberg struct intel_vss { 1402d8ff0b5SMika Westerberg u16 vendor; 1412d8ff0b5SMika Westerberg u16 model; 1422d8ff0b5SMika Westerberg u8 mc; 1432d8ff0b5SMika Westerberg u8 flags; 1442d8ff0b5SMika Westerberg u16 pci_devid; 1452d8ff0b5SMika Westerberg u32 nvm_version; 1462d8ff0b5SMika Westerberg }; 1472d8ff0b5SMika Westerberg 1482d8ff0b5SMika Westerberg #define INTEL_VSS_FLAGS_RTD3 BIT(0) 1492d8ff0b5SMika Westerberg 1502d8ff0b5SMika Westerberg static const struct intel_vss *parse_intel_vss(const void *ep_name, size_t size) 1512d8ff0b5SMika Westerberg { 1522d8ff0b5SMika Westerberg const void *end = ep_name + size; 1532d8ff0b5SMika Westerberg 1542d8ff0b5SMika Westerberg while (ep_name < end) { 1552d8ff0b5SMika Westerberg const struct ep_name_entry *ep = ep_name; 1562d8ff0b5SMika Westerberg 1572d8ff0b5SMika Westerberg if (!ep->len) 1582d8ff0b5SMika Westerberg break; 1592d8ff0b5SMika Westerberg if (ep_name + ep->len > end) 1602d8ff0b5SMika Westerberg break; 1612d8ff0b5SMika Westerberg 1622d8ff0b5SMika Westerberg if (ep->type == EP_NAME_INTEL_VSS) 1632d8ff0b5SMika Westerberg return (const struct intel_vss *)ep->data; 1642d8ff0b5SMika Westerberg 1652d8ff0b5SMika Westerberg ep_name += ep->len; 1662d8ff0b5SMika Westerberg } 1672d8ff0b5SMika Westerberg 1682d8ff0b5SMika Westerberg return NULL; 1692d8ff0b5SMika Westerberg } 1702d8ff0b5SMika Westerberg 171b5db76dbSMika Westerberg static bool intel_vss_is_rtd3(const void *ep_name, size_t size) 172b5db76dbSMika Westerberg { 173b5db76dbSMika Westerberg const struct intel_vss *vss; 174b5db76dbSMika Westerberg 175b5db76dbSMika Westerberg vss = parse_intel_vss(ep_name, size); 176b5db76dbSMika Westerberg if (vss) 177b5db76dbSMika Westerberg return !!(vss->flags & INTEL_VSS_FLAGS_RTD3); 178b5db76dbSMika Westerberg 179b5db76dbSMika Westerberg return false; 180b5db76dbSMika Westerberg } 181b5db76dbSMika Westerberg 182f67cf491SMika Westerberg static inline struct tb *icm_to_tb(struct icm *icm) 183f67cf491SMika Westerberg { 184f67cf491SMika Westerberg return ((void *)icm - sizeof(struct tb)); 185f67cf491SMika Westerberg } 186f67cf491SMika Westerberg 187f67cf491SMika Westerberg static inline u8 phy_port_from_route(u64 route, u8 depth) 188f67cf491SMika Westerberg { 189d1ff7024SMika Westerberg u8 link; 190d1ff7024SMika Westerberg 191d1ff7024SMika Westerberg link = depth ? route >> ((depth - 1) * 8) : route; 192d1ff7024SMika Westerberg return tb_phy_port_from_link(link); 193f67cf491SMika Westerberg } 194f67cf491SMika Westerberg 195f67cf491SMika Westerberg static inline u8 dual_link_from_link(u8 link) 196f67cf491SMika Westerberg { 197f67cf491SMika Westerberg return link ? ((link - 1) ^ 0x01) + 1 : 0; 198f67cf491SMika Westerberg } 199f67cf491SMika Westerberg 200f67cf491SMika Westerberg static inline u64 get_route(u32 route_hi, u32 route_lo) 201f67cf491SMika Westerberg { 202f67cf491SMika Westerberg return (u64)route_hi << 32 | route_lo; 203f67cf491SMika Westerberg } 204f67cf491SMika Westerberg 2054bac471dSRadion Mirchevsky static inline u64 get_parent_route(u64 route) 2064bac471dSRadion Mirchevsky { 2074bac471dSRadion Mirchevsky int depth = tb_route_length(route); 2084bac471dSRadion Mirchevsky return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0; 2094bac471dSRadion Mirchevsky } 2104bac471dSRadion Mirchevsky 2110d53827dSMika Westerberg static int pci2cio_wait_completion(struct icm *icm, unsigned long timeout_msec) 2120d53827dSMika Westerberg { 2130d53827dSMika Westerberg unsigned long end = jiffies + msecs_to_jiffies(timeout_msec); 2140d53827dSMika Westerberg u32 cmd; 2150d53827dSMika Westerberg 2160d53827dSMika Westerberg do { 2170d53827dSMika Westerberg pci_read_config_dword(icm->upstream_port, 2180d53827dSMika Westerberg icm->vnd_cap + PCIE2CIO_CMD, &cmd); 2190d53827dSMika Westerberg if (!(cmd & PCIE2CIO_CMD_START)) { 2200d53827dSMika Westerberg if (cmd & PCIE2CIO_CMD_TIMEOUT) 2210d53827dSMika Westerberg break; 2220d53827dSMika Westerberg return 0; 2230d53827dSMika Westerberg } 2240d53827dSMika Westerberg 2250d53827dSMika Westerberg msleep(50); 2260d53827dSMika Westerberg } while (time_before(jiffies, end)); 2270d53827dSMika Westerberg 2280d53827dSMika Westerberg return -ETIMEDOUT; 2290d53827dSMika Westerberg } 2300d53827dSMika Westerberg 2310d53827dSMika Westerberg static int pcie2cio_read(struct icm *icm, enum tb_cfg_space cs, 2320d53827dSMika Westerberg unsigned int port, unsigned int index, u32 *data) 2330d53827dSMika Westerberg { 2340d53827dSMika Westerberg struct pci_dev *pdev = icm->upstream_port; 2350d53827dSMika Westerberg int ret, vnd_cap = icm->vnd_cap; 2360d53827dSMika Westerberg u32 cmd; 2370d53827dSMika Westerberg 2380d53827dSMika Westerberg cmd = index; 2390d53827dSMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 2400d53827dSMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 2410d53827dSMika Westerberg cmd |= PCIE2CIO_CMD_START; 2420d53827dSMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 2430d53827dSMika Westerberg 2440d53827dSMika Westerberg ret = pci2cio_wait_completion(icm, 5000); 2450d53827dSMika Westerberg if (ret) 2460d53827dSMika Westerberg return ret; 2470d53827dSMika Westerberg 2480d53827dSMika Westerberg pci_read_config_dword(pdev, vnd_cap + PCIE2CIO_RDDATA, data); 2490d53827dSMika Westerberg return 0; 2500d53827dSMika Westerberg } 2510d53827dSMika Westerberg 2520d53827dSMika Westerberg static int pcie2cio_write(struct icm *icm, enum tb_cfg_space cs, 2530d53827dSMika Westerberg unsigned int port, unsigned int index, u32 data) 2540d53827dSMika Westerberg { 2550d53827dSMika Westerberg struct pci_dev *pdev = icm->upstream_port; 2560d53827dSMika Westerberg int vnd_cap = icm->vnd_cap; 2570d53827dSMika Westerberg u32 cmd; 2580d53827dSMika Westerberg 2590d53827dSMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_WRDATA, data); 2600d53827dSMika Westerberg 2610d53827dSMika Westerberg cmd = index; 2620d53827dSMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 2630d53827dSMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 2640d53827dSMika Westerberg cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START; 2650d53827dSMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 2660d53827dSMika Westerberg 2670d53827dSMika Westerberg return pci2cio_wait_completion(icm, 5000); 2680d53827dSMika Westerberg } 2690d53827dSMika Westerberg 270f67cf491SMika Westerberg static bool icm_match(const struct tb_cfg_request *req, 271f67cf491SMika Westerberg const struct ctl_pkg *pkg) 272f67cf491SMika Westerberg { 273f67cf491SMika Westerberg const struct icm_pkg_header *res_hdr = pkg->buffer; 274f67cf491SMika Westerberg const struct icm_pkg_header *req_hdr = req->request; 275f67cf491SMika Westerberg 276f67cf491SMika Westerberg if (pkg->frame.eof != req->response_type) 277f67cf491SMika Westerberg return false; 278f67cf491SMika Westerberg if (res_hdr->code != req_hdr->code) 279f67cf491SMika Westerberg return false; 280f67cf491SMika Westerberg 281f67cf491SMika Westerberg return true; 282f67cf491SMika Westerberg } 283f67cf491SMika Westerberg 284f67cf491SMika Westerberg static bool icm_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) 285f67cf491SMika Westerberg { 286f67cf491SMika Westerberg const struct icm_pkg_header *hdr = pkg->buffer; 287f67cf491SMika Westerberg 288f67cf491SMika Westerberg if (hdr->packet_id < req->npackets) { 289f67cf491SMika Westerberg size_t offset = hdr->packet_id * req->response_size; 290f67cf491SMika Westerberg 291f67cf491SMika Westerberg memcpy(req->response + offset, pkg->buffer, req->response_size); 292f67cf491SMika Westerberg } 293f67cf491SMika Westerberg 294f67cf491SMika Westerberg return hdr->packet_id == hdr->total_packets - 1; 295f67cf491SMika Westerberg } 296f67cf491SMika Westerberg 297f67cf491SMika Westerberg static int icm_request(struct tb *tb, const void *request, size_t request_size, 298f67cf491SMika Westerberg void *response, size_t response_size, size_t npackets, 299f67cf491SMika Westerberg unsigned int timeout_msec) 300f67cf491SMika Westerberg { 301f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 302f67cf491SMika Westerberg int retries = 3; 303f67cf491SMika Westerberg 304f67cf491SMika Westerberg do { 305f67cf491SMika Westerberg struct tb_cfg_request *req; 306f67cf491SMika Westerberg struct tb_cfg_result res; 307f67cf491SMika Westerberg 308f67cf491SMika Westerberg req = tb_cfg_request_alloc(); 309f67cf491SMika Westerberg if (!req) 310f67cf491SMika Westerberg return -ENOMEM; 311f67cf491SMika Westerberg 312f67cf491SMika Westerberg req->match = icm_match; 313f67cf491SMika Westerberg req->copy = icm_copy; 314f67cf491SMika Westerberg req->request = request; 315f67cf491SMika Westerberg req->request_size = request_size; 316f67cf491SMika Westerberg req->request_type = TB_CFG_PKG_ICM_CMD; 317f67cf491SMika Westerberg req->response = response; 318f67cf491SMika Westerberg req->npackets = npackets; 319f67cf491SMika Westerberg req->response_size = response_size; 320f67cf491SMika Westerberg req->response_type = TB_CFG_PKG_ICM_RESP; 321f67cf491SMika Westerberg 322f67cf491SMika Westerberg mutex_lock(&icm->request_lock); 323f67cf491SMika Westerberg res = tb_cfg_request_sync(tb->ctl, req, timeout_msec); 324f67cf491SMika Westerberg mutex_unlock(&icm->request_lock); 325f67cf491SMika Westerberg 326f67cf491SMika Westerberg tb_cfg_request_put(req); 327f67cf491SMika Westerberg 328f67cf491SMika Westerberg if (res.err != -ETIMEDOUT) 329f67cf491SMika Westerberg return res.err == 1 ? -EIO : res.err; 330f67cf491SMika Westerberg 331f67cf491SMika Westerberg usleep_range(20, 50); 332f67cf491SMika Westerberg } while (retries--); 333f67cf491SMika Westerberg 334f67cf491SMika Westerberg return -ETIMEDOUT; 335f67cf491SMika Westerberg } 336f67cf491SMika Westerberg 3373cdb9446SMika Westerberg /* 3383cdb9446SMika Westerberg * If rescan is queued to run (we are resuming), postpone it to give the 3393cdb9446SMika Westerberg * firmware some more time to send device connected notifications for next 3403cdb9446SMika Westerberg * devices in the chain. 3413cdb9446SMika Westerberg */ 3423cdb9446SMika Westerberg static void icm_postpone_rescan(struct tb *tb) 3433cdb9446SMika Westerberg { 3443cdb9446SMika Westerberg struct icm *icm = tb_priv(tb); 3453cdb9446SMika Westerberg 3463cdb9446SMika Westerberg if (delayed_work_pending(&icm->rescan_work)) 3473cdb9446SMika Westerberg mod_delayed_work(tb->wq, &icm->rescan_work, 3483cdb9446SMika Westerberg msecs_to_jiffies(500)); 3493cdb9446SMika Westerberg } 3503cdb9446SMika Westerberg 3513cdb9446SMika Westerberg static void icm_veto_begin(struct tb *tb) 3523cdb9446SMika Westerberg { 3533cdb9446SMika Westerberg struct icm *icm = tb_priv(tb); 3543cdb9446SMika Westerberg 3553cdb9446SMika Westerberg if (!icm->veto) { 3563cdb9446SMika Westerberg icm->veto = true; 3573cdb9446SMika Westerberg /* Keep the domain powered while veto is in effect */ 3583cdb9446SMika Westerberg pm_runtime_get(&tb->dev); 3593cdb9446SMika Westerberg } 3603cdb9446SMika Westerberg } 3613cdb9446SMika Westerberg 3623cdb9446SMika Westerberg static void icm_veto_end(struct tb *tb) 3633cdb9446SMika Westerberg { 3643cdb9446SMika Westerberg struct icm *icm = tb_priv(tb); 3653cdb9446SMika Westerberg 3663cdb9446SMika Westerberg if (icm->veto) { 3673cdb9446SMika Westerberg icm->veto = false; 3683cdb9446SMika Westerberg /* Allow the domain suspend now */ 3693cdb9446SMika Westerberg pm_runtime_mark_last_busy(&tb->dev); 3703cdb9446SMika Westerberg pm_runtime_put_autosuspend(&tb->dev); 3713cdb9446SMika Westerberg } 3723cdb9446SMika Westerberg } 3733cdb9446SMika Westerberg 374354a7a77SMika Westerberg static bool icm_firmware_running(const struct tb_nhi *nhi) 375354a7a77SMika Westerberg { 376354a7a77SMika Westerberg u32 val; 377354a7a77SMika Westerberg 378354a7a77SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 379354a7a77SMika Westerberg return !!(val & REG_FW_STS_ICM_EN); 380354a7a77SMika Westerberg } 381354a7a77SMika Westerberg 382f67cf491SMika Westerberg static bool icm_fr_is_supported(struct tb *tb) 383f67cf491SMika Westerberg { 384630b3affSLukas Wunner return !x86_apple_machine; 385f67cf491SMika Westerberg } 386f67cf491SMika Westerberg 387f67cf491SMika Westerberg static inline int icm_fr_get_switch_index(u32 port) 388f67cf491SMika Westerberg { 389f67cf491SMika Westerberg int index; 390f67cf491SMika Westerberg 391f67cf491SMika Westerberg if ((port & ICM_PORT_TYPE_MASK) != TB_TYPE_PORT) 392f67cf491SMika Westerberg return 0; 393f67cf491SMika Westerberg 394f67cf491SMika Westerberg index = port >> ICM_PORT_INDEX_SHIFT; 395f67cf491SMika Westerberg return index != 0xff ? index : 0; 396f67cf491SMika Westerberg } 397f67cf491SMika Westerberg 398f67cf491SMika Westerberg static int icm_fr_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 399f67cf491SMika Westerberg { 400f67cf491SMika Westerberg struct icm_fr_pkg_get_topology_response *switches, *sw; 401f67cf491SMika Westerberg struct icm_fr_pkg_get_topology request = { 402f67cf491SMika Westerberg .hdr = { .code = ICM_GET_TOPOLOGY }, 403f67cf491SMika Westerberg }; 404f67cf491SMika Westerberg size_t npackets = ICM_GET_TOPOLOGY_PACKETS; 405f67cf491SMika Westerberg int ret, index; 406f67cf491SMika Westerberg u8 i; 407f67cf491SMika Westerberg 408f67cf491SMika Westerberg switches = kcalloc(npackets, sizeof(*switches), GFP_KERNEL); 409f67cf491SMika Westerberg if (!switches) 410f67cf491SMika Westerberg return -ENOMEM; 411f67cf491SMika Westerberg 412f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), switches, 413f67cf491SMika Westerberg sizeof(*switches), npackets, ICM_TIMEOUT); 414f67cf491SMika Westerberg if (ret) 415f67cf491SMika Westerberg goto err_free; 416f67cf491SMika Westerberg 417f67cf491SMika Westerberg sw = &switches[0]; 418f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[link]); 419f67cf491SMika Westerberg if (!index) { 420f67cf491SMika Westerberg ret = -ENODEV; 421f67cf491SMika Westerberg goto err_free; 422f67cf491SMika Westerberg } 423f67cf491SMika Westerberg 424f67cf491SMika Westerberg sw = &switches[index]; 425f67cf491SMika Westerberg for (i = 1; i < depth; i++) { 426f67cf491SMika Westerberg unsigned int j; 427f67cf491SMika Westerberg 428f67cf491SMika Westerberg if (!(sw->first_data & ICM_SWITCH_USED)) { 429f67cf491SMika Westerberg ret = -ENODEV; 430f67cf491SMika Westerberg goto err_free; 431f67cf491SMika Westerberg } 432f67cf491SMika Westerberg 433f67cf491SMika Westerberg for (j = 0; j < ARRAY_SIZE(sw->ports); j++) { 434f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[j]); 435f67cf491SMika Westerberg if (index > sw->switch_index) { 436f67cf491SMika Westerberg sw = &switches[index]; 437f67cf491SMika Westerberg break; 438f67cf491SMika Westerberg } 439f67cf491SMika Westerberg } 440f67cf491SMika Westerberg } 441f67cf491SMika Westerberg 442f67cf491SMika Westerberg *route = get_route(sw->route_hi, sw->route_lo); 443f67cf491SMika Westerberg 444f67cf491SMika Westerberg err_free: 445f67cf491SMika Westerberg kfree(switches); 446f67cf491SMika Westerberg return ret; 447f67cf491SMika Westerberg } 448f67cf491SMika Westerberg 449d04522faSMika Westerberg static void icm_fr_save_devices(struct tb *tb) 450d04522faSMika Westerberg { 451d04522faSMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_SAVE_DEVS, 0); 452d04522faSMika Westerberg } 453d04522faSMika Westerberg 4543080e197SMika Westerberg static int 4559aaa3b8bSMika Westerberg icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 4569039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 4573080e197SMika Westerberg { 4583080e197SMika Westerberg struct icm_fr_pkg_driver_ready_response reply; 4593080e197SMika Westerberg struct icm_pkg_driver_ready request = { 4603080e197SMika Westerberg .hdr.code = ICM_DRIVER_READY, 4613080e197SMika Westerberg }; 4623080e197SMika Westerberg int ret; 4633080e197SMika Westerberg 4643080e197SMika Westerberg memset(&reply, 0, sizeof(reply)); 4653080e197SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 4663080e197SMika Westerberg 1, ICM_TIMEOUT); 4673080e197SMika Westerberg if (ret) 4683080e197SMika Westerberg return ret; 4693080e197SMika Westerberg 4703080e197SMika Westerberg if (security_level) 4713080e197SMika Westerberg *security_level = reply.security_level & ICM_FR_SLEVEL_MASK; 4723080e197SMika Westerberg 4733080e197SMika Westerberg return 0; 4743080e197SMika Westerberg } 4753080e197SMika Westerberg 476f67cf491SMika Westerberg static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) 477f67cf491SMika Westerberg { 478f67cf491SMika Westerberg struct icm_fr_pkg_approve_device request; 479f67cf491SMika Westerberg struct icm_fr_pkg_approve_device reply; 480f67cf491SMika Westerberg int ret; 481f67cf491SMika Westerberg 482f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 483f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 484f67cf491SMika Westerberg request.hdr.code = ICM_APPROVE_DEVICE; 485f67cf491SMika Westerberg request.connection_id = sw->connection_id; 486f67cf491SMika Westerberg request.connection_key = sw->connection_key; 487f67cf491SMika Westerberg 488f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 489f67cf491SMika Westerberg /* Use larger timeout as establishing tunnels can take some time */ 490f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 4910b0a0bd0SMika Westerberg 1, ICM_APPROVE_TIMEOUT); 492f67cf491SMika Westerberg if (ret) 493f67cf491SMika Westerberg return ret; 494f67cf491SMika Westerberg 495f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 496f67cf491SMika Westerberg tb_warn(tb, "PCIe tunnel creation failed\n"); 497f67cf491SMika Westerberg return -EIO; 498f67cf491SMika Westerberg } 499f67cf491SMika Westerberg 500f67cf491SMika Westerberg return 0; 501f67cf491SMika Westerberg } 502f67cf491SMika Westerberg 503f67cf491SMika Westerberg static int icm_fr_add_switch_key(struct tb *tb, struct tb_switch *sw) 504f67cf491SMika Westerberg { 505f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key request; 506f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key_response reply; 507f67cf491SMika Westerberg int ret; 508f67cf491SMika Westerberg 509f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 510f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 511f67cf491SMika Westerberg request.hdr.code = ICM_ADD_DEVICE_KEY; 512f67cf491SMika Westerberg request.connection_id = sw->connection_id; 513f67cf491SMika Westerberg request.connection_key = sw->connection_key; 514f67cf491SMika Westerberg memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 515f67cf491SMika Westerberg 516f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 517f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 518f67cf491SMika Westerberg 1, ICM_TIMEOUT); 519f67cf491SMika Westerberg if (ret) 520f67cf491SMika Westerberg return ret; 521f67cf491SMika Westerberg 522f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 523f67cf491SMika Westerberg tb_warn(tb, "Adding key to switch failed\n"); 524f67cf491SMika Westerberg return -EIO; 525f67cf491SMika Westerberg } 526f67cf491SMika Westerberg 527f67cf491SMika Westerberg return 0; 528f67cf491SMika Westerberg } 529f67cf491SMika Westerberg 530f67cf491SMika Westerberg static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 531f67cf491SMika Westerberg const u8 *challenge, u8 *response) 532f67cf491SMika Westerberg { 533f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device request; 534f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device_response reply; 535f67cf491SMika Westerberg int ret; 536f67cf491SMika Westerberg 537f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 538f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 539f67cf491SMika Westerberg request.hdr.code = ICM_CHALLENGE_DEVICE; 540f67cf491SMika Westerberg request.connection_id = sw->connection_id; 541f67cf491SMika Westerberg request.connection_key = sw->connection_key; 542f67cf491SMika Westerberg memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 543f67cf491SMika Westerberg 544f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 545f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 546f67cf491SMika Westerberg 1, ICM_TIMEOUT); 547f67cf491SMika Westerberg if (ret) 548f67cf491SMika Westerberg return ret; 549f67cf491SMika Westerberg 550f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 551f67cf491SMika Westerberg return -EKEYREJECTED; 552f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 553f67cf491SMika Westerberg return -ENOKEY; 554f67cf491SMika Westerberg 555f67cf491SMika Westerberg memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 556f67cf491SMika Westerberg 557f67cf491SMika Westerberg return 0; 558f67cf491SMika Westerberg } 559f67cf491SMika Westerberg 560*180b0689SMika Westerberg static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, 561*180b0689SMika Westerberg int transmit_path, int transmit_ring, 562*180b0689SMika Westerberg int receive_path, int receive_ring) 563d1ff7024SMika Westerberg { 564d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain_response reply; 565d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain request; 566d1ff7024SMika Westerberg int ret; 567d1ff7024SMika Westerberg 568d1ff7024SMika Westerberg memset(&request, 0, sizeof(request)); 569d1ff7024SMika Westerberg request.hdr.code = ICM_APPROVE_XDOMAIN; 570d1ff7024SMika Westerberg request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; 571d1ff7024SMika Westerberg memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 572d1ff7024SMika Westerberg 573*180b0689SMika Westerberg request.transmit_path = transmit_path; 574*180b0689SMika Westerberg request.transmit_ring = transmit_ring; 575*180b0689SMika Westerberg request.receive_path = receive_path; 576*180b0689SMika Westerberg request.receive_ring = receive_ring; 577d1ff7024SMika Westerberg 578d1ff7024SMika Westerberg memset(&reply, 0, sizeof(reply)); 579d1ff7024SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 580d1ff7024SMika Westerberg 1, ICM_TIMEOUT); 581d1ff7024SMika Westerberg if (ret) 582d1ff7024SMika Westerberg return ret; 583d1ff7024SMika Westerberg 584d1ff7024SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 585d1ff7024SMika Westerberg return -EIO; 586d1ff7024SMika Westerberg 587d1ff7024SMika Westerberg return 0; 588d1ff7024SMika Westerberg } 589d1ff7024SMika Westerberg 590*180b0689SMika Westerberg static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, 591*180b0689SMika Westerberg int transmit_path, int transmit_ring, 592*180b0689SMika Westerberg int receive_path, int receive_ring) 593d1ff7024SMika Westerberg { 594d1ff7024SMika Westerberg u8 phy_port; 595d1ff7024SMika Westerberg u8 cmd; 596d1ff7024SMika Westerberg 597d1ff7024SMika Westerberg phy_port = tb_phy_port_from_link(xd->link); 598d1ff7024SMika Westerberg if (phy_port == 0) 599d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PA; 600d1ff7024SMika Westerberg else 601d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PB; 602d1ff7024SMika Westerberg 603d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 1); 604d1ff7024SMika Westerberg usleep_range(10, 50); 605d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 2); 606d1ff7024SMika Westerberg return 0; 607d1ff7024SMika Westerberg } 608d1ff7024SMika Westerberg 609b5db76dbSMika Westerberg static struct tb_switch *alloc_switch(struct tb_switch *parent_sw, u64 route, 610b5db76dbSMika Westerberg const uuid_t *uuid) 611ee487dd2SMika Westerberg { 612b5db76dbSMika Westerberg struct tb *tb = parent_sw->tb; 613ee487dd2SMika Westerberg struct tb_switch *sw; 614ee487dd2SMika Westerberg 615b5db76dbSMika Westerberg sw = tb_switch_alloc(tb, &parent_sw->dev, route); 616b5db76dbSMika Westerberg if (IS_ERR(sw)) { 617b5db76dbSMika Westerberg tb_warn(tb, "failed to allocate switch at %llx\n", route); 618b5db76dbSMika Westerberg return sw; 619b5db76dbSMika Westerberg } 620ee487dd2SMika Westerberg 621ee487dd2SMika Westerberg sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); 622fd21b79eSAditya Pakki if (!sw->uuid) { 623fd21b79eSAditya Pakki tb_switch_put(sw); 624b5db76dbSMika Westerberg return ERR_PTR(-ENOMEM); 625fd21b79eSAditya Pakki } 626ee487dd2SMika Westerberg 627b5db76dbSMika Westerberg init_completion(&sw->rpm_complete); 628b5db76dbSMika Westerberg return sw; 629b5db76dbSMika Westerberg } 630b5db76dbSMika Westerberg 631b5db76dbSMika Westerberg static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw) 632b5db76dbSMika Westerberg { 633b5db76dbSMika Westerberg u64 route = tb_route(sw); 634b5db76dbSMika Westerberg int ret; 6352d8ff0b5SMika Westerberg 636ee487dd2SMika Westerberg /* Link the two switches now */ 637ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 638ee487dd2SMika Westerberg tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); 639ee487dd2SMika Westerberg 6403cdb9446SMika Westerberg ret = tb_switch_add(sw); 641b5db76dbSMika Westerberg if (ret) 642ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 6432d8ff0b5SMika Westerberg 644b5db76dbSMika Westerberg return ret; 645ee487dd2SMika Westerberg } 646ee487dd2SMika Westerberg 647ee487dd2SMika Westerberg static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, 648ee487dd2SMika Westerberg u64 route, u8 connection_id, u8 connection_key, 64914862ee3SYehezkel Bernat u8 link, u8 depth, bool boot) 650ee487dd2SMika Westerberg { 651ee487dd2SMika Westerberg /* Disconnect from parent */ 652ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 653ee487dd2SMika Westerberg /* Re-connect via updated port*/ 654ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 655ee487dd2SMika Westerberg 656ee487dd2SMika Westerberg /* Update with the new addressing information */ 657ee487dd2SMika Westerberg sw->config.route_hi = upper_32_bits(route); 658ee487dd2SMika Westerberg sw->config.route_lo = lower_32_bits(route); 659ee487dd2SMika Westerberg sw->connection_id = connection_id; 660ee487dd2SMika Westerberg sw->connection_key = connection_key; 661ee487dd2SMika Westerberg sw->link = link; 662ee487dd2SMika Westerberg sw->depth = depth; 66314862ee3SYehezkel Bernat sw->boot = boot; 664ee487dd2SMika Westerberg 665ee487dd2SMika Westerberg /* This switch still exists */ 666ee487dd2SMika Westerberg sw->is_unplugged = false; 6674f7c2e0dSMika Westerberg 6684f7c2e0dSMika Westerberg /* Runtime resume is now complete */ 6694f7c2e0dSMika Westerberg complete(&sw->rpm_complete); 670ee487dd2SMika Westerberg } 671ee487dd2SMika Westerberg 672f67cf491SMika Westerberg static void remove_switch(struct tb_switch *sw) 673f67cf491SMika Westerberg { 674f67cf491SMika Westerberg struct tb_switch *parent_sw; 675f67cf491SMika Westerberg 676f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 677f67cf491SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 678f67cf491SMika Westerberg tb_switch_remove(sw); 679f67cf491SMika Westerberg } 680f67cf491SMika Westerberg 681ee487dd2SMika Westerberg static void add_xdomain(struct tb_switch *sw, u64 route, 682ee487dd2SMika Westerberg const uuid_t *local_uuid, const uuid_t *remote_uuid, 683ee487dd2SMika Westerberg u8 link, u8 depth) 684ee487dd2SMika Westerberg { 685ee487dd2SMika Westerberg struct tb_xdomain *xd; 686ee487dd2SMika Westerberg 6872d8ff0b5SMika Westerberg pm_runtime_get_sync(&sw->dev); 6882d8ff0b5SMika Westerberg 689ee487dd2SMika Westerberg xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid); 690ee487dd2SMika Westerberg if (!xd) 6912d8ff0b5SMika Westerberg goto out; 692ee487dd2SMika Westerberg 693ee487dd2SMika Westerberg xd->link = link; 694ee487dd2SMika Westerberg xd->depth = depth; 695ee487dd2SMika Westerberg 696ee487dd2SMika Westerberg tb_port_at(route, sw)->xdomain = xd; 697ee487dd2SMika Westerberg 698ee487dd2SMika Westerberg tb_xdomain_add(xd); 6992d8ff0b5SMika Westerberg 7002d8ff0b5SMika Westerberg out: 7012d8ff0b5SMika Westerberg pm_runtime_mark_last_busy(&sw->dev); 7022d8ff0b5SMika Westerberg pm_runtime_put_autosuspend(&sw->dev); 703ee487dd2SMika Westerberg } 704ee487dd2SMika Westerberg 705ee487dd2SMika Westerberg static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link) 706ee487dd2SMika Westerberg { 707ee487dd2SMika Westerberg xd->link = link; 708ee487dd2SMika Westerberg xd->route = route; 709ee487dd2SMika Westerberg xd->is_unplugged = false; 710ee487dd2SMika Westerberg } 711ee487dd2SMika Westerberg 71279fae987SMika Westerberg static void remove_xdomain(struct tb_xdomain *xd) 71379fae987SMika Westerberg { 71479fae987SMika Westerberg struct tb_switch *sw; 71579fae987SMika Westerberg 71679fae987SMika Westerberg sw = tb_to_switch(xd->dev.parent); 71779fae987SMika Westerberg tb_port_at(xd->route, sw)->xdomain = NULL; 71879fae987SMika Westerberg tb_xdomain_remove(xd); 71979fae987SMika Westerberg } 72079fae987SMika Westerberg 721f67cf491SMika Westerberg static void 722f67cf491SMika Westerberg icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 723f67cf491SMika Westerberg { 724f67cf491SMika Westerberg const struct icm_fr_event_device_connected *pkg = 725f67cf491SMika Westerberg (const struct icm_fr_event_device_connected *)hdr; 726ee487dd2SMika Westerberg enum tb_security_level security_level; 727f67cf491SMika Westerberg struct tb_switch *sw, *parent_sw; 72891c0c120SMika Westerberg bool boot, dual_lane, speed_gen3; 729f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 730f67cf491SMika Westerberg bool authorized = false; 73179fae987SMika Westerberg struct tb_xdomain *xd; 732f67cf491SMika Westerberg u8 link, depth; 733f67cf491SMika Westerberg u64 route; 734f67cf491SMika Westerberg int ret; 735f67cf491SMika Westerberg 7363cdb9446SMika Westerberg icm_postpone_rescan(tb); 7373cdb9446SMika Westerberg 738f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 739f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 740f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 741f67cf491SMika Westerberg authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 742ee487dd2SMika Westerberg security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 743ee487dd2SMika Westerberg ICM_FLAGS_SLEVEL_SHIFT; 74414862ee3SYehezkel Bernat boot = pkg->link_info & ICM_LINK_INFO_BOOT; 74591c0c120SMika Westerberg dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; 74691c0c120SMika Westerberg speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; 747f67cf491SMika Westerberg 748cb653eecSMika Westerberg if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 749cb653eecSMika Westerberg tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", 750cb653eecSMika Westerberg link, depth); 751cb653eecSMika Westerberg return; 752cb653eecSMika Westerberg } 753cb653eecSMika Westerberg 754f67cf491SMika Westerberg sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 755f67cf491SMika Westerberg if (sw) { 756f67cf491SMika Westerberg u8 phy_port, sw_phy_port; 757f67cf491SMika Westerberg 758f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 759fdd92e89SMika Westerberg sw_phy_port = tb_phy_port_from_link(sw->link); 760fdd92e89SMika Westerberg phy_port = tb_phy_port_from_link(link); 761f67cf491SMika Westerberg 762f67cf491SMika Westerberg /* 763f67cf491SMika Westerberg * On resume ICM will send us connected events for the 764f67cf491SMika Westerberg * devices that still are present. However, that 765f67cf491SMika Westerberg * information might have changed for example by the 766f67cf491SMika Westerberg * fact that a switch on a dual-link connection might 767f67cf491SMika Westerberg * have been enumerated using the other link now. Make 768f67cf491SMika Westerberg * sure our book keeping matches that. 769f67cf491SMika Westerberg */ 770f67cf491SMika Westerberg if (sw->depth == depth && sw_phy_port == phy_port && 771f67cf491SMika Westerberg !!sw->authorized == authorized) { 772fdd92e89SMika Westerberg /* 773fdd92e89SMika Westerberg * It was enumerated through another link so update 774fdd92e89SMika Westerberg * route string accordingly. 775fdd92e89SMika Westerberg */ 776fdd92e89SMika Westerberg if (sw->link != link) { 777fdd92e89SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 778fdd92e89SMika Westerberg if (ret) { 779fdd92e89SMika Westerberg tb_err(tb, "failed to update route string for switch at %u.%u\n", 780fdd92e89SMika Westerberg link, depth); 781fdd92e89SMika Westerberg tb_switch_put(sw); 782fdd92e89SMika Westerberg return; 783fdd92e89SMika Westerberg } 784fdd92e89SMika Westerberg } else { 785fdd92e89SMika Westerberg route = tb_route(sw); 786fdd92e89SMika Westerberg } 787fdd92e89SMika Westerberg 788ee487dd2SMika Westerberg update_switch(parent_sw, sw, route, pkg->connection_id, 78914862ee3SYehezkel Bernat pkg->connection_key, link, depth, boot); 790f67cf491SMika Westerberg tb_switch_put(sw); 791f67cf491SMika Westerberg return; 792f67cf491SMika Westerberg } 793f67cf491SMika Westerberg 794f67cf491SMika Westerberg /* 795f67cf491SMika Westerberg * User connected the same switch to another physical 796f67cf491SMika Westerberg * port or to another part of the topology. Remove the 797f67cf491SMika Westerberg * existing switch now before adding the new one. 798f67cf491SMika Westerberg */ 799f67cf491SMika Westerberg remove_switch(sw); 800f67cf491SMika Westerberg tb_switch_put(sw); 801f67cf491SMika Westerberg } 802f67cf491SMika Westerberg 803f67cf491SMika Westerberg /* 804f67cf491SMika Westerberg * If the switch was not found by UUID, look for a switch on 805f67cf491SMika Westerberg * same physical port (taking possible link aggregation into 806f67cf491SMika Westerberg * account) and depth. If we found one it is definitely a stale 807f67cf491SMika Westerberg * one so remove it first. 808f67cf491SMika Westerberg */ 809f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 810f67cf491SMika Westerberg if (!sw) { 811f67cf491SMika Westerberg u8 dual_link; 812f67cf491SMika Westerberg 813f67cf491SMika Westerberg dual_link = dual_link_from_link(link); 814f67cf491SMika Westerberg if (dual_link) 815f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, dual_link, depth); 816f67cf491SMika Westerberg } 817f67cf491SMika Westerberg if (sw) { 818f67cf491SMika Westerberg remove_switch(sw); 819f67cf491SMika Westerberg tb_switch_put(sw); 820f67cf491SMika Westerberg } 821f67cf491SMika Westerberg 82279fae987SMika Westerberg /* Remove existing XDomain connection if found */ 82379fae987SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 82479fae987SMika Westerberg if (xd) { 82579fae987SMika Westerberg remove_xdomain(xd); 82679fae987SMika Westerberg tb_xdomain_put(xd); 82779fae987SMika Westerberg } 82879fae987SMika Westerberg 829f67cf491SMika Westerberg parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); 830f67cf491SMika Westerberg if (!parent_sw) { 831f67cf491SMika Westerberg tb_err(tb, "failed to find parent switch for %u.%u\n", 832f67cf491SMika Westerberg link, depth); 833f67cf491SMika Westerberg return; 834f67cf491SMika Westerberg } 835f67cf491SMika Westerberg 836fdd92e89SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 837fdd92e89SMika Westerberg if (ret) { 838fdd92e89SMika Westerberg tb_err(tb, "failed to find route string for switch at %u.%u\n", 839fdd92e89SMika Westerberg link, depth); 840fdd92e89SMika Westerberg tb_switch_put(parent_sw); 841fdd92e89SMika Westerberg return; 842fdd92e89SMika Westerberg } 843fdd92e89SMika Westerberg 844b5db76dbSMika Westerberg pm_runtime_get_sync(&parent_sw->dev); 845b5db76dbSMika Westerberg 846b5db76dbSMika Westerberg sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); 847b5db76dbSMika Westerberg if (!IS_ERR(sw)) { 848b5db76dbSMika Westerberg sw->connection_id = pkg->connection_id; 849b5db76dbSMika Westerberg sw->connection_key = pkg->connection_key; 850b5db76dbSMika Westerberg sw->link = link; 851b5db76dbSMika Westerberg sw->depth = depth; 852b5db76dbSMika Westerberg sw->authorized = authorized; 853b5db76dbSMika Westerberg sw->security_level = security_level; 854b5db76dbSMika Westerberg sw->boot = boot; 85591c0c120SMika Westerberg sw->link_speed = speed_gen3 ? 20 : 10; 85691c0c120SMika Westerberg sw->link_width = dual_lane ? 2 : 1; 857b5db76dbSMika Westerberg sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name)); 858b5db76dbSMika Westerberg 859b5db76dbSMika Westerberg if (add_switch(parent_sw, sw)) 860b5db76dbSMika Westerberg tb_switch_put(sw); 861b5db76dbSMika Westerberg } 862b5db76dbSMika Westerberg 863b5db76dbSMika Westerberg pm_runtime_mark_last_busy(&parent_sw->dev); 864b5db76dbSMika Westerberg pm_runtime_put_autosuspend(&parent_sw->dev); 865f67cf491SMika Westerberg 866f67cf491SMika Westerberg tb_switch_put(parent_sw); 867f67cf491SMika Westerberg } 868f67cf491SMika Westerberg 869f67cf491SMika Westerberg static void 870f67cf491SMika Westerberg icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 871f67cf491SMika Westerberg { 872f67cf491SMika Westerberg const struct icm_fr_event_device_disconnected *pkg = 873f67cf491SMika Westerberg (const struct icm_fr_event_device_disconnected *)hdr; 874f67cf491SMika Westerberg struct tb_switch *sw; 875f67cf491SMika Westerberg u8 link, depth; 876f67cf491SMika Westerberg 877f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 878f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 879f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 880f67cf491SMika Westerberg 881f0342e75SMika Westerberg if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) { 882f67cf491SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 883f67cf491SMika Westerberg return; 884f67cf491SMika Westerberg } 885f67cf491SMika Westerberg 886f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 887f67cf491SMika Westerberg if (!sw) { 888f67cf491SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 889f67cf491SMika Westerberg depth); 890f67cf491SMika Westerberg return; 891f67cf491SMika Westerberg } 892f67cf491SMika Westerberg 893b658eb9dSMika Westerberg pm_runtime_get_sync(sw->dev.parent); 894b658eb9dSMika Westerberg 895f67cf491SMika Westerberg remove_switch(sw); 896b658eb9dSMika Westerberg 897b658eb9dSMika Westerberg pm_runtime_mark_last_busy(sw->dev.parent); 898b658eb9dSMika Westerberg pm_runtime_put_autosuspend(sw->dev.parent); 899b658eb9dSMika Westerberg 900f67cf491SMika Westerberg tb_switch_put(sw); 901f67cf491SMika Westerberg } 902f67cf491SMika Westerberg 903d1ff7024SMika Westerberg static void 904d1ff7024SMika Westerberg icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 905d1ff7024SMika Westerberg { 906d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_connected *pkg = 907d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_connected *)hdr; 908d1ff7024SMika Westerberg struct tb_xdomain *xd; 909d1ff7024SMika Westerberg struct tb_switch *sw; 910d1ff7024SMika Westerberg u8 link, depth; 911d1ff7024SMika Westerberg u64 route; 912d1ff7024SMika Westerberg 913d1ff7024SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 914d1ff7024SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 915d1ff7024SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 916d1ff7024SMika Westerberg 917f0342e75SMika Westerberg if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) { 918d1ff7024SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 919d1ff7024SMika Westerberg return; 920d1ff7024SMika Westerberg } 921d1ff7024SMika Westerberg 922d1ff7024SMika Westerberg route = get_route(pkg->local_route_hi, pkg->local_route_lo); 923d1ff7024SMika Westerberg 924d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 925d1ff7024SMika Westerberg if (xd) { 926d1ff7024SMika Westerberg u8 xd_phy_port, phy_port; 927d1ff7024SMika Westerberg 928d1ff7024SMika Westerberg xd_phy_port = phy_port_from_route(xd->route, xd->depth); 929d1ff7024SMika Westerberg phy_port = phy_port_from_route(route, depth); 930d1ff7024SMika Westerberg 931d1ff7024SMika Westerberg if (xd->depth == depth && xd_phy_port == phy_port) { 932ee487dd2SMika Westerberg update_xdomain(xd, route, link); 933d1ff7024SMika Westerberg tb_xdomain_put(xd); 934d1ff7024SMika Westerberg return; 935d1ff7024SMika Westerberg } 936d1ff7024SMika Westerberg 937d1ff7024SMika Westerberg /* 938d1ff7024SMika Westerberg * If we find an existing XDomain connection remove it 939d1ff7024SMika Westerberg * now. We need to go through login handshake and 940d1ff7024SMika Westerberg * everything anyway to be able to re-establish the 941d1ff7024SMika Westerberg * connection. 942d1ff7024SMika Westerberg */ 943d1ff7024SMika Westerberg remove_xdomain(xd); 944d1ff7024SMika Westerberg tb_xdomain_put(xd); 945d1ff7024SMika Westerberg } 946d1ff7024SMika Westerberg 947d1ff7024SMika Westerberg /* 948d1ff7024SMika Westerberg * Look if there already exists an XDomain in the same place 949d1ff7024SMika Westerberg * than the new one and in that case remove it because it is 950d1ff7024SMika Westerberg * most likely another host that got disconnected. 951d1ff7024SMika Westerberg */ 952d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 953d1ff7024SMika Westerberg if (!xd) { 954d1ff7024SMika Westerberg u8 dual_link; 955d1ff7024SMika Westerberg 956d1ff7024SMika Westerberg dual_link = dual_link_from_link(link); 957d1ff7024SMika Westerberg if (dual_link) 958d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, dual_link, 959d1ff7024SMika Westerberg depth); 960d1ff7024SMika Westerberg } 961d1ff7024SMika Westerberg if (xd) { 962d1ff7024SMika Westerberg remove_xdomain(xd); 963d1ff7024SMika Westerberg tb_xdomain_put(xd); 964d1ff7024SMika Westerberg } 965d1ff7024SMika Westerberg 966d1ff7024SMika Westerberg /* 967d1ff7024SMika Westerberg * If the user disconnected a switch during suspend and 968d1ff7024SMika Westerberg * connected another host to the same port, remove the switch 969d1ff7024SMika Westerberg * first. 970d1ff7024SMika Westerberg */ 9718f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, route); 9728f965efdSMika Westerberg if (sw) { 973d1ff7024SMika Westerberg remove_switch(sw); 9748f965efdSMika Westerberg tb_switch_put(sw); 9758f965efdSMika Westerberg } 976d1ff7024SMika Westerberg 977d1ff7024SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 978d1ff7024SMika Westerberg if (!sw) { 979d1ff7024SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 980d1ff7024SMika Westerberg depth); 981d1ff7024SMika Westerberg return; 982d1ff7024SMika Westerberg } 983d1ff7024SMika Westerberg 984ee487dd2SMika Westerberg add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link, 985ee487dd2SMika Westerberg depth); 986d1ff7024SMika Westerberg tb_switch_put(sw); 987d1ff7024SMika Westerberg } 988d1ff7024SMika Westerberg 989d1ff7024SMika Westerberg static void 990d1ff7024SMika Westerberg icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 991d1ff7024SMika Westerberg { 992d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_disconnected *pkg = 993d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_disconnected *)hdr; 994d1ff7024SMika Westerberg struct tb_xdomain *xd; 995d1ff7024SMika Westerberg 996d1ff7024SMika Westerberg /* 997d1ff7024SMika Westerberg * If the connection is through one or multiple devices, the 998d1ff7024SMika Westerberg * XDomain device is removed along with them so it is fine if we 999d1ff7024SMika Westerberg * cannot find it here. 1000d1ff7024SMika Westerberg */ 1001d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 1002d1ff7024SMika Westerberg if (xd) { 1003d1ff7024SMika Westerberg remove_xdomain(xd); 1004d1ff7024SMika Westerberg tb_xdomain_put(xd); 1005d1ff7024SMika Westerberg } 1006d1ff7024SMika Westerberg } 1007d1ff7024SMika Westerberg 10080d53827dSMika Westerberg static int icm_tr_cio_reset(struct tb *tb) 10090d53827dSMika Westerberg { 10100d53827dSMika Westerberg return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x777, BIT(1)); 10110d53827dSMika Westerberg } 10120d53827dSMika Westerberg 10134bac471dSRadion Mirchevsky static int 10144bac471dSRadion Mirchevsky icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 10159039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 10164bac471dSRadion Mirchevsky { 10174bac471dSRadion Mirchevsky struct icm_tr_pkg_driver_ready_response reply; 10184bac471dSRadion Mirchevsky struct icm_pkg_driver_ready request = { 10194bac471dSRadion Mirchevsky .hdr.code = ICM_DRIVER_READY, 10204bac471dSRadion Mirchevsky }; 10214bac471dSRadion Mirchevsky int ret; 10224bac471dSRadion Mirchevsky 10234bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10244bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10254bac471dSRadion Mirchevsky 1, 20000); 10264bac471dSRadion Mirchevsky if (ret) 10274bac471dSRadion Mirchevsky return ret; 10284bac471dSRadion Mirchevsky 10294bac471dSRadion Mirchevsky if (security_level) 10304bac471dSRadion Mirchevsky *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK; 10319039387eSMika Westerberg if (proto_version) 10329039387eSMika Westerberg *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> 10339039387eSMika Westerberg ICM_TR_INFO_PROTO_VERSION_SHIFT; 10344bac471dSRadion Mirchevsky if (nboot_acl) 10354bac471dSRadion Mirchevsky *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >> 10364bac471dSRadion Mirchevsky ICM_TR_INFO_BOOT_ACL_SHIFT; 10372d8ff0b5SMika Westerberg if (rpm) 10382d8ff0b5SMika Westerberg *rpm = !!(reply.hdr.flags & ICM_TR_FLAGS_RTD3); 10392d8ff0b5SMika Westerberg 10404bac471dSRadion Mirchevsky return 0; 10414bac471dSRadion Mirchevsky } 10424bac471dSRadion Mirchevsky 10434bac471dSRadion Mirchevsky static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw) 10444bac471dSRadion Mirchevsky { 10454bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_device request; 10464bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_device reply; 10474bac471dSRadion Mirchevsky int ret; 10484bac471dSRadion Mirchevsky 10494bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10504bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10514bac471dSRadion Mirchevsky request.hdr.code = ICM_APPROVE_DEVICE; 10524bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10534bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10544bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10554bac471dSRadion Mirchevsky 10564bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10574bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10584bac471dSRadion Mirchevsky 1, ICM_APPROVE_TIMEOUT); 10594bac471dSRadion Mirchevsky if (ret) 10604bac471dSRadion Mirchevsky return ret; 10614bac471dSRadion Mirchevsky 10624bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) { 10634bac471dSRadion Mirchevsky tb_warn(tb, "PCIe tunnel creation failed\n"); 10644bac471dSRadion Mirchevsky return -EIO; 10654bac471dSRadion Mirchevsky } 10664bac471dSRadion Mirchevsky 10674bac471dSRadion Mirchevsky return 0; 10684bac471dSRadion Mirchevsky } 10694bac471dSRadion Mirchevsky 10704bac471dSRadion Mirchevsky static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw) 10714bac471dSRadion Mirchevsky { 10724bac471dSRadion Mirchevsky struct icm_tr_pkg_add_device_key_response reply; 10734bac471dSRadion Mirchevsky struct icm_tr_pkg_add_device_key request; 10744bac471dSRadion Mirchevsky int ret; 10754bac471dSRadion Mirchevsky 10764bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10774bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10784bac471dSRadion Mirchevsky request.hdr.code = ICM_ADD_DEVICE_KEY; 10794bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10804bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10814bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10824bac471dSRadion Mirchevsky memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 10834bac471dSRadion Mirchevsky 10844bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10854bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10864bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 10874bac471dSRadion Mirchevsky if (ret) 10884bac471dSRadion Mirchevsky return ret; 10894bac471dSRadion Mirchevsky 10904bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) { 10914bac471dSRadion Mirchevsky tb_warn(tb, "Adding key to switch failed\n"); 10924bac471dSRadion Mirchevsky return -EIO; 10934bac471dSRadion Mirchevsky } 10944bac471dSRadion Mirchevsky 10954bac471dSRadion Mirchevsky return 0; 10964bac471dSRadion Mirchevsky } 10974bac471dSRadion Mirchevsky 10984bac471dSRadion Mirchevsky static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 10994bac471dSRadion Mirchevsky const u8 *challenge, u8 *response) 11004bac471dSRadion Mirchevsky { 11014bac471dSRadion Mirchevsky struct icm_tr_pkg_challenge_device_response reply; 11024bac471dSRadion Mirchevsky struct icm_tr_pkg_challenge_device request; 11034bac471dSRadion Mirchevsky int ret; 11044bac471dSRadion Mirchevsky 11054bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11064bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 11074bac471dSRadion Mirchevsky request.hdr.code = ICM_CHALLENGE_DEVICE; 11084bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 11094bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 11104bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 11114bac471dSRadion Mirchevsky memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 11124bac471dSRadion Mirchevsky 11134bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11144bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11154bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11164bac471dSRadion Mirchevsky if (ret) 11174bac471dSRadion Mirchevsky return ret; 11184bac471dSRadion Mirchevsky 11194bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11204bac471dSRadion Mirchevsky return -EKEYREJECTED; 11214bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 11224bac471dSRadion Mirchevsky return -ENOKEY; 11234bac471dSRadion Mirchevsky 11244bac471dSRadion Mirchevsky memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 11254bac471dSRadion Mirchevsky 11264bac471dSRadion Mirchevsky return 0; 11274bac471dSRadion Mirchevsky } 11284bac471dSRadion Mirchevsky 1129*180b0689SMika Westerberg static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, 1130*180b0689SMika Westerberg int transmit_path, int transmit_ring, 1131*180b0689SMika Westerberg int receive_path, int receive_ring) 11324bac471dSRadion Mirchevsky { 11334bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_xdomain_response reply; 11344bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_xdomain request; 11354bac471dSRadion Mirchevsky int ret; 11364bac471dSRadion Mirchevsky 11374bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11384bac471dSRadion Mirchevsky request.hdr.code = ICM_APPROVE_XDOMAIN; 11394bac471dSRadion Mirchevsky request.route_hi = upper_32_bits(xd->route); 11404bac471dSRadion Mirchevsky request.route_lo = lower_32_bits(xd->route); 1141*180b0689SMika Westerberg request.transmit_path = transmit_path; 1142*180b0689SMika Westerberg request.transmit_ring = transmit_ring; 1143*180b0689SMika Westerberg request.receive_path = receive_path; 1144*180b0689SMika Westerberg request.receive_ring = receive_ring; 11454bac471dSRadion Mirchevsky memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 11464bac471dSRadion Mirchevsky 11474bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11484bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11494bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11504bac471dSRadion Mirchevsky if (ret) 11514bac471dSRadion Mirchevsky return ret; 11524bac471dSRadion Mirchevsky 11534bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11544bac471dSRadion Mirchevsky return -EIO; 11554bac471dSRadion Mirchevsky 11564bac471dSRadion Mirchevsky return 0; 11574bac471dSRadion Mirchevsky } 11584bac471dSRadion Mirchevsky 11594bac471dSRadion Mirchevsky static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd, 11604bac471dSRadion Mirchevsky int stage) 11614bac471dSRadion Mirchevsky { 11624bac471dSRadion Mirchevsky struct icm_tr_pkg_disconnect_xdomain_response reply; 11634bac471dSRadion Mirchevsky struct icm_tr_pkg_disconnect_xdomain request; 11644bac471dSRadion Mirchevsky int ret; 11654bac471dSRadion Mirchevsky 11664bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11674bac471dSRadion Mirchevsky request.hdr.code = ICM_DISCONNECT_XDOMAIN; 11684bac471dSRadion Mirchevsky request.stage = stage; 11694bac471dSRadion Mirchevsky request.route_hi = upper_32_bits(xd->route); 11704bac471dSRadion Mirchevsky request.route_lo = lower_32_bits(xd->route); 11714bac471dSRadion Mirchevsky memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 11724bac471dSRadion Mirchevsky 11734bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11744bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11754bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11764bac471dSRadion Mirchevsky if (ret) 11774bac471dSRadion Mirchevsky return ret; 11784bac471dSRadion Mirchevsky 11794bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11804bac471dSRadion Mirchevsky return -EIO; 11814bac471dSRadion Mirchevsky 11824bac471dSRadion Mirchevsky return 0; 11834bac471dSRadion Mirchevsky } 11844bac471dSRadion Mirchevsky 1185*180b0689SMika Westerberg static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, 1186*180b0689SMika Westerberg int transmit_path, int transmit_ring, 1187*180b0689SMika Westerberg int receive_path, int receive_ring) 11884bac471dSRadion Mirchevsky { 11894bac471dSRadion Mirchevsky int ret; 11904bac471dSRadion Mirchevsky 11914bac471dSRadion Mirchevsky ret = icm_tr_xdomain_tear_down(tb, xd, 1); 11924bac471dSRadion Mirchevsky if (ret) 11934bac471dSRadion Mirchevsky return ret; 11944bac471dSRadion Mirchevsky 11954bac471dSRadion Mirchevsky usleep_range(10, 50); 11964bac471dSRadion Mirchevsky return icm_tr_xdomain_tear_down(tb, xd, 2); 11974bac471dSRadion Mirchevsky } 11984bac471dSRadion Mirchevsky 11994bac471dSRadion Mirchevsky static void 12003cdb9446SMika Westerberg __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, 12013cdb9446SMika Westerberg bool force_rtd3) 12024bac471dSRadion Mirchevsky { 12034bac471dSRadion Mirchevsky const struct icm_tr_event_device_connected *pkg = 12044bac471dSRadion Mirchevsky (const struct icm_tr_event_device_connected *)hdr; 120591c0c120SMika Westerberg bool authorized, boot, dual_lane, speed_gen3; 12064bac471dSRadion Mirchevsky enum tb_security_level security_level; 12074bac471dSRadion Mirchevsky struct tb_switch *sw, *parent_sw; 12084bac471dSRadion Mirchevsky struct tb_xdomain *xd; 12094bac471dSRadion Mirchevsky u64 route; 12104bac471dSRadion Mirchevsky 12113cdb9446SMika Westerberg icm_postpone_rescan(tb); 12123cdb9446SMika Westerberg 12134bac471dSRadion Mirchevsky /* 12144bac471dSRadion Mirchevsky * Currently we don't use the QoS information coming with the 12154bac471dSRadion Mirchevsky * device connected message so simply just ignore that extra 12164bac471dSRadion Mirchevsky * packet for now. 12174bac471dSRadion Mirchevsky */ 12184bac471dSRadion Mirchevsky if (pkg->hdr.packet_id) 12194bac471dSRadion Mirchevsky return; 12204bac471dSRadion Mirchevsky 12214bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 12224bac471dSRadion Mirchevsky authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 12234bac471dSRadion Mirchevsky security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 12244bac471dSRadion Mirchevsky ICM_FLAGS_SLEVEL_SHIFT; 12254bac471dSRadion Mirchevsky boot = pkg->link_info & ICM_LINK_INFO_BOOT; 122691c0c120SMika Westerberg dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; 122791c0c120SMika Westerberg speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; 12284bac471dSRadion Mirchevsky 12294bac471dSRadion Mirchevsky if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 12304bac471dSRadion Mirchevsky tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n", 12314bac471dSRadion Mirchevsky route); 12324bac471dSRadion Mirchevsky return; 12334bac471dSRadion Mirchevsky } 12344bac471dSRadion Mirchevsky 12354bac471dSRadion Mirchevsky sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 12364bac471dSRadion Mirchevsky if (sw) { 12374bac471dSRadion Mirchevsky /* Update the switch if it is still in the same place */ 12384bac471dSRadion Mirchevsky if (tb_route(sw) == route && !!sw->authorized == authorized) { 12394bac471dSRadion Mirchevsky parent_sw = tb_to_switch(sw->dev.parent); 12404bac471dSRadion Mirchevsky update_switch(parent_sw, sw, route, pkg->connection_id, 12414bac471dSRadion Mirchevsky 0, 0, 0, boot); 12424bac471dSRadion Mirchevsky tb_switch_put(sw); 12434bac471dSRadion Mirchevsky return; 12444bac471dSRadion Mirchevsky } 12454bac471dSRadion Mirchevsky 12464bac471dSRadion Mirchevsky remove_switch(sw); 12474bac471dSRadion Mirchevsky tb_switch_put(sw); 12484bac471dSRadion Mirchevsky } 12494bac471dSRadion Mirchevsky 12504bac471dSRadion Mirchevsky /* Another switch with the same address */ 12514bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, route); 12524bac471dSRadion Mirchevsky if (sw) { 12534bac471dSRadion Mirchevsky remove_switch(sw); 12544bac471dSRadion Mirchevsky tb_switch_put(sw); 12554bac471dSRadion Mirchevsky } 12564bac471dSRadion Mirchevsky 12574bac471dSRadion Mirchevsky /* XDomain connection with the same address */ 12584bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 12594bac471dSRadion Mirchevsky if (xd) { 12604bac471dSRadion Mirchevsky remove_xdomain(xd); 12614bac471dSRadion Mirchevsky tb_xdomain_put(xd); 12624bac471dSRadion Mirchevsky } 12634bac471dSRadion Mirchevsky 12644bac471dSRadion Mirchevsky parent_sw = tb_switch_find_by_route(tb, get_parent_route(route)); 12654bac471dSRadion Mirchevsky if (!parent_sw) { 12664bac471dSRadion Mirchevsky tb_err(tb, "failed to find parent switch for %llx\n", route); 12674bac471dSRadion Mirchevsky return; 12684bac471dSRadion Mirchevsky } 12694bac471dSRadion Mirchevsky 1270b5db76dbSMika Westerberg pm_runtime_get_sync(&parent_sw->dev); 1271b5db76dbSMika Westerberg 1272b5db76dbSMika Westerberg sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); 1273b5db76dbSMika Westerberg if (!IS_ERR(sw)) { 1274b5db76dbSMika Westerberg sw->connection_id = pkg->connection_id; 1275b5db76dbSMika Westerberg sw->authorized = authorized; 1276b5db76dbSMika Westerberg sw->security_level = security_level; 1277b5db76dbSMika Westerberg sw->boot = boot; 127891c0c120SMika Westerberg sw->link_speed = speed_gen3 ? 20 : 10; 127991c0c120SMika Westerberg sw->link_width = dual_lane ? 2 : 1; 1280b5db76dbSMika Westerberg sw->rpm = force_rtd3; 1281b5db76dbSMika Westerberg if (!sw->rpm) 1282b5db76dbSMika Westerberg sw->rpm = intel_vss_is_rtd3(pkg->ep_name, 1283b5db76dbSMika Westerberg sizeof(pkg->ep_name)); 1284b5db76dbSMika Westerberg 1285b5db76dbSMika Westerberg if (add_switch(parent_sw, sw)) 1286b5db76dbSMika Westerberg tb_switch_put(sw); 1287b5db76dbSMika Westerberg } 1288b5db76dbSMika Westerberg 1289b5db76dbSMika Westerberg pm_runtime_mark_last_busy(&parent_sw->dev); 1290b5db76dbSMika Westerberg pm_runtime_put_autosuspend(&parent_sw->dev); 12914bac471dSRadion Mirchevsky 12924bac471dSRadion Mirchevsky tb_switch_put(parent_sw); 12934bac471dSRadion Mirchevsky } 12944bac471dSRadion Mirchevsky 12954bac471dSRadion Mirchevsky static void 12963cdb9446SMika Westerberg icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 12973cdb9446SMika Westerberg { 12983cdb9446SMika Westerberg __icm_tr_device_connected(tb, hdr, false); 12993cdb9446SMika Westerberg } 13003cdb9446SMika Westerberg 13013cdb9446SMika Westerberg static void 13024bac471dSRadion Mirchevsky icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 13034bac471dSRadion Mirchevsky { 13044bac471dSRadion Mirchevsky const struct icm_tr_event_device_disconnected *pkg = 13054bac471dSRadion Mirchevsky (const struct icm_tr_event_device_disconnected *)hdr; 13064bac471dSRadion Mirchevsky struct tb_switch *sw; 13074bac471dSRadion Mirchevsky u64 route; 13084bac471dSRadion Mirchevsky 13094bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 13104bac471dSRadion Mirchevsky 13114bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, route); 13124bac471dSRadion Mirchevsky if (!sw) { 13134bac471dSRadion Mirchevsky tb_warn(tb, "no switch exists at %llx, ignoring\n", route); 13144bac471dSRadion Mirchevsky return; 13154bac471dSRadion Mirchevsky } 1316b658eb9dSMika Westerberg pm_runtime_get_sync(sw->dev.parent); 13174bac471dSRadion Mirchevsky 13184bac471dSRadion Mirchevsky remove_switch(sw); 1319b658eb9dSMika Westerberg 1320b658eb9dSMika Westerberg pm_runtime_mark_last_busy(sw->dev.parent); 1321b658eb9dSMika Westerberg pm_runtime_put_autosuspend(sw->dev.parent); 1322b658eb9dSMika Westerberg 13234bac471dSRadion Mirchevsky tb_switch_put(sw); 13244bac471dSRadion Mirchevsky } 13254bac471dSRadion Mirchevsky 13264bac471dSRadion Mirchevsky static void 13274bac471dSRadion Mirchevsky icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 13284bac471dSRadion Mirchevsky { 13294bac471dSRadion Mirchevsky const struct icm_tr_event_xdomain_connected *pkg = 13304bac471dSRadion Mirchevsky (const struct icm_tr_event_xdomain_connected *)hdr; 13314bac471dSRadion Mirchevsky struct tb_xdomain *xd; 13324bac471dSRadion Mirchevsky struct tb_switch *sw; 13334bac471dSRadion Mirchevsky u64 route; 13344bac471dSRadion Mirchevsky 13354bac471dSRadion Mirchevsky if (!tb->root_switch) 13364bac471dSRadion Mirchevsky return; 13374bac471dSRadion Mirchevsky 13384bac471dSRadion Mirchevsky route = get_route(pkg->local_route_hi, pkg->local_route_lo); 13394bac471dSRadion Mirchevsky 13404bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 13414bac471dSRadion Mirchevsky if (xd) { 13424bac471dSRadion Mirchevsky if (xd->route == route) { 13434bac471dSRadion Mirchevsky update_xdomain(xd, route, 0); 13444bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13454bac471dSRadion Mirchevsky return; 13464bac471dSRadion Mirchevsky } 13474bac471dSRadion Mirchevsky 13484bac471dSRadion Mirchevsky remove_xdomain(xd); 13494bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13504bac471dSRadion Mirchevsky } 13514bac471dSRadion Mirchevsky 13524bac471dSRadion Mirchevsky /* An existing xdomain with the same address */ 13534bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 13544bac471dSRadion Mirchevsky if (xd) { 13554bac471dSRadion Mirchevsky remove_xdomain(xd); 13564bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13574bac471dSRadion Mirchevsky } 13584bac471dSRadion Mirchevsky 13594bac471dSRadion Mirchevsky /* 13604bac471dSRadion Mirchevsky * If the user disconnected a switch during suspend and 13614bac471dSRadion Mirchevsky * connected another host to the same port, remove the switch 13624bac471dSRadion Mirchevsky * first. 13634bac471dSRadion Mirchevsky */ 13648f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, route); 13658f965efdSMika Westerberg if (sw) { 13664bac471dSRadion Mirchevsky remove_switch(sw); 13678f965efdSMika Westerberg tb_switch_put(sw); 13688f965efdSMika Westerberg } 13694bac471dSRadion Mirchevsky 13704bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, get_parent_route(route)); 13714bac471dSRadion Mirchevsky if (!sw) { 13724bac471dSRadion Mirchevsky tb_warn(tb, "no switch exists at %llx, ignoring\n", route); 13734bac471dSRadion Mirchevsky return; 13744bac471dSRadion Mirchevsky } 13754bac471dSRadion Mirchevsky 13764bac471dSRadion Mirchevsky add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0); 13774bac471dSRadion Mirchevsky tb_switch_put(sw); 13784bac471dSRadion Mirchevsky } 13794bac471dSRadion Mirchevsky 13804bac471dSRadion Mirchevsky static void 13814bac471dSRadion Mirchevsky icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 13824bac471dSRadion Mirchevsky { 13834bac471dSRadion Mirchevsky const struct icm_tr_event_xdomain_disconnected *pkg = 13844bac471dSRadion Mirchevsky (const struct icm_tr_event_xdomain_disconnected *)hdr; 13854bac471dSRadion Mirchevsky struct tb_xdomain *xd; 13864bac471dSRadion Mirchevsky u64 route; 13874bac471dSRadion Mirchevsky 13884bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 13894bac471dSRadion Mirchevsky 13904bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 13914bac471dSRadion Mirchevsky if (xd) { 13924bac471dSRadion Mirchevsky remove_xdomain(xd); 13934bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13944bac471dSRadion Mirchevsky } 13954bac471dSRadion Mirchevsky } 13964bac471dSRadion Mirchevsky 1397f67cf491SMika Westerberg static struct pci_dev *get_upstream_port(struct pci_dev *pdev) 1398f67cf491SMika Westerberg { 1399f67cf491SMika Westerberg struct pci_dev *parent; 1400f67cf491SMika Westerberg 1401f67cf491SMika Westerberg parent = pci_upstream_bridge(pdev); 1402f67cf491SMika Westerberg while (parent) { 1403f67cf491SMika Westerberg if (!pci_is_pcie(parent)) 1404f67cf491SMika Westerberg return NULL; 1405f67cf491SMika Westerberg if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) 1406f67cf491SMika Westerberg break; 1407f67cf491SMika Westerberg parent = pci_upstream_bridge(parent); 1408f67cf491SMika Westerberg } 1409f67cf491SMika Westerberg 1410f67cf491SMika Westerberg if (!parent) 1411f67cf491SMika Westerberg return NULL; 1412f67cf491SMika Westerberg 1413f67cf491SMika Westerberg switch (parent->device) { 1414f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: 1415f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: 1416f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: 1417f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: 1418f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: 1419c4630d6aSMika Westerberg case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: 1420c4630d6aSMika Westerberg case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: 1421f67cf491SMika Westerberg return parent; 1422f67cf491SMika Westerberg } 1423f67cf491SMika Westerberg 1424f67cf491SMika Westerberg return NULL; 1425f67cf491SMika Westerberg } 1426f67cf491SMika Westerberg 1427f67cf491SMika Westerberg static bool icm_ar_is_supported(struct tb *tb) 1428f67cf491SMika Westerberg { 1429f67cf491SMika Westerberg struct pci_dev *upstream_port; 1430f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1431f67cf491SMika Westerberg 1432f67cf491SMika Westerberg /* 1433f67cf491SMika Westerberg * Starting from Alpine Ridge we can use ICM on Apple machines 1434f67cf491SMika Westerberg * as well. We just need to reset and re-enable it first. 1435354a7a77SMika Westerberg * However, only start it if explicitly asked by the user. 1436f67cf491SMika Westerberg */ 1437354a7a77SMika Westerberg if (icm_firmware_running(tb->nhi)) 1438f67cf491SMika Westerberg return true; 1439354a7a77SMika Westerberg if (!start_icm) 1440354a7a77SMika Westerberg return false; 1441f67cf491SMika Westerberg 1442f67cf491SMika Westerberg /* 1443f67cf491SMika Westerberg * Find the upstream PCIe port in case we need to do reset 1444f67cf491SMika Westerberg * through its vendor specific registers. 1445f67cf491SMika Westerberg */ 1446f67cf491SMika Westerberg upstream_port = get_upstream_port(tb->nhi->pdev); 1447f67cf491SMika Westerberg if (upstream_port) { 1448f67cf491SMika Westerberg int cap; 1449f67cf491SMika Westerberg 1450f67cf491SMika Westerberg cap = pci_find_ext_capability(upstream_port, 1451f67cf491SMika Westerberg PCI_EXT_CAP_ID_VNDR); 1452f67cf491SMika Westerberg if (cap > 0) { 1453f67cf491SMika Westerberg icm->upstream_port = upstream_port; 1454f67cf491SMika Westerberg icm->vnd_cap = cap; 1455f67cf491SMika Westerberg 1456f67cf491SMika Westerberg return true; 1457f67cf491SMika Westerberg } 1458f67cf491SMika Westerberg } 1459f67cf491SMika Westerberg 1460f67cf491SMika Westerberg return false; 1461f67cf491SMika Westerberg } 1462f67cf491SMika Westerberg 14630d53827dSMika Westerberg static int icm_ar_cio_reset(struct tb *tb) 14640d53827dSMika Westerberg { 14650d53827dSMika Westerberg return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x50, BIT(9)); 14660d53827dSMika Westerberg } 14670d53827dSMika Westerberg 1468f67cf491SMika Westerberg static int icm_ar_get_mode(struct tb *tb) 1469f67cf491SMika Westerberg { 1470f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1471e4be8c9bSMika Westerberg int retries = 60; 1472f67cf491SMika Westerberg u32 val; 1473f67cf491SMika Westerberg 1474f67cf491SMika Westerberg do { 1475f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1476f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1477f67cf491SMika Westerberg break; 1478e4be8c9bSMika Westerberg msleep(50); 1479f67cf491SMika Westerberg } while (--retries); 1480f67cf491SMika Westerberg 1481f67cf491SMika Westerberg if (!retries) { 1482f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "ICM firmware not authenticated\n"); 1483f67cf491SMika Westerberg return -ENODEV; 1484f67cf491SMika Westerberg } 1485f67cf491SMika Westerberg 1486f67cf491SMika Westerberg return nhi_mailbox_mode(nhi); 1487f67cf491SMika Westerberg } 1488f67cf491SMika Westerberg 14899aaa3b8bSMika Westerberg static int 14909aaa3b8bSMika Westerberg icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, 14919039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 14929aaa3b8bSMika Westerberg { 14939aaa3b8bSMika Westerberg struct icm_ar_pkg_driver_ready_response reply; 14949aaa3b8bSMika Westerberg struct icm_pkg_driver_ready request = { 14959aaa3b8bSMika Westerberg .hdr.code = ICM_DRIVER_READY, 14969aaa3b8bSMika Westerberg }; 14979aaa3b8bSMika Westerberg int ret; 14989aaa3b8bSMika Westerberg 14999aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 15009aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 15019aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 15029aaa3b8bSMika Westerberg if (ret) 15039aaa3b8bSMika Westerberg return ret; 15049aaa3b8bSMika Westerberg 15059aaa3b8bSMika Westerberg if (security_level) 15069aaa3b8bSMika Westerberg *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK; 15079aaa3b8bSMika Westerberg if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED)) 15089aaa3b8bSMika Westerberg *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >> 15099aaa3b8bSMika Westerberg ICM_AR_INFO_BOOT_ACL_SHIFT; 15102d8ff0b5SMika Westerberg if (rpm) 15112d8ff0b5SMika Westerberg *rpm = !!(reply.hdr.flags & ICM_AR_FLAGS_RTD3); 15122d8ff0b5SMika Westerberg 15139aaa3b8bSMika Westerberg return 0; 15149aaa3b8bSMika Westerberg } 15159aaa3b8bSMika Westerberg 1516f67cf491SMika Westerberg static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 1517f67cf491SMika Westerberg { 1518f67cf491SMika Westerberg struct icm_ar_pkg_get_route_response reply; 1519f67cf491SMika Westerberg struct icm_ar_pkg_get_route request = { 1520f67cf491SMika Westerberg .hdr = { .code = ICM_GET_ROUTE }, 1521f67cf491SMika Westerberg .link_info = depth << ICM_LINK_INFO_DEPTH_SHIFT | link, 1522f67cf491SMika Westerberg }; 1523f67cf491SMika Westerberg int ret; 1524f67cf491SMika Westerberg 1525f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 1526f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 1527f67cf491SMika Westerberg 1, ICM_TIMEOUT); 1528f67cf491SMika Westerberg if (ret) 1529f67cf491SMika Westerberg return ret; 1530f67cf491SMika Westerberg 1531f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 1532f67cf491SMika Westerberg return -EIO; 1533f67cf491SMika Westerberg 1534f67cf491SMika Westerberg *route = get_route(reply.route_hi, reply.route_lo); 1535f67cf491SMika Westerberg return 0; 1536f67cf491SMika Westerberg } 1537f67cf491SMika Westerberg 15389aaa3b8bSMika Westerberg static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) 15399aaa3b8bSMika Westerberg { 15409aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 15419aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 15429aaa3b8bSMika Westerberg .hdr = { .code = ICM_PREBOOT_ACL }, 15439aaa3b8bSMika Westerberg }; 15449aaa3b8bSMika Westerberg int ret, i; 15459aaa3b8bSMika Westerberg 15469aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 15479aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 15489aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 15499aaa3b8bSMika Westerberg if (ret) 15509aaa3b8bSMika Westerberg return ret; 15519aaa3b8bSMika Westerberg 15529aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 15539aaa3b8bSMika Westerberg return -EIO; 15549aaa3b8bSMika Westerberg 15559aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 15569aaa3b8bSMika Westerberg u32 *uuid = (u32 *)&uuids[i]; 15579aaa3b8bSMika Westerberg 15589aaa3b8bSMika Westerberg uuid[0] = reply.acl[i].uuid_lo; 15599aaa3b8bSMika Westerberg uuid[1] = reply.acl[i].uuid_hi; 15609aaa3b8bSMika Westerberg 15619aaa3b8bSMika Westerberg if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) { 15629aaa3b8bSMika Westerberg /* Map empty entries to null UUID */ 15639aaa3b8bSMika Westerberg uuid[0] = 0; 15649aaa3b8bSMika Westerberg uuid[1] = 0; 1565dd010bd7SMika Westerberg } else if (uuid[0] != 0 || uuid[1] != 0) { 15669aaa3b8bSMika Westerberg /* Upper two DWs are always one's */ 15679aaa3b8bSMika Westerberg uuid[2] = 0xffffffff; 15689aaa3b8bSMika Westerberg uuid[3] = 0xffffffff; 15699aaa3b8bSMika Westerberg } 15709aaa3b8bSMika Westerberg } 15719aaa3b8bSMika Westerberg 15729aaa3b8bSMika Westerberg return ret; 15739aaa3b8bSMika Westerberg } 15749aaa3b8bSMika Westerberg 15759aaa3b8bSMika Westerberg static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, 15769aaa3b8bSMika Westerberg size_t nuuids) 15779aaa3b8bSMika Westerberg { 15789aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 15799aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 15809aaa3b8bSMika Westerberg .hdr = { 15819aaa3b8bSMika Westerberg .code = ICM_PREBOOT_ACL, 15829aaa3b8bSMika Westerberg .flags = ICM_FLAGS_WRITE, 15839aaa3b8bSMika Westerberg }, 15849aaa3b8bSMika Westerberg }; 15859aaa3b8bSMika Westerberg int ret, i; 15869aaa3b8bSMika Westerberg 15879aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 15889aaa3b8bSMika Westerberg const u32 *uuid = (const u32 *)&uuids[i]; 15899aaa3b8bSMika Westerberg 15909aaa3b8bSMika Westerberg if (uuid_is_null(&uuids[i])) { 15919aaa3b8bSMika Westerberg /* 15929aaa3b8bSMika Westerberg * Map null UUID to the empty (all one) entries 15939aaa3b8bSMika Westerberg * for ICM. 15949aaa3b8bSMika Westerberg */ 15959aaa3b8bSMika Westerberg request.acl[i].uuid_lo = 0xffffffff; 15969aaa3b8bSMika Westerberg request.acl[i].uuid_hi = 0xffffffff; 15979aaa3b8bSMika Westerberg } else { 15989aaa3b8bSMika Westerberg /* Two high DWs need to be set to all one */ 15999aaa3b8bSMika Westerberg if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff) 16009aaa3b8bSMika Westerberg return -EINVAL; 16019aaa3b8bSMika Westerberg 16029aaa3b8bSMika Westerberg request.acl[i].uuid_lo = uuid[0]; 16039aaa3b8bSMika Westerberg request.acl[i].uuid_hi = uuid[1]; 16049aaa3b8bSMika Westerberg } 16059aaa3b8bSMika Westerberg } 16069aaa3b8bSMika Westerberg 16079aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 16089aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 16099aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 16109aaa3b8bSMika Westerberg if (ret) 16119aaa3b8bSMika Westerberg return ret; 16129aaa3b8bSMika Westerberg 16139aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 16149aaa3b8bSMika Westerberg return -EIO; 16159aaa3b8bSMika Westerberg 16169aaa3b8bSMika Westerberg return 0; 16179aaa3b8bSMika Westerberg } 16189aaa3b8bSMika Westerberg 16193cdb9446SMika Westerberg static int 16203cdb9446SMika Westerberg icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, 16219039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 16223cdb9446SMika Westerberg { 16233cdb9446SMika Westerberg struct icm_tr_pkg_driver_ready_response reply; 16243cdb9446SMika Westerberg struct icm_pkg_driver_ready request = { 16253cdb9446SMika Westerberg .hdr.code = ICM_DRIVER_READY, 16263cdb9446SMika Westerberg }; 16273cdb9446SMika Westerberg int ret; 16283cdb9446SMika Westerberg 16293cdb9446SMika Westerberg memset(&reply, 0, sizeof(reply)); 16303cdb9446SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 16313cdb9446SMika Westerberg 1, 20000); 16323cdb9446SMika Westerberg if (ret) 16333cdb9446SMika Westerberg return ret; 16343cdb9446SMika Westerberg 16359039387eSMika Westerberg if (proto_version) 16369039387eSMika Westerberg *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> 16379039387eSMika Westerberg ICM_TR_INFO_PROTO_VERSION_SHIFT; 16389039387eSMika Westerberg 16393cdb9446SMika Westerberg /* Ice Lake always supports RTD3 */ 16403cdb9446SMika Westerberg if (rpm) 16413cdb9446SMika Westerberg *rpm = true; 16423cdb9446SMika Westerberg 16433cdb9446SMika Westerberg return 0; 16443cdb9446SMika Westerberg } 16453cdb9446SMika Westerberg 16463cdb9446SMika Westerberg static void icm_icl_set_uuid(struct tb *tb) 16473cdb9446SMika Westerberg { 16483cdb9446SMika Westerberg struct tb_nhi *nhi = tb->nhi; 16493cdb9446SMika Westerberg u32 uuid[4]; 16503cdb9446SMika Westerberg 16513cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]); 16523cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]); 16533cdb9446SMika Westerberg uuid[2] = 0xffffffff; 16543cdb9446SMika Westerberg uuid[3] = 0xffffffff; 16553cdb9446SMika Westerberg 16563cdb9446SMika Westerberg tb->root_switch->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL); 16573cdb9446SMika Westerberg } 16583cdb9446SMika Westerberg 16593cdb9446SMika Westerberg static void 16603cdb9446SMika Westerberg icm_icl_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 16613cdb9446SMika Westerberg { 16623cdb9446SMika Westerberg __icm_tr_device_connected(tb, hdr, true); 16633cdb9446SMika Westerberg } 16643cdb9446SMika Westerberg 16653cdb9446SMika Westerberg static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr) 16663cdb9446SMika Westerberg { 16673cdb9446SMika Westerberg const struct icm_icl_event_rtd3_veto *pkg = 16683cdb9446SMika Westerberg (const struct icm_icl_event_rtd3_veto *)hdr; 16693cdb9446SMika Westerberg 16703cdb9446SMika Westerberg tb_dbg(tb, "ICM rtd3 veto=0x%08x\n", pkg->veto_reason); 16713cdb9446SMika Westerberg 16723cdb9446SMika Westerberg if (pkg->veto_reason) 16733cdb9446SMika Westerberg icm_veto_begin(tb); 16743cdb9446SMika Westerberg else 16753cdb9446SMika Westerberg icm_veto_end(tb); 16763cdb9446SMika Westerberg } 16773cdb9446SMika Westerberg 167857d8df68SMika Westerberg static bool icm_tgl_is_supported(struct tb *tb) 167957d8df68SMika Westerberg { 168049f2a7f4SMika Westerberg u32 val; 168149f2a7f4SMika Westerberg 168257d8df68SMika Westerberg /* 168357d8df68SMika Westerberg * If the firmware is not running use software CM. This platform 168457d8df68SMika Westerberg * should fully support both. 168557d8df68SMika Westerberg */ 168649f2a7f4SMika Westerberg val = ioread32(tb->nhi->iobase + REG_FW_STS); 168749f2a7f4SMika Westerberg return !!(val & REG_FW_STS_NVM_AUTH_DONE); 168857d8df68SMika Westerberg } 168957d8df68SMika Westerberg 1690f67cf491SMika Westerberg static void icm_handle_notification(struct work_struct *work) 1691f67cf491SMika Westerberg { 1692f67cf491SMika Westerberg struct icm_notification *n = container_of(work, typeof(*n), work); 1693f67cf491SMika Westerberg struct tb *tb = n->tb; 1694f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1695f67cf491SMika Westerberg 1696f67cf491SMika Westerberg mutex_lock(&tb->lock); 1697f67cf491SMika Westerberg 169886da809dSMika Westerberg /* 169986da809dSMika Westerberg * When the domain is stopped we flush its workqueue but before 170086da809dSMika Westerberg * that the root switch is removed. In that case we should treat 170186da809dSMika Westerberg * the queued events as being canceled. 170286da809dSMika Westerberg */ 170386da809dSMika Westerberg if (tb->root_switch) { 1704f67cf491SMika Westerberg switch (n->pkg->code) { 1705f67cf491SMika Westerberg case ICM_EVENT_DEVICE_CONNECTED: 1706f67cf491SMika Westerberg icm->device_connected(tb, n->pkg); 1707f67cf491SMika Westerberg break; 1708f67cf491SMika Westerberg case ICM_EVENT_DEVICE_DISCONNECTED: 1709f67cf491SMika Westerberg icm->device_disconnected(tb, n->pkg); 1710f67cf491SMika Westerberg break; 1711d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_CONNECTED: 17125ca67688SMika Westerberg if (tb_is_xdomain_enabled()) 1713d1ff7024SMika Westerberg icm->xdomain_connected(tb, n->pkg); 1714d1ff7024SMika Westerberg break; 1715d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_DISCONNECTED: 17165ca67688SMika Westerberg if (tb_is_xdomain_enabled()) 1717d1ff7024SMika Westerberg icm->xdomain_disconnected(tb, n->pkg); 1718d1ff7024SMika Westerberg break; 17193cdb9446SMika Westerberg case ICM_EVENT_RTD3_VETO: 17203cdb9446SMika Westerberg icm->rtd3_veto(tb, n->pkg); 17213cdb9446SMika Westerberg break; 1722f67cf491SMika Westerberg } 172386da809dSMika Westerberg } 1724f67cf491SMika Westerberg 1725f67cf491SMika Westerberg mutex_unlock(&tb->lock); 1726f67cf491SMika Westerberg 1727f67cf491SMika Westerberg kfree(n->pkg); 1728f67cf491SMika Westerberg kfree(n); 1729f67cf491SMika Westerberg } 1730f67cf491SMika Westerberg 1731f67cf491SMika Westerberg static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, 1732f67cf491SMika Westerberg const void *buf, size_t size) 1733f67cf491SMika Westerberg { 1734f67cf491SMika Westerberg struct icm_notification *n; 1735f67cf491SMika Westerberg 1736f67cf491SMika Westerberg n = kmalloc(sizeof(*n), GFP_KERNEL); 1737f67cf491SMika Westerberg if (!n) 1738f67cf491SMika Westerberg return; 1739f67cf491SMika Westerberg 1740f67cf491SMika Westerberg INIT_WORK(&n->work, icm_handle_notification); 1741f67cf491SMika Westerberg n->pkg = kmemdup(buf, size, GFP_KERNEL); 1742f67cf491SMika Westerberg n->tb = tb; 1743f67cf491SMika Westerberg 1744f67cf491SMika Westerberg queue_work(tb->wq, &n->work); 1745f67cf491SMika Westerberg } 1746f67cf491SMika Westerberg 1747f67cf491SMika Westerberg static int 17489aaa3b8bSMika Westerberg __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, 17499039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 1750f67cf491SMika Westerberg { 17513080e197SMika Westerberg struct icm *icm = tb_priv(tb); 175244b51bbbSMika Westerberg unsigned int retries = 50; 1753f67cf491SMika Westerberg int ret; 1754f67cf491SMika Westerberg 17559039387eSMika Westerberg ret = icm->driver_ready(tb, security_level, proto_version, nboot_acl, 17569039387eSMika Westerberg rpm); 17573080e197SMika Westerberg if (ret) { 17583080e197SMika Westerberg tb_err(tb, "failed to send driver ready to ICM\n"); 1759f67cf491SMika Westerberg return ret; 17603080e197SMika Westerberg } 1761f67cf491SMika Westerberg 1762f67cf491SMika Westerberg /* 1763f67cf491SMika Westerberg * Hold on here until the switch config space is accessible so 1764f67cf491SMika Westerberg * that we can read root switch config successfully. 1765f67cf491SMika Westerberg */ 1766f67cf491SMika Westerberg do { 1767f67cf491SMika Westerberg struct tb_cfg_result res; 1768f67cf491SMika Westerberg u32 tmp; 1769f67cf491SMika Westerberg 1770f67cf491SMika Westerberg res = tb_cfg_read_raw(tb->ctl, &tmp, 0, 0, TB_CFG_SWITCH, 1771f67cf491SMika Westerberg 0, 1, 100); 1772f67cf491SMika Westerberg if (!res.err) 1773f67cf491SMika Westerberg return 0; 1774f67cf491SMika Westerberg 1775f67cf491SMika Westerberg msleep(50); 1776f67cf491SMika Westerberg } while (--retries); 1777f67cf491SMika Westerberg 177844b51bbbSMika Westerberg tb_err(tb, "failed to read root switch config space, giving up\n"); 1779f67cf491SMika Westerberg return -ETIMEDOUT; 1780f67cf491SMika Westerberg } 1781f67cf491SMika Westerberg 1782f67cf491SMika Westerberg static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) 1783f67cf491SMika Westerberg { 1784f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1785f67cf491SMika Westerberg u32 val; 1786f67cf491SMika Westerberg 1787ea9d7bb7SMika Westerberg if (!icm->upstream_port) 1788ea9d7bb7SMika Westerberg return -ENODEV; 1789ea9d7bb7SMika Westerberg 1790f67cf491SMika Westerberg /* Put ARC to wait for CIO reset event to happen */ 1791f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1792f67cf491SMika Westerberg val |= REG_FW_STS_CIO_RESET_REQ; 1793f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1794f67cf491SMika Westerberg 1795f67cf491SMika Westerberg /* Re-start ARC */ 1796f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1797f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_INVERT; 1798f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_CPU; 1799f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1800f67cf491SMika Westerberg 1801f67cf491SMika Westerberg /* Trigger CIO reset now */ 18020d53827dSMika Westerberg return icm->cio_reset(tb); 1803f67cf491SMika Westerberg } 1804f67cf491SMika Westerberg 1805f67cf491SMika Westerberg static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) 1806f67cf491SMika Westerberg { 1807f67cf491SMika Westerberg unsigned int retries = 10; 1808f67cf491SMika Westerberg int ret; 1809f67cf491SMika Westerberg u32 val; 1810f67cf491SMika Westerberg 1811f67cf491SMika Westerberg /* Check if the ICM firmware is already running */ 1812354a7a77SMika Westerberg if (icm_firmware_running(nhi)) 1813f67cf491SMika Westerberg return 0; 1814f67cf491SMika Westerberg 181562efe699SMika Westerberg dev_dbg(&nhi->pdev->dev, "starting ICM firmware\n"); 1816f67cf491SMika Westerberg 1817f67cf491SMika Westerberg ret = icm_firmware_reset(tb, nhi); 1818f67cf491SMika Westerberg if (ret) 1819f67cf491SMika Westerberg return ret; 1820f67cf491SMika Westerberg 1821f67cf491SMika Westerberg /* Wait until the ICM firmware tells us it is up and running */ 1822f67cf491SMika Westerberg do { 1823f67cf491SMika Westerberg /* Check that the ICM firmware is running */ 1824f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1825f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1826f67cf491SMika Westerberg return 0; 1827f67cf491SMika Westerberg 1828f67cf491SMika Westerberg msleep(300); 1829f67cf491SMika Westerberg } while (--retries); 1830f67cf491SMika Westerberg 1831f67cf491SMika Westerberg return -ETIMEDOUT; 1832f67cf491SMika Westerberg } 1833f67cf491SMika Westerberg 1834f67cf491SMika Westerberg static int icm_reset_phy_port(struct tb *tb, int phy_port) 1835f67cf491SMika Westerberg { 1836f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1837f67cf491SMika Westerberg u32 state0, state1; 1838f67cf491SMika Westerberg int port0, port1; 1839f67cf491SMika Westerberg u32 val0, val1; 1840f67cf491SMika Westerberg int ret; 1841f67cf491SMika Westerberg 1842f67cf491SMika Westerberg if (!icm->upstream_port) 1843f67cf491SMika Westerberg return 0; 1844f67cf491SMika Westerberg 1845f67cf491SMika Westerberg if (phy_port) { 1846f67cf491SMika Westerberg port0 = 3; 1847f67cf491SMika Westerberg port1 = 4; 1848f67cf491SMika Westerberg } else { 1849f67cf491SMika Westerberg port0 = 1; 1850f67cf491SMika Westerberg port1 = 2; 1851f67cf491SMika Westerberg } 1852f67cf491SMika Westerberg 1853f67cf491SMika Westerberg /* 1854f67cf491SMika Westerberg * Read link status of both null ports belonging to a single 1855f67cf491SMika Westerberg * physical port. 1856f67cf491SMika Westerberg */ 1857f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1858f67cf491SMika Westerberg if (ret) 1859f67cf491SMika Westerberg return ret; 1860f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1861f67cf491SMika Westerberg if (ret) 1862f67cf491SMika Westerberg return ret; 1863f67cf491SMika Westerberg 1864f67cf491SMika Westerberg state0 = val0 & PHY_PORT_CS1_LINK_STATE_MASK; 1865f67cf491SMika Westerberg state0 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1866f67cf491SMika Westerberg state1 = val1 & PHY_PORT_CS1_LINK_STATE_MASK; 1867f67cf491SMika Westerberg state1 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1868f67cf491SMika Westerberg 1869f67cf491SMika Westerberg /* If they are both up we need to reset them now */ 1870f67cf491SMika Westerberg if (state0 != TB_PORT_UP || state1 != TB_PORT_UP) 1871f67cf491SMika Westerberg return 0; 1872f67cf491SMika Westerberg 1873f67cf491SMika Westerberg val0 |= PHY_PORT_CS1_LINK_DISABLE; 1874f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1875f67cf491SMika Westerberg if (ret) 1876f67cf491SMika Westerberg return ret; 1877f67cf491SMika Westerberg 1878f67cf491SMika Westerberg val1 |= PHY_PORT_CS1_LINK_DISABLE; 1879f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1880f67cf491SMika Westerberg if (ret) 1881f67cf491SMika Westerberg return ret; 1882f67cf491SMika Westerberg 1883f67cf491SMika Westerberg /* Wait a bit and then re-enable both ports */ 1884f67cf491SMika Westerberg usleep_range(10, 100); 1885f67cf491SMika Westerberg 1886f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1887f67cf491SMika Westerberg if (ret) 1888f67cf491SMika Westerberg return ret; 1889f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1890f67cf491SMika Westerberg if (ret) 1891f67cf491SMika Westerberg return ret; 1892f67cf491SMika Westerberg 1893f67cf491SMika Westerberg val0 &= ~PHY_PORT_CS1_LINK_DISABLE; 1894f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1895f67cf491SMika Westerberg if (ret) 1896f67cf491SMika Westerberg return ret; 1897f67cf491SMika Westerberg 1898f67cf491SMika Westerberg val1 &= ~PHY_PORT_CS1_LINK_DISABLE; 1899f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1900f67cf491SMika Westerberg } 1901f67cf491SMika Westerberg 1902f67cf491SMika Westerberg static int icm_firmware_init(struct tb *tb) 1903f67cf491SMika Westerberg { 1904f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1905f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1906f67cf491SMika Westerberg int ret; 1907f67cf491SMika Westerberg 1908f67cf491SMika Westerberg ret = icm_firmware_start(tb, nhi); 1909f67cf491SMika Westerberg if (ret) { 1910f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "could not start ICM firmware\n"); 1911f67cf491SMika Westerberg return ret; 1912f67cf491SMika Westerberg } 1913f67cf491SMika Westerberg 1914f67cf491SMika Westerberg if (icm->get_mode) { 1915f67cf491SMika Westerberg ret = icm->get_mode(tb); 1916f67cf491SMika Westerberg 1917f67cf491SMika Westerberg switch (ret) { 1918e6b245ccSMika Westerberg case NHI_FW_SAFE_MODE: 1919e6b245ccSMika Westerberg icm->safe_mode = true; 1920e6b245ccSMika Westerberg break; 1921e6b245ccSMika Westerberg 1922f67cf491SMika Westerberg case NHI_FW_CM_MODE: 1923f67cf491SMika Westerberg /* Ask ICM to accept all Thunderbolt devices */ 1924f67cf491SMika Westerberg nhi_mailbox_cmd(nhi, NHI_MAILBOX_ALLOW_ALL_DEVS, 0); 1925f67cf491SMika Westerberg break; 1926f67cf491SMika Westerberg 1927f67cf491SMika Westerberg default: 1928e4be8c9bSMika Westerberg if (ret < 0) 1929e4be8c9bSMika Westerberg return ret; 1930e4be8c9bSMika Westerberg 1931f67cf491SMika Westerberg tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); 1932f67cf491SMika Westerberg return -ENODEV; 1933f67cf491SMika Westerberg } 1934f67cf491SMika Westerberg } 1935f67cf491SMika Westerberg 1936f67cf491SMika Westerberg /* 1937f67cf491SMika Westerberg * Reset both physical ports if there is anything connected to 1938f67cf491SMika Westerberg * them already. 1939f67cf491SMika Westerberg */ 1940f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 0); 1941f67cf491SMika Westerberg if (ret) 1942f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port0\n"); 1943f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 1); 1944f67cf491SMika Westerberg if (ret) 1945f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port1\n"); 1946f67cf491SMika Westerberg 1947f67cf491SMika Westerberg return 0; 1948f67cf491SMika Westerberg } 1949f67cf491SMika Westerberg 1950f67cf491SMika Westerberg static int icm_driver_ready(struct tb *tb) 1951f67cf491SMika Westerberg { 1952e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1953f67cf491SMika Westerberg int ret; 1954f67cf491SMika Westerberg 1955f67cf491SMika Westerberg ret = icm_firmware_init(tb); 1956f67cf491SMika Westerberg if (ret) 1957f67cf491SMika Westerberg return ret; 1958f67cf491SMika Westerberg 1959e6b245ccSMika Westerberg if (icm->safe_mode) { 1960e6b245ccSMika Westerberg tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); 1961e6b245ccSMika Westerberg tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); 1962e6b245ccSMika Westerberg tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); 1963e6b245ccSMika Westerberg return 0; 1964e6b245ccSMika Westerberg } 1965e6b245ccSMika Westerberg 19669039387eSMika Westerberg ret = __icm_driver_ready(tb, &tb->security_level, &icm->proto_version, 19679039387eSMika Westerberg &tb->nboot_acl, &icm->rpm); 19689aaa3b8bSMika Westerberg if (ret) 19699aaa3b8bSMika Westerberg return ret; 19709aaa3b8bSMika Westerberg 19719aaa3b8bSMika Westerberg /* 19729aaa3b8bSMika Westerberg * Make sure the number of supported preboot ACL matches what we 19739aaa3b8bSMika Westerberg * expect or disable the whole feature. 19749aaa3b8bSMika Westerberg */ 19759aaa3b8bSMika Westerberg if (tb->nboot_acl > icm->max_boot_acl) 19769aaa3b8bSMika Westerberg tb->nboot_acl = 0; 19779aaa3b8bSMika Westerberg 19789039387eSMika Westerberg if (icm->proto_version >= 3) 19799039387eSMika Westerberg tb_dbg(tb, "USB4 proxy operations supported\n"); 19809039387eSMika Westerberg 19819aaa3b8bSMika Westerberg return 0; 1982f67cf491SMika Westerberg } 1983f67cf491SMika Westerberg 1984f67cf491SMika Westerberg static int icm_suspend(struct tb *tb) 1985f67cf491SMika Westerberg { 1986d04522faSMika Westerberg struct icm *icm = tb_priv(tb); 1987a684c5b1SRafael J. Wysocki 1988d04522faSMika Westerberg if (icm->save_devices) 1989d04522faSMika Westerberg icm->save_devices(tb); 1990a684c5b1SRafael J. Wysocki 1991d04522faSMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 1992a684c5b1SRafael J. Wysocki return 0; 1993f67cf491SMika Westerberg } 1994f67cf491SMika Westerberg 1995f67cf491SMika Westerberg /* 1996f67cf491SMika Westerberg * Mark all switches (except root switch) below this one unplugged. ICM 1997f67cf491SMika Westerberg * firmware will send us an updated list of switches after we have send 1998f67cf491SMika Westerberg * it driver ready command. If a switch is not in that list it will be 1999f67cf491SMika Westerberg * removed when we perform rescan. 2000f67cf491SMika Westerberg */ 2001f67cf491SMika Westerberg static void icm_unplug_children(struct tb_switch *sw) 2002f67cf491SMika Westerberg { 2003b433d010SMika Westerberg struct tb_port *port; 2004f67cf491SMika Westerberg 2005f67cf491SMika Westerberg if (tb_route(sw)) 2006f67cf491SMika Westerberg sw->is_unplugged = true; 2007f67cf491SMika Westerberg 2008b433d010SMika Westerberg tb_switch_for_each_port(sw, port) { 2009dfe40ca4SMika Westerberg if (port->xdomain) 2010d1ff7024SMika Westerberg port->xdomain->is_unplugged = true; 2011dfe40ca4SMika Westerberg else if (tb_port_has_remote(port)) 2012f67cf491SMika Westerberg icm_unplug_children(port->remote->sw); 2013f67cf491SMika Westerberg } 2014f67cf491SMika Westerberg } 2015f67cf491SMika Westerberg 20164f7c2e0dSMika Westerberg static int complete_rpm(struct device *dev, void *data) 20174f7c2e0dSMika Westerberg { 20184f7c2e0dSMika Westerberg struct tb_switch *sw = tb_to_switch(dev); 20194f7c2e0dSMika Westerberg 20204f7c2e0dSMika Westerberg if (sw) 20214f7c2e0dSMika Westerberg complete(&sw->rpm_complete); 20224f7c2e0dSMika Westerberg return 0; 20234f7c2e0dSMika Westerberg } 20244f7c2e0dSMika Westerberg 20254f7c2e0dSMika Westerberg static void remove_unplugged_switch(struct tb_switch *sw) 20264f7c2e0dSMika Westerberg { 2027600c0849SMika Westerberg struct device *parent = get_device(sw->dev.parent); 2028600c0849SMika Westerberg 2029600c0849SMika Westerberg pm_runtime_get_sync(parent); 20304f7c2e0dSMika Westerberg 20314f7c2e0dSMika Westerberg /* 20324f7c2e0dSMika Westerberg * Signal this and switches below for rpm_complete because 20334f7c2e0dSMika Westerberg * tb_switch_remove() calls pm_runtime_get_sync() that then waits 20344f7c2e0dSMika Westerberg * for it. 20354f7c2e0dSMika Westerberg */ 20364f7c2e0dSMika Westerberg complete_rpm(&sw->dev, NULL); 20374f7c2e0dSMika Westerberg bus_for_each_dev(&tb_bus_type, &sw->dev, NULL, complete_rpm); 20384f7c2e0dSMika Westerberg tb_switch_remove(sw); 20394f7c2e0dSMika Westerberg 2040600c0849SMika Westerberg pm_runtime_mark_last_busy(parent); 2041600c0849SMika Westerberg pm_runtime_put_autosuspend(parent); 2042600c0849SMika Westerberg 2043600c0849SMika Westerberg put_device(parent); 20444f7c2e0dSMika Westerberg } 20454f7c2e0dSMika Westerberg 2046f67cf491SMika Westerberg static void icm_free_unplugged_children(struct tb_switch *sw) 2047f67cf491SMika Westerberg { 2048b433d010SMika Westerberg struct tb_port *port; 2049f67cf491SMika Westerberg 2050b433d010SMika Westerberg tb_switch_for_each_port(sw, port) { 2051d1ff7024SMika Westerberg if (port->xdomain && port->xdomain->is_unplugged) { 2052d1ff7024SMika Westerberg tb_xdomain_remove(port->xdomain); 2053d1ff7024SMika Westerberg port->xdomain = NULL; 2054dfe40ca4SMika Westerberg } else if (tb_port_has_remote(port)) { 2055f67cf491SMika Westerberg if (port->remote->sw->is_unplugged) { 20564f7c2e0dSMika Westerberg remove_unplugged_switch(port->remote->sw); 2057f67cf491SMika Westerberg port->remote = NULL; 2058f67cf491SMika Westerberg } else { 2059f67cf491SMika Westerberg icm_free_unplugged_children(port->remote->sw); 2060f67cf491SMika Westerberg } 2061f67cf491SMika Westerberg } 2062f67cf491SMika Westerberg } 2063dfe40ca4SMika Westerberg } 2064f67cf491SMika Westerberg 2065f67cf491SMika Westerberg static void icm_rescan_work(struct work_struct *work) 2066f67cf491SMika Westerberg { 2067f67cf491SMika Westerberg struct icm *icm = container_of(work, struct icm, rescan_work.work); 2068f67cf491SMika Westerberg struct tb *tb = icm_to_tb(icm); 2069f67cf491SMika Westerberg 2070f67cf491SMika Westerberg mutex_lock(&tb->lock); 2071f67cf491SMika Westerberg if (tb->root_switch) 2072f67cf491SMika Westerberg icm_free_unplugged_children(tb->root_switch); 2073f67cf491SMika Westerberg mutex_unlock(&tb->lock); 2074f67cf491SMika Westerberg } 2075f67cf491SMika Westerberg 2076f67cf491SMika Westerberg static void icm_complete(struct tb *tb) 2077f67cf491SMika Westerberg { 2078f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 2079f67cf491SMika Westerberg 2080f67cf491SMika Westerberg if (tb->nhi->going_away) 2081f67cf491SMika Westerberg return; 2082f67cf491SMika Westerberg 20833cdb9446SMika Westerberg /* 20843cdb9446SMika Westerberg * If RTD3 was vetoed before we entered system suspend allow it 20853cdb9446SMika Westerberg * again now before driver ready is sent. Firmware sends a new RTD3 20863cdb9446SMika Westerberg * veto if it is still the case after we have sent it driver ready 20873cdb9446SMika Westerberg * command. 20883cdb9446SMika Westerberg */ 20893cdb9446SMika Westerberg icm_veto_end(tb); 2090f67cf491SMika Westerberg icm_unplug_children(tb->root_switch); 2091f67cf491SMika Westerberg 2092f67cf491SMika Westerberg /* 2093f67cf491SMika Westerberg * Now all existing children should be resumed, start events 2094f67cf491SMika Westerberg * from ICM to get updated status. 2095f67cf491SMika Westerberg */ 20969039387eSMika Westerberg __icm_driver_ready(tb, NULL, NULL, NULL, NULL); 2097f67cf491SMika Westerberg 2098f67cf491SMika Westerberg /* 2099f67cf491SMika Westerberg * We do not get notifications of devices that have been 2100f67cf491SMika Westerberg * unplugged during suspend so schedule rescan to clean them up 2101f67cf491SMika Westerberg * if any. 2102f67cf491SMika Westerberg */ 2103f67cf491SMika Westerberg queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500)); 2104f67cf491SMika Westerberg } 2105f67cf491SMika Westerberg 21062d8ff0b5SMika Westerberg static int icm_runtime_suspend(struct tb *tb) 21072d8ff0b5SMika Westerberg { 21082d8ff0b5SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 21092d8ff0b5SMika Westerberg return 0; 21102d8ff0b5SMika Westerberg } 21112d8ff0b5SMika Westerberg 21124f7c2e0dSMika Westerberg static int icm_runtime_suspend_switch(struct tb_switch *sw) 21134f7c2e0dSMika Westerberg { 21144f7c2e0dSMika Westerberg if (tb_route(sw)) 21154f7c2e0dSMika Westerberg reinit_completion(&sw->rpm_complete); 21164f7c2e0dSMika Westerberg return 0; 21174f7c2e0dSMika Westerberg } 21184f7c2e0dSMika Westerberg 21194f7c2e0dSMika Westerberg static int icm_runtime_resume_switch(struct tb_switch *sw) 21204f7c2e0dSMika Westerberg { 21214f7c2e0dSMika Westerberg if (tb_route(sw)) { 21224f7c2e0dSMika Westerberg if (!wait_for_completion_timeout(&sw->rpm_complete, 21234f7c2e0dSMika Westerberg msecs_to_jiffies(500))) { 21244f7c2e0dSMika Westerberg dev_dbg(&sw->dev, "runtime resuming timed out\n"); 21254f7c2e0dSMika Westerberg } 21264f7c2e0dSMika Westerberg } 21274f7c2e0dSMika Westerberg return 0; 21284f7c2e0dSMika Westerberg } 21294f7c2e0dSMika Westerberg 21302d8ff0b5SMika Westerberg static int icm_runtime_resume(struct tb *tb) 21312d8ff0b5SMika Westerberg { 21322d8ff0b5SMika Westerberg /* 21332d8ff0b5SMika Westerberg * We can reuse the same resume functionality than with system 21342d8ff0b5SMika Westerberg * suspend. 21352d8ff0b5SMika Westerberg */ 21362d8ff0b5SMika Westerberg icm_complete(tb); 21372d8ff0b5SMika Westerberg return 0; 21382d8ff0b5SMika Westerberg } 21392d8ff0b5SMika Westerberg 2140f67cf491SMika Westerberg static int icm_start(struct tb *tb) 2141f67cf491SMika Westerberg { 2142e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 2143f67cf491SMika Westerberg int ret; 2144f67cf491SMika Westerberg 2145e6b245ccSMika Westerberg if (icm->safe_mode) 2146e6b245ccSMika Westerberg tb->root_switch = tb_switch_alloc_safe_mode(tb, &tb->dev, 0); 2147e6b245ccSMika Westerberg else 2148f67cf491SMika Westerberg tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); 2149444ac384SMika Westerberg if (IS_ERR(tb->root_switch)) 2150444ac384SMika Westerberg return PTR_ERR(tb->root_switch); 2151f67cf491SMika Westerberg 2152f437c24bSMika Westerberg tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm; 21532d8ff0b5SMika Westerberg tb->root_switch->rpm = icm->rpm; 2154e6b245ccSMika Westerberg 21553cdb9446SMika Westerberg if (icm->set_uuid) 21563cdb9446SMika Westerberg icm->set_uuid(tb); 21573cdb9446SMika Westerberg 2158f67cf491SMika Westerberg ret = tb_switch_add(tb->root_switch); 2159d1ff7024SMika Westerberg if (ret) { 2160f67cf491SMika Westerberg tb_switch_put(tb->root_switch); 2161d1ff7024SMika Westerberg tb->root_switch = NULL; 2162d1ff7024SMika Westerberg } 2163f67cf491SMika Westerberg 2164f67cf491SMika Westerberg return ret; 2165f67cf491SMika Westerberg } 2166f67cf491SMika Westerberg 2167f67cf491SMika Westerberg static void icm_stop(struct tb *tb) 2168f67cf491SMika Westerberg { 2169f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 2170f67cf491SMika Westerberg 2171f67cf491SMika Westerberg cancel_delayed_work(&icm->rescan_work); 2172f67cf491SMika Westerberg tb_switch_remove(tb->root_switch); 2173f67cf491SMika Westerberg tb->root_switch = NULL; 2174f67cf491SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 21759039387eSMika Westerberg kfree(icm->last_nvm_auth); 21769039387eSMika Westerberg icm->last_nvm_auth = NULL; 2177f67cf491SMika Westerberg } 2178f67cf491SMika Westerberg 2179e6b245ccSMika Westerberg static int icm_disconnect_pcie_paths(struct tb *tb) 2180e6b245ccSMika Westerberg { 2181e6b245ccSMika Westerberg return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); 2182e6b245ccSMika Westerberg } 2183e6b245ccSMika Westerberg 21849039387eSMika Westerberg static void icm_usb4_switch_nvm_auth_complete(void *data) 21859039387eSMika Westerberg { 21869039387eSMika Westerberg struct usb4_switch_nvm_auth *auth = data; 21879039387eSMika Westerberg struct icm *icm = auth->icm; 21889039387eSMika Westerberg struct tb *tb = icm_to_tb(icm); 21899039387eSMika Westerberg 21909039387eSMika Westerberg tb_dbg(tb, "NVM_AUTH response for %llx flags %#x status %#x\n", 21919039387eSMika Westerberg get_route(auth->reply.route_hi, auth->reply.route_lo), 21929039387eSMika Westerberg auth->reply.hdr.flags, auth->reply.status); 21939039387eSMika Westerberg 21949039387eSMika Westerberg mutex_lock(&tb->lock); 21959039387eSMika Westerberg if (WARN_ON(icm->last_nvm_auth)) 21969039387eSMika Westerberg kfree(icm->last_nvm_auth); 21979039387eSMika Westerberg icm->last_nvm_auth = auth; 21989039387eSMika Westerberg mutex_unlock(&tb->lock); 21999039387eSMika Westerberg } 22009039387eSMika Westerberg 22019039387eSMika Westerberg static int icm_usb4_switch_nvm_authenticate(struct tb *tb, u64 route) 22029039387eSMika Westerberg { 22039039387eSMika Westerberg struct usb4_switch_nvm_auth *auth; 22049039387eSMika Westerberg struct icm *icm = tb_priv(tb); 22059039387eSMika Westerberg struct tb_cfg_request *req; 22069039387eSMika Westerberg int ret; 22079039387eSMika Westerberg 22089039387eSMika Westerberg auth = kzalloc(sizeof(*auth), GFP_KERNEL); 22099039387eSMika Westerberg if (!auth) 22109039387eSMika Westerberg return -ENOMEM; 22119039387eSMika Westerberg 22129039387eSMika Westerberg auth->icm = icm; 22139039387eSMika Westerberg auth->request.hdr.code = ICM_USB4_SWITCH_OP; 22149039387eSMika Westerberg auth->request.route_hi = upper_32_bits(route); 22159039387eSMika Westerberg auth->request.route_lo = lower_32_bits(route); 22169039387eSMika Westerberg auth->request.opcode = USB4_SWITCH_OP_NVM_AUTH; 22179039387eSMika Westerberg 22189039387eSMika Westerberg req = tb_cfg_request_alloc(); 22199039387eSMika Westerberg if (!req) { 22209039387eSMika Westerberg ret = -ENOMEM; 22219039387eSMika Westerberg goto err_free_auth; 22229039387eSMika Westerberg } 22239039387eSMika Westerberg 22249039387eSMika Westerberg req->match = icm_match; 22259039387eSMika Westerberg req->copy = icm_copy; 22269039387eSMika Westerberg req->request = &auth->request; 22279039387eSMika Westerberg req->request_size = sizeof(auth->request); 22289039387eSMika Westerberg req->request_type = TB_CFG_PKG_ICM_CMD; 22299039387eSMika Westerberg req->response = &auth->reply; 22309039387eSMika Westerberg req->npackets = 1; 22319039387eSMika Westerberg req->response_size = sizeof(auth->reply); 22329039387eSMika Westerberg req->response_type = TB_CFG_PKG_ICM_RESP; 22339039387eSMika Westerberg 22349039387eSMika Westerberg tb_dbg(tb, "NVM_AUTH request for %llx\n", route); 22359039387eSMika Westerberg 22369039387eSMika Westerberg mutex_lock(&icm->request_lock); 22379039387eSMika Westerberg ret = tb_cfg_request(tb->ctl, req, icm_usb4_switch_nvm_auth_complete, 22389039387eSMika Westerberg auth); 22399039387eSMika Westerberg mutex_unlock(&icm->request_lock); 22409039387eSMika Westerberg 22419039387eSMika Westerberg tb_cfg_request_put(req); 22429039387eSMika Westerberg if (ret) 22439039387eSMika Westerberg goto err_free_auth; 22449039387eSMika Westerberg return 0; 22459039387eSMika Westerberg 22469039387eSMika Westerberg err_free_auth: 22479039387eSMika Westerberg kfree(auth); 22489039387eSMika Westerberg return ret; 22499039387eSMika Westerberg } 22509039387eSMika Westerberg 22519039387eSMika Westerberg static int icm_usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, 22529039387eSMika Westerberg u8 *status, const void *tx_data, size_t tx_data_len, 22539039387eSMika Westerberg void *rx_data, size_t rx_data_len) 22549039387eSMika Westerberg { 22559039387eSMika Westerberg struct icm_usb4_switch_op_response reply; 22569039387eSMika Westerberg struct icm_usb4_switch_op request; 22579039387eSMika Westerberg struct tb *tb = sw->tb; 22589039387eSMika Westerberg struct icm *icm = tb_priv(tb); 22599039387eSMika Westerberg u64 route = tb_route(sw); 22609039387eSMika Westerberg int ret; 22619039387eSMika Westerberg 22629039387eSMika Westerberg /* 22639039387eSMika Westerberg * USB4 router operation proxy is supported in firmware if the 22649039387eSMika Westerberg * protocol version is 3 or higher. 22659039387eSMika Westerberg */ 22669039387eSMika Westerberg if (icm->proto_version < 3) 22679039387eSMika Westerberg return -EOPNOTSUPP; 22689039387eSMika Westerberg 22699039387eSMika Westerberg /* 22709039387eSMika Westerberg * NVM_AUTH is a special USB4 proxy operation that does not 22719039387eSMika Westerberg * return immediately so handle it separately. 22729039387eSMika Westerberg */ 22739039387eSMika Westerberg if (opcode == USB4_SWITCH_OP_NVM_AUTH) 22749039387eSMika Westerberg return icm_usb4_switch_nvm_authenticate(tb, route); 22759039387eSMika Westerberg 22769039387eSMika Westerberg memset(&request, 0, sizeof(request)); 22779039387eSMika Westerberg request.hdr.code = ICM_USB4_SWITCH_OP; 22789039387eSMika Westerberg request.route_hi = upper_32_bits(route); 22799039387eSMika Westerberg request.route_lo = lower_32_bits(route); 22809039387eSMika Westerberg request.opcode = opcode; 22819039387eSMika Westerberg if (metadata) 22829039387eSMika Westerberg request.metadata = *metadata; 22839039387eSMika Westerberg 22849039387eSMika Westerberg if (tx_data_len) { 22859039387eSMika Westerberg request.data_len_valid |= ICM_USB4_SWITCH_DATA_VALID; 22869039387eSMika Westerberg if (tx_data_len < ARRAY_SIZE(request.data)) 22879039387eSMika Westerberg request.data_len_valid = 22889039387eSMika Westerberg tx_data_len & ICM_USB4_SWITCH_DATA_LEN_MASK; 22899039387eSMika Westerberg memcpy(request.data, tx_data, tx_data_len * sizeof(u32)); 22909039387eSMika Westerberg } 22919039387eSMika Westerberg 22929039387eSMika Westerberg memset(&reply, 0, sizeof(reply)); 22939039387eSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 22949039387eSMika Westerberg 1, ICM_TIMEOUT); 22959039387eSMika Westerberg if (ret) 22969039387eSMika Westerberg return ret; 22979039387eSMika Westerberg 22989039387eSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 22999039387eSMika Westerberg return -EIO; 23009039387eSMika Westerberg 23019039387eSMika Westerberg if (status) 23029039387eSMika Westerberg *status = reply.status; 23039039387eSMika Westerberg 23049039387eSMika Westerberg if (metadata) 23059039387eSMika Westerberg *metadata = reply.metadata; 23069039387eSMika Westerberg 23079039387eSMika Westerberg if (rx_data_len) 23089039387eSMika Westerberg memcpy(rx_data, reply.data, rx_data_len * sizeof(u32)); 23099039387eSMika Westerberg 23109039387eSMika Westerberg return 0; 23119039387eSMika Westerberg } 23129039387eSMika Westerberg 23139039387eSMika Westerberg static int icm_usb4_switch_nvm_authenticate_status(struct tb_switch *sw, 23149039387eSMika Westerberg u32 *status) 23159039387eSMika Westerberg { 23169039387eSMika Westerberg struct usb4_switch_nvm_auth *auth; 23179039387eSMika Westerberg struct tb *tb = sw->tb; 23189039387eSMika Westerberg struct icm *icm = tb_priv(tb); 23199039387eSMika Westerberg int ret = 0; 23209039387eSMika Westerberg 23219039387eSMika Westerberg if (icm->proto_version < 3) 23229039387eSMika Westerberg return -EOPNOTSUPP; 23239039387eSMika Westerberg 23249039387eSMika Westerberg auth = icm->last_nvm_auth; 23259039387eSMika Westerberg icm->last_nvm_auth = NULL; 23269039387eSMika Westerberg 23279039387eSMika Westerberg if (auth && auth->reply.route_hi == sw->config.route_hi && 23289039387eSMika Westerberg auth->reply.route_lo == sw->config.route_lo) { 2329c14556fcSMika Westerberg tb_dbg(tb, "NVM_AUTH found for %llx flags %#x status %#x\n", 23309039387eSMika Westerberg tb_route(sw), auth->reply.hdr.flags, auth->reply.status); 23319039387eSMika Westerberg if (auth->reply.hdr.flags & ICM_FLAGS_ERROR) 23329039387eSMika Westerberg ret = -EIO; 23339039387eSMika Westerberg else 23349039387eSMika Westerberg *status = auth->reply.status; 23359039387eSMika Westerberg } else { 23369039387eSMika Westerberg *status = 0; 23379039387eSMika Westerberg } 23389039387eSMika Westerberg 23399039387eSMika Westerberg kfree(auth); 23409039387eSMika Westerberg return ret; 23419039387eSMika Westerberg } 23429039387eSMika Westerberg 23439aaa3b8bSMika Westerberg /* Falcon Ridge */ 2344f67cf491SMika Westerberg static const struct tb_cm_ops icm_fr_ops = { 2345f67cf491SMika Westerberg .driver_ready = icm_driver_ready, 2346f67cf491SMika Westerberg .start = icm_start, 2347f67cf491SMika Westerberg .stop = icm_stop, 2348f67cf491SMika Westerberg .suspend = icm_suspend, 2349f67cf491SMika Westerberg .complete = icm_complete, 2350f67cf491SMika Westerberg .handle_event = icm_handle_event, 2351f67cf491SMika Westerberg .approve_switch = icm_fr_approve_switch, 2352f67cf491SMika Westerberg .add_switch_key = icm_fr_add_switch_key, 2353f67cf491SMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 2354e6b245ccSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 2355d1ff7024SMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 2356d1ff7024SMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 2357f67cf491SMika Westerberg }; 2358f67cf491SMika Westerberg 23599aaa3b8bSMika Westerberg /* Alpine Ridge */ 23609aaa3b8bSMika Westerberg static const struct tb_cm_ops icm_ar_ops = { 23619aaa3b8bSMika Westerberg .driver_ready = icm_driver_ready, 23629aaa3b8bSMika Westerberg .start = icm_start, 23639aaa3b8bSMika Westerberg .stop = icm_stop, 23649aaa3b8bSMika Westerberg .suspend = icm_suspend, 23659aaa3b8bSMika Westerberg .complete = icm_complete, 23662d8ff0b5SMika Westerberg .runtime_suspend = icm_runtime_suspend, 23672d8ff0b5SMika Westerberg .runtime_resume = icm_runtime_resume, 23684f7c2e0dSMika Westerberg .runtime_suspend_switch = icm_runtime_suspend_switch, 23694f7c2e0dSMika Westerberg .runtime_resume_switch = icm_runtime_resume_switch, 23709aaa3b8bSMika Westerberg .handle_event = icm_handle_event, 23719aaa3b8bSMika Westerberg .get_boot_acl = icm_ar_get_boot_acl, 23729aaa3b8bSMika Westerberg .set_boot_acl = icm_ar_set_boot_acl, 23739aaa3b8bSMika Westerberg .approve_switch = icm_fr_approve_switch, 23749aaa3b8bSMika Westerberg .add_switch_key = icm_fr_add_switch_key, 23759aaa3b8bSMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 23769aaa3b8bSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 23779aaa3b8bSMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 23789aaa3b8bSMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 23799aaa3b8bSMika Westerberg }; 23809aaa3b8bSMika Westerberg 23814bac471dSRadion Mirchevsky /* Titan Ridge */ 23824bac471dSRadion Mirchevsky static const struct tb_cm_ops icm_tr_ops = { 23834bac471dSRadion Mirchevsky .driver_ready = icm_driver_ready, 23844bac471dSRadion Mirchevsky .start = icm_start, 23854bac471dSRadion Mirchevsky .stop = icm_stop, 23864bac471dSRadion Mirchevsky .suspend = icm_suspend, 23874bac471dSRadion Mirchevsky .complete = icm_complete, 23882d8ff0b5SMika Westerberg .runtime_suspend = icm_runtime_suspend, 23892d8ff0b5SMika Westerberg .runtime_resume = icm_runtime_resume, 23904f7c2e0dSMika Westerberg .runtime_suspend_switch = icm_runtime_suspend_switch, 23914f7c2e0dSMika Westerberg .runtime_resume_switch = icm_runtime_resume_switch, 23924bac471dSRadion Mirchevsky .handle_event = icm_handle_event, 23934bac471dSRadion Mirchevsky .get_boot_acl = icm_ar_get_boot_acl, 23944bac471dSRadion Mirchevsky .set_boot_acl = icm_ar_set_boot_acl, 23954bac471dSRadion Mirchevsky .approve_switch = icm_tr_approve_switch, 23964bac471dSRadion Mirchevsky .add_switch_key = icm_tr_add_switch_key, 23974bac471dSRadion Mirchevsky .challenge_switch_key = icm_tr_challenge_switch_key, 23984bac471dSRadion Mirchevsky .disconnect_pcie_paths = icm_disconnect_pcie_paths, 23994bac471dSRadion Mirchevsky .approve_xdomain_paths = icm_tr_approve_xdomain_paths, 24004bac471dSRadion Mirchevsky .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, 24019039387eSMika Westerberg .usb4_switch_op = icm_usb4_switch_op, 24029039387eSMika Westerberg .usb4_switch_nvm_authenticate_status = 24039039387eSMika Westerberg icm_usb4_switch_nvm_authenticate_status, 24044bac471dSRadion Mirchevsky }; 24054bac471dSRadion Mirchevsky 24063cdb9446SMika Westerberg /* Ice Lake */ 24073cdb9446SMika Westerberg static const struct tb_cm_ops icm_icl_ops = { 24083cdb9446SMika Westerberg .driver_ready = icm_driver_ready, 24093cdb9446SMika Westerberg .start = icm_start, 24103cdb9446SMika Westerberg .stop = icm_stop, 24113cdb9446SMika Westerberg .complete = icm_complete, 24123cdb9446SMika Westerberg .runtime_suspend = icm_runtime_suspend, 24133cdb9446SMika Westerberg .runtime_resume = icm_runtime_resume, 24143cdb9446SMika Westerberg .handle_event = icm_handle_event, 24153cdb9446SMika Westerberg .approve_xdomain_paths = icm_tr_approve_xdomain_paths, 24163cdb9446SMika Westerberg .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, 24179039387eSMika Westerberg .usb4_switch_op = icm_usb4_switch_op, 24189039387eSMika Westerberg .usb4_switch_nvm_authenticate_status = 24199039387eSMika Westerberg icm_usb4_switch_nvm_authenticate_status, 24203cdb9446SMika Westerberg }; 24213cdb9446SMika Westerberg 2422f67cf491SMika Westerberg struct tb *icm_probe(struct tb_nhi *nhi) 2423f67cf491SMika Westerberg { 2424f67cf491SMika Westerberg struct icm *icm; 2425f67cf491SMika Westerberg struct tb *tb; 2426f67cf491SMika Westerberg 24277f0a34d7SMika Westerberg tb = tb_domain_alloc(nhi, ICM_TIMEOUT, sizeof(struct icm)); 2428f67cf491SMika Westerberg if (!tb) 2429f67cf491SMika Westerberg return NULL; 2430f67cf491SMika Westerberg 2431f67cf491SMika Westerberg icm = tb_priv(tb); 2432f67cf491SMika Westerberg INIT_DELAYED_WORK(&icm->rescan_work, icm_rescan_work); 2433f67cf491SMika Westerberg mutex_init(&icm->request_lock); 2434f67cf491SMika Westerberg 2435f67cf491SMika Westerberg switch (nhi->pdev->device) { 2436f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: 2437f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: 2438f437c24bSMika Westerberg icm->can_upgrade_nvm = true; 2439f67cf491SMika Westerberg icm->is_supported = icm_fr_is_supported; 2440f67cf491SMika Westerberg icm->get_route = icm_fr_get_route; 2441d04522faSMika Westerberg icm->save_devices = icm_fr_save_devices; 24423080e197SMika Westerberg icm->driver_ready = icm_fr_driver_ready; 2443f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 2444f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 2445d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 2446d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 2447f67cf491SMika Westerberg tb->cm_ops = &icm_fr_ops; 2448f67cf491SMika Westerberg break; 2449f67cf491SMika Westerberg 2450f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI: 2451f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI: 2452f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: 2453f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: 2454f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: 24559aaa3b8bSMika Westerberg icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 2456f437c24bSMika Westerberg /* 2457f437c24bSMika Westerberg * NVM upgrade has not been tested on Apple systems and 2458f437c24bSMika Westerberg * they don't provide images publicly either. To be on 2459f437c24bSMika Westerberg * the safe side prevent root switch NVM upgrade on Macs 2460f437c24bSMika Westerberg * for now. 2461f437c24bSMika Westerberg */ 2462f437c24bSMika Westerberg icm->can_upgrade_nvm = !x86_apple_machine; 2463f67cf491SMika Westerberg icm->is_supported = icm_ar_is_supported; 24640d53827dSMika Westerberg icm->cio_reset = icm_ar_cio_reset; 2465f67cf491SMika Westerberg icm->get_mode = icm_ar_get_mode; 2466f67cf491SMika Westerberg icm->get_route = icm_ar_get_route; 2467d04522faSMika Westerberg icm->save_devices = icm_fr_save_devices; 24689aaa3b8bSMika Westerberg icm->driver_ready = icm_ar_driver_ready; 2469f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 2470f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 2471d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 2472d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 24739aaa3b8bSMika Westerberg tb->cm_ops = &icm_ar_ops; 2474f67cf491SMika Westerberg break; 24754bac471dSRadion Mirchevsky 24764bac471dSRadion Mirchevsky case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI: 24774bac471dSRadion Mirchevsky case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI: 24784bac471dSRadion Mirchevsky icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 2479f437c24bSMika Westerberg icm->can_upgrade_nvm = !x86_apple_machine; 24804bac471dSRadion Mirchevsky icm->is_supported = icm_ar_is_supported; 24810d53827dSMika Westerberg icm->cio_reset = icm_tr_cio_reset; 24824bac471dSRadion Mirchevsky icm->get_mode = icm_ar_get_mode; 24834bac471dSRadion Mirchevsky icm->driver_ready = icm_tr_driver_ready; 24844bac471dSRadion Mirchevsky icm->device_connected = icm_tr_device_connected; 24854bac471dSRadion Mirchevsky icm->device_disconnected = icm_tr_device_disconnected; 24864bac471dSRadion Mirchevsky icm->xdomain_connected = icm_tr_xdomain_connected; 24874bac471dSRadion Mirchevsky icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 24884bac471dSRadion Mirchevsky tb->cm_ops = &icm_tr_ops; 24894bac471dSRadion Mirchevsky break; 24903cdb9446SMika Westerberg 24913cdb9446SMika Westerberg case PCI_DEVICE_ID_INTEL_ICL_NHI0: 24923cdb9446SMika Westerberg case PCI_DEVICE_ID_INTEL_ICL_NHI1: 2493354a7a77SMika Westerberg icm->is_supported = icm_fr_is_supported; 24943cdb9446SMika Westerberg icm->driver_ready = icm_icl_driver_ready; 24953cdb9446SMika Westerberg icm->set_uuid = icm_icl_set_uuid; 24963cdb9446SMika Westerberg icm->device_connected = icm_icl_device_connected; 24973cdb9446SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 24983cdb9446SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 24993cdb9446SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 25003cdb9446SMika Westerberg icm->rtd3_veto = icm_icl_rtd3_veto; 25013cdb9446SMika Westerberg tb->cm_ops = &icm_icl_ops; 25023cdb9446SMika Westerberg break; 250357d8df68SMika Westerberg 250457d8df68SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_NHI0: 250557d8df68SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_NHI1: 2506f6439c53SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_H_NHI0: 2507f6439c53SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_H_NHI1: 250857d8df68SMika Westerberg icm->is_supported = icm_tgl_is_supported; 250957d8df68SMika Westerberg icm->driver_ready = icm_icl_driver_ready; 251057d8df68SMika Westerberg icm->set_uuid = icm_icl_set_uuid; 251157d8df68SMika Westerberg icm->device_connected = icm_icl_device_connected; 251257d8df68SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 251357d8df68SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 251457d8df68SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 251557d8df68SMika Westerberg icm->rtd3_veto = icm_icl_rtd3_veto; 251657d8df68SMika Westerberg tb->cm_ops = &icm_icl_ops; 251757d8df68SMika Westerberg break; 2518db0746e3SMika Westerberg 2519db0746e3SMika Westerberg case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI: 2520db0746e3SMika Westerberg icm->is_supported = icm_tgl_is_supported; 2521db0746e3SMika Westerberg icm->get_mode = icm_ar_get_mode; 2522db0746e3SMika Westerberg icm->driver_ready = icm_tr_driver_ready; 2523db0746e3SMika Westerberg icm->device_connected = icm_tr_device_connected; 2524db0746e3SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 2525db0746e3SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 2526db0746e3SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 2527db0746e3SMika Westerberg tb->cm_ops = &icm_tr_ops; 2528db0746e3SMika Westerberg break; 2529f67cf491SMika Westerberg } 2530f67cf491SMika Westerberg 2531f67cf491SMika Westerberg if (!icm->is_supported || !icm->is_supported(tb)) { 2532f67cf491SMika Westerberg dev_dbg(&nhi->pdev->dev, "ICM not supported on this controller\n"); 2533f67cf491SMika Westerberg tb_domain_put(tb); 2534f67cf491SMika Westerberg return NULL; 2535f67cf491SMika Westerberg } 2536f67cf491SMika Westerberg 2537e0258805SMika Westerberg tb_dbg(tb, "using firmware connection manager\n"); 2538e0258805SMika Westerberg 2539f67cf491SMika Westerberg return tb; 2540f67cf491SMika Westerberg } 2541