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 88d1ff7024SMika Westerberg * @xdomain_connected - Handle XDomain connected ICM message 89d1ff7024SMika Westerberg * @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 560d1ff7024SMika Westerberg static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 561d1ff7024SMika Westerberg { 562d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain_response reply; 563d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain request; 564d1ff7024SMika Westerberg int ret; 565d1ff7024SMika Westerberg 566d1ff7024SMika Westerberg memset(&request, 0, sizeof(request)); 567d1ff7024SMika Westerberg request.hdr.code = ICM_APPROVE_XDOMAIN; 568d1ff7024SMika Westerberg request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; 569d1ff7024SMika Westerberg memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 570d1ff7024SMika Westerberg 571d1ff7024SMika Westerberg request.transmit_path = xd->transmit_path; 572d1ff7024SMika Westerberg request.transmit_ring = xd->transmit_ring; 573d1ff7024SMika Westerberg request.receive_path = xd->receive_path; 574d1ff7024SMika Westerberg request.receive_ring = xd->receive_ring; 575d1ff7024SMika Westerberg 576d1ff7024SMika Westerberg memset(&reply, 0, sizeof(reply)); 577d1ff7024SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 578d1ff7024SMika Westerberg 1, ICM_TIMEOUT); 579d1ff7024SMika Westerberg if (ret) 580d1ff7024SMika Westerberg return ret; 581d1ff7024SMika Westerberg 582d1ff7024SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 583d1ff7024SMika Westerberg return -EIO; 584d1ff7024SMika Westerberg 585d1ff7024SMika Westerberg return 0; 586d1ff7024SMika Westerberg } 587d1ff7024SMika Westerberg 588d1ff7024SMika Westerberg static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 589d1ff7024SMika Westerberg { 590d1ff7024SMika Westerberg u8 phy_port; 591d1ff7024SMika Westerberg u8 cmd; 592d1ff7024SMika Westerberg 593d1ff7024SMika Westerberg phy_port = tb_phy_port_from_link(xd->link); 594d1ff7024SMika Westerberg if (phy_port == 0) 595d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PA; 596d1ff7024SMika Westerberg else 597d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PB; 598d1ff7024SMika Westerberg 599d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 1); 600d1ff7024SMika Westerberg usleep_range(10, 50); 601d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 2); 602d1ff7024SMika Westerberg return 0; 603d1ff7024SMika Westerberg } 604d1ff7024SMika Westerberg 605b5db76dbSMika Westerberg static struct tb_switch *alloc_switch(struct tb_switch *parent_sw, u64 route, 606b5db76dbSMika Westerberg const uuid_t *uuid) 607ee487dd2SMika Westerberg { 608b5db76dbSMika Westerberg struct tb *tb = parent_sw->tb; 609ee487dd2SMika Westerberg struct tb_switch *sw; 610ee487dd2SMika Westerberg 611b5db76dbSMika Westerberg sw = tb_switch_alloc(tb, &parent_sw->dev, route); 612b5db76dbSMika Westerberg if (IS_ERR(sw)) { 613b5db76dbSMika Westerberg tb_warn(tb, "failed to allocate switch at %llx\n", route); 614b5db76dbSMika Westerberg return sw; 615b5db76dbSMika Westerberg } 616ee487dd2SMika Westerberg 617ee487dd2SMika Westerberg sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); 618fd21b79eSAditya Pakki if (!sw->uuid) { 619fd21b79eSAditya Pakki tb_switch_put(sw); 620b5db76dbSMika Westerberg return ERR_PTR(-ENOMEM); 621fd21b79eSAditya Pakki } 622ee487dd2SMika Westerberg 623b5db76dbSMika Westerberg init_completion(&sw->rpm_complete); 624b5db76dbSMika Westerberg return sw; 625b5db76dbSMika Westerberg } 626b5db76dbSMika Westerberg 627b5db76dbSMika Westerberg static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw) 628b5db76dbSMika Westerberg { 629b5db76dbSMika Westerberg u64 route = tb_route(sw); 630b5db76dbSMika Westerberg int ret; 6312d8ff0b5SMika Westerberg 632ee487dd2SMika Westerberg /* Link the two switches now */ 633ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 634ee487dd2SMika Westerberg tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); 635ee487dd2SMika Westerberg 6363cdb9446SMika Westerberg ret = tb_switch_add(sw); 637b5db76dbSMika Westerberg if (ret) 638ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 6392d8ff0b5SMika Westerberg 640b5db76dbSMika Westerberg return ret; 641ee487dd2SMika Westerberg } 642ee487dd2SMika Westerberg 643ee487dd2SMika Westerberg static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, 644ee487dd2SMika Westerberg u64 route, u8 connection_id, u8 connection_key, 64514862ee3SYehezkel Bernat u8 link, u8 depth, bool boot) 646ee487dd2SMika Westerberg { 647ee487dd2SMika Westerberg /* Disconnect from parent */ 648ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 649ee487dd2SMika Westerberg /* Re-connect via updated port*/ 650ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 651ee487dd2SMika Westerberg 652ee487dd2SMika Westerberg /* Update with the new addressing information */ 653ee487dd2SMika Westerberg sw->config.route_hi = upper_32_bits(route); 654ee487dd2SMika Westerberg sw->config.route_lo = lower_32_bits(route); 655ee487dd2SMika Westerberg sw->connection_id = connection_id; 656ee487dd2SMika Westerberg sw->connection_key = connection_key; 657ee487dd2SMika Westerberg sw->link = link; 658ee487dd2SMika Westerberg sw->depth = depth; 65914862ee3SYehezkel Bernat sw->boot = boot; 660ee487dd2SMika Westerberg 661ee487dd2SMika Westerberg /* This switch still exists */ 662ee487dd2SMika Westerberg sw->is_unplugged = false; 6634f7c2e0dSMika Westerberg 6644f7c2e0dSMika Westerberg /* Runtime resume is now complete */ 6654f7c2e0dSMika Westerberg complete(&sw->rpm_complete); 666ee487dd2SMika Westerberg } 667ee487dd2SMika Westerberg 668f67cf491SMika Westerberg static void remove_switch(struct tb_switch *sw) 669f67cf491SMika Westerberg { 670f67cf491SMika Westerberg struct tb_switch *parent_sw; 671f67cf491SMika Westerberg 672f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 673f67cf491SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 674f67cf491SMika Westerberg tb_switch_remove(sw); 675f67cf491SMika Westerberg } 676f67cf491SMika Westerberg 677ee487dd2SMika Westerberg static void add_xdomain(struct tb_switch *sw, u64 route, 678ee487dd2SMika Westerberg const uuid_t *local_uuid, const uuid_t *remote_uuid, 679ee487dd2SMika Westerberg u8 link, u8 depth) 680ee487dd2SMika Westerberg { 681ee487dd2SMika Westerberg struct tb_xdomain *xd; 682ee487dd2SMika Westerberg 6832d8ff0b5SMika Westerberg pm_runtime_get_sync(&sw->dev); 6842d8ff0b5SMika Westerberg 685ee487dd2SMika Westerberg xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid); 686ee487dd2SMika Westerberg if (!xd) 6872d8ff0b5SMika Westerberg goto out; 688ee487dd2SMika Westerberg 689ee487dd2SMika Westerberg xd->link = link; 690ee487dd2SMika Westerberg xd->depth = depth; 691ee487dd2SMika Westerberg 692ee487dd2SMika Westerberg tb_port_at(route, sw)->xdomain = xd; 693ee487dd2SMika Westerberg 694ee487dd2SMika Westerberg tb_xdomain_add(xd); 6952d8ff0b5SMika Westerberg 6962d8ff0b5SMika Westerberg out: 6972d8ff0b5SMika Westerberg pm_runtime_mark_last_busy(&sw->dev); 6982d8ff0b5SMika Westerberg pm_runtime_put_autosuspend(&sw->dev); 699ee487dd2SMika Westerberg } 700ee487dd2SMika Westerberg 701ee487dd2SMika Westerberg static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link) 702ee487dd2SMika Westerberg { 703ee487dd2SMika Westerberg xd->link = link; 704ee487dd2SMika Westerberg xd->route = route; 705ee487dd2SMika Westerberg xd->is_unplugged = false; 706ee487dd2SMika Westerberg } 707ee487dd2SMika Westerberg 70879fae987SMika Westerberg static void remove_xdomain(struct tb_xdomain *xd) 70979fae987SMika Westerberg { 71079fae987SMika Westerberg struct tb_switch *sw; 71179fae987SMika Westerberg 71279fae987SMika Westerberg sw = tb_to_switch(xd->dev.parent); 71379fae987SMika Westerberg tb_port_at(xd->route, sw)->xdomain = NULL; 71479fae987SMika Westerberg tb_xdomain_remove(xd); 71579fae987SMika Westerberg } 71679fae987SMika Westerberg 717f67cf491SMika Westerberg static void 718f67cf491SMika Westerberg icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 719f67cf491SMika Westerberg { 720f67cf491SMika Westerberg const struct icm_fr_event_device_connected *pkg = 721f67cf491SMika Westerberg (const struct icm_fr_event_device_connected *)hdr; 722ee487dd2SMika Westerberg enum tb_security_level security_level; 723f67cf491SMika Westerberg struct tb_switch *sw, *parent_sw; 72491c0c120SMika Westerberg bool boot, dual_lane, speed_gen3; 725f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 726f67cf491SMika Westerberg bool authorized = false; 72779fae987SMika Westerberg struct tb_xdomain *xd; 728f67cf491SMika Westerberg u8 link, depth; 729f67cf491SMika Westerberg u64 route; 730f67cf491SMika Westerberg int ret; 731f67cf491SMika Westerberg 7323cdb9446SMika Westerberg icm_postpone_rescan(tb); 7333cdb9446SMika Westerberg 734f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 735f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 736f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 737f67cf491SMika Westerberg authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 738ee487dd2SMika Westerberg security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 739ee487dd2SMika Westerberg ICM_FLAGS_SLEVEL_SHIFT; 74014862ee3SYehezkel Bernat boot = pkg->link_info & ICM_LINK_INFO_BOOT; 74191c0c120SMika Westerberg dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; 74291c0c120SMika Westerberg speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; 743f67cf491SMika Westerberg 744cb653eecSMika Westerberg if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 745cb653eecSMika Westerberg tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", 746cb653eecSMika Westerberg link, depth); 747cb653eecSMika Westerberg return; 748cb653eecSMika Westerberg } 749cb653eecSMika Westerberg 750f67cf491SMika Westerberg sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 751f67cf491SMika Westerberg if (sw) { 752f67cf491SMika Westerberg u8 phy_port, sw_phy_port; 753f67cf491SMika Westerberg 754f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 755fdd92e89SMika Westerberg sw_phy_port = tb_phy_port_from_link(sw->link); 756fdd92e89SMika Westerberg phy_port = tb_phy_port_from_link(link); 757f67cf491SMika Westerberg 758f67cf491SMika Westerberg /* 759f67cf491SMika Westerberg * On resume ICM will send us connected events for the 760f67cf491SMika Westerberg * devices that still are present. However, that 761f67cf491SMika Westerberg * information might have changed for example by the 762f67cf491SMika Westerberg * fact that a switch on a dual-link connection might 763f67cf491SMika Westerberg * have been enumerated using the other link now. Make 764f67cf491SMika Westerberg * sure our book keeping matches that. 765f67cf491SMika Westerberg */ 766f67cf491SMika Westerberg if (sw->depth == depth && sw_phy_port == phy_port && 767f67cf491SMika Westerberg !!sw->authorized == authorized) { 768fdd92e89SMika Westerberg /* 769fdd92e89SMika Westerberg * It was enumerated through another link so update 770fdd92e89SMika Westerberg * route string accordingly. 771fdd92e89SMika Westerberg */ 772fdd92e89SMika Westerberg if (sw->link != link) { 773fdd92e89SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 774fdd92e89SMika Westerberg if (ret) { 775fdd92e89SMika Westerberg tb_err(tb, "failed to update route string for switch at %u.%u\n", 776fdd92e89SMika Westerberg link, depth); 777fdd92e89SMika Westerberg tb_switch_put(sw); 778fdd92e89SMika Westerberg return; 779fdd92e89SMika Westerberg } 780fdd92e89SMika Westerberg } else { 781fdd92e89SMika Westerberg route = tb_route(sw); 782fdd92e89SMika Westerberg } 783fdd92e89SMika Westerberg 784ee487dd2SMika Westerberg update_switch(parent_sw, sw, route, pkg->connection_id, 78514862ee3SYehezkel Bernat pkg->connection_key, link, depth, boot); 786f67cf491SMika Westerberg tb_switch_put(sw); 787f67cf491SMika Westerberg return; 788f67cf491SMika Westerberg } 789f67cf491SMika Westerberg 790f67cf491SMika Westerberg /* 791f67cf491SMika Westerberg * User connected the same switch to another physical 792f67cf491SMika Westerberg * port or to another part of the topology. Remove the 793f67cf491SMika Westerberg * existing switch now before adding the new one. 794f67cf491SMika Westerberg */ 795f67cf491SMika Westerberg remove_switch(sw); 796f67cf491SMika Westerberg tb_switch_put(sw); 797f67cf491SMika Westerberg } 798f67cf491SMika Westerberg 799f67cf491SMika Westerberg /* 800f67cf491SMika Westerberg * If the switch was not found by UUID, look for a switch on 801f67cf491SMika Westerberg * same physical port (taking possible link aggregation into 802f67cf491SMika Westerberg * account) and depth. If we found one it is definitely a stale 803f67cf491SMika Westerberg * one so remove it first. 804f67cf491SMika Westerberg */ 805f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 806f67cf491SMika Westerberg if (!sw) { 807f67cf491SMika Westerberg u8 dual_link; 808f67cf491SMika Westerberg 809f67cf491SMika Westerberg dual_link = dual_link_from_link(link); 810f67cf491SMika Westerberg if (dual_link) 811f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, dual_link, depth); 812f67cf491SMika Westerberg } 813f67cf491SMika Westerberg if (sw) { 814f67cf491SMika Westerberg remove_switch(sw); 815f67cf491SMika Westerberg tb_switch_put(sw); 816f67cf491SMika Westerberg } 817f67cf491SMika Westerberg 81879fae987SMika Westerberg /* Remove existing XDomain connection if found */ 81979fae987SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 82079fae987SMika Westerberg if (xd) { 82179fae987SMika Westerberg remove_xdomain(xd); 82279fae987SMika Westerberg tb_xdomain_put(xd); 82379fae987SMika Westerberg } 82479fae987SMika Westerberg 825f67cf491SMika Westerberg parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); 826f67cf491SMika Westerberg if (!parent_sw) { 827f67cf491SMika Westerberg tb_err(tb, "failed to find parent switch for %u.%u\n", 828f67cf491SMika Westerberg link, depth); 829f67cf491SMika Westerberg return; 830f67cf491SMika Westerberg } 831f67cf491SMika Westerberg 832fdd92e89SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 833fdd92e89SMika Westerberg if (ret) { 834fdd92e89SMika Westerberg tb_err(tb, "failed to find route string for switch at %u.%u\n", 835fdd92e89SMika Westerberg link, depth); 836fdd92e89SMika Westerberg tb_switch_put(parent_sw); 837fdd92e89SMika Westerberg return; 838fdd92e89SMika Westerberg } 839fdd92e89SMika Westerberg 840b5db76dbSMika Westerberg pm_runtime_get_sync(&parent_sw->dev); 841b5db76dbSMika Westerberg 842b5db76dbSMika Westerberg sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); 843b5db76dbSMika Westerberg if (!IS_ERR(sw)) { 844b5db76dbSMika Westerberg sw->connection_id = pkg->connection_id; 845b5db76dbSMika Westerberg sw->connection_key = pkg->connection_key; 846b5db76dbSMika Westerberg sw->link = link; 847b5db76dbSMika Westerberg sw->depth = depth; 848b5db76dbSMika Westerberg sw->authorized = authorized; 849b5db76dbSMika Westerberg sw->security_level = security_level; 850b5db76dbSMika Westerberg sw->boot = boot; 85191c0c120SMika Westerberg sw->link_speed = speed_gen3 ? 20 : 10; 85291c0c120SMika Westerberg sw->link_width = dual_lane ? 2 : 1; 853b5db76dbSMika Westerberg sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name)); 854b5db76dbSMika Westerberg 855b5db76dbSMika Westerberg if (add_switch(parent_sw, sw)) 856b5db76dbSMika Westerberg tb_switch_put(sw); 857b5db76dbSMika Westerberg } 858b5db76dbSMika Westerberg 859b5db76dbSMika Westerberg pm_runtime_mark_last_busy(&parent_sw->dev); 860b5db76dbSMika Westerberg pm_runtime_put_autosuspend(&parent_sw->dev); 861f67cf491SMika Westerberg 862f67cf491SMika Westerberg tb_switch_put(parent_sw); 863f67cf491SMika Westerberg } 864f67cf491SMika Westerberg 865f67cf491SMika Westerberg static void 866f67cf491SMika Westerberg icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 867f67cf491SMika Westerberg { 868f67cf491SMika Westerberg const struct icm_fr_event_device_disconnected *pkg = 869f67cf491SMika Westerberg (const struct icm_fr_event_device_disconnected *)hdr; 870f67cf491SMika Westerberg struct tb_switch *sw; 871f67cf491SMika Westerberg u8 link, depth; 872f67cf491SMika Westerberg 873f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 874f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 875f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 876f67cf491SMika Westerberg 877f0342e75SMika Westerberg if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) { 878f67cf491SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 879f67cf491SMika Westerberg return; 880f67cf491SMika Westerberg } 881f67cf491SMika Westerberg 882f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 883f67cf491SMika Westerberg if (!sw) { 884f67cf491SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 885f67cf491SMika Westerberg depth); 886f67cf491SMika Westerberg return; 887f67cf491SMika Westerberg } 888f67cf491SMika Westerberg 889b658eb9dSMika Westerberg pm_runtime_get_sync(sw->dev.parent); 890b658eb9dSMika Westerberg 891f67cf491SMika Westerberg remove_switch(sw); 892b658eb9dSMika Westerberg 893b658eb9dSMika Westerberg pm_runtime_mark_last_busy(sw->dev.parent); 894b658eb9dSMika Westerberg pm_runtime_put_autosuspend(sw->dev.parent); 895b658eb9dSMika Westerberg 896f67cf491SMika Westerberg tb_switch_put(sw); 897f67cf491SMika Westerberg } 898f67cf491SMika Westerberg 899d1ff7024SMika Westerberg static void 900d1ff7024SMika Westerberg icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 901d1ff7024SMika Westerberg { 902d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_connected *pkg = 903d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_connected *)hdr; 904d1ff7024SMika Westerberg struct tb_xdomain *xd; 905d1ff7024SMika Westerberg struct tb_switch *sw; 906d1ff7024SMika Westerberg u8 link, depth; 907d1ff7024SMika Westerberg u64 route; 908d1ff7024SMika Westerberg 909d1ff7024SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 910d1ff7024SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 911d1ff7024SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 912d1ff7024SMika Westerberg 913f0342e75SMika Westerberg if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) { 914d1ff7024SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 915d1ff7024SMika Westerberg return; 916d1ff7024SMika Westerberg } 917d1ff7024SMika Westerberg 918d1ff7024SMika Westerberg route = get_route(pkg->local_route_hi, pkg->local_route_lo); 919d1ff7024SMika Westerberg 920d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 921d1ff7024SMika Westerberg if (xd) { 922d1ff7024SMika Westerberg u8 xd_phy_port, phy_port; 923d1ff7024SMika Westerberg 924d1ff7024SMika Westerberg xd_phy_port = phy_port_from_route(xd->route, xd->depth); 925d1ff7024SMika Westerberg phy_port = phy_port_from_route(route, depth); 926d1ff7024SMika Westerberg 927d1ff7024SMika Westerberg if (xd->depth == depth && xd_phy_port == phy_port) { 928ee487dd2SMika Westerberg update_xdomain(xd, route, link); 929d1ff7024SMika Westerberg tb_xdomain_put(xd); 930d1ff7024SMika Westerberg return; 931d1ff7024SMika Westerberg } 932d1ff7024SMika Westerberg 933d1ff7024SMika Westerberg /* 934d1ff7024SMika Westerberg * If we find an existing XDomain connection remove it 935d1ff7024SMika Westerberg * now. We need to go through login handshake and 936d1ff7024SMika Westerberg * everything anyway to be able to re-establish the 937d1ff7024SMika Westerberg * connection. 938d1ff7024SMika Westerberg */ 939d1ff7024SMika Westerberg remove_xdomain(xd); 940d1ff7024SMika Westerberg tb_xdomain_put(xd); 941d1ff7024SMika Westerberg } 942d1ff7024SMika Westerberg 943d1ff7024SMika Westerberg /* 944d1ff7024SMika Westerberg * Look if there already exists an XDomain in the same place 945d1ff7024SMika Westerberg * than the new one and in that case remove it because it is 946d1ff7024SMika Westerberg * most likely another host that got disconnected. 947d1ff7024SMika Westerberg */ 948d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 949d1ff7024SMika Westerberg if (!xd) { 950d1ff7024SMika Westerberg u8 dual_link; 951d1ff7024SMika Westerberg 952d1ff7024SMika Westerberg dual_link = dual_link_from_link(link); 953d1ff7024SMika Westerberg if (dual_link) 954d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, dual_link, 955d1ff7024SMika Westerberg depth); 956d1ff7024SMika Westerberg } 957d1ff7024SMika Westerberg if (xd) { 958d1ff7024SMika Westerberg remove_xdomain(xd); 959d1ff7024SMika Westerberg tb_xdomain_put(xd); 960d1ff7024SMika Westerberg } 961d1ff7024SMika Westerberg 962d1ff7024SMika Westerberg /* 963d1ff7024SMika Westerberg * If the user disconnected a switch during suspend and 964d1ff7024SMika Westerberg * connected another host to the same port, remove the switch 965d1ff7024SMika Westerberg * first. 966d1ff7024SMika Westerberg */ 9678f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, route); 9688f965efdSMika Westerberg if (sw) { 969d1ff7024SMika Westerberg remove_switch(sw); 9708f965efdSMika Westerberg tb_switch_put(sw); 9718f965efdSMika Westerberg } 972d1ff7024SMika Westerberg 973d1ff7024SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 974d1ff7024SMika Westerberg if (!sw) { 975d1ff7024SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 976d1ff7024SMika Westerberg depth); 977d1ff7024SMika Westerberg return; 978d1ff7024SMika Westerberg } 979d1ff7024SMika Westerberg 980ee487dd2SMika Westerberg add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link, 981ee487dd2SMika Westerberg depth); 982d1ff7024SMika Westerberg tb_switch_put(sw); 983d1ff7024SMika Westerberg } 984d1ff7024SMika Westerberg 985d1ff7024SMika Westerberg static void 986d1ff7024SMika Westerberg icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 987d1ff7024SMika Westerberg { 988d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_disconnected *pkg = 989d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_disconnected *)hdr; 990d1ff7024SMika Westerberg struct tb_xdomain *xd; 991d1ff7024SMika Westerberg 992d1ff7024SMika Westerberg /* 993d1ff7024SMika Westerberg * If the connection is through one or multiple devices, the 994d1ff7024SMika Westerberg * XDomain device is removed along with them so it is fine if we 995d1ff7024SMika Westerberg * cannot find it here. 996d1ff7024SMika Westerberg */ 997d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 998d1ff7024SMika Westerberg if (xd) { 999d1ff7024SMika Westerberg remove_xdomain(xd); 1000d1ff7024SMika Westerberg tb_xdomain_put(xd); 1001d1ff7024SMika Westerberg } 1002d1ff7024SMika Westerberg } 1003d1ff7024SMika Westerberg 10040d53827dSMika Westerberg static int icm_tr_cio_reset(struct tb *tb) 10050d53827dSMika Westerberg { 10060d53827dSMika Westerberg return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x777, BIT(1)); 10070d53827dSMika Westerberg } 10080d53827dSMika Westerberg 10094bac471dSRadion Mirchevsky static int 10104bac471dSRadion Mirchevsky icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 10119039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 10124bac471dSRadion Mirchevsky { 10134bac471dSRadion Mirchevsky struct icm_tr_pkg_driver_ready_response reply; 10144bac471dSRadion Mirchevsky struct icm_pkg_driver_ready request = { 10154bac471dSRadion Mirchevsky .hdr.code = ICM_DRIVER_READY, 10164bac471dSRadion Mirchevsky }; 10174bac471dSRadion Mirchevsky int ret; 10184bac471dSRadion Mirchevsky 10194bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10204bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10214bac471dSRadion Mirchevsky 1, 20000); 10224bac471dSRadion Mirchevsky if (ret) 10234bac471dSRadion Mirchevsky return ret; 10244bac471dSRadion Mirchevsky 10254bac471dSRadion Mirchevsky if (security_level) 10264bac471dSRadion Mirchevsky *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK; 10279039387eSMika Westerberg if (proto_version) 10289039387eSMika Westerberg *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> 10299039387eSMika Westerberg ICM_TR_INFO_PROTO_VERSION_SHIFT; 10304bac471dSRadion Mirchevsky if (nboot_acl) 10314bac471dSRadion Mirchevsky *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >> 10324bac471dSRadion Mirchevsky ICM_TR_INFO_BOOT_ACL_SHIFT; 10332d8ff0b5SMika Westerberg if (rpm) 10342d8ff0b5SMika Westerberg *rpm = !!(reply.hdr.flags & ICM_TR_FLAGS_RTD3); 10352d8ff0b5SMika Westerberg 10364bac471dSRadion Mirchevsky return 0; 10374bac471dSRadion Mirchevsky } 10384bac471dSRadion Mirchevsky 10394bac471dSRadion Mirchevsky static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw) 10404bac471dSRadion Mirchevsky { 10414bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_device request; 10424bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_device reply; 10434bac471dSRadion Mirchevsky int ret; 10444bac471dSRadion Mirchevsky 10454bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10464bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10474bac471dSRadion Mirchevsky request.hdr.code = ICM_APPROVE_DEVICE; 10484bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10494bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10504bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10514bac471dSRadion Mirchevsky 10524bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10534bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10544bac471dSRadion Mirchevsky 1, ICM_APPROVE_TIMEOUT); 10554bac471dSRadion Mirchevsky if (ret) 10564bac471dSRadion Mirchevsky return ret; 10574bac471dSRadion Mirchevsky 10584bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) { 10594bac471dSRadion Mirchevsky tb_warn(tb, "PCIe tunnel creation failed\n"); 10604bac471dSRadion Mirchevsky return -EIO; 10614bac471dSRadion Mirchevsky } 10624bac471dSRadion Mirchevsky 10634bac471dSRadion Mirchevsky return 0; 10644bac471dSRadion Mirchevsky } 10654bac471dSRadion Mirchevsky 10664bac471dSRadion Mirchevsky static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw) 10674bac471dSRadion Mirchevsky { 10684bac471dSRadion Mirchevsky struct icm_tr_pkg_add_device_key_response reply; 10694bac471dSRadion Mirchevsky struct icm_tr_pkg_add_device_key request; 10704bac471dSRadion Mirchevsky int ret; 10714bac471dSRadion Mirchevsky 10724bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10734bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10744bac471dSRadion Mirchevsky request.hdr.code = ICM_ADD_DEVICE_KEY; 10754bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10764bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10774bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10784bac471dSRadion Mirchevsky memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 10794bac471dSRadion Mirchevsky 10804bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10814bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10824bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 10834bac471dSRadion Mirchevsky if (ret) 10844bac471dSRadion Mirchevsky return ret; 10854bac471dSRadion Mirchevsky 10864bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) { 10874bac471dSRadion Mirchevsky tb_warn(tb, "Adding key to switch failed\n"); 10884bac471dSRadion Mirchevsky return -EIO; 10894bac471dSRadion Mirchevsky } 10904bac471dSRadion Mirchevsky 10914bac471dSRadion Mirchevsky return 0; 10924bac471dSRadion Mirchevsky } 10934bac471dSRadion Mirchevsky 10944bac471dSRadion Mirchevsky static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 10954bac471dSRadion Mirchevsky const u8 *challenge, u8 *response) 10964bac471dSRadion Mirchevsky { 10974bac471dSRadion Mirchevsky struct icm_tr_pkg_challenge_device_response reply; 10984bac471dSRadion Mirchevsky struct icm_tr_pkg_challenge_device request; 10994bac471dSRadion Mirchevsky int ret; 11004bac471dSRadion Mirchevsky 11014bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11024bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 11034bac471dSRadion Mirchevsky request.hdr.code = ICM_CHALLENGE_DEVICE; 11044bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 11054bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 11064bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 11074bac471dSRadion Mirchevsky memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 11084bac471dSRadion Mirchevsky 11094bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11104bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11114bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11124bac471dSRadion Mirchevsky if (ret) 11134bac471dSRadion Mirchevsky return ret; 11144bac471dSRadion Mirchevsky 11154bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11164bac471dSRadion Mirchevsky return -EKEYREJECTED; 11174bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 11184bac471dSRadion Mirchevsky return -ENOKEY; 11194bac471dSRadion Mirchevsky 11204bac471dSRadion Mirchevsky memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 11214bac471dSRadion Mirchevsky 11224bac471dSRadion Mirchevsky return 0; 11234bac471dSRadion Mirchevsky } 11244bac471dSRadion Mirchevsky 11254bac471dSRadion Mirchevsky static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 11264bac471dSRadion Mirchevsky { 11274bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_xdomain_response reply; 11284bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_xdomain request; 11294bac471dSRadion Mirchevsky int ret; 11304bac471dSRadion Mirchevsky 11314bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11324bac471dSRadion Mirchevsky request.hdr.code = ICM_APPROVE_XDOMAIN; 11334bac471dSRadion Mirchevsky request.route_hi = upper_32_bits(xd->route); 11344bac471dSRadion Mirchevsky request.route_lo = lower_32_bits(xd->route); 11354bac471dSRadion Mirchevsky request.transmit_path = xd->transmit_path; 11364bac471dSRadion Mirchevsky request.transmit_ring = xd->transmit_ring; 11374bac471dSRadion Mirchevsky request.receive_path = xd->receive_path; 11384bac471dSRadion Mirchevsky request.receive_ring = xd->receive_ring; 11394bac471dSRadion Mirchevsky memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 11404bac471dSRadion Mirchevsky 11414bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11424bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11434bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11444bac471dSRadion Mirchevsky if (ret) 11454bac471dSRadion Mirchevsky return ret; 11464bac471dSRadion Mirchevsky 11474bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11484bac471dSRadion Mirchevsky return -EIO; 11494bac471dSRadion Mirchevsky 11504bac471dSRadion Mirchevsky return 0; 11514bac471dSRadion Mirchevsky } 11524bac471dSRadion Mirchevsky 11534bac471dSRadion Mirchevsky static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd, 11544bac471dSRadion Mirchevsky int stage) 11554bac471dSRadion Mirchevsky { 11564bac471dSRadion Mirchevsky struct icm_tr_pkg_disconnect_xdomain_response reply; 11574bac471dSRadion Mirchevsky struct icm_tr_pkg_disconnect_xdomain request; 11584bac471dSRadion Mirchevsky int ret; 11594bac471dSRadion Mirchevsky 11604bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11614bac471dSRadion Mirchevsky request.hdr.code = ICM_DISCONNECT_XDOMAIN; 11624bac471dSRadion Mirchevsky request.stage = stage; 11634bac471dSRadion Mirchevsky request.route_hi = upper_32_bits(xd->route); 11644bac471dSRadion Mirchevsky request.route_lo = lower_32_bits(xd->route); 11654bac471dSRadion Mirchevsky memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 11664bac471dSRadion Mirchevsky 11674bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11684bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11694bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11704bac471dSRadion Mirchevsky if (ret) 11714bac471dSRadion Mirchevsky return ret; 11724bac471dSRadion Mirchevsky 11734bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11744bac471dSRadion Mirchevsky return -EIO; 11754bac471dSRadion Mirchevsky 11764bac471dSRadion Mirchevsky return 0; 11774bac471dSRadion Mirchevsky } 11784bac471dSRadion Mirchevsky 11794bac471dSRadion Mirchevsky static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 11804bac471dSRadion Mirchevsky { 11814bac471dSRadion Mirchevsky int ret; 11824bac471dSRadion Mirchevsky 11834bac471dSRadion Mirchevsky ret = icm_tr_xdomain_tear_down(tb, xd, 1); 11844bac471dSRadion Mirchevsky if (ret) 11854bac471dSRadion Mirchevsky return ret; 11864bac471dSRadion Mirchevsky 11874bac471dSRadion Mirchevsky usleep_range(10, 50); 11884bac471dSRadion Mirchevsky return icm_tr_xdomain_tear_down(tb, xd, 2); 11894bac471dSRadion Mirchevsky } 11904bac471dSRadion Mirchevsky 11914bac471dSRadion Mirchevsky static void 11923cdb9446SMika Westerberg __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, 11933cdb9446SMika Westerberg bool force_rtd3) 11944bac471dSRadion Mirchevsky { 11954bac471dSRadion Mirchevsky const struct icm_tr_event_device_connected *pkg = 11964bac471dSRadion Mirchevsky (const struct icm_tr_event_device_connected *)hdr; 119791c0c120SMika Westerberg bool authorized, boot, dual_lane, speed_gen3; 11984bac471dSRadion Mirchevsky enum tb_security_level security_level; 11994bac471dSRadion Mirchevsky struct tb_switch *sw, *parent_sw; 12004bac471dSRadion Mirchevsky struct tb_xdomain *xd; 12014bac471dSRadion Mirchevsky u64 route; 12024bac471dSRadion Mirchevsky 12033cdb9446SMika Westerberg icm_postpone_rescan(tb); 12043cdb9446SMika Westerberg 12054bac471dSRadion Mirchevsky /* 12064bac471dSRadion Mirchevsky * Currently we don't use the QoS information coming with the 12074bac471dSRadion Mirchevsky * device connected message so simply just ignore that extra 12084bac471dSRadion Mirchevsky * packet for now. 12094bac471dSRadion Mirchevsky */ 12104bac471dSRadion Mirchevsky if (pkg->hdr.packet_id) 12114bac471dSRadion Mirchevsky return; 12124bac471dSRadion Mirchevsky 12134bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 12144bac471dSRadion Mirchevsky authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 12154bac471dSRadion Mirchevsky security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 12164bac471dSRadion Mirchevsky ICM_FLAGS_SLEVEL_SHIFT; 12174bac471dSRadion Mirchevsky boot = pkg->link_info & ICM_LINK_INFO_BOOT; 121891c0c120SMika Westerberg dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; 121991c0c120SMika Westerberg speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; 12204bac471dSRadion Mirchevsky 12214bac471dSRadion Mirchevsky if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 12224bac471dSRadion Mirchevsky tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n", 12234bac471dSRadion Mirchevsky route); 12244bac471dSRadion Mirchevsky return; 12254bac471dSRadion Mirchevsky } 12264bac471dSRadion Mirchevsky 12274bac471dSRadion Mirchevsky sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 12284bac471dSRadion Mirchevsky if (sw) { 12294bac471dSRadion Mirchevsky /* Update the switch if it is still in the same place */ 12304bac471dSRadion Mirchevsky if (tb_route(sw) == route && !!sw->authorized == authorized) { 12314bac471dSRadion Mirchevsky parent_sw = tb_to_switch(sw->dev.parent); 12324bac471dSRadion Mirchevsky update_switch(parent_sw, sw, route, pkg->connection_id, 12334bac471dSRadion Mirchevsky 0, 0, 0, boot); 12344bac471dSRadion Mirchevsky tb_switch_put(sw); 12354bac471dSRadion Mirchevsky return; 12364bac471dSRadion Mirchevsky } 12374bac471dSRadion Mirchevsky 12384bac471dSRadion Mirchevsky remove_switch(sw); 12394bac471dSRadion Mirchevsky tb_switch_put(sw); 12404bac471dSRadion Mirchevsky } 12414bac471dSRadion Mirchevsky 12424bac471dSRadion Mirchevsky /* Another switch with the same address */ 12434bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, route); 12444bac471dSRadion Mirchevsky if (sw) { 12454bac471dSRadion Mirchevsky remove_switch(sw); 12464bac471dSRadion Mirchevsky tb_switch_put(sw); 12474bac471dSRadion Mirchevsky } 12484bac471dSRadion Mirchevsky 12494bac471dSRadion Mirchevsky /* XDomain connection with the same address */ 12504bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 12514bac471dSRadion Mirchevsky if (xd) { 12524bac471dSRadion Mirchevsky remove_xdomain(xd); 12534bac471dSRadion Mirchevsky tb_xdomain_put(xd); 12544bac471dSRadion Mirchevsky } 12554bac471dSRadion Mirchevsky 12564bac471dSRadion Mirchevsky parent_sw = tb_switch_find_by_route(tb, get_parent_route(route)); 12574bac471dSRadion Mirchevsky if (!parent_sw) { 12584bac471dSRadion Mirchevsky tb_err(tb, "failed to find parent switch for %llx\n", route); 12594bac471dSRadion Mirchevsky return; 12604bac471dSRadion Mirchevsky } 12614bac471dSRadion Mirchevsky 1262b5db76dbSMika Westerberg pm_runtime_get_sync(&parent_sw->dev); 1263b5db76dbSMika Westerberg 1264b5db76dbSMika Westerberg sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); 1265b5db76dbSMika Westerberg if (!IS_ERR(sw)) { 1266b5db76dbSMika Westerberg sw->connection_id = pkg->connection_id; 1267b5db76dbSMika Westerberg sw->authorized = authorized; 1268b5db76dbSMika Westerberg sw->security_level = security_level; 1269b5db76dbSMika Westerberg sw->boot = boot; 127091c0c120SMika Westerberg sw->link_speed = speed_gen3 ? 20 : 10; 127191c0c120SMika Westerberg sw->link_width = dual_lane ? 2 : 1; 1272b5db76dbSMika Westerberg sw->rpm = force_rtd3; 1273b5db76dbSMika Westerberg if (!sw->rpm) 1274b5db76dbSMika Westerberg sw->rpm = intel_vss_is_rtd3(pkg->ep_name, 1275b5db76dbSMika Westerberg sizeof(pkg->ep_name)); 1276b5db76dbSMika Westerberg 1277b5db76dbSMika Westerberg if (add_switch(parent_sw, sw)) 1278b5db76dbSMika Westerberg tb_switch_put(sw); 1279b5db76dbSMika Westerberg } 1280b5db76dbSMika Westerberg 1281b5db76dbSMika Westerberg pm_runtime_mark_last_busy(&parent_sw->dev); 1282b5db76dbSMika Westerberg pm_runtime_put_autosuspend(&parent_sw->dev); 12834bac471dSRadion Mirchevsky 12844bac471dSRadion Mirchevsky tb_switch_put(parent_sw); 12854bac471dSRadion Mirchevsky } 12864bac471dSRadion Mirchevsky 12874bac471dSRadion Mirchevsky static void 12883cdb9446SMika Westerberg icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 12893cdb9446SMika Westerberg { 12903cdb9446SMika Westerberg __icm_tr_device_connected(tb, hdr, false); 12913cdb9446SMika Westerberg } 12923cdb9446SMika Westerberg 12933cdb9446SMika Westerberg static void 12944bac471dSRadion Mirchevsky icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 12954bac471dSRadion Mirchevsky { 12964bac471dSRadion Mirchevsky const struct icm_tr_event_device_disconnected *pkg = 12974bac471dSRadion Mirchevsky (const struct icm_tr_event_device_disconnected *)hdr; 12984bac471dSRadion Mirchevsky struct tb_switch *sw; 12994bac471dSRadion Mirchevsky u64 route; 13004bac471dSRadion Mirchevsky 13014bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 13024bac471dSRadion Mirchevsky 13034bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, route); 13044bac471dSRadion Mirchevsky if (!sw) { 13054bac471dSRadion Mirchevsky tb_warn(tb, "no switch exists at %llx, ignoring\n", route); 13064bac471dSRadion Mirchevsky return; 13074bac471dSRadion Mirchevsky } 1308b658eb9dSMika Westerberg pm_runtime_get_sync(sw->dev.parent); 13094bac471dSRadion Mirchevsky 13104bac471dSRadion Mirchevsky remove_switch(sw); 1311b658eb9dSMika Westerberg 1312b658eb9dSMika Westerberg pm_runtime_mark_last_busy(sw->dev.parent); 1313b658eb9dSMika Westerberg pm_runtime_put_autosuspend(sw->dev.parent); 1314b658eb9dSMika Westerberg 13154bac471dSRadion Mirchevsky tb_switch_put(sw); 13164bac471dSRadion Mirchevsky } 13174bac471dSRadion Mirchevsky 13184bac471dSRadion Mirchevsky static void 13194bac471dSRadion Mirchevsky icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 13204bac471dSRadion Mirchevsky { 13214bac471dSRadion Mirchevsky const struct icm_tr_event_xdomain_connected *pkg = 13224bac471dSRadion Mirchevsky (const struct icm_tr_event_xdomain_connected *)hdr; 13234bac471dSRadion Mirchevsky struct tb_xdomain *xd; 13244bac471dSRadion Mirchevsky struct tb_switch *sw; 13254bac471dSRadion Mirchevsky u64 route; 13264bac471dSRadion Mirchevsky 13274bac471dSRadion Mirchevsky if (!tb->root_switch) 13284bac471dSRadion Mirchevsky return; 13294bac471dSRadion Mirchevsky 13304bac471dSRadion Mirchevsky route = get_route(pkg->local_route_hi, pkg->local_route_lo); 13314bac471dSRadion Mirchevsky 13324bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 13334bac471dSRadion Mirchevsky if (xd) { 13344bac471dSRadion Mirchevsky if (xd->route == route) { 13354bac471dSRadion Mirchevsky update_xdomain(xd, route, 0); 13364bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13374bac471dSRadion Mirchevsky return; 13384bac471dSRadion Mirchevsky } 13394bac471dSRadion Mirchevsky 13404bac471dSRadion Mirchevsky remove_xdomain(xd); 13414bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13424bac471dSRadion Mirchevsky } 13434bac471dSRadion Mirchevsky 13444bac471dSRadion Mirchevsky /* An existing xdomain with the same address */ 13454bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 13464bac471dSRadion Mirchevsky if (xd) { 13474bac471dSRadion Mirchevsky remove_xdomain(xd); 13484bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13494bac471dSRadion Mirchevsky } 13504bac471dSRadion Mirchevsky 13514bac471dSRadion Mirchevsky /* 13524bac471dSRadion Mirchevsky * If the user disconnected a switch during suspend and 13534bac471dSRadion Mirchevsky * connected another host to the same port, remove the switch 13544bac471dSRadion Mirchevsky * first. 13554bac471dSRadion Mirchevsky */ 13568f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, route); 13578f965efdSMika Westerberg if (sw) { 13584bac471dSRadion Mirchevsky remove_switch(sw); 13598f965efdSMika Westerberg tb_switch_put(sw); 13608f965efdSMika Westerberg } 13614bac471dSRadion Mirchevsky 13624bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, get_parent_route(route)); 13634bac471dSRadion Mirchevsky if (!sw) { 13644bac471dSRadion Mirchevsky tb_warn(tb, "no switch exists at %llx, ignoring\n", route); 13654bac471dSRadion Mirchevsky return; 13664bac471dSRadion Mirchevsky } 13674bac471dSRadion Mirchevsky 13684bac471dSRadion Mirchevsky add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0); 13694bac471dSRadion Mirchevsky tb_switch_put(sw); 13704bac471dSRadion Mirchevsky } 13714bac471dSRadion Mirchevsky 13724bac471dSRadion Mirchevsky static void 13734bac471dSRadion Mirchevsky icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 13744bac471dSRadion Mirchevsky { 13754bac471dSRadion Mirchevsky const struct icm_tr_event_xdomain_disconnected *pkg = 13764bac471dSRadion Mirchevsky (const struct icm_tr_event_xdomain_disconnected *)hdr; 13774bac471dSRadion Mirchevsky struct tb_xdomain *xd; 13784bac471dSRadion Mirchevsky u64 route; 13794bac471dSRadion Mirchevsky 13804bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 13814bac471dSRadion Mirchevsky 13824bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 13834bac471dSRadion Mirchevsky if (xd) { 13844bac471dSRadion Mirchevsky remove_xdomain(xd); 13854bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13864bac471dSRadion Mirchevsky } 13874bac471dSRadion Mirchevsky } 13884bac471dSRadion Mirchevsky 1389f67cf491SMika Westerberg static struct pci_dev *get_upstream_port(struct pci_dev *pdev) 1390f67cf491SMika Westerberg { 1391f67cf491SMika Westerberg struct pci_dev *parent; 1392f67cf491SMika Westerberg 1393f67cf491SMika Westerberg parent = pci_upstream_bridge(pdev); 1394f67cf491SMika Westerberg while (parent) { 1395f67cf491SMika Westerberg if (!pci_is_pcie(parent)) 1396f67cf491SMika Westerberg return NULL; 1397f67cf491SMika Westerberg if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) 1398f67cf491SMika Westerberg break; 1399f67cf491SMika Westerberg parent = pci_upstream_bridge(parent); 1400f67cf491SMika Westerberg } 1401f67cf491SMika Westerberg 1402f67cf491SMika Westerberg if (!parent) 1403f67cf491SMika Westerberg return NULL; 1404f67cf491SMika Westerberg 1405f67cf491SMika Westerberg switch (parent->device) { 1406f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: 1407f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: 1408f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: 1409f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: 1410f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: 1411c4630d6aSMika Westerberg case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: 1412c4630d6aSMika Westerberg case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: 1413f67cf491SMika Westerberg return parent; 1414f67cf491SMika Westerberg } 1415f67cf491SMika Westerberg 1416f67cf491SMika Westerberg return NULL; 1417f67cf491SMika Westerberg } 1418f67cf491SMika Westerberg 1419f67cf491SMika Westerberg static bool icm_ar_is_supported(struct tb *tb) 1420f67cf491SMika Westerberg { 1421f67cf491SMika Westerberg struct pci_dev *upstream_port; 1422f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1423f67cf491SMika Westerberg 1424f67cf491SMika Westerberg /* 1425f67cf491SMika Westerberg * Starting from Alpine Ridge we can use ICM on Apple machines 1426f67cf491SMika Westerberg * as well. We just need to reset and re-enable it first. 1427354a7a77SMika Westerberg * However, only start it if explicitly asked by the user. 1428f67cf491SMika Westerberg */ 1429354a7a77SMika Westerberg if (icm_firmware_running(tb->nhi)) 1430f67cf491SMika Westerberg return true; 1431354a7a77SMika Westerberg if (!start_icm) 1432354a7a77SMika Westerberg return false; 1433f67cf491SMika Westerberg 1434f67cf491SMika Westerberg /* 1435f67cf491SMika Westerberg * Find the upstream PCIe port in case we need to do reset 1436f67cf491SMika Westerberg * through its vendor specific registers. 1437f67cf491SMika Westerberg */ 1438f67cf491SMika Westerberg upstream_port = get_upstream_port(tb->nhi->pdev); 1439f67cf491SMika Westerberg if (upstream_port) { 1440f67cf491SMika Westerberg int cap; 1441f67cf491SMika Westerberg 1442f67cf491SMika Westerberg cap = pci_find_ext_capability(upstream_port, 1443f67cf491SMika Westerberg PCI_EXT_CAP_ID_VNDR); 1444f67cf491SMika Westerberg if (cap > 0) { 1445f67cf491SMika Westerberg icm->upstream_port = upstream_port; 1446f67cf491SMika Westerberg icm->vnd_cap = cap; 1447f67cf491SMika Westerberg 1448f67cf491SMika Westerberg return true; 1449f67cf491SMika Westerberg } 1450f67cf491SMika Westerberg } 1451f67cf491SMika Westerberg 1452f67cf491SMika Westerberg return false; 1453f67cf491SMika Westerberg } 1454f67cf491SMika Westerberg 14550d53827dSMika Westerberg static int icm_ar_cio_reset(struct tb *tb) 14560d53827dSMika Westerberg { 14570d53827dSMika Westerberg return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x50, BIT(9)); 14580d53827dSMika Westerberg } 14590d53827dSMika Westerberg 1460f67cf491SMika Westerberg static int icm_ar_get_mode(struct tb *tb) 1461f67cf491SMika Westerberg { 1462f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1463e4be8c9bSMika Westerberg int retries = 60; 1464f67cf491SMika Westerberg u32 val; 1465f67cf491SMika Westerberg 1466f67cf491SMika Westerberg do { 1467f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1468f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1469f67cf491SMika Westerberg break; 1470e4be8c9bSMika Westerberg msleep(50); 1471f67cf491SMika Westerberg } while (--retries); 1472f67cf491SMika Westerberg 1473f67cf491SMika Westerberg if (!retries) { 1474f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "ICM firmware not authenticated\n"); 1475f67cf491SMika Westerberg return -ENODEV; 1476f67cf491SMika Westerberg } 1477f67cf491SMika Westerberg 1478f67cf491SMika Westerberg return nhi_mailbox_mode(nhi); 1479f67cf491SMika Westerberg } 1480f67cf491SMika Westerberg 14819aaa3b8bSMika Westerberg static int 14829aaa3b8bSMika Westerberg icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, 14839039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 14849aaa3b8bSMika Westerberg { 14859aaa3b8bSMika Westerberg struct icm_ar_pkg_driver_ready_response reply; 14869aaa3b8bSMika Westerberg struct icm_pkg_driver_ready request = { 14879aaa3b8bSMika Westerberg .hdr.code = ICM_DRIVER_READY, 14889aaa3b8bSMika Westerberg }; 14899aaa3b8bSMika Westerberg int ret; 14909aaa3b8bSMika Westerberg 14919aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 14929aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 14939aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 14949aaa3b8bSMika Westerberg if (ret) 14959aaa3b8bSMika Westerberg return ret; 14969aaa3b8bSMika Westerberg 14979aaa3b8bSMika Westerberg if (security_level) 14989aaa3b8bSMika Westerberg *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK; 14999aaa3b8bSMika Westerberg if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED)) 15009aaa3b8bSMika Westerberg *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >> 15019aaa3b8bSMika Westerberg ICM_AR_INFO_BOOT_ACL_SHIFT; 15022d8ff0b5SMika Westerberg if (rpm) 15032d8ff0b5SMika Westerberg *rpm = !!(reply.hdr.flags & ICM_AR_FLAGS_RTD3); 15042d8ff0b5SMika Westerberg 15059aaa3b8bSMika Westerberg return 0; 15069aaa3b8bSMika Westerberg } 15079aaa3b8bSMika Westerberg 1508f67cf491SMika Westerberg static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 1509f67cf491SMika Westerberg { 1510f67cf491SMika Westerberg struct icm_ar_pkg_get_route_response reply; 1511f67cf491SMika Westerberg struct icm_ar_pkg_get_route request = { 1512f67cf491SMika Westerberg .hdr = { .code = ICM_GET_ROUTE }, 1513f67cf491SMika Westerberg .link_info = depth << ICM_LINK_INFO_DEPTH_SHIFT | link, 1514f67cf491SMika Westerberg }; 1515f67cf491SMika Westerberg int ret; 1516f67cf491SMika Westerberg 1517f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 1518f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 1519f67cf491SMika Westerberg 1, ICM_TIMEOUT); 1520f67cf491SMika Westerberg if (ret) 1521f67cf491SMika Westerberg return ret; 1522f67cf491SMika Westerberg 1523f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 1524f67cf491SMika Westerberg return -EIO; 1525f67cf491SMika Westerberg 1526f67cf491SMika Westerberg *route = get_route(reply.route_hi, reply.route_lo); 1527f67cf491SMika Westerberg return 0; 1528f67cf491SMika Westerberg } 1529f67cf491SMika Westerberg 15309aaa3b8bSMika Westerberg static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) 15319aaa3b8bSMika Westerberg { 15329aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 15339aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 15349aaa3b8bSMika Westerberg .hdr = { .code = ICM_PREBOOT_ACL }, 15359aaa3b8bSMika Westerberg }; 15369aaa3b8bSMika Westerberg int ret, i; 15379aaa3b8bSMika Westerberg 15389aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 15399aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 15409aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 15419aaa3b8bSMika Westerberg if (ret) 15429aaa3b8bSMika Westerberg return ret; 15439aaa3b8bSMika Westerberg 15449aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 15459aaa3b8bSMika Westerberg return -EIO; 15469aaa3b8bSMika Westerberg 15479aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 15489aaa3b8bSMika Westerberg u32 *uuid = (u32 *)&uuids[i]; 15499aaa3b8bSMika Westerberg 15509aaa3b8bSMika Westerberg uuid[0] = reply.acl[i].uuid_lo; 15519aaa3b8bSMika Westerberg uuid[1] = reply.acl[i].uuid_hi; 15529aaa3b8bSMika Westerberg 15539aaa3b8bSMika Westerberg if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) { 15549aaa3b8bSMika Westerberg /* Map empty entries to null UUID */ 15559aaa3b8bSMika Westerberg uuid[0] = 0; 15569aaa3b8bSMika Westerberg uuid[1] = 0; 1557dd010bd7SMika Westerberg } else if (uuid[0] != 0 || uuid[1] != 0) { 15589aaa3b8bSMika Westerberg /* Upper two DWs are always one's */ 15599aaa3b8bSMika Westerberg uuid[2] = 0xffffffff; 15609aaa3b8bSMika Westerberg uuid[3] = 0xffffffff; 15619aaa3b8bSMika Westerberg } 15629aaa3b8bSMika Westerberg } 15639aaa3b8bSMika Westerberg 15649aaa3b8bSMika Westerberg return ret; 15659aaa3b8bSMika Westerberg } 15669aaa3b8bSMika Westerberg 15679aaa3b8bSMika Westerberg static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, 15689aaa3b8bSMika Westerberg size_t nuuids) 15699aaa3b8bSMika Westerberg { 15709aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 15719aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 15729aaa3b8bSMika Westerberg .hdr = { 15739aaa3b8bSMika Westerberg .code = ICM_PREBOOT_ACL, 15749aaa3b8bSMika Westerberg .flags = ICM_FLAGS_WRITE, 15759aaa3b8bSMika Westerberg }, 15769aaa3b8bSMika Westerberg }; 15779aaa3b8bSMika Westerberg int ret, i; 15789aaa3b8bSMika Westerberg 15799aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 15809aaa3b8bSMika Westerberg const u32 *uuid = (const u32 *)&uuids[i]; 15819aaa3b8bSMika Westerberg 15829aaa3b8bSMika Westerberg if (uuid_is_null(&uuids[i])) { 15839aaa3b8bSMika Westerberg /* 15849aaa3b8bSMika Westerberg * Map null UUID to the empty (all one) entries 15859aaa3b8bSMika Westerberg * for ICM. 15869aaa3b8bSMika Westerberg */ 15879aaa3b8bSMika Westerberg request.acl[i].uuid_lo = 0xffffffff; 15889aaa3b8bSMika Westerberg request.acl[i].uuid_hi = 0xffffffff; 15899aaa3b8bSMika Westerberg } else { 15909aaa3b8bSMika Westerberg /* Two high DWs need to be set to all one */ 15919aaa3b8bSMika Westerberg if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff) 15929aaa3b8bSMika Westerberg return -EINVAL; 15939aaa3b8bSMika Westerberg 15949aaa3b8bSMika Westerberg request.acl[i].uuid_lo = uuid[0]; 15959aaa3b8bSMika Westerberg request.acl[i].uuid_hi = uuid[1]; 15969aaa3b8bSMika Westerberg } 15979aaa3b8bSMika Westerberg } 15989aaa3b8bSMika Westerberg 15999aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 16009aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 16019aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 16029aaa3b8bSMika Westerberg if (ret) 16039aaa3b8bSMika Westerberg return ret; 16049aaa3b8bSMika Westerberg 16059aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 16069aaa3b8bSMika Westerberg return -EIO; 16079aaa3b8bSMika Westerberg 16089aaa3b8bSMika Westerberg return 0; 16099aaa3b8bSMika Westerberg } 16109aaa3b8bSMika Westerberg 16113cdb9446SMika Westerberg static int 16123cdb9446SMika Westerberg icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, 16139039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 16143cdb9446SMika Westerberg { 16153cdb9446SMika Westerberg struct icm_tr_pkg_driver_ready_response reply; 16163cdb9446SMika Westerberg struct icm_pkg_driver_ready request = { 16173cdb9446SMika Westerberg .hdr.code = ICM_DRIVER_READY, 16183cdb9446SMika Westerberg }; 16193cdb9446SMika Westerberg int ret; 16203cdb9446SMika Westerberg 16213cdb9446SMika Westerberg memset(&reply, 0, sizeof(reply)); 16223cdb9446SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 16233cdb9446SMika Westerberg 1, 20000); 16243cdb9446SMika Westerberg if (ret) 16253cdb9446SMika Westerberg return ret; 16263cdb9446SMika Westerberg 16279039387eSMika Westerberg if (proto_version) 16289039387eSMika Westerberg *proto_version = (reply.info & ICM_TR_INFO_PROTO_VERSION_MASK) >> 16299039387eSMika Westerberg ICM_TR_INFO_PROTO_VERSION_SHIFT; 16309039387eSMika Westerberg 16313cdb9446SMika Westerberg /* Ice Lake always supports RTD3 */ 16323cdb9446SMika Westerberg if (rpm) 16333cdb9446SMika Westerberg *rpm = true; 16343cdb9446SMika Westerberg 16353cdb9446SMika Westerberg return 0; 16363cdb9446SMika Westerberg } 16373cdb9446SMika Westerberg 16383cdb9446SMika Westerberg static void icm_icl_set_uuid(struct tb *tb) 16393cdb9446SMika Westerberg { 16403cdb9446SMika Westerberg struct tb_nhi *nhi = tb->nhi; 16413cdb9446SMika Westerberg u32 uuid[4]; 16423cdb9446SMika Westerberg 16433cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]); 16443cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]); 16453cdb9446SMika Westerberg uuid[2] = 0xffffffff; 16463cdb9446SMika Westerberg uuid[3] = 0xffffffff; 16473cdb9446SMika Westerberg 16483cdb9446SMika Westerberg tb->root_switch->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL); 16493cdb9446SMika Westerberg } 16503cdb9446SMika Westerberg 16513cdb9446SMika Westerberg static void 16523cdb9446SMika Westerberg icm_icl_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 16533cdb9446SMika Westerberg { 16543cdb9446SMika Westerberg __icm_tr_device_connected(tb, hdr, true); 16553cdb9446SMika Westerberg } 16563cdb9446SMika Westerberg 16573cdb9446SMika Westerberg static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr) 16583cdb9446SMika Westerberg { 16593cdb9446SMika Westerberg const struct icm_icl_event_rtd3_veto *pkg = 16603cdb9446SMika Westerberg (const struct icm_icl_event_rtd3_veto *)hdr; 16613cdb9446SMika Westerberg 16623cdb9446SMika Westerberg tb_dbg(tb, "ICM rtd3 veto=0x%08x\n", pkg->veto_reason); 16633cdb9446SMika Westerberg 16643cdb9446SMika Westerberg if (pkg->veto_reason) 16653cdb9446SMika Westerberg icm_veto_begin(tb); 16663cdb9446SMika Westerberg else 16673cdb9446SMika Westerberg icm_veto_end(tb); 16683cdb9446SMika Westerberg } 16693cdb9446SMika Westerberg 167057d8df68SMika Westerberg static bool icm_tgl_is_supported(struct tb *tb) 167157d8df68SMika Westerberg { 167249f2a7f4SMika Westerberg u32 val; 167349f2a7f4SMika Westerberg 167457d8df68SMika Westerberg /* 167557d8df68SMika Westerberg * If the firmware is not running use software CM. This platform 167657d8df68SMika Westerberg * should fully support both. 167757d8df68SMika Westerberg */ 167849f2a7f4SMika Westerberg val = ioread32(tb->nhi->iobase + REG_FW_STS); 167949f2a7f4SMika Westerberg return !!(val & REG_FW_STS_NVM_AUTH_DONE); 168057d8df68SMika Westerberg } 168157d8df68SMika Westerberg 1682f67cf491SMika Westerberg static void icm_handle_notification(struct work_struct *work) 1683f67cf491SMika Westerberg { 1684f67cf491SMika Westerberg struct icm_notification *n = container_of(work, typeof(*n), work); 1685f67cf491SMika Westerberg struct tb *tb = n->tb; 1686f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1687f67cf491SMika Westerberg 1688f67cf491SMika Westerberg mutex_lock(&tb->lock); 1689f67cf491SMika Westerberg 169086da809dSMika Westerberg /* 169186da809dSMika Westerberg * When the domain is stopped we flush its workqueue but before 169286da809dSMika Westerberg * that the root switch is removed. In that case we should treat 169386da809dSMika Westerberg * the queued events as being canceled. 169486da809dSMika Westerberg */ 169586da809dSMika Westerberg if (tb->root_switch) { 1696f67cf491SMika Westerberg switch (n->pkg->code) { 1697f67cf491SMika Westerberg case ICM_EVENT_DEVICE_CONNECTED: 1698f67cf491SMika Westerberg icm->device_connected(tb, n->pkg); 1699f67cf491SMika Westerberg break; 1700f67cf491SMika Westerberg case ICM_EVENT_DEVICE_DISCONNECTED: 1701f67cf491SMika Westerberg icm->device_disconnected(tb, n->pkg); 1702f67cf491SMika Westerberg break; 1703d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_CONNECTED: 1704d1ff7024SMika Westerberg icm->xdomain_connected(tb, n->pkg); 1705d1ff7024SMika Westerberg break; 1706d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_DISCONNECTED: 1707d1ff7024SMika Westerberg icm->xdomain_disconnected(tb, n->pkg); 1708d1ff7024SMika Westerberg break; 17093cdb9446SMika Westerberg case ICM_EVENT_RTD3_VETO: 17103cdb9446SMika Westerberg icm->rtd3_veto(tb, n->pkg); 17113cdb9446SMika Westerberg break; 1712f67cf491SMika Westerberg } 171386da809dSMika Westerberg } 1714f67cf491SMika Westerberg 1715f67cf491SMika Westerberg mutex_unlock(&tb->lock); 1716f67cf491SMika Westerberg 1717f67cf491SMika Westerberg kfree(n->pkg); 1718f67cf491SMika Westerberg kfree(n); 1719f67cf491SMika Westerberg } 1720f67cf491SMika Westerberg 1721f67cf491SMika Westerberg static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, 1722f67cf491SMika Westerberg const void *buf, size_t size) 1723f67cf491SMika Westerberg { 1724f67cf491SMika Westerberg struct icm_notification *n; 1725f67cf491SMika Westerberg 1726f67cf491SMika Westerberg n = kmalloc(sizeof(*n), GFP_KERNEL); 1727f67cf491SMika Westerberg if (!n) 1728f67cf491SMika Westerberg return; 1729f67cf491SMika Westerberg 1730f67cf491SMika Westerberg INIT_WORK(&n->work, icm_handle_notification); 1731f67cf491SMika Westerberg n->pkg = kmemdup(buf, size, GFP_KERNEL); 1732f67cf491SMika Westerberg n->tb = tb; 1733f67cf491SMika Westerberg 1734f67cf491SMika Westerberg queue_work(tb->wq, &n->work); 1735f67cf491SMika Westerberg } 1736f67cf491SMika Westerberg 1737f67cf491SMika Westerberg static int 17389aaa3b8bSMika Westerberg __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, 17399039387eSMika Westerberg u8 *proto_version, size_t *nboot_acl, bool *rpm) 1740f67cf491SMika Westerberg { 17413080e197SMika Westerberg struct icm *icm = tb_priv(tb); 174244b51bbbSMika Westerberg unsigned int retries = 50; 1743f67cf491SMika Westerberg int ret; 1744f67cf491SMika Westerberg 17459039387eSMika Westerberg ret = icm->driver_ready(tb, security_level, proto_version, nboot_acl, 17469039387eSMika Westerberg rpm); 17473080e197SMika Westerberg if (ret) { 17483080e197SMika Westerberg tb_err(tb, "failed to send driver ready to ICM\n"); 1749f67cf491SMika Westerberg return ret; 17503080e197SMika Westerberg } 1751f67cf491SMika Westerberg 1752f67cf491SMika Westerberg /* 1753f67cf491SMika Westerberg * Hold on here until the switch config space is accessible so 1754f67cf491SMika Westerberg * that we can read root switch config successfully. 1755f67cf491SMika Westerberg */ 1756f67cf491SMika Westerberg do { 1757f67cf491SMika Westerberg struct tb_cfg_result res; 1758f67cf491SMika Westerberg u32 tmp; 1759f67cf491SMika Westerberg 1760f67cf491SMika Westerberg res = tb_cfg_read_raw(tb->ctl, &tmp, 0, 0, TB_CFG_SWITCH, 1761f67cf491SMika Westerberg 0, 1, 100); 1762f67cf491SMika Westerberg if (!res.err) 1763f67cf491SMika Westerberg return 0; 1764f67cf491SMika Westerberg 1765f67cf491SMika Westerberg msleep(50); 1766f67cf491SMika Westerberg } while (--retries); 1767f67cf491SMika Westerberg 176844b51bbbSMika Westerberg tb_err(tb, "failed to read root switch config space, giving up\n"); 1769f67cf491SMika Westerberg return -ETIMEDOUT; 1770f67cf491SMika Westerberg } 1771f67cf491SMika Westerberg 1772f67cf491SMika Westerberg static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) 1773f67cf491SMika Westerberg { 1774f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1775f67cf491SMika Westerberg u32 val; 1776f67cf491SMika Westerberg 1777ea9d7bb7SMika Westerberg if (!icm->upstream_port) 1778ea9d7bb7SMika Westerberg return -ENODEV; 1779ea9d7bb7SMika Westerberg 1780f67cf491SMika Westerberg /* Put ARC to wait for CIO reset event to happen */ 1781f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1782f67cf491SMika Westerberg val |= REG_FW_STS_CIO_RESET_REQ; 1783f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1784f67cf491SMika Westerberg 1785f67cf491SMika Westerberg /* Re-start ARC */ 1786f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1787f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_INVERT; 1788f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_CPU; 1789f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1790f67cf491SMika Westerberg 1791f67cf491SMika Westerberg /* Trigger CIO reset now */ 17920d53827dSMika Westerberg return icm->cio_reset(tb); 1793f67cf491SMika Westerberg } 1794f67cf491SMika Westerberg 1795f67cf491SMika Westerberg static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) 1796f67cf491SMika Westerberg { 1797f67cf491SMika Westerberg unsigned int retries = 10; 1798f67cf491SMika Westerberg int ret; 1799f67cf491SMika Westerberg u32 val; 1800f67cf491SMika Westerberg 1801f67cf491SMika Westerberg /* Check if the ICM firmware is already running */ 1802354a7a77SMika Westerberg if (icm_firmware_running(nhi)) 1803f67cf491SMika Westerberg return 0; 1804f67cf491SMika Westerberg 180562efe699SMika Westerberg dev_dbg(&nhi->pdev->dev, "starting ICM firmware\n"); 1806f67cf491SMika Westerberg 1807f67cf491SMika Westerberg ret = icm_firmware_reset(tb, nhi); 1808f67cf491SMika Westerberg if (ret) 1809f67cf491SMika Westerberg return ret; 1810f67cf491SMika Westerberg 1811f67cf491SMika Westerberg /* Wait until the ICM firmware tells us it is up and running */ 1812f67cf491SMika Westerberg do { 1813f67cf491SMika Westerberg /* Check that the ICM firmware is running */ 1814f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1815f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1816f67cf491SMika Westerberg return 0; 1817f67cf491SMika Westerberg 1818f67cf491SMika Westerberg msleep(300); 1819f67cf491SMika Westerberg } while (--retries); 1820f67cf491SMika Westerberg 1821f67cf491SMika Westerberg return -ETIMEDOUT; 1822f67cf491SMika Westerberg } 1823f67cf491SMika Westerberg 1824f67cf491SMika Westerberg static int icm_reset_phy_port(struct tb *tb, int phy_port) 1825f67cf491SMika Westerberg { 1826f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1827f67cf491SMika Westerberg u32 state0, state1; 1828f67cf491SMika Westerberg int port0, port1; 1829f67cf491SMika Westerberg u32 val0, val1; 1830f67cf491SMika Westerberg int ret; 1831f67cf491SMika Westerberg 1832f67cf491SMika Westerberg if (!icm->upstream_port) 1833f67cf491SMika Westerberg return 0; 1834f67cf491SMika Westerberg 1835f67cf491SMika Westerberg if (phy_port) { 1836f67cf491SMika Westerberg port0 = 3; 1837f67cf491SMika Westerberg port1 = 4; 1838f67cf491SMika Westerberg } else { 1839f67cf491SMika Westerberg port0 = 1; 1840f67cf491SMika Westerberg port1 = 2; 1841f67cf491SMika Westerberg } 1842f67cf491SMika Westerberg 1843f67cf491SMika Westerberg /* 1844f67cf491SMika Westerberg * Read link status of both null ports belonging to a single 1845f67cf491SMika Westerberg * physical port. 1846f67cf491SMika Westerberg */ 1847f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1848f67cf491SMika Westerberg if (ret) 1849f67cf491SMika Westerberg return ret; 1850f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1851f67cf491SMika Westerberg if (ret) 1852f67cf491SMika Westerberg return ret; 1853f67cf491SMika Westerberg 1854f67cf491SMika Westerberg state0 = val0 & PHY_PORT_CS1_LINK_STATE_MASK; 1855f67cf491SMika Westerberg state0 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1856f67cf491SMika Westerberg state1 = val1 & PHY_PORT_CS1_LINK_STATE_MASK; 1857f67cf491SMika Westerberg state1 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1858f67cf491SMika Westerberg 1859f67cf491SMika Westerberg /* If they are both up we need to reset them now */ 1860f67cf491SMika Westerberg if (state0 != TB_PORT_UP || state1 != TB_PORT_UP) 1861f67cf491SMika Westerberg return 0; 1862f67cf491SMika Westerberg 1863f67cf491SMika Westerberg val0 |= PHY_PORT_CS1_LINK_DISABLE; 1864f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1865f67cf491SMika Westerberg if (ret) 1866f67cf491SMika Westerberg return ret; 1867f67cf491SMika Westerberg 1868f67cf491SMika Westerberg val1 |= PHY_PORT_CS1_LINK_DISABLE; 1869f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1870f67cf491SMika Westerberg if (ret) 1871f67cf491SMika Westerberg return ret; 1872f67cf491SMika Westerberg 1873f67cf491SMika Westerberg /* Wait a bit and then re-enable both ports */ 1874f67cf491SMika Westerberg usleep_range(10, 100); 1875f67cf491SMika Westerberg 1876f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1877f67cf491SMika Westerberg if (ret) 1878f67cf491SMika Westerberg return ret; 1879f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1880f67cf491SMika Westerberg if (ret) 1881f67cf491SMika Westerberg return ret; 1882f67cf491SMika Westerberg 1883f67cf491SMika Westerberg val0 &= ~PHY_PORT_CS1_LINK_DISABLE; 1884f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1885f67cf491SMika Westerberg if (ret) 1886f67cf491SMika Westerberg return ret; 1887f67cf491SMika Westerberg 1888f67cf491SMika Westerberg val1 &= ~PHY_PORT_CS1_LINK_DISABLE; 1889f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1890f67cf491SMika Westerberg } 1891f67cf491SMika Westerberg 1892f67cf491SMika Westerberg static int icm_firmware_init(struct tb *tb) 1893f67cf491SMika Westerberg { 1894f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1895f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1896f67cf491SMika Westerberg int ret; 1897f67cf491SMika Westerberg 1898f67cf491SMika Westerberg ret = icm_firmware_start(tb, nhi); 1899f67cf491SMika Westerberg if (ret) { 1900f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "could not start ICM firmware\n"); 1901f67cf491SMika Westerberg return ret; 1902f67cf491SMika Westerberg } 1903f67cf491SMika Westerberg 1904f67cf491SMika Westerberg if (icm->get_mode) { 1905f67cf491SMika Westerberg ret = icm->get_mode(tb); 1906f67cf491SMika Westerberg 1907f67cf491SMika Westerberg switch (ret) { 1908e6b245ccSMika Westerberg case NHI_FW_SAFE_MODE: 1909e6b245ccSMika Westerberg icm->safe_mode = true; 1910e6b245ccSMika Westerberg break; 1911e6b245ccSMika Westerberg 1912f67cf491SMika Westerberg case NHI_FW_CM_MODE: 1913f67cf491SMika Westerberg /* Ask ICM to accept all Thunderbolt devices */ 1914f67cf491SMika Westerberg nhi_mailbox_cmd(nhi, NHI_MAILBOX_ALLOW_ALL_DEVS, 0); 1915f67cf491SMika Westerberg break; 1916f67cf491SMika Westerberg 1917f67cf491SMika Westerberg default: 1918e4be8c9bSMika Westerberg if (ret < 0) 1919e4be8c9bSMika Westerberg return ret; 1920e4be8c9bSMika Westerberg 1921f67cf491SMika Westerberg tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); 1922f67cf491SMika Westerberg return -ENODEV; 1923f67cf491SMika Westerberg } 1924f67cf491SMika Westerberg } 1925f67cf491SMika Westerberg 1926f67cf491SMika Westerberg /* 1927f67cf491SMika Westerberg * Reset both physical ports if there is anything connected to 1928f67cf491SMika Westerberg * them already. 1929f67cf491SMika Westerberg */ 1930f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 0); 1931f67cf491SMika Westerberg if (ret) 1932f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port0\n"); 1933f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 1); 1934f67cf491SMika Westerberg if (ret) 1935f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port1\n"); 1936f67cf491SMika Westerberg 1937f67cf491SMika Westerberg return 0; 1938f67cf491SMika Westerberg } 1939f67cf491SMika Westerberg 1940f67cf491SMika Westerberg static int icm_driver_ready(struct tb *tb) 1941f67cf491SMika Westerberg { 1942e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1943f67cf491SMika Westerberg int ret; 1944f67cf491SMika Westerberg 1945f67cf491SMika Westerberg ret = icm_firmware_init(tb); 1946f67cf491SMika Westerberg if (ret) 1947f67cf491SMika Westerberg return ret; 1948f67cf491SMika Westerberg 1949e6b245ccSMika Westerberg if (icm->safe_mode) { 1950e6b245ccSMika Westerberg tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); 1951e6b245ccSMika Westerberg tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); 1952e6b245ccSMika Westerberg tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); 1953e6b245ccSMika Westerberg return 0; 1954e6b245ccSMika Westerberg } 1955e6b245ccSMika Westerberg 19569039387eSMika Westerberg ret = __icm_driver_ready(tb, &tb->security_level, &icm->proto_version, 19579039387eSMika Westerberg &tb->nboot_acl, &icm->rpm); 19589aaa3b8bSMika Westerberg if (ret) 19599aaa3b8bSMika Westerberg return ret; 19609aaa3b8bSMika Westerberg 19619aaa3b8bSMika Westerberg /* 19629aaa3b8bSMika Westerberg * Make sure the number of supported preboot ACL matches what we 19639aaa3b8bSMika Westerberg * expect or disable the whole feature. 19649aaa3b8bSMika Westerberg */ 19659aaa3b8bSMika Westerberg if (tb->nboot_acl > icm->max_boot_acl) 19669aaa3b8bSMika Westerberg tb->nboot_acl = 0; 19679aaa3b8bSMika Westerberg 19689039387eSMika Westerberg if (icm->proto_version >= 3) 19699039387eSMika Westerberg tb_dbg(tb, "USB4 proxy operations supported\n"); 19709039387eSMika Westerberg 19719aaa3b8bSMika Westerberg return 0; 1972f67cf491SMika Westerberg } 1973f67cf491SMika Westerberg 1974f67cf491SMika Westerberg static int icm_suspend(struct tb *tb) 1975f67cf491SMika Westerberg { 1976d04522faSMika Westerberg struct icm *icm = tb_priv(tb); 1977a684c5b1SRafael J. Wysocki 1978d04522faSMika Westerberg if (icm->save_devices) 1979d04522faSMika Westerberg icm->save_devices(tb); 1980a684c5b1SRafael J. Wysocki 1981d04522faSMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 1982a684c5b1SRafael J. Wysocki return 0; 1983f67cf491SMika Westerberg } 1984f67cf491SMika Westerberg 1985f67cf491SMika Westerberg /* 1986f67cf491SMika Westerberg * Mark all switches (except root switch) below this one unplugged. ICM 1987f67cf491SMika Westerberg * firmware will send us an updated list of switches after we have send 1988f67cf491SMika Westerberg * it driver ready command. If a switch is not in that list it will be 1989f67cf491SMika Westerberg * removed when we perform rescan. 1990f67cf491SMika Westerberg */ 1991f67cf491SMika Westerberg static void icm_unplug_children(struct tb_switch *sw) 1992f67cf491SMika Westerberg { 1993b433d010SMika Westerberg struct tb_port *port; 1994f67cf491SMika Westerberg 1995f67cf491SMika Westerberg if (tb_route(sw)) 1996f67cf491SMika Westerberg sw->is_unplugged = true; 1997f67cf491SMika Westerberg 1998b433d010SMika Westerberg tb_switch_for_each_port(sw, port) { 1999dfe40ca4SMika Westerberg if (port->xdomain) 2000d1ff7024SMika Westerberg port->xdomain->is_unplugged = true; 2001dfe40ca4SMika Westerberg else if (tb_port_has_remote(port)) 2002f67cf491SMika Westerberg icm_unplug_children(port->remote->sw); 2003f67cf491SMika Westerberg } 2004f67cf491SMika Westerberg } 2005f67cf491SMika Westerberg 20064f7c2e0dSMika Westerberg static int complete_rpm(struct device *dev, void *data) 20074f7c2e0dSMika Westerberg { 20084f7c2e0dSMika Westerberg struct tb_switch *sw = tb_to_switch(dev); 20094f7c2e0dSMika Westerberg 20104f7c2e0dSMika Westerberg if (sw) 20114f7c2e0dSMika Westerberg complete(&sw->rpm_complete); 20124f7c2e0dSMika Westerberg return 0; 20134f7c2e0dSMika Westerberg } 20144f7c2e0dSMika Westerberg 20154f7c2e0dSMika Westerberg static void remove_unplugged_switch(struct tb_switch *sw) 20164f7c2e0dSMika Westerberg { 20174f7c2e0dSMika Westerberg pm_runtime_get_sync(sw->dev.parent); 20184f7c2e0dSMika Westerberg 20194f7c2e0dSMika Westerberg /* 20204f7c2e0dSMika Westerberg * Signal this and switches below for rpm_complete because 20214f7c2e0dSMika Westerberg * tb_switch_remove() calls pm_runtime_get_sync() that then waits 20224f7c2e0dSMika Westerberg * for it. 20234f7c2e0dSMika Westerberg */ 20244f7c2e0dSMika Westerberg complete_rpm(&sw->dev, NULL); 20254f7c2e0dSMika Westerberg bus_for_each_dev(&tb_bus_type, &sw->dev, NULL, complete_rpm); 20264f7c2e0dSMika Westerberg tb_switch_remove(sw); 20274f7c2e0dSMika Westerberg 20284f7c2e0dSMika Westerberg pm_runtime_mark_last_busy(sw->dev.parent); 20294f7c2e0dSMika Westerberg pm_runtime_put_autosuspend(sw->dev.parent); 20304f7c2e0dSMika Westerberg } 20314f7c2e0dSMika Westerberg 2032f67cf491SMika Westerberg static void icm_free_unplugged_children(struct tb_switch *sw) 2033f67cf491SMika Westerberg { 2034b433d010SMika Westerberg struct tb_port *port; 2035f67cf491SMika Westerberg 2036b433d010SMika Westerberg tb_switch_for_each_port(sw, port) { 2037d1ff7024SMika Westerberg if (port->xdomain && port->xdomain->is_unplugged) { 2038d1ff7024SMika Westerberg tb_xdomain_remove(port->xdomain); 2039d1ff7024SMika Westerberg port->xdomain = NULL; 2040dfe40ca4SMika Westerberg } else if (tb_port_has_remote(port)) { 2041f67cf491SMika Westerberg if (port->remote->sw->is_unplugged) { 20424f7c2e0dSMika Westerberg remove_unplugged_switch(port->remote->sw); 2043f67cf491SMika Westerberg port->remote = NULL; 2044f67cf491SMika Westerberg } else { 2045f67cf491SMika Westerberg icm_free_unplugged_children(port->remote->sw); 2046f67cf491SMika Westerberg } 2047f67cf491SMika Westerberg } 2048f67cf491SMika Westerberg } 2049dfe40ca4SMika Westerberg } 2050f67cf491SMika Westerberg 2051f67cf491SMika Westerberg static void icm_rescan_work(struct work_struct *work) 2052f67cf491SMika Westerberg { 2053f67cf491SMika Westerberg struct icm *icm = container_of(work, struct icm, rescan_work.work); 2054f67cf491SMika Westerberg struct tb *tb = icm_to_tb(icm); 2055f67cf491SMika Westerberg 2056f67cf491SMika Westerberg mutex_lock(&tb->lock); 2057f67cf491SMika Westerberg if (tb->root_switch) 2058f67cf491SMika Westerberg icm_free_unplugged_children(tb->root_switch); 2059f67cf491SMika Westerberg mutex_unlock(&tb->lock); 2060f67cf491SMika Westerberg } 2061f67cf491SMika Westerberg 2062f67cf491SMika Westerberg static void icm_complete(struct tb *tb) 2063f67cf491SMika Westerberg { 2064f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 2065f67cf491SMika Westerberg 2066f67cf491SMika Westerberg if (tb->nhi->going_away) 2067f67cf491SMika Westerberg return; 2068f67cf491SMika Westerberg 20693cdb9446SMika Westerberg /* 20703cdb9446SMika Westerberg * If RTD3 was vetoed before we entered system suspend allow it 20713cdb9446SMika Westerberg * again now before driver ready is sent. Firmware sends a new RTD3 20723cdb9446SMika Westerberg * veto if it is still the case after we have sent it driver ready 20733cdb9446SMika Westerberg * command. 20743cdb9446SMika Westerberg */ 20753cdb9446SMika Westerberg icm_veto_end(tb); 2076f67cf491SMika Westerberg icm_unplug_children(tb->root_switch); 2077f67cf491SMika Westerberg 2078f67cf491SMika Westerberg /* 2079f67cf491SMika Westerberg * Now all existing children should be resumed, start events 2080f67cf491SMika Westerberg * from ICM to get updated status. 2081f67cf491SMika Westerberg */ 20829039387eSMika Westerberg __icm_driver_ready(tb, NULL, NULL, NULL, NULL); 2083f67cf491SMika Westerberg 2084f67cf491SMika Westerberg /* 2085f67cf491SMika Westerberg * We do not get notifications of devices that have been 2086f67cf491SMika Westerberg * unplugged during suspend so schedule rescan to clean them up 2087f67cf491SMika Westerberg * if any. 2088f67cf491SMika Westerberg */ 2089f67cf491SMika Westerberg queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500)); 2090f67cf491SMika Westerberg } 2091f67cf491SMika Westerberg 20922d8ff0b5SMika Westerberg static int icm_runtime_suspend(struct tb *tb) 20932d8ff0b5SMika Westerberg { 20942d8ff0b5SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 20952d8ff0b5SMika Westerberg return 0; 20962d8ff0b5SMika Westerberg } 20972d8ff0b5SMika Westerberg 20984f7c2e0dSMika Westerberg static int icm_runtime_suspend_switch(struct tb_switch *sw) 20994f7c2e0dSMika Westerberg { 21004f7c2e0dSMika Westerberg if (tb_route(sw)) 21014f7c2e0dSMika Westerberg reinit_completion(&sw->rpm_complete); 21024f7c2e0dSMika Westerberg return 0; 21034f7c2e0dSMika Westerberg } 21044f7c2e0dSMika Westerberg 21054f7c2e0dSMika Westerberg static int icm_runtime_resume_switch(struct tb_switch *sw) 21064f7c2e0dSMika Westerberg { 21074f7c2e0dSMika Westerberg if (tb_route(sw)) { 21084f7c2e0dSMika Westerberg if (!wait_for_completion_timeout(&sw->rpm_complete, 21094f7c2e0dSMika Westerberg msecs_to_jiffies(500))) { 21104f7c2e0dSMika Westerberg dev_dbg(&sw->dev, "runtime resuming timed out\n"); 21114f7c2e0dSMika Westerberg } 21124f7c2e0dSMika Westerberg } 21134f7c2e0dSMika Westerberg return 0; 21144f7c2e0dSMika Westerberg } 21154f7c2e0dSMika Westerberg 21162d8ff0b5SMika Westerberg static int icm_runtime_resume(struct tb *tb) 21172d8ff0b5SMika Westerberg { 21182d8ff0b5SMika Westerberg /* 21192d8ff0b5SMika Westerberg * We can reuse the same resume functionality than with system 21202d8ff0b5SMika Westerberg * suspend. 21212d8ff0b5SMika Westerberg */ 21222d8ff0b5SMika Westerberg icm_complete(tb); 21232d8ff0b5SMika Westerberg return 0; 21242d8ff0b5SMika Westerberg } 21252d8ff0b5SMika Westerberg 2126f67cf491SMika Westerberg static int icm_start(struct tb *tb) 2127f67cf491SMika Westerberg { 2128e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 2129f67cf491SMika Westerberg int ret; 2130f67cf491SMika Westerberg 2131e6b245ccSMika Westerberg if (icm->safe_mode) 2132e6b245ccSMika Westerberg tb->root_switch = tb_switch_alloc_safe_mode(tb, &tb->dev, 0); 2133e6b245ccSMika Westerberg else 2134f67cf491SMika Westerberg tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); 2135444ac384SMika Westerberg if (IS_ERR(tb->root_switch)) 2136444ac384SMika Westerberg return PTR_ERR(tb->root_switch); 2137f67cf491SMika Westerberg 2138f437c24bSMika Westerberg tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm; 21392d8ff0b5SMika Westerberg tb->root_switch->rpm = icm->rpm; 2140e6b245ccSMika Westerberg 21413cdb9446SMika Westerberg if (icm->set_uuid) 21423cdb9446SMika Westerberg icm->set_uuid(tb); 21433cdb9446SMika Westerberg 2144f67cf491SMika Westerberg ret = tb_switch_add(tb->root_switch); 2145d1ff7024SMika Westerberg if (ret) { 2146f67cf491SMika Westerberg tb_switch_put(tb->root_switch); 2147d1ff7024SMika Westerberg tb->root_switch = NULL; 2148d1ff7024SMika Westerberg } 2149f67cf491SMika Westerberg 2150f67cf491SMika Westerberg return ret; 2151f67cf491SMika Westerberg } 2152f67cf491SMika Westerberg 2153f67cf491SMika Westerberg static void icm_stop(struct tb *tb) 2154f67cf491SMika Westerberg { 2155f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 2156f67cf491SMika Westerberg 2157f67cf491SMika Westerberg cancel_delayed_work(&icm->rescan_work); 2158f67cf491SMika Westerberg tb_switch_remove(tb->root_switch); 2159f67cf491SMika Westerberg tb->root_switch = NULL; 2160f67cf491SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 21619039387eSMika Westerberg kfree(icm->last_nvm_auth); 21629039387eSMika Westerberg icm->last_nvm_auth = NULL; 2163f67cf491SMika Westerberg } 2164f67cf491SMika Westerberg 2165e6b245ccSMika Westerberg static int icm_disconnect_pcie_paths(struct tb *tb) 2166e6b245ccSMika Westerberg { 2167e6b245ccSMika Westerberg return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); 2168e6b245ccSMika Westerberg } 2169e6b245ccSMika Westerberg 21709039387eSMika Westerberg static void icm_usb4_switch_nvm_auth_complete(void *data) 21719039387eSMika Westerberg { 21729039387eSMika Westerberg struct usb4_switch_nvm_auth *auth = data; 21739039387eSMika Westerberg struct icm *icm = auth->icm; 21749039387eSMika Westerberg struct tb *tb = icm_to_tb(icm); 21759039387eSMika Westerberg 21769039387eSMika Westerberg tb_dbg(tb, "NVM_AUTH response for %llx flags %#x status %#x\n", 21779039387eSMika Westerberg get_route(auth->reply.route_hi, auth->reply.route_lo), 21789039387eSMika Westerberg auth->reply.hdr.flags, auth->reply.status); 21799039387eSMika Westerberg 21809039387eSMika Westerberg mutex_lock(&tb->lock); 21819039387eSMika Westerberg if (WARN_ON(icm->last_nvm_auth)) 21829039387eSMika Westerberg kfree(icm->last_nvm_auth); 21839039387eSMika Westerberg icm->last_nvm_auth = auth; 21849039387eSMika Westerberg mutex_unlock(&tb->lock); 21859039387eSMika Westerberg } 21869039387eSMika Westerberg 21879039387eSMika Westerberg static int icm_usb4_switch_nvm_authenticate(struct tb *tb, u64 route) 21889039387eSMika Westerberg { 21899039387eSMika Westerberg struct usb4_switch_nvm_auth *auth; 21909039387eSMika Westerberg struct icm *icm = tb_priv(tb); 21919039387eSMika Westerberg struct tb_cfg_request *req; 21929039387eSMika Westerberg int ret; 21939039387eSMika Westerberg 21949039387eSMika Westerberg auth = kzalloc(sizeof(*auth), GFP_KERNEL); 21959039387eSMika Westerberg if (!auth) 21969039387eSMika Westerberg return -ENOMEM; 21979039387eSMika Westerberg 21989039387eSMika Westerberg auth->icm = icm; 21999039387eSMika Westerberg auth->request.hdr.code = ICM_USB4_SWITCH_OP; 22009039387eSMika Westerberg auth->request.route_hi = upper_32_bits(route); 22019039387eSMika Westerberg auth->request.route_lo = lower_32_bits(route); 22029039387eSMika Westerberg auth->request.opcode = USB4_SWITCH_OP_NVM_AUTH; 22039039387eSMika Westerberg 22049039387eSMika Westerberg req = tb_cfg_request_alloc(); 22059039387eSMika Westerberg if (!req) { 22069039387eSMika Westerberg ret = -ENOMEM; 22079039387eSMika Westerberg goto err_free_auth; 22089039387eSMika Westerberg } 22099039387eSMika Westerberg 22109039387eSMika Westerberg req->match = icm_match; 22119039387eSMika Westerberg req->copy = icm_copy; 22129039387eSMika Westerberg req->request = &auth->request; 22139039387eSMika Westerberg req->request_size = sizeof(auth->request); 22149039387eSMika Westerberg req->request_type = TB_CFG_PKG_ICM_CMD; 22159039387eSMika Westerberg req->response = &auth->reply; 22169039387eSMika Westerberg req->npackets = 1; 22179039387eSMika Westerberg req->response_size = sizeof(auth->reply); 22189039387eSMika Westerberg req->response_type = TB_CFG_PKG_ICM_RESP; 22199039387eSMika Westerberg 22209039387eSMika Westerberg tb_dbg(tb, "NVM_AUTH request for %llx\n", route); 22219039387eSMika Westerberg 22229039387eSMika Westerberg mutex_lock(&icm->request_lock); 22239039387eSMika Westerberg ret = tb_cfg_request(tb->ctl, req, icm_usb4_switch_nvm_auth_complete, 22249039387eSMika Westerberg auth); 22259039387eSMika Westerberg mutex_unlock(&icm->request_lock); 22269039387eSMika Westerberg 22279039387eSMika Westerberg tb_cfg_request_put(req); 22289039387eSMika Westerberg if (ret) 22299039387eSMika Westerberg goto err_free_auth; 22309039387eSMika Westerberg return 0; 22319039387eSMika Westerberg 22329039387eSMika Westerberg err_free_auth: 22339039387eSMika Westerberg kfree(auth); 22349039387eSMika Westerberg return ret; 22359039387eSMika Westerberg } 22369039387eSMika Westerberg 22379039387eSMika Westerberg static int icm_usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, 22389039387eSMika Westerberg u8 *status, const void *tx_data, size_t tx_data_len, 22399039387eSMika Westerberg void *rx_data, size_t rx_data_len) 22409039387eSMika Westerberg { 22419039387eSMika Westerberg struct icm_usb4_switch_op_response reply; 22429039387eSMika Westerberg struct icm_usb4_switch_op request; 22439039387eSMika Westerberg struct tb *tb = sw->tb; 22449039387eSMika Westerberg struct icm *icm = tb_priv(tb); 22459039387eSMika Westerberg u64 route = tb_route(sw); 22469039387eSMika Westerberg int ret; 22479039387eSMika Westerberg 22489039387eSMika Westerberg /* 22499039387eSMika Westerberg * USB4 router operation proxy is supported in firmware if the 22509039387eSMika Westerberg * protocol version is 3 or higher. 22519039387eSMika Westerberg */ 22529039387eSMika Westerberg if (icm->proto_version < 3) 22539039387eSMika Westerberg return -EOPNOTSUPP; 22549039387eSMika Westerberg 22559039387eSMika Westerberg /* 22569039387eSMika Westerberg * NVM_AUTH is a special USB4 proxy operation that does not 22579039387eSMika Westerberg * return immediately so handle it separately. 22589039387eSMika Westerberg */ 22599039387eSMika Westerberg if (opcode == USB4_SWITCH_OP_NVM_AUTH) 22609039387eSMika Westerberg return icm_usb4_switch_nvm_authenticate(tb, route); 22619039387eSMika Westerberg 22629039387eSMika Westerberg memset(&request, 0, sizeof(request)); 22639039387eSMika Westerberg request.hdr.code = ICM_USB4_SWITCH_OP; 22649039387eSMika Westerberg request.route_hi = upper_32_bits(route); 22659039387eSMika Westerberg request.route_lo = lower_32_bits(route); 22669039387eSMika Westerberg request.opcode = opcode; 22679039387eSMika Westerberg if (metadata) 22689039387eSMika Westerberg request.metadata = *metadata; 22699039387eSMika Westerberg 22709039387eSMika Westerberg if (tx_data_len) { 22719039387eSMika Westerberg request.data_len_valid |= ICM_USB4_SWITCH_DATA_VALID; 22729039387eSMika Westerberg if (tx_data_len < ARRAY_SIZE(request.data)) 22739039387eSMika Westerberg request.data_len_valid = 22749039387eSMika Westerberg tx_data_len & ICM_USB4_SWITCH_DATA_LEN_MASK; 22759039387eSMika Westerberg memcpy(request.data, tx_data, tx_data_len * sizeof(u32)); 22769039387eSMika Westerberg } 22779039387eSMika Westerberg 22789039387eSMika Westerberg memset(&reply, 0, sizeof(reply)); 22799039387eSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 22809039387eSMika Westerberg 1, ICM_TIMEOUT); 22819039387eSMika Westerberg if (ret) 22829039387eSMika Westerberg return ret; 22839039387eSMika Westerberg 22849039387eSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 22859039387eSMika Westerberg return -EIO; 22869039387eSMika Westerberg 22879039387eSMika Westerberg if (status) 22889039387eSMika Westerberg *status = reply.status; 22899039387eSMika Westerberg 22909039387eSMika Westerberg if (metadata) 22919039387eSMika Westerberg *metadata = reply.metadata; 22929039387eSMika Westerberg 22939039387eSMika Westerberg if (rx_data_len) 22949039387eSMika Westerberg memcpy(rx_data, reply.data, rx_data_len * sizeof(u32)); 22959039387eSMika Westerberg 22969039387eSMika Westerberg return 0; 22979039387eSMika Westerberg } 22989039387eSMika Westerberg 22999039387eSMika Westerberg static int icm_usb4_switch_nvm_authenticate_status(struct tb_switch *sw, 23009039387eSMika Westerberg u32 *status) 23019039387eSMika Westerberg { 23029039387eSMika Westerberg struct usb4_switch_nvm_auth *auth; 23039039387eSMika Westerberg struct tb *tb = sw->tb; 23049039387eSMika Westerberg struct icm *icm = tb_priv(tb); 23059039387eSMika Westerberg int ret = 0; 23069039387eSMika Westerberg 23079039387eSMika Westerberg if (icm->proto_version < 3) 23089039387eSMika Westerberg return -EOPNOTSUPP; 23099039387eSMika Westerberg 23109039387eSMika Westerberg auth = icm->last_nvm_auth; 23119039387eSMika Westerberg icm->last_nvm_auth = NULL; 23129039387eSMika Westerberg 23139039387eSMika Westerberg if (auth && auth->reply.route_hi == sw->config.route_hi && 23149039387eSMika Westerberg auth->reply.route_lo == sw->config.route_lo) { 23159039387eSMika Westerberg tb_dbg(tb, "NVM_AUTH found for %llx flags 0x%#x status %#x\n", 23169039387eSMika Westerberg tb_route(sw), auth->reply.hdr.flags, auth->reply.status); 23179039387eSMika Westerberg if (auth->reply.hdr.flags & ICM_FLAGS_ERROR) 23189039387eSMika Westerberg ret = -EIO; 23199039387eSMika Westerberg else 23209039387eSMika Westerberg *status = auth->reply.status; 23219039387eSMika Westerberg } else { 23229039387eSMika Westerberg *status = 0; 23239039387eSMika Westerberg } 23249039387eSMika Westerberg 23259039387eSMika Westerberg kfree(auth); 23269039387eSMika Westerberg return ret; 23279039387eSMika Westerberg } 23289039387eSMika Westerberg 23299aaa3b8bSMika Westerberg /* Falcon Ridge */ 2330f67cf491SMika Westerberg static const struct tb_cm_ops icm_fr_ops = { 2331f67cf491SMika Westerberg .driver_ready = icm_driver_ready, 2332f67cf491SMika Westerberg .start = icm_start, 2333f67cf491SMika Westerberg .stop = icm_stop, 2334f67cf491SMika Westerberg .suspend = icm_suspend, 2335f67cf491SMika Westerberg .complete = icm_complete, 2336f67cf491SMika Westerberg .handle_event = icm_handle_event, 2337f67cf491SMika Westerberg .approve_switch = icm_fr_approve_switch, 2338f67cf491SMika Westerberg .add_switch_key = icm_fr_add_switch_key, 2339f67cf491SMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 2340e6b245ccSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 2341d1ff7024SMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 2342d1ff7024SMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 2343f67cf491SMika Westerberg }; 2344f67cf491SMika Westerberg 23459aaa3b8bSMika Westerberg /* Alpine Ridge */ 23469aaa3b8bSMika Westerberg static const struct tb_cm_ops icm_ar_ops = { 23479aaa3b8bSMika Westerberg .driver_ready = icm_driver_ready, 23489aaa3b8bSMika Westerberg .start = icm_start, 23499aaa3b8bSMika Westerberg .stop = icm_stop, 23509aaa3b8bSMika Westerberg .suspend = icm_suspend, 23519aaa3b8bSMika Westerberg .complete = icm_complete, 23522d8ff0b5SMika Westerberg .runtime_suspend = icm_runtime_suspend, 23532d8ff0b5SMika Westerberg .runtime_resume = icm_runtime_resume, 23544f7c2e0dSMika Westerberg .runtime_suspend_switch = icm_runtime_suspend_switch, 23554f7c2e0dSMika Westerberg .runtime_resume_switch = icm_runtime_resume_switch, 23569aaa3b8bSMika Westerberg .handle_event = icm_handle_event, 23579aaa3b8bSMika Westerberg .get_boot_acl = icm_ar_get_boot_acl, 23589aaa3b8bSMika Westerberg .set_boot_acl = icm_ar_set_boot_acl, 23599aaa3b8bSMika Westerberg .approve_switch = icm_fr_approve_switch, 23609aaa3b8bSMika Westerberg .add_switch_key = icm_fr_add_switch_key, 23619aaa3b8bSMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 23629aaa3b8bSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 23639aaa3b8bSMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 23649aaa3b8bSMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 23659aaa3b8bSMika Westerberg }; 23669aaa3b8bSMika Westerberg 23674bac471dSRadion Mirchevsky /* Titan Ridge */ 23684bac471dSRadion Mirchevsky static const struct tb_cm_ops icm_tr_ops = { 23694bac471dSRadion Mirchevsky .driver_ready = icm_driver_ready, 23704bac471dSRadion Mirchevsky .start = icm_start, 23714bac471dSRadion Mirchevsky .stop = icm_stop, 23724bac471dSRadion Mirchevsky .suspend = icm_suspend, 23734bac471dSRadion Mirchevsky .complete = icm_complete, 23742d8ff0b5SMika Westerberg .runtime_suspend = icm_runtime_suspend, 23752d8ff0b5SMika Westerberg .runtime_resume = icm_runtime_resume, 23764f7c2e0dSMika Westerberg .runtime_suspend_switch = icm_runtime_suspend_switch, 23774f7c2e0dSMika Westerberg .runtime_resume_switch = icm_runtime_resume_switch, 23784bac471dSRadion Mirchevsky .handle_event = icm_handle_event, 23794bac471dSRadion Mirchevsky .get_boot_acl = icm_ar_get_boot_acl, 23804bac471dSRadion Mirchevsky .set_boot_acl = icm_ar_set_boot_acl, 23814bac471dSRadion Mirchevsky .approve_switch = icm_tr_approve_switch, 23824bac471dSRadion Mirchevsky .add_switch_key = icm_tr_add_switch_key, 23834bac471dSRadion Mirchevsky .challenge_switch_key = icm_tr_challenge_switch_key, 23844bac471dSRadion Mirchevsky .disconnect_pcie_paths = icm_disconnect_pcie_paths, 23854bac471dSRadion Mirchevsky .approve_xdomain_paths = icm_tr_approve_xdomain_paths, 23864bac471dSRadion Mirchevsky .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, 23879039387eSMika Westerberg .usb4_switch_op = icm_usb4_switch_op, 23889039387eSMika Westerberg .usb4_switch_nvm_authenticate_status = 23899039387eSMika Westerberg icm_usb4_switch_nvm_authenticate_status, 23904bac471dSRadion Mirchevsky }; 23914bac471dSRadion Mirchevsky 23923cdb9446SMika Westerberg /* Ice Lake */ 23933cdb9446SMika Westerberg static const struct tb_cm_ops icm_icl_ops = { 23943cdb9446SMika Westerberg .driver_ready = icm_driver_ready, 23953cdb9446SMika Westerberg .start = icm_start, 23963cdb9446SMika Westerberg .stop = icm_stop, 23973cdb9446SMika Westerberg .complete = icm_complete, 23983cdb9446SMika Westerberg .runtime_suspend = icm_runtime_suspend, 23993cdb9446SMika Westerberg .runtime_resume = icm_runtime_resume, 24003cdb9446SMika Westerberg .handle_event = icm_handle_event, 24013cdb9446SMika Westerberg .approve_xdomain_paths = icm_tr_approve_xdomain_paths, 24023cdb9446SMika Westerberg .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, 24039039387eSMika Westerberg .usb4_switch_op = icm_usb4_switch_op, 24049039387eSMika Westerberg .usb4_switch_nvm_authenticate_status = 24059039387eSMika Westerberg icm_usb4_switch_nvm_authenticate_status, 24063cdb9446SMika Westerberg }; 24073cdb9446SMika Westerberg 2408f67cf491SMika Westerberg struct tb *icm_probe(struct tb_nhi *nhi) 2409f67cf491SMika Westerberg { 2410f67cf491SMika Westerberg struct icm *icm; 2411f67cf491SMika Westerberg struct tb *tb; 2412f67cf491SMika Westerberg 2413f67cf491SMika Westerberg tb = tb_domain_alloc(nhi, sizeof(struct icm)); 2414f67cf491SMika Westerberg if (!tb) 2415f67cf491SMika Westerberg return NULL; 2416f67cf491SMika Westerberg 2417f67cf491SMika Westerberg icm = tb_priv(tb); 2418f67cf491SMika Westerberg INIT_DELAYED_WORK(&icm->rescan_work, icm_rescan_work); 2419f67cf491SMika Westerberg mutex_init(&icm->request_lock); 2420f67cf491SMika Westerberg 2421f67cf491SMika Westerberg switch (nhi->pdev->device) { 2422f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: 2423f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: 2424f437c24bSMika Westerberg icm->can_upgrade_nvm = true; 2425f67cf491SMika Westerberg icm->is_supported = icm_fr_is_supported; 2426f67cf491SMika Westerberg icm->get_route = icm_fr_get_route; 2427d04522faSMika Westerberg icm->save_devices = icm_fr_save_devices; 24283080e197SMika Westerberg icm->driver_ready = icm_fr_driver_ready; 2429f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 2430f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 2431d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 2432d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 2433f67cf491SMika Westerberg tb->cm_ops = &icm_fr_ops; 2434f67cf491SMika Westerberg break; 2435f67cf491SMika Westerberg 2436f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI: 2437f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI: 2438f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: 2439f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: 2440f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: 24419aaa3b8bSMika Westerberg icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 2442f437c24bSMika Westerberg /* 2443f437c24bSMika Westerberg * NVM upgrade has not been tested on Apple systems and 2444f437c24bSMika Westerberg * they don't provide images publicly either. To be on 2445f437c24bSMika Westerberg * the safe side prevent root switch NVM upgrade on Macs 2446f437c24bSMika Westerberg * for now. 2447f437c24bSMika Westerberg */ 2448f437c24bSMika Westerberg icm->can_upgrade_nvm = !x86_apple_machine; 2449f67cf491SMika Westerberg icm->is_supported = icm_ar_is_supported; 24500d53827dSMika Westerberg icm->cio_reset = icm_ar_cio_reset; 2451f67cf491SMika Westerberg icm->get_mode = icm_ar_get_mode; 2452f67cf491SMika Westerberg icm->get_route = icm_ar_get_route; 2453d04522faSMika Westerberg icm->save_devices = icm_fr_save_devices; 24549aaa3b8bSMika Westerberg icm->driver_ready = icm_ar_driver_ready; 2455f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 2456f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 2457d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 2458d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 24599aaa3b8bSMika Westerberg tb->cm_ops = &icm_ar_ops; 2460f67cf491SMika Westerberg break; 24614bac471dSRadion Mirchevsky 24624bac471dSRadion Mirchevsky case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI: 24634bac471dSRadion Mirchevsky case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI: 24644bac471dSRadion Mirchevsky icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 2465f437c24bSMika Westerberg icm->can_upgrade_nvm = !x86_apple_machine; 24664bac471dSRadion Mirchevsky icm->is_supported = icm_ar_is_supported; 24670d53827dSMika Westerberg icm->cio_reset = icm_tr_cio_reset; 24684bac471dSRadion Mirchevsky icm->get_mode = icm_ar_get_mode; 24694bac471dSRadion Mirchevsky icm->driver_ready = icm_tr_driver_ready; 24704bac471dSRadion Mirchevsky icm->device_connected = icm_tr_device_connected; 24714bac471dSRadion Mirchevsky icm->device_disconnected = icm_tr_device_disconnected; 24724bac471dSRadion Mirchevsky icm->xdomain_connected = icm_tr_xdomain_connected; 24734bac471dSRadion Mirchevsky icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 24744bac471dSRadion Mirchevsky tb->cm_ops = &icm_tr_ops; 24754bac471dSRadion Mirchevsky break; 24763cdb9446SMika Westerberg 24773cdb9446SMika Westerberg case PCI_DEVICE_ID_INTEL_ICL_NHI0: 24783cdb9446SMika Westerberg case PCI_DEVICE_ID_INTEL_ICL_NHI1: 2479354a7a77SMika Westerberg icm->is_supported = icm_fr_is_supported; 24803cdb9446SMika Westerberg icm->driver_ready = icm_icl_driver_ready; 24813cdb9446SMika Westerberg icm->set_uuid = icm_icl_set_uuid; 24823cdb9446SMika Westerberg icm->device_connected = icm_icl_device_connected; 24833cdb9446SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 24843cdb9446SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 24853cdb9446SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 24863cdb9446SMika Westerberg icm->rtd3_veto = icm_icl_rtd3_veto; 24873cdb9446SMika Westerberg tb->cm_ops = &icm_icl_ops; 24883cdb9446SMika Westerberg break; 248957d8df68SMika Westerberg 249057d8df68SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_NHI0: 249157d8df68SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_NHI1: 249257d8df68SMika Westerberg icm->is_supported = icm_tgl_is_supported; 249357d8df68SMika Westerberg icm->driver_ready = icm_icl_driver_ready; 249457d8df68SMika Westerberg icm->set_uuid = icm_icl_set_uuid; 249557d8df68SMika Westerberg icm->device_connected = icm_icl_device_connected; 249657d8df68SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 249757d8df68SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 249857d8df68SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 249957d8df68SMika Westerberg icm->rtd3_veto = icm_icl_rtd3_veto; 250057d8df68SMika Westerberg tb->cm_ops = &icm_icl_ops; 250157d8df68SMika Westerberg break; 2502*db0746e3SMika Westerberg 2503*db0746e3SMika Westerberg case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI: 2504*db0746e3SMika Westerberg icm->is_supported = icm_tgl_is_supported; 2505*db0746e3SMika Westerberg icm->get_mode = icm_ar_get_mode; 2506*db0746e3SMika Westerberg icm->driver_ready = icm_tr_driver_ready; 2507*db0746e3SMika Westerberg icm->device_connected = icm_tr_device_connected; 2508*db0746e3SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 2509*db0746e3SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 2510*db0746e3SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 2511*db0746e3SMika Westerberg tb->cm_ops = &icm_tr_ops; 2512*db0746e3SMika Westerberg break; 2513f67cf491SMika Westerberg } 2514f67cf491SMika Westerberg 2515f67cf491SMika Westerberg if (!icm->is_supported || !icm->is_supported(tb)) { 2516f67cf491SMika Westerberg dev_dbg(&nhi->pdev->dev, "ICM not supported on this controller\n"); 2517f67cf491SMika Westerberg tb_domain_put(tb); 2518f67cf491SMika Westerberg return NULL; 2519f67cf491SMika Westerberg } 2520f67cf491SMika Westerberg 2521e0258805SMika Westerberg tb_dbg(tb, "using firmware connection manager\n"); 2522e0258805SMika Westerberg 2523f67cf491SMika Westerberg return tb; 2524f67cf491SMika Westerberg } 2525