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 /** 52f67cf491SMika Westerberg * struct icm - Internal connection manager private data 53f67cf491SMika Westerberg * @request_lock: Makes sure only one message is send to ICM at time 54f67cf491SMika Westerberg * @rescan_work: Work used to rescan the surviving switches after resume 55f67cf491SMika Westerberg * @upstream_port: Pointer to the PCIe upstream port this host 56f67cf491SMika Westerberg * controller is connected. This is only set for systems 57f67cf491SMika Westerberg * where ICM needs to be started manually 58f67cf491SMika Westerberg * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides 59f67cf491SMika Westerberg * (only set when @upstream_port is not %NULL) 60e6b245ccSMika Westerberg * @safe_mode: ICM is in safe mode 619aaa3b8bSMika Westerberg * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) 622d8ff0b5SMika Westerberg * @rpm: Does the controller support runtime PM (RTD3) 63f437c24bSMika Westerberg * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller 643cdb9446SMika Westerberg * @veto: Is RTD3 veto in effect 65f67cf491SMika Westerberg * @is_supported: Checks if we can support ICM on this controller 660d53827dSMika Westerberg * @cio_reset: Trigger CIO reset 67f67cf491SMika Westerberg * @get_mode: Read and return the ICM firmware mode (optional) 68f67cf491SMika Westerberg * @get_route: Find a route string for given switch 69d04522faSMika Westerberg * @save_devices: Ask ICM to save devices to ACL when suspending (optional) 703080e197SMika Westerberg * @driver_ready: Send driver ready message to ICM 713cdb9446SMika Westerberg * @set_uuid: Set UUID for the root switch (optional) 72f67cf491SMika Westerberg * @device_connected: Handle device connected ICM message 73f67cf491SMika Westerberg * @device_disconnected: Handle device disconnected ICM message 74d1ff7024SMika Westerberg * @xdomain_connected - Handle XDomain connected ICM message 75d1ff7024SMika Westerberg * @xdomain_disconnected - Handle XDomain disconnected ICM message 763cdb9446SMika Westerberg * @rtd3_veto: Handle RTD3 veto notification ICM message 77f67cf491SMika Westerberg */ 78f67cf491SMika Westerberg struct icm { 79f67cf491SMika Westerberg struct mutex request_lock; 80f67cf491SMika Westerberg struct delayed_work rescan_work; 81f67cf491SMika Westerberg struct pci_dev *upstream_port; 829aaa3b8bSMika Westerberg size_t max_boot_acl; 83f67cf491SMika Westerberg int vnd_cap; 84e6b245ccSMika Westerberg bool safe_mode; 852d8ff0b5SMika Westerberg bool rpm; 86f437c24bSMika Westerberg bool can_upgrade_nvm; 873cdb9446SMika Westerberg bool veto; 88f67cf491SMika Westerberg bool (*is_supported)(struct tb *tb); 890d53827dSMika Westerberg int (*cio_reset)(struct tb *tb); 90f67cf491SMika Westerberg int (*get_mode)(struct tb *tb); 91f67cf491SMika Westerberg int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); 92d04522faSMika Westerberg void (*save_devices)(struct tb *tb); 933080e197SMika Westerberg int (*driver_ready)(struct tb *tb, 949aaa3b8bSMika Westerberg enum tb_security_level *security_level, 952d8ff0b5SMika Westerberg size_t *nboot_acl, bool *rpm); 963cdb9446SMika Westerberg void (*set_uuid)(struct tb *tb); 97f67cf491SMika Westerberg void (*device_connected)(struct tb *tb, 98f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 99f67cf491SMika Westerberg void (*device_disconnected)(struct tb *tb, 100f67cf491SMika Westerberg const struct icm_pkg_header *hdr); 101d1ff7024SMika Westerberg void (*xdomain_connected)(struct tb *tb, 102d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 103d1ff7024SMika Westerberg void (*xdomain_disconnected)(struct tb *tb, 104d1ff7024SMika Westerberg const struct icm_pkg_header *hdr); 1053cdb9446SMika Westerberg void (*rtd3_veto)(struct tb *tb, const struct icm_pkg_header *hdr); 106f67cf491SMika Westerberg }; 107f67cf491SMika Westerberg 108f67cf491SMika Westerberg struct icm_notification { 109f67cf491SMika Westerberg struct work_struct work; 110f67cf491SMika Westerberg struct icm_pkg_header *pkg; 111f67cf491SMika Westerberg struct tb *tb; 112f67cf491SMika Westerberg }; 113f67cf491SMika Westerberg 1142d8ff0b5SMika Westerberg struct ep_name_entry { 1152d8ff0b5SMika Westerberg u8 len; 1162d8ff0b5SMika Westerberg u8 type; 1173084ea9eSGustavo A. R. Silva u8 data[]; 1182d8ff0b5SMika Westerberg }; 1192d8ff0b5SMika Westerberg 1202d8ff0b5SMika Westerberg #define EP_NAME_INTEL_VSS 0x10 1212d8ff0b5SMika Westerberg 1222d8ff0b5SMika Westerberg /* Intel Vendor specific structure */ 1232d8ff0b5SMika Westerberg struct intel_vss { 1242d8ff0b5SMika Westerberg u16 vendor; 1252d8ff0b5SMika Westerberg u16 model; 1262d8ff0b5SMika Westerberg u8 mc; 1272d8ff0b5SMika Westerberg u8 flags; 1282d8ff0b5SMika Westerberg u16 pci_devid; 1292d8ff0b5SMika Westerberg u32 nvm_version; 1302d8ff0b5SMika Westerberg }; 1312d8ff0b5SMika Westerberg 1322d8ff0b5SMika Westerberg #define INTEL_VSS_FLAGS_RTD3 BIT(0) 1332d8ff0b5SMika Westerberg 1342d8ff0b5SMika Westerberg static const struct intel_vss *parse_intel_vss(const void *ep_name, size_t size) 1352d8ff0b5SMika Westerberg { 1362d8ff0b5SMika Westerberg const void *end = ep_name + size; 1372d8ff0b5SMika Westerberg 1382d8ff0b5SMika Westerberg while (ep_name < end) { 1392d8ff0b5SMika Westerberg const struct ep_name_entry *ep = ep_name; 1402d8ff0b5SMika Westerberg 1412d8ff0b5SMika Westerberg if (!ep->len) 1422d8ff0b5SMika Westerberg break; 1432d8ff0b5SMika Westerberg if (ep_name + ep->len > end) 1442d8ff0b5SMika Westerberg break; 1452d8ff0b5SMika Westerberg 1462d8ff0b5SMika Westerberg if (ep->type == EP_NAME_INTEL_VSS) 1472d8ff0b5SMika Westerberg return (const struct intel_vss *)ep->data; 1482d8ff0b5SMika Westerberg 1492d8ff0b5SMika Westerberg ep_name += ep->len; 1502d8ff0b5SMika Westerberg } 1512d8ff0b5SMika Westerberg 1522d8ff0b5SMika Westerberg return NULL; 1532d8ff0b5SMika Westerberg } 1542d8ff0b5SMika Westerberg 155b5db76dbSMika Westerberg static bool intel_vss_is_rtd3(const void *ep_name, size_t size) 156b5db76dbSMika Westerberg { 157b5db76dbSMika Westerberg const struct intel_vss *vss; 158b5db76dbSMika Westerberg 159b5db76dbSMika Westerberg vss = parse_intel_vss(ep_name, size); 160b5db76dbSMika Westerberg if (vss) 161b5db76dbSMika Westerberg return !!(vss->flags & INTEL_VSS_FLAGS_RTD3); 162b5db76dbSMika Westerberg 163b5db76dbSMika Westerberg return false; 164b5db76dbSMika Westerberg } 165b5db76dbSMika Westerberg 166f67cf491SMika Westerberg static inline struct tb *icm_to_tb(struct icm *icm) 167f67cf491SMika Westerberg { 168f67cf491SMika Westerberg return ((void *)icm - sizeof(struct tb)); 169f67cf491SMika Westerberg } 170f67cf491SMika Westerberg 171f67cf491SMika Westerberg static inline u8 phy_port_from_route(u64 route, u8 depth) 172f67cf491SMika Westerberg { 173d1ff7024SMika Westerberg u8 link; 174d1ff7024SMika Westerberg 175d1ff7024SMika Westerberg link = depth ? route >> ((depth - 1) * 8) : route; 176d1ff7024SMika Westerberg return tb_phy_port_from_link(link); 177f67cf491SMika Westerberg } 178f67cf491SMika Westerberg 179f67cf491SMika Westerberg static inline u8 dual_link_from_link(u8 link) 180f67cf491SMika Westerberg { 181f67cf491SMika Westerberg return link ? ((link - 1) ^ 0x01) + 1 : 0; 182f67cf491SMika Westerberg } 183f67cf491SMika Westerberg 184f67cf491SMika Westerberg static inline u64 get_route(u32 route_hi, u32 route_lo) 185f67cf491SMika Westerberg { 186f67cf491SMika Westerberg return (u64)route_hi << 32 | route_lo; 187f67cf491SMika Westerberg } 188f67cf491SMika Westerberg 1894bac471dSRadion Mirchevsky static inline u64 get_parent_route(u64 route) 1904bac471dSRadion Mirchevsky { 1914bac471dSRadion Mirchevsky int depth = tb_route_length(route); 1924bac471dSRadion Mirchevsky return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0; 1934bac471dSRadion Mirchevsky } 1944bac471dSRadion Mirchevsky 1950d53827dSMika Westerberg static int pci2cio_wait_completion(struct icm *icm, unsigned long timeout_msec) 1960d53827dSMika Westerberg { 1970d53827dSMika Westerberg unsigned long end = jiffies + msecs_to_jiffies(timeout_msec); 1980d53827dSMika Westerberg u32 cmd; 1990d53827dSMika Westerberg 2000d53827dSMika Westerberg do { 2010d53827dSMika Westerberg pci_read_config_dword(icm->upstream_port, 2020d53827dSMika Westerberg icm->vnd_cap + PCIE2CIO_CMD, &cmd); 2030d53827dSMika Westerberg if (!(cmd & PCIE2CIO_CMD_START)) { 2040d53827dSMika Westerberg if (cmd & PCIE2CIO_CMD_TIMEOUT) 2050d53827dSMika Westerberg break; 2060d53827dSMika Westerberg return 0; 2070d53827dSMika Westerberg } 2080d53827dSMika Westerberg 2090d53827dSMika Westerberg msleep(50); 2100d53827dSMika Westerberg } while (time_before(jiffies, end)); 2110d53827dSMika Westerberg 2120d53827dSMika Westerberg return -ETIMEDOUT; 2130d53827dSMika Westerberg } 2140d53827dSMika Westerberg 2150d53827dSMika Westerberg static int pcie2cio_read(struct icm *icm, enum tb_cfg_space cs, 2160d53827dSMika Westerberg unsigned int port, unsigned int index, u32 *data) 2170d53827dSMika Westerberg { 2180d53827dSMika Westerberg struct pci_dev *pdev = icm->upstream_port; 2190d53827dSMika Westerberg int ret, vnd_cap = icm->vnd_cap; 2200d53827dSMika Westerberg u32 cmd; 2210d53827dSMika Westerberg 2220d53827dSMika Westerberg cmd = index; 2230d53827dSMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 2240d53827dSMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 2250d53827dSMika Westerberg cmd |= PCIE2CIO_CMD_START; 2260d53827dSMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 2270d53827dSMika Westerberg 2280d53827dSMika Westerberg ret = pci2cio_wait_completion(icm, 5000); 2290d53827dSMika Westerberg if (ret) 2300d53827dSMika Westerberg return ret; 2310d53827dSMika Westerberg 2320d53827dSMika Westerberg pci_read_config_dword(pdev, vnd_cap + PCIE2CIO_RDDATA, data); 2330d53827dSMika Westerberg return 0; 2340d53827dSMika Westerberg } 2350d53827dSMika Westerberg 2360d53827dSMika Westerberg static int pcie2cio_write(struct icm *icm, enum tb_cfg_space cs, 2370d53827dSMika Westerberg unsigned int port, unsigned int index, u32 data) 2380d53827dSMika Westerberg { 2390d53827dSMika Westerberg struct pci_dev *pdev = icm->upstream_port; 2400d53827dSMika Westerberg int vnd_cap = icm->vnd_cap; 2410d53827dSMika Westerberg u32 cmd; 2420d53827dSMika Westerberg 2430d53827dSMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_WRDATA, data); 2440d53827dSMika Westerberg 2450d53827dSMika Westerberg cmd = index; 2460d53827dSMika Westerberg cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; 2470d53827dSMika Westerberg cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; 2480d53827dSMika Westerberg cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START; 2490d53827dSMika Westerberg pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); 2500d53827dSMika Westerberg 2510d53827dSMika Westerberg return pci2cio_wait_completion(icm, 5000); 2520d53827dSMika Westerberg } 2530d53827dSMika Westerberg 254f67cf491SMika Westerberg static bool icm_match(const struct tb_cfg_request *req, 255f67cf491SMika Westerberg const struct ctl_pkg *pkg) 256f67cf491SMika Westerberg { 257f67cf491SMika Westerberg const struct icm_pkg_header *res_hdr = pkg->buffer; 258f67cf491SMika Westerberg const struct icm_pkg_header *req_hdr = req->request; 259f67cf491SMika Westerberg 260f67cf491SMika Westerberg if (pkg->frame.eof != req->response_type) 261f67cf491SMika Westerberg return false; 262f67cf491SMika Westerberg if (res_hdr->code != req_hdr->code) 263f67cf491SMika Westerberg return false; 264f67cf491SMika Westerberg 265f67cf491SMika Westerberg return true; 266f67cf491SMika Westerberg } 267f67cf491SMika Westerberg 268f67cf491SMika Westerberg static bool icm_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) 269f67cf491SMika Westerberg { 270f67cf491SMika Westerberg const struct icm_pkg_header *hdr = pkg->buffer; 271f67cf491SMika Westerberg 272f67cf491SMika Westerberg if (hdr->packet_id < req->npackets) { 273f67cf491SMika Westerberg size_t offset = hdr->packet_id * req->response_size; 274f67cf491SMika Westerberg 275f67cf491SMika Westerberg memcpy(req->response + offset, pkg->buffer, req->response_size); 276f67cf491SMika Westerberg } 277f67cf491SMika Westerberg 278f67cf491SMika Westerberg return hdr->packet_id == hdr->total_packets - 1; 279f67cf491SMika Westerberg } 280f67cf491SMika Westerberg 281f67cf491SMika Westerberg static int icm_request(struct tb *tb, const void *request, size_t request_size, 282f67cf491SMika Westerberg void *response, size_t response_size, size_t npackets, 283f67cf491SMika Westerberg unsigned int timeout_msec) 284f67cf491SMika Westerberg { 285f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 286f67cf491SMika Westerberg int retries = 3; 287f67cf491SMika Westerberg 288f67cf491SMika Westerberg do { 289f67cf491SMika Westerberg struct tb_cfg_request *req; 290f67cf491SMika Westerberg struct tb_cfg_result res; 291f67cf491SMika Westerberg 292f67cf491SMika Westerberg req = tb_cfg_request_alloc(); 293f67cf491SMika Westerberg if (!req) 294f67cf491SMika Westerberg return -ENOMEM; 295f67cf491SMika Westerberg 296f67cf491SMika Westerberg req->match = icm_match; 297f67cf491SMika Westerberg req->copy = icm_copy; 298f67cf491SMika Westerberg req->request = request; 299f67cf491SMika Westerberg req->request_size = request_size; 300f67cf491SMika Westerberg req->request_type = TB_CFG_PKG_ICM_CMD; 301f67cf491SMika Westerberg req->response = response; 302f67cf491SMika Westerberg req->npackets = npackets; 303f67cf491SMika Westerberg req->response_size = response_size; 304f67cf491SMika Westerberg req->response_type = TB_CFG_PKG_ICM_RESP; 305f67cf491SMika Westerberg 306f67cf491SMika Westerberg mutex_lock(&icm->request_lock); 307f67cf491SMika Westerberg res = tb_cfg_request_sync(tb->ctl, req, timeout_msec); 308f67cf491SMika Westerberg mutex_unlock(&icm->request_lock); 309f67cf491SMika Westerberg 310f67cf491SMika Westerberg tb_cfg_request_put(req); 311f67cf491SMika Westerberg 312f67cf491SMika Westerberg if (res.err != -ETIMEDOUT) 313f67cf491SMika Westerberg return res.err == 1 ? -EIO : res.err; 314f67cf491SMika Westerberg 315f67cf491SMika Westerberg usleep_range(20, 50); 316f67cf491SMika Westerberg } while (retries--); 317f67cf491SMika Westerberg 318f67cf491SMika Westerberg return -ETIMEDOUT; 319f67cf491SMika Westerberg } 320f67cf491SMika Westerberg 3213cdb9446SMika Westerberg /* 3223cdb9446SMika Westerberg * If rescan is queued to run (we are resuming), postpone it to give the 3233cdb9446SMika Westerberg * firmware some more time to send device connected notifications for next 3243cdb9446SMika Westerberg * devices in the chain. 3253cdb9446SMika Westerberg */ 3263cdb9446SMika Westerberg static void icm_postpone_rescan(struct tb *tb) 3273cdb9446SMika Westerberg { 3283cdb9446SMika Westerberg struct icm *icm = tb_priv(tb); 3293cdb9446SMika Westerberg 3303cdb9446SMika Westerberg if (delayed_work_pending(&icm->rescan_work)) 3313cdb9446SMika Westerberg mod_delayed_work(tb->wq, &icm->rescan_work, 3323cdb9446SMika Westerberg msecs_to_jiffies(500)); 3333cdb9446SMika Westerberg } 3343cdb9446SMika Westerberg 3353cdb9446SMika Westerberg static void icm_veto_begin(struct tb *tb) 3363cdb9446SMika Westerberg { 3373cdb9446SMika Westerberg struct icm *icm = tb_priv(tb); 3383cdb9446SMika Westerberg 3393cdb9446SMika Westerberg if (!icm->veto) { 3403cdb9446SMika Westerberg icm->veto = true; 3413cdb9446SMika Westerberg /* Keep the domain powered while veto is in effect */ 3423cdb9446SMika Westerberg pm_runtime_get(&tb->dev); 3433cdb9446SMika Westerberg } 3443cdb9446SMika Westerberg } 3453cdb9446SMika Westerberg 3463cdb9446SMika Westerberg static void icm_veto_end(struct tb *tb) 3473cdb9446SMika Westerberg { 3483cdb9446SMika Westerberg struct icm *icm = tb_priv(tb); 3493cdb9446SMika Westerberg 3503cdb9446SMika Westerberg if (icm->veto) { 3513cdb9446SMika Westerberg icm->veto = false; 3523cdb9446SMika Westerberg /* Allow the domain suspend now */ 3533cdb9446SMika Westerberg pm_runtime_mark_last_busy(&tb->dev); 3543cdb9446SMika Westerberg pm_runtime_put_autosuspend(&tb->dev); 3553cdb9446SMika Westerberg } 3563cdb9446SMika Westerberg } 3573cdb9446SMika Westerberg 358354a7a77SMika Westerberg static bool icm_firmware_running(const struct tb_nhi *nhi) 359354a7a77SMika Westerberg { 360354a7a77SMika Westerberg u32 val; 361354a7a77SMika Westerberg 362354a7a77SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 363354a7a77SMika Westerberg return !!(val & REG_FW_STS_ICM_EN); 364354a7a77SMika Westerberg } 365354a7a77SMika Westerberg 366f67cf491SMika Westerberg static bool icm_fr_is_supported(struct tb *tb) 367f67cf491SMika Westerberg { 368630b3affSLukas Wunner return !x86_apple_machine; 369f67cf491SMika Westerberg } 370f67cf491SMika Westerberg 371f67cf491SMika Westerberg static inline int icm_fr_get_switch_index(u32 port) 372f67cf491SMika Westerberg { 373f67cf491SMika Westerberg int index; 374f67cf491SMika Westerberg 375f67cf491SMika Westerberg if ((port & ICM_PORT_TYPE_MASK) != TB_TYPE_PORT) 376f67cf491SMika Westerberg return 0; 377f67cf491SMika Westerberg 378f67cf491SMika Westerberg index = port >> ICM_PORT_INDEX_SHIFT; 379f67cf491SMika Westerberg return index != 0xff ? index : 0; 380f67cf491SMika Westerberg } 381f67cf491SMika Westerberg 382f67cf491SMika Westerberg static int icm_fr_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 383f67cf491SMika Westerberg { 384f67cf491SMika Westerberg struct icm_fr_pkg_get_topology_response *switches, *sw; 385f67cf491SMika Westerberg struct icm_fr_pkg_get_topology request = { 386f67cf491SMika Westerberg .hdr = { .code = ICM_GET_TOPOLOGY }, 387f67cf491SMika Westerberg }; 388f67cf491SMika Westerberg size_t npackets = ICM_GET_TOPOLOGY_PACKETS; 389f67cf491SMika Westerberg int ret, index; 390f67cf491SMika Westerberg u8 i; 391f67cf491SMika Westerberg 392f67cf491SMika Westerberg switches = kcalloc(npackets, sizeof(*switches), GFP_KERNEL); 393f67cf491SMika Westerberg if (!switches) 394f67cf491SMika Westerberg return -ENOMEM; 395f67cf491SMika Westerberg 396f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), switches, 397f67cf491SMika Westerberg sizeof(*switches), npackets, ICM_TIMEOUT); 398f67cf491SMika Westerberg if (ret) 399f67cf491SMika Westerberg goto err_free; 400f67cf491SMika Westerberg 401f67cf491SMika Westerberg sw = &switches[0]; 402f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[link]); 403f67cf491SMika Westerberg if (!index) { 404f67cf491SMika Westerberg ret = -ENODEV; 405f67cf491SMika Westerberg goto err_free; 406f67cf491SMika Westerberg } 407f67cf491SMika Westerberg 408f67cf491SMika Westerberg sw = &switches[index]; 409f67cf491SMika Westerberg for (i = 1; i < depth; i++) { 410f67cf491SMika Westerberg unsigned int j; 411f67cf491SMika Westerberg 412f67cf491SMika Westerberg if (!(sw->first_data & ICM_SWITCH_USED)) { 413f67cf491SMika Westerberg ret = -ENODEV; 414f67cf491SMika Westerberg goto err_free; 415f67cf491SMika Westerberg } 416f67cf491SMika Westerberg 417f67cf491SMika Westerberg for (j = 0; j < ARRAY_SIZE(sw->ports); j++) { 418f67cf491SMika Westerberg index = icm_fr_get_switch_index(sw->ports[j]); 419f67cf491SMika Westerberg if (index > sw->switch_index) { 420f67cf491SMika Westerberg sw = &switches[index]; 421f67cf491SMika Westerberg break; 422f67cf491SMika Westerberg } 423f67cf491SMika Westerberg } 424f67cf491SMika Westerberg } 425f67cf491SMika Westerberg 426f67cf491SMika Westerberg *route = get_route(sw->route_hi, sw->route_lo); 427f67cf491SMika Westerberg 428f67cf491SMika Westerberg err_free: 429f67cf491SMika Westerberg kfree(switches); 430f67cf491SMika Westerberg return ret; 431f67cf491SMika Westerberg } 432f67cf491SMika Westerberg 433d04522faSMika Westerberg static void icm_fr_save_devices(struct tb *tb) 434d04522faSMika Westerberg { 435d04522faSMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_SAVE_DEVS, 0); 436d04522faSMika Westerberg } 437d04522faSMika Westerberg 4383080e197SMika Westerberg static int 4399aaa3b8bSMika Westerberg icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 4402d8ff0b5SMika Westerberg size_t *nboot_acl, bool *rpm) 4413080e197SMika Westerberg { 4423080e197SMika Westerberg struct icm_fr_pkg_driver_ready_response reply; 4433080e197SMika Westerberg struct icm_pkg_driver_ready request = { 4443080e197SMika Westerberg .hdr.code = ICM_DRIVER_READY, 4453080e197SMika Westerberg }; 4463080e197SMika Westerberg int ret; 4473080e197SMika Westerberg 4483080e197SMika Westerberg memset(&reply, 0, sizeof(reply)); 4493080e197SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 4503080e197SMika Westerberg 1, ICM_TIMEOUT); 4513080e197SMika Westerberg if (ret) 4523080e197SMika Westerberg return ret; 4533080e197SMika Westerberg 4543080e197SMika Westerberg if (security_level) 4553080e197SMika Westerberg *security_level = reply.security_level & ICM_FR_SLEVEL_MASK; 4563080e197SMika Westerberg 4573080e197SMika Westerberg return 0; 4583080e197SMika Westerberg } 4593080e197SMika Westerberg 460f67cf491SMika Westerberg static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) 461f67cf491SMika Westerberg { 462f67cf491SMika Westerberg struct icm_fr_pkg_approve_device request; 463f67cf491SMika Westerberg struct icm_fr_pkg_approve_device reply; 464f67cf491SMika Westerberg int ret; 465f67cf491SMika Westerberg 466f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 467f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 468f67cf491SMika Westerberg request.hdr.code = ICM_APPROVE_DEVICE; 469f67cf491SMika Westerberg request.connection_id = sw->connection_id; 470f67cf491SMika Westerberg request.connection_key = sw->connection_key; 471f67cf491SMika Westerberg 472f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 473f67cf491SMika Westerberg /* Use larger timeout as establishing tunnels can take some time */ 474f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 4750b0a0bd0SMika Westerberg 1, ICM_APPROVE_TIMEOUT); 476f67cf491SMika Westerberg if (ret) 477f67cf491SMika Westerberg return ret; 478f67cf491SMika Westerberg 479f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 480f67cf491SMika Westerberg tb_warn(tb, "PCIe tunnel creation failed\n"); 481f67cf491SMika Westerberg return -EIO; 482f67cf491SMika Westerberg } 483f67cf491SMika Westerberg 484f67cf491SMika Westerberg return 0; 485f67cf491SMika Westerberg } 486f67cf491SMika Westerberg 487f67cf491SMika Westerberg static int icm_fr_add_switch_key(struct tb *tb, struct tb_switch *sw) 488f67cf491SMika Westerberg { 489f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key request; 490f67cf491SMika Westerberg struct icm_fr_pkg_add_device_key_response reply; 491f67cf491SMika Westerberg int ret; 492f67cf491SMika Westerberg 493f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 494f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 495f67cf491SMika Westerberg request.hdr.code = ICM_ADD_DEVICE_KEY; 496f67cf491SMika Westerberg request.connection_id = sw->connection_id; 497f67cf491SMika Westerberg request.connection_key = sw->connection_key; 498f67cf491SMika Westerberg memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 499f67cf491SMika Westerberg 500f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 501f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 502f67cf491SMika Westerberg 1, ICM_TIMEOUT); 503f67cf491SMika Westerberg if (ret) 504f67cf491SMika Westerberg return ret; 505f67cf491SMika Westerberg 506f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) { 507f67cf491SMika Westerberg tb_warn(tb, "Adding key to switch failed\n"); 508f67cf491SMika Westerberg return -EIO; 509f67cf491SMika Westerberg } 510f67cf491SMika Westerberg 511f67cf491SMika Westerberg return 0; 512f67cf491SMika Westerberg } 513f67cf491SMika Westerberg 514f67cf491SMika Westerberg static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 515f67cf491SMika Westerberg const u8 *challenge, u8 *response) 516f67cf491SMika Westerberg { 517f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device request; 518f67cf491SMika Westerberg struct icm_fr_pkg_challenge_device_response reply; 519f67cf491SMika Westerberg int ret; 520f67cf491SMika Westerberg 521f67cf491SMika Westerberg memset(&request, 0, sizeof(request)); 522f67cf491SMika Westerberg memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 523f67cf491SMika Westerberg request.hdr.code = ICM_CHALLENGE_DEVICE; 524f67cf491SMika Westerberg request.connection_id = sw->connection_id; 525f67cf491SMika Westerberg request.connection_key = sw->connection_key; 526f67cf491SMika Westerberg memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 527f67cf491SMika Westerberg 528f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 529f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 530f67cf491SMika Westerberg 1, ICM_TIMEOUT); 531f67cf491SMika Westerberg if (ret) 532f67cf491SMika Westerberg return ret; 533f67cf491SMika Westerberg 534f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 535f67cf491SMika Westerberg return -EKEYREJECTED; 536f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 537f67cf491SMika Westerberg return -ENOKEY; 538f67cf491SMika Westerberg 539f67cf491SMika Westerberg memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 540f67cf491SMika Westerberg 541f67cf491SMika Westerberg return 0; 542f67cf491SMika Westerberg } 543f67cf491SMika Westerberg 544d1ff7024SMika Westerberg static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 545d1ff7024SMika Westerberg { 546d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain_response reply; 547d1ff7024SMika Westerberg struct icm_fr_pkg_approve_xdomain request; 548d1ff7024SMika Westerberg int ret; 549d1ff7024SMika Westerberg 550d1ff7024SMika Westerberg memset(&request, 0, sizeof(request)); 551d1ff7024SMika Westerberg request.hdr.code = ICM_APPROVE_XDOMAIN; 552d1ff7024SMika Westerberg request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; 553d1ff7024SMika Westerberg memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 554d1ff7024SMika Westerberg 555d1ff7024SMika Westerberg request.transmit_path = xd->transmit_path; 556d1ff7024SMika Westerberg request.transmit_ring = xd->transmit_ring; 557d1ff7024SMika Westerberg request.receive_path = xd->receive_path; 558d1ff7024SMika Westerberg request.receive_ring = xd->receive_ring; 559d1ff7024SMika Westerberg 560d1ff7024SMika Westerberg memset(&reply, 0, sizeof(reply)); 561d1ff7024SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 562d1ff7024SMika Westerberg 1, ICM_TIMEOUT); 563d1ff7024SMika Westerberg if (ret) 564d1ff7024SMika Westerberg return ret; 565d1ff7024SMika Westerberg 566d1ff7024SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 567d1ff7024SMika Westerberg return -EIO; 568d1ff7024SMika Westerberg 569d1ff7024SMika Westerberg return 0; 570d1ff7024SMika Westerberg } 571d1ff7024SMika Westerberg 572d1ff7024SMika Westerberg static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 573d1ff7024SMika Westerberg { 574d1ff7024SMika Westerberg u8 phy_port; 575d1ff7024SMika Westerberg u8 cmd; 576d1ff7024SMika Westerberg 577d1ff7024SMika Westerberg phy_port = tb_phy_port_from_link(xd->link); 578d1ff7024SMika Westerberg if (phy_port == 0) 579d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PA; 580d1ff7024SMika Westerberg else 581d1ff7024SMika Westerberg cmd = NHI_MAILBOX_DISCONNECT_PB; 582d1ff7024SMika Westerberg 583d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 1); 584d1ff7024SMika Westerberg usleep_range(10, 50); 585d1ff7024SMika Westerberg nhi_mailbox_cmd(tb->nhi, cmd, 2); 586d1ff7024SMika Westerberg return 0; 587d1ff7024SMika Westerberg } 588d1ff7024SMika Westerberg 589b5db76dbSMika Westerberg static struct tb_switch *alloc_switch(struct tb_switch *parent_sw, u64 route, 590b5db76dbSMika Westerberg const uuid_t *uuid) 591ee487dd2SMika Westerberg { 592b5db76dbSMika Westerberg struct tb *tb = parent_sw->tb; 593ee487dd2SMika Westerberg struct tb_switch *sw; 594ee487dd2SMika Westerberg 595b5db76dbSMika Westerberg sw = tb_switch_alloc(tb, &parent_sw->dev, route); 596b5db76dbSMika Westerberg if (IS_ERR(sw)) { 597b5db76dbSMika Westerberg tb_warn(tb, "failed to allocate switch at %llx\n", route); 598b5db76dbSMika Westerberg return sw; 599b5db76dbSMika Westerberg } 600ee487dd2SMika Westerberg 601ee487dd2SMika Westerberg sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); 602fd21b79eSAditya Pakki if (!sw->uuid) { 603fd21b79eSAditya Pakki tb_switch_put(sw); 604b5db76dbSMika Westerberg return ERR_PTR(-ENOMEM); 605fd21b79eSAditya Pakki } 606ee487dd2SMika Westerberg 607b5db76dbSMika Westerberg init_completion(&sw->rpm_complete); 608b5db76dbSMika Westerberg return sw; 609b5db76dbSMika Westerberg } 610b5db76dbSMika Westerberg 611b5db76dbSMika Westerberg static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw) 612b5db76dbSMika Westerberg { 613b5db76dbSMika Westerberg u64 route = tb_route(sw); 614b5db76dbSMika Westerberg int ret; 6152d8ff0b5SMika Westerberg 616ee487dd2SMika Westerberg /* Link the two switches now */ 617ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 618ee487dd2SMika Westerberg tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); 619ee487dd2SMika Westerberg 6203cdb9446SMika Westerberg ret = tb_switch_add(sw); 621b5db76dbSMika Westerberg if (ret) 622ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 6232d8ff0b5SMika Westerberg 624b5db76dbSMika Westerberg return ret; 625ee487dd2SMika Westerberg } 626ee487dd2SMika Westerberg 627ee487dd2SMika Westerberg static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, 628ee487dd2SMika Westerberg u64 route, u8 connection_id, u8 connection_key, 62914862ee3SYehezkel Bernat u8 link, u8 depth, bool boot) 630ee487dd2SMika Westerberg { 631ee487dd2SMika Westerberg /* Disconnect from parent */ 632ee487dd2SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 633ee487dd2SMika Westerberg /* Re-connect via updated port*/ 634ee487dd2SMika Westerberg tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); 635ee487dd2SMika Westerberg 636ee487dd2SMika Westerberg /* Update with the new addressing information */ 637ee487dd2SMika Westerberg sw->config.route_hi = upper_32_bits(route); 638ee487dd2SMika Westerberg sw->config.route_lo = lower_32_bits(route); 639ee487dd2SMika Westerberg sw->connection_id = connection_id; 640ee487dd2SMika Westerberg sw->connection_key = connection_key; 641ee487dd2SMika Westerberg sw->link = link; 642ee487dd2SMika Westerberg sw->depth = depth; 64314862ee3SYehezkel Bernat sw->boot = boot; 644ee487dd2SMika Westerberg 645ee487dd2SMika Westerberg /* This switch still exists */ 646ee487dd2SMika Westerberg sw->is_unplugged = false; 6474f7c2e0dSMika Westerberg 6484f7c2e0dSMika Westerberg /* Runtime resume is now complete */ 6494f7c2e0dSMika Westerberg complete(&sw->rpm_complete); 650ee487dd2SMika Westerberg } 651ee487dd2SMika Westerberg 652f67cf491SMika Westerberg static void remove_switch(struct tb_switch *sw) 653f67cf491SMika Westerberg { 654f67cf491SMika Westerberg struct tb_switch *parent_sw; 655f67cf491SMika Westerberg 656f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 657f67cf491SMika Westerberg tb_port_at(tb_route(sw), parent_sw)->remote = NULL; 658f67cf491SMika Westerberg tb_switch_remove(sw); 659f67cf491SMika Westerberg } 660f67cf491SMika Westerberg 661ee487dd2SMika Westerberg static void add_xdomain(struct tb_switch *sw, u64 route, 662ee487dd2SMika Westerberg const uuid_t *local_uuid, const uuid_t *remote_uuid, 663ee487dd2SMika Westerberg u8 link, u8 depth) 664ee487dd2SMika Westerberg { 665ee487dd2SMika Westerberg struct tb_xdomain *xd; 666ee487dd2SMika Westerberg 6672d8ff0b5SMika Westerberg pm_runtime_get_sync(&sw->dev); 6682d8ff0b5SMika Westerberg 669ee487dd2SMika Westerberg xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid); 670ee487dd2SMika Westerberg if (!xd) 6712d8ff0b5SMika Westerberg goto out; 672ee487dd2SMika Westerberg 673ee487dd2SMika Westerberg xd->link = link; 674ee487dd2SMika Westerberg xd->depth = depth; 675ee487dd2SMika Westerberg 676ee487dd2SMika Westerberg tb_port_at(route, sw)->xdomain = xd; 677ee487dd2SMika Westerberg 678ee487dd2SMika Westerberg tb_xdomain_add(xd); 6792d8ff0b5SMika Westerberg 6802d8ff0b5SMika Westerberg out: 6812d8ff0b5SMika Westerberg pm_runtime_mark_last_busy(&sw->dev); 6822d8ff0b5SMika Westerberg pm_runtime_put_autosuspend(&sw->dev); 683ee487dd2SMika Westerberg } 684ee487dd2SMika Westerberg 685ee487dd2SMika Westerberg static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link) 686ee487dd2SMika Westerberg { 687ee487dd2SMika Westerberg xd->link = link; 688ee487dd2SMika Westerberg xd->route = route; 689ee487dd2SMika Westerberg xd->is_unplugged = false; 690ee487dd2SMika Westerberg } 691ee487dd2SMika Westerberg 69279fae987SMika Westerberg static void remove_xdomain(struct tb_xdomain *xd) 69379fae987SMika Westerberg { 69479fae987SMika Westerberg struct tb_switch *sw; 69579fae987SMika Westerberg 69679fae987SMika Westerberg sw = tb_to_switch(xd->dev.parent); 69779fae987SMika Westerberg tb_port_at(xd->route, sw)->xdomain = NULL; 69879fae987SMika Westerberg tb_xdomain_remove(xd); 69979fae987SMika Westerberg } 70079fae987SMika Westerberg 701f67cf491SMika Westerberg static void 702f67cf491SMika Westerberg icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 703f67cf491SMika Westerberg { 704f67cf491SMika Westerberg const struct icm_fr_event_device_connected *pkg = 705f67cf491SMika Westerberg (const struct icm_fr_event_device_connected *)hdr; 706ee487dd2SMika Westerberg enum tb_security_level security_level; 707f67cf491SMika Westerberg struct tb_switch *sw, *parent_sw; 70891c0c120SMika Westerberg bool boot, dual_lane, speed_gen3; 709f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 710f67cf491SMika Westerberg bool authorized = false; 71179fae987SMika Westerberg struct tb_xdomain *xd; 712f67cf491SMika Westerberg u8 link, depth; 713f67cf491SMika Westerberg u64 route; 714f67cf491SMika Westerberg int ret; 715f67cf491SMika Westerberg 7163cdb9446SMika Westerberg icm_postpone_rescan(tb); 7173cdb9446SMika Westerberg 718f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 719f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 720f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 721f67cf491SMika Westerberg authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 722ee487dd2SMika Westerberg security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 723ee487dd2SMika Westerberg ICM_FLAGS_SLEVEL_SHIFT; 72414862ee3SYehezkel Bernat boot = pkg->link_info & ICM_LINK_INFO_BOOT; 72591c0c120SMika Westerberg dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; 72691c0c120SMika Westerberg speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; 727f67cf491SMika Westerberg 728cb653eecSMika Westerberg if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 729cb653eecSMika Westerberg tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", 730cb653eecSMika Westerberg link, depth); 731cb653eecSMika Westerberg return; 732cb653eecSMika Westerberg } 733cb653eecSMika Westerberg 734f67cf491SMika Westerberg sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 735f67cf491SMika Westerberg if (sw) { 736f67cf491SMika Westerberg u8 phy_port, sw_phy_port; 737f67cf491SMika Westerberg 738f67cf491SMika Westerberg parent_sw = tb_to_switch(sw->dev.parent); 739fdd92e89SMika Westerberg sw_phy_port = tb_phy_port_from_link(sw->link); 740fdd92e89SMika Westerberg phy_port = tb_phy_port_from_link(link); 741f67cf491SMika Westerberg 742f67cf491SMika Westerberg /* 743f67cf491SMika Westerberg * On resume ICM will send us connected events for the 744f67cf491SMika Westerberg * devices that still are present. However, that 745f67cf491SMika Westerberg * information might have changed for example by the 746f67cf491SMika Westerberg * fact that a switch on a dual-link connection might 747f67cf491SMika Westerberg * have been enumerated using the other link now. Make 748f67cf491SMika Westerberg * sure our book keeping matches that. 749f67cf491SMika Westerberg */ 750f67cf491SMika Westerberg if (sw->depth == depth && sw_phy_port == phy_port && 751f67cf491SMika Westerberg !!sw->authorized == authorized) { 752fdd92e89SMika Westerberg /* 753fdd92e89SMika Westerberg * It was enumerated through another link so update 754fdd92e89SMika Westerberg * route string accordingly. 755fdd92e89SMika Westerberg */ 756fdd92e89SMika Westerberg if (sw->link != link) { 757fdd92e89SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 758fdd92e89SMika Westerberg if (ret) { 759fdd92e89SMika Westerberg tb_err(tb, "failed to update route string for switch at %u.%u\n", 760fdd92e89SMika Westerberg link, depth); 761fdd92e89SMika Westerberg tb_switch_put(sw); 762fdd92e89SMika Westerberg return; 763fdd92e89SMika Westerberg } 764fdd92e89SMika Westerberg } else { 765fdd92e89SMika Westerberg route = tb_route(sw); 766fdd92e89SMika Westerberg } 767fdd92e89SMika Westerberg 768ee487dd2SMika Westerberg update_switch(parent_sw, sw, route, pkg->connection_id, 76914862ee3SYehezkel Bernat pkg->connection_key, link, depth, boot); 770f67cf491SMika Westerberg tb_switch_put(sw); 771f67cf491SMika Westerberg return; 772f67cf491SMika Westerberg } 773f67cf491SMika Westerberg 774f67cf491SMika Westerberg /* 775f67cf491SMika Westerberg * User connected the same switch to another physical 776f67cf491SMika Westerberg * port or to another part of the topology. Remove the 777f67cf491SMika Westerberg * existing switch now before adding the new one. 778f67cf491SMika Westerberg */ 779f67cf491SMika Westerberg remove_switch(sw); 780f67cf491SMika Westerberg tb_switch_put(sw); 781f67cf491SMika Westerberg } 782f67cf491SMika Westerberg 783f67cf491SMika Westerberg /* 784f67cf491SMika Westerberg * If the switch was not found by UUID, look for a switch on 785f67cf491SMika Westerberg * same physical port (taking possible link aggregation into 786f67cf491SMika Westerberg * account) and depth. If we found one it is definitely a stale 787f67cf491SMika Westerberg * one so remove it first. 788f67cf491SMika Westerberg */ 789f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 790f67cf491SMika Westerberg if (!sw) { 791f67cf491SMika Westerberg u8 dual_link; 792f67cf491SMika Westerberg 793f67cf491SMika Westerberg dual_link = dual_link_from_link(link); 794f67cf491SMika Westerberg if (dual_link) 795f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, dual_link, depth); 796f67cf491SMika Westerberg } 797f67cf491SMika Westerberg if (sw) { 798f67cf491SMika Westerberg remove_switch(sw); 799f67cf491SMika Westerberg tb_switch_put(sw); 800f67cf491SMika Westerberg } 801f67cf491SMika Westerberg 80279fae987SMika Westerberg /* Remove existing XDomain connection if found */ 80379fae987SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 80479fae987SMika Westerberg if (xd) { 80579fae987SMika Westerberg remove_xdomain(xd); 80679fae987SMika Westerberg tb_xdomain_put(xd); 80779fae987SMika Westerberg } 80879fae987SMika Westerberg 809f67cf491SMika Westerberg parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); 810f67cf491SMika Westerberg if (!parent_sw) { 811f67cf491SMika Westerberg tb_err(tb, "failed to find parent switch for %u.%u\n", 812f67cf491SMika Westerberg link, depth); 813f67cf491SMika Westerberg return; 814f67cf491SMika Westerberg } 815f67cf491SMika Westerberg 816fdd92e89SMika Westerberg ret = icm->get_route(tb, link, depth, &route); 817fdd92e89SMika Westerberg if (ret) { 818fdd92e89SMika Westerberg tb_err(tb, "failed to find route string for switch at %u.%u\n", 819fdd92e89SMika Westerberg link, depth); 820fdd92e89SMika Westerberg tb_switch_put(parent_sw); 821fdd92e89SMika Westerberg return; 822fdd92e89SMika Westerberg } 823fdd92e89SMika Westerberg 824b5db76dbSMika Westerberg pm_runtime_get_sync(&parent_sw->dev); 825b5db76dbSMika Westerberg 826b5db76dbSMika Westerberg sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); 827b5db76dbSMika Westerberg if (!IS_ERR(sw)) { 828b5db76dbSMika Westerberg sw->connection_id = pkg->connection_id; 829b5db76dbSMika Westerberg sw->connection_key = pkg->connection_key; 830b5db76dbSMika Westerberg sw->link = link; 831b5db76dbSMika Westerberg sw->depth = depth; 832b5db76dbSMika Westerberg sw->authorized = authorized; 833b5db76dbSMika Westerberg sw->security_level = security_level; 834b5db76dbSMika Westerberg sw->boot = boot; 83591c0c120SMika Westerberg sw->link_speed = speed_gen3 ? 20 : 10; 83691c0c120SMika Westerberg sw->link_width = dual_lane ? 2 : 1; 837b5db76dbSMika Westerberg sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name)); 838b5db76dbSMika Westerberg 839b5db76dbSMika Westerberg if (add_switch(parent_sw, sw)) 840b5db76dbSMika Westerberg tb_switch_put(sw); 841b5db76dbSMika Westerberg } 842b5db76dbSMika Westerberg 843b5db76dbSMika Westerberg pm_runtime_mark_last_busy(&parent_sw->dev); 844b5db76dbSMika Westerberg pm_runtime_put_autosuspend(&parent_sw->dev); 845f67cf491SMika Westerberg 846f67cf491SMika Westerberg tb_switch_put(parent_sw); 847f67cf491SMika Westerberg } 848f67cf491SMika Westerberg 849f67cf491SMika Westerberg static void 850f67cf491SMika Westerberg icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 851f67cf491SMika Westerberg { 852f67cf491SMika Westerberg const struct icm_fr_event_device_disconnected *pkg = 853f67cf491SMika Westerberg (const struct icm_fr_event_device_disconnected *)hdr; 854f67cf491SMika Westerberg struct tb_switch *sw; 855f67cf491SMika Westerberg u8 link, depth; 856f67cf491SMika Westerberg 857f67cf491SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 858f67cf491SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 859f67cf491SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 860f67cf491SMika Westerberg 861f0342e75SMika Westerberg if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) { 862f67cf491SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 863f67cf491SMika Westerberg return; 864f67cf491SMika Westerberg } 865f67cf491SMika Westerberg 866f67cf491SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 867f67cf491SMika Westerberg if (!sw) { 868f67cf491SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 869f67cf491SMika Westerberg depth); 870f67cf491SMika Westerberg return; 871f67cf491SMika Westerberg } 872f67cf491SMika Westerberg 873f67cf491SMika Westerberg remove_switch(sw); 874f67cf491SMika Westerberg tb_switch_put(sw); 875f67cf491SMika Westerberg } 876f67cf491SMika Westerberg 877d1ff7024SMika Westerberg static void 878d1ff7024SMika Westerberg icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 879d1ff7024SMika Westerberg { 880d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_connected *pkg = 881d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_connected *)hdr; 882d1ff7024SMika Westerberg struct tb_xdomain *xd; 883d1ff7024SMika Westerberg struct tb_switch *sw; 884d1ff7024SMika Westerberg u8 link, depth; 885d1ff7024SMika Westerberg u64 route; 886d1ff7024SMika Westerberg 887d1ff7024SMika Westerberg link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; 888d1ff7024SMika Westerberg depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> 889d1ff7024SMika Westerberg ICM_LINK_INFO_DEPTH_SHIFT; 890d1ff7024SMika Westerberg 891f0342e75SMika Westerberg if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) { 892d1ff7024SMika Westerberg tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); 893d1ff7024SMika Westerberg return; 894d1ff7024SMika Westerberg } 895d1ff7024SMika Westerberg 896d1ff7024SMika Westerberg route = get_route(pkg->local_route_hi, pkg->local_route_lo); 897d1ff7024SMika Westerberg 898d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 899d1ff7024SMika Westerberg if (xd) { 900d1ff7024SMika Westerberg u8 xd_phy_port, phy_port; 901d1ff7024SMika Westerberg 902d1ff7024SMika Westerberg xd_phy_port = phy_port_from_route(xd->route, xd->depth); 903d1ff7024SMika Westerberg phy_port = phy_port_from_route(route, depth); 904d1ff7024SMika Westerberg 905d1ff7024SMika Westerberg if (xd->depth == depth && xd_phy_port == phy_port) { 906ee487dd2SMika Westerberg update_xdomain(xd, route, link); 907d1ff7024SMika Westerberg tb_xdomain_put(xd); 908d1ff7024SMika Westerberg return; 909d1ff7024SMika Westerberg } 910d1ff7024SMika Westerberg 911d1ff7024SMika Westerberg /* 912d1ff7024SMika Westerberg * If we find an existing XDomain connection remove it 913d1ff7024SMika Westerberg * now. We need to go through login handshake and 914d1ff7024SMika Westerberg * everything anyway to be able to re-establish the 915d1ff7024SMika Westerberg * connection. 916d1ff7024SMika Westerberg */ 917d1ff7024SMika Westerberg remove_xdomain(xd); 918d1ff7024SMika Westerberg tb_xdomain_put(xd); 919d1ff7024SMika Westerberg } 920d1ff7024SMika Westerberg 921d1ff7024SMika Westerberg /* 922d1ff7024SMika Westerberg * Look if there already exists an XDomain in the same place 923d1ff7024SMika Westerberg * than the new one and in that case remove it because it is 924d1ff7024SMika Westerberg * most likely another host that got disconnected. 925d1ff7024SMika Westerberg */ 926d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, link, depth); 927d1ff7024SMika Westerberg if (!xd) { 928d1ff7024SMika Westerberg u8 dual_link; 929d1ff7024SMika Westerberg 930d1ff7024SMika Westerberg dual_link = dual_link_from_link(link); 931d1ff7024SMika Westerberg if (dual_link) 932d1ff7024SMika Westerberg xd = tb_xdomain_find_by_link_depth(tb, dual_link, 933d1ff7024SMika Westerberg depth); 934d1ff7024SMika Westerberg } 935d1ff7024SMika Westerberg if (xd) { 936d1ff7024SMika Westerberg remove_xdomain(xd); 937d1ff7024SMika Westerberg tb_xdomain_put(xd); 938d1ff7024SMika Westerberg } 939d1ff7024SMika Westerberg 940d1ff7024SMika Westerberg /* 941d1ff7024SMika Westerberg * If the user disconnected a switch during suspend and 942d1ff7024SMika Westerberg * connected another host to the same port, remove the switch 943d1ff7024SMika Westerberg * first. 944d1ff7024SMika Westerberg */ 9458f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, route); 9468f965efdSMika Westerberg if (sw) { 947d1ff7024SMika Westerberg remove_switch(sw); 9488f965efdSMika Westerberg tb_switch_put(sw); 9498f965efdSMika Westerberg } 950d1ff7024SMika Westerberg 951d1ff7024SMika Westerberg sw = tb_switch_find_by_link_depth(tb, link, depth); 952d1ff7024SMika Westerberg if (!sw) { 953d1ff7024SMika Westerberg tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, 954d1ff7024SMika Westerberg depth); 955d1ff7024SMika Westerberg return; 956d1ff7024SMika Westerberg } 957d1ff7024SMika Westerberg 958ee487dd2SMika Westerberg add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link, 959ee487dd2SMika Westerberg depth); 960d1ff7024SMika Westerberg tb_switch_put(sw); 961d1ff7024SMika Westerberg } 962d1ff7024SMika Westerberg 963d1ff7024SMika Westerberg static void 964d1ff7024SMika Westerberg icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 965d1ff7024SMika Westerberg { 966d1ff7024SMika Westerberg const struct icm_fr_event_xdomain_disconnected *pkg = 967d1ff7024SMika Westerberg (const struct icm_fr_event_xdomain_disconnected *)hdr; 968d1ff7024SMika Westerberg struct tb_xdomain *xd; 969d1ff7024SMika Westerberg 970d1ff7024SMika Westerberg /* 971d1ff7024SMika Westerberg * If the connection is through one or multiple devices, the 972d1ff7024SMika Westerberg * XDomain device is removed along with them so it is fine if we 973d1ff7024SMika Westerberg * cannot find it here. 974d1ff7024SMika Westerberg */ 975d1ff7024SMika Westerberg xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 976d1ff7024SMika Westerberg if (xd) { 977d1ff7024SMika Westerberg remove_xdomain(xd); 978d1ff7024SMika Westerberg tb_xdomain_put(xd); 979d1ff7024SMika Westerberg } 980d1ff7024SMika Westerberg } 981d1ff7024SMika Westerberg 9820d53827dSMika Westerberg static int icm_tr_cio_reset(struct tb *tb) 9830d53827dSMika Westerberg { 9840d53827dSMika Westerberg return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x777, BIT(1)); 9850d53827dSMika Westerberg } 9860d53827dSMika Westerberg 9874bac471dSRadion Mirchevsky static int 9884bac471dSRadion Mirchevsky icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 9892d8ff0b5SMika Westerberg size_t *nboot_acl, bool *rpm) 9904bac471dSRadion Mirchevsky { 9914bac471dSRadion Mirchevsky struct icm_tr_pkg_driver_ready_response reply; 9924bac471dSRadion Mirchevsky struct icm_pkg_driver_ready request = { 9934bac471dSRadion Mirchevsky .hdr.code = ICM_DRIVER_READY, 9944bac471dSRadion Mirchevsky }; 9954bac471dSRadion Mirchevsky int ret; 9964bac471dSRadion Mirchevsky 9974bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 9984bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 9994bac471dSRadion Mirchevsky 1, 20000); 10004bac471dSRadion Mirchevsky if (ret) 10014bac471dSRadion Mirchevsky return ret; 10024bac471dSRadion Mirchevsky 10034bac471dSRadion Mirchevsky if (security_level) 10044bac471dSRadion Mirchevsky *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK; 10054bac471dSRadion Mirchevsky if (nboot_acl) 10064bac471dSRadion Mirchevsky *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >> 10074bac471dSRadion Mirchevsky ICM_TR_INFO_BOOT_ACL_SHIFT; 10082d8ff0b5SMika Westerberg if (rpm) 10092d8ff0b5SMika Westerberg *rpm = !!(reply.hdr.flags & ICM_TR_FLAGS_RTD3); 10102d8ff0b5SMika Westerberg 10114bac471dSRadion Mirchevsky return 0; 10124bac471dSRadion Mirchevsky } 10134bac471dSRadion Mirchevsky 10144bac471dSRadion Mirchevsky static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw) 10154bac471dSRadion Mirchevsky { 10164bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_device request; 10174bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_device reply; 10184bac471dSRadion Mirchevsky int ret; 10194bac471dSRadion Mirchevsky 10204bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10214bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10224bac471dSRadion Mirchevsky request.hdr.code = ICM_APPROVE_DEVICE; 10234bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10244bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10254bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10264bac471dSRadion Mirchevsky 10274bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10284bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10294bac471dSRadion Mirchevsky 1, ICM_APPROVE_TIMEOUT); 10304bac471dSRadion Mirchevsky if (ret) 10314bac471dSRadion Mirchevsky return ret; 10324bac471dSRadion Mirchevsky 10334bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) { 10344bac471dSRadion Mirchevsky tb_warn(tb, "PCIe tunnel creation failed\n"); 10354bac471dSRadion Mirchevsky return -EIO; 10364bac471dSRadion Mirchevsky } 10374bac471dSRadion Mirchevsky 10384bac471dSRadion Mirchevsky return 0; 10394bac471dSRadion Mirchevsky } 10404bac471dSRadion Mirchevsky 10414bac471dSRadion Mirchevsky static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw) 10424bac471dSRadion Mirchevsky { 10434bac471dSRadion Mirchevsky struct icm_tr_pkg_add_device_key_response reply; 10444bac471dSRadion Mirchevsky struct icm_tr_pkg_add_device_key request; 10454bac471dSRadion Mirchevsky int ret; 10464bac471dSRadion Mirchevsky 10474bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10484bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10494bac471dSRadion Mirchevsky request.hdr.code = ICM_ADD_DEVICE_KEY; 10504bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10514bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10524bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10534bac471dSRadion Mirchevsky memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); 10544bac471dSRadion Mirchevsky 10554bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10564bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10574bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 10584bac471dSRadion Mirchevsky if (ret) 10594bac471dSRadion Mirchevsky return ret; 10604bac471dSRadion Mirchevsky 10614bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) { 10624bac471dSRadion Mirchevsky tb_warn(tb, "Adding key to switch failed\n"); 10634bac471dSRadion Mirchevsky return -EIO; 10644bac471dSRadion Mirchevsky } 10654bac471dSRadion Mirchevsky 10664bac471dSRadion Mirchevsky return 0; 10674bac471dSRadion Mirchevsky } 10684bac471dSRadion Mirchevsky 10694bac471dSRadion Mirchevsky static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, 10704bac471dSRadion Mirchevsky const u8 *challenge, u8 *response) 10714bac471dSRadion Mirchevsky { 10724bac471dSRadion Mirchevsky struct icm_tr_pkg_challenge_device_response reply; 10734bac471dSRadion Mirchevsky struct icm_tr_pkg_challenge_device request; 10744bac471dSRadion Mirchevsky int ret; 10754bac471dSRadion Mirchevsky 10764bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 10774bac471dSRadion Mirchevsky memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); 10784bac471dSRadion Mirchevsky request.hdr.code = ICM_CHALLENGE_DEVICE; 10794bac471dSRadion Mirchevsky request.route_lo = sw->config.route_lo; 10804bac471dSRadion Mirchevsky request.route_hi = sw->config.route_hi; 10814bac471dSRadion Mirchevsky request.connection_id = sw->connection_id; 10824bac471dSRadion Mirchevsky memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); 10834bac471dSRadion Mirchevsky 10844bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 10854bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 10864bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 10874bac471dSRadion Mirchevsky if (ret) 10884bac471dSRadion Mirchevsky return ret; 10894bac471dSRadion Mirchevsky 10904bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 10914bac471dSRadion Mirchevsky return -EKEYREJECTED; 10924bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_NO_KEY) 10934bac471dSRadion Mirchevsky return -ENOKEY; 10944bac471dSRadion Mirchevsky 10954bac471dSRadion Mirchevsky memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); 10964bac471dSRadion Mirchevsky 10974bac471dSRadion Mirchevsky return 0; 10984bac471dSRadion Mirchevsky } 10994bac471dSRadion Mirchevsky 11004bac471dSRadion Mirchevsky static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 11014bac471dSRadion Mirchevsky { 11024bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_xdomain_response reply; 11034bac471dSRadion Mirchevsky struct icm_tr_pkg_approve_xdomain request; 11044bac471dSRadion Mirchevsky int ret; 11054bac471dSRadion Mirchevsky 11064bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11074bac471dSRadion Mirchevsky request.hdr.code = ICM_APPROVE_XDOMAIN; 11084bac471dSRadion Mirchevsky request.route_hi = upper_32_bits(xd->route); 11094bac471dSRadion Mirchevsky request.route_lo = lower_32_bits(xd->route); 11104bac471dSRadion Mirchevsky request.transmit_path = xd->transmit_path; 11114bac471dSRadion Mirchevsky request.transmit_ring = xd->transmit_ring; 11124bac471dSRadion Mirchevsky request.receive_path = xd->receive_path; 11134bac471dSRadion Mirchevsky request.receive_ring = xd->receive_ring; 11144bac471dSRadion Mirchevsky memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 11154bac471dSRadion Mirchevsky 11164bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11174bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11184bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11194bac471dSRadion Mirchevsky if (ret) 11204bac471dSRadion Mirchevsky return ret; 11214bac471dSRadion Mirchevsky 11224bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11234bac471dSRadion Mirchevsky return -EIO; 11244bac471dSRadion Mirchevsky 11254bac471dSRadion Mirchevsky return 0; 11264bac471dSRadion Mirchevsky } 11274bac471dSRadion Mirchevsky 11284bac471dSRadion Mirchevsky static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd, 11294bac471dSRadion Mirchevsky int stage) 11304bac471dSRadion Mirchevsky { 11314bac471dSRadion Mirchevsky struct icm_tr_pkg_disconnect_xdomain_response reply; 11324bac471dSRadion Mirchevsky struct icm_tr_pkg_disconnect_xdomain request; 11334bac471dSRadion Mirchevsky int ret; 11344bac471dSRadion Mirchevsky 11354bac471dSRadion Mirchevsky memset(&request, 0, sizeof(request)); 11364bac471dSRadion Mirchevsky request.hdr.code = ICM_DISCONNECT_XDOMAIN; 11374bac471dSRadion Mirchevsky request.stage = stage; 11384bac471dSRadion Mirchevsky request.route_hi = upper_32_bits(xd->route); 11394bac471dSRadion Mirchevsky request.route_lo = lower_32_bits(xd->route); 11404bac471dSRadion Mirchevsky memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); 11414bac471dSRadion Mirchevsky 11424bac471dSRadion Mirchevsky memset(&reply, 0, sizeof(reply)); 11434bac471dSRadion Mirchevsky ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 11444bac471dSRadion Mirchevsky 1, ICM_TIMEOUT); 11454bac471dSRadion Mirchevsky if (ret) 11464bac471dSRadion Mirchevsky return ret; 11474bac471dSRadion Mirchevsky 11484bac471dSRadion Mirchevsky if (reply.hdr.flags & ICM_FLAGS_ERROR) 11494bac471dSRadion Mirchevsky return -EIO; 11504bac471dSRadion Mirchevsky 11514bac471dSRadion Mirchevsky return 0; 11524bac471dSRadion Mirchevsky } 11534bac471dSRadion Mirchevsky 11544bac471dSRadion Mirchevsky static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) 11554bac471dSRadion Mirchevsky { 11564bac471dSRadion Mirchevsky int ret; 11574bac471dSRadion Mirchevsky 11584bac471dSRadion Mirchevsky ret = icm_tr_xdomain_tear_down(tb, xd, 1); 11594bac471dSRadion Mirchevsky if (ret) 11604bac471dSRadion Mirchevsky return ret; 11614bac471dSRadion Mirchevsky 11624bac471dSRadion Mirchevsky usleep_range(10, 50); 11634bac471dSRadion Mirchevsky return icm_tr_xdomain_tear_down(tb, xd, 2); 11644bac471dSRadion Mirchevsky } 11654bac471dSRadion Mirchevsky 11664bac471dSRadion Mirchevsky static void 11673cdb9446SMika Westerberg __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, 11683cdb9446SMika Westerberg bool force_rtd3) 11694bac471dSRadion Mirchevsky { 11704bac471dSRadion Mirchevsky const struct icm_tr_event_device_connected *pkg = 11714bac471dSRadion Mirchevsky (const struct icm_tr_event_device_connected *)hdr; 117291c0c120SMika Westerberg bool authorized, boot, dual_lane, speed_gen3; 11734bac471dSRadion Mirchevsky enum tb_security_level security_level; 11744bac471dSRadion Mirchevsky struct tb_switch *sw, *parent_sw; 11754bac471dSRadion Mirchevsky struct tb_xdomain *xd; 11764bac471dSRadion Mirchevsky u64 route; 11774bac471dSRadion Mirchevsky 11783cdb9446SMika Westerberg icm_postpone_rescan(tb); 11793cdb9446SMika Westerberg 11804bac471dSRadion Mirchevsky /* 11814bac471dSRadion Mirchevsky * Currently we don't use the QoS information coming with the 11824bac471dSRadion Mirchevsky * device connected message so simply just ignore that extra 11834bac471dSRadion Mirchevsky * packet for now. 11844bac471dSRadion Mirchevsky */ 11854bac471dSRadion Mirchevsky if (pkg->hdr.packet_id) 11864bac471dSRadion Mirchevsky return; 11874bac471dSRadion Mirchevsky 11884bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 11894bac471dSRadion Mirchevsky authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; 11904bac471dSRadion Mirchevsky security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> 11914bac471dSRadion Mirchevsky ICM_FLAGS_SLEVEL_SHIFT; 11924bac471dSRadion Mirchevsky boot = pkg->link_info & ICM_LINK_INFO_BOOT; 119391c0c120SMika Westerberg dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; 119491c0c120SMika Westerberg speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; 11954bac471dSRadion Mirchevsky 11964bac471dSRadion Mirchevsky if (pkg->link_info & ICM_LINK_INFO_REJECTED) { 11974bac471dSRadion Mirchevsky tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n", 11984bac471dSRadion Mirchevsky route); 11994bac471dSRadion Mirchevsky return; 12004bac471dSRadion Mirchevsky } 12014bac471dSRadion Mirchevsky 12024bac471dSRadion Mirchevsky sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); 12034bac471dSRadion Mirchevsky if (sw) { 12044bac471dSRadion Mirchevsky /* Update the switch if it is still in the same place */ 12054bac471dSRadion Mirchevsky if (tb_route(sw) == route && !!sw->authorized == authorized) { 12064bac471dSRadion Mirchevsky parent_sw = tb_to_switch(sw->dev.parent); 12074bac471dSRadion Mirchevsky update_switch(parent_sw, sw, route, pkg->connection_id, 12084bac471dSRadion Mirchevsky 0, 0, 0, boot); 12094bac471dSRadion Mirchevsky tb_switch_put(sw); 12104bac471dSRadion Mirchevsky return; 12114bac471dSRadion Mirchevsky } 12124bac471dSRadion Mirchevsky 12134bac471dSRadion Mirchevsky remove_switch(sw); 12144bac471dSRadion Mirchevsky tb_switch_put(sw); 12154bac471dSRadion Mirchevsky } 12164bac471dSRadion Mirchevsky 12174bac471dSRadion Mirchevsky /* Another switch with the same address */ 12184bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, route); 12194bac471dSRadion Mirchevsky if (sw) { 12204bac471dSRadion Mirchevsky remove_switch(sw); 12214bac471dSRadion Mirchevsky tb_switch_put(sw); 12224bac471dSRadion Mirchevsky } 12234bac471dSRadion Mirchevsky 12244bac471dSRadion Mirchevsky /* XDomain connection with the same address */ 12254bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 12264bac471dSRadion Mirchevsky if (xd) { 12274bac471dSRadion Mirchevsky remove_xdomain(xd); 12284bac471dSRadion Mirchevsky tb_xdomain_put(xd); 12294bac471dSRadion Mirchevsky } 12304bac471dSRadion Mirchevsky 12314bac471dSRadion Mirchevsky parent_sw = tb_switch_find_by_route(tb, get_parent_route(route)); 12324bac471dSRadion Mirchevsky if (!parent_sw) { 12334bac471dSRadion Mirchevsky tb_err(tb, "failed to find parent switch for %llx\n", route); 12344bac471dSRadion Mirchevsky return; 12354bac471dSRadion Mirchevsky } 12364bac471dSRadion Mirchevsky 1237b5db76dbSMika Westerberg pm_runtime_get_sync(&parent_sw->dev); 1238b5db76dbSMika Westerberg 1239b5db76dbSMika Westerberg sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); 1240b5db76dbSMika Westerberg if (!IS_ERR(sw)) { 1241b5db76dbSMika Westerberg sw->connection_id = pkg->connection_id; 1242b5db76dbSMika Westerberg sw->authorized = authorized; 1243b5db76dbSMika Westerberg sw->security_level = security_level; 1244b5db76dbSMika Westerberg sw->boot = boot; 124591c0c120SMika Westerberg sw->link_speed = speed_gen3 ? 20 : 10; 124691c0c120SMika Westerberg sw->link_width = dual_lane ? 2 : 1; 1247b5db76dbSMika Westerberg sw->rpm = force_rtd3; 1248b5db76dbSMika Westerberg if (!sw->rpm) 1249b5db76dbSMika Westerberg sw->rpm = intel_vss_is_rtd3(pkg->ep_name, 1250b5db76dbSMika Westerberg sizeof(pkg->ep_name)); 1251b5db76dbSMika Westerberg 1252b5db76dbSMika Westerberg if (add_switch(parent_sw, sw)) 1253b5db76dbSMika Westerberg tb_switch_put(sw); 1254b5db76dbSMika Westerberg } 1255b5db76dbSMika Westerberg 1256b5db76dbSMika Westerberg pm_runtime_mark_last_busy(&parent_sw->dev); 1257b5db76dbSMika Westerberg pm_runtime_put_autosuspend(&parent_sw->dev); 12584bac471dSRadion Mirchevsky 12594bac471dSRadion Mirchevsky tb_switch_put(parent_sw); 12604bac471dSRadion Mirchevsky } 12614bac471dSRadion Mirchevsky 12624bac471dSRadion Mirchevsky static void 12633cdb9446SMika Westerberg icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 12643cdb9446SMika Westerberg { 12653cdb9446SMika Westerberg __icm_tr_device_connected(tb, hdr, false); 12663cdb9446SMika Westerberg } 12673cdb9446SMika Westerberg 12683cdb9446SMika Westerberg static void 12694bac471dSRadion Mirchevsky icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 12704bac471dSRadion Mirchevsky { 12714bac471dSRadion Mirchevsky const struct icm_tr_event_device_disconnected *pkg = 12724bac471dSRadion Mirchevsky (const struct icm_tr_event_device_disconnected *)hdr; 12734bac471dSRadion Mirchevsky struct tb_switch *sw; 12744bac471dSRadion Mirchevsky u64 route; 12754bac471dSRadion Mirchevsky 12764bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 12774bac471dSRadion Mirchevsky 12784bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, route); 12794bac471dSRadion Mirchevsky if (!sw) { 12804bac471dSRadion Mirchevsky tb_warn(tb, "no switch exists at %llx, ignoring\n", route); 12814bac471dSRadion Mirchevsky return; 12824bac471dSRadion Mirchevsky } 12834bac471dSRadion Mirchevsky 12844bac471dSRadion Mirchevsky remove_switch(sw); 12854bac471dSRadion Mirchevsky tb_switch_put(sw); 12864bac471dSRadion Mirchevsky } 12874bac471dSRadion Mirchevsky 12884bac471dSRadion Mirchevsky static void 12894bac471dSRadion Mirchevsky icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) 12904bac471dSRadion Mirchevsky { 12914bac471dSRadion Mirchevsky const struct icm_tr_event_xdomain_connected *pkg = 12924bac471dSRadion Mirchevsky (const struct icm_tr_event_xdomain_connected *)hdr; 12934bac471dSRadion Mirchevsky struct tb_xdomain *xd; 12944bac471dSRadion Mirchevsky struct tb_switch *sw; 12954bac471dSRadion Mirchevsky u64 route; 12964bac471dSRadion Mirchevsky 12974bac471dSRadion Mirchevsky if (!tb->root_switch) 12984bac471dSRadion Mirchevsky return; 12994bac471dSRadion Mirchevsky 13004bac471dSRadion Mirchevsky route = get_route(pkg->local_route_hi, pkg->local_route_lo); 13014bac471dSRadion Mirchevsky 13024bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); 13034bac471dSRadion Mirchevsky if (xd) { 13044bac471dSRadion Mirchevsky if (xd->route == route) { 13054bac471dSRadion Mirchevsky update_xdomain(xd, route, 0); 13064bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13074bac471dSRadion Mirchevsky return; 13084bac471dSRadion Mirchevsky } 13094bac471dSRadion Mirchevsky 13104bac471dSRadion Mirchevsky remove_xdomain(xd); 13114bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13124bac471dSRadion Mirchevsky } 13134bac471dSRadion Mirchevsky 13144bac471dSRadion Mirchevsky /* An existing xdomain with the same address */ 13154bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 13164bac471dSRadion Mirchevsky if (xd) { 13174bac471dSRadion Mirchevsky remove_xdomain(xd); 13184bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13194bac471dSRadion Mirchevsky } 13204bac471dSRadion Mirchevsky 13214bac471dSRadion Mirchevsky /* 13224bac471dSRadion Mirchevsky * If the user disconnected a switch during suspend and 13234bac471dSRadion Mirchevsky * connected another host to the same port, remove the switch 13244bac471dSRadion Mirchevsky * first. 13254bac471dSRadion Mirchevsky */ 13268f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, route); 13278f965efdSMika Westerberg if (sw) { 13284bac471dSRadion Mirchevsky remove_switch(sw); 13298f965efdSMika Westerberg tb_switch_put(sw); 13308f965efdSMika Westerberg } 13314bac471dSRadion Mirchevsky 13324bac471dSRadion Mirchevsky sw = tb_switch_find_by_route(tb, get_parent_route(route)); 13334bac471dSRadion Mirchevsky if (!sw) { 13344bac471dSRadion Mirchevsky tb_warn(tb, "no switch exists at %llx, ignoring\n", route); 13354bac471dSRadion Mirchevsky return; 13364bac471dSRadion Mirchevsky } 13374bac471dSRadion Mirchevsky 13384bac471dSRadion Mirchevsky add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0); 13394bac471dSRadion Mirchevsky tb_switch_put(sw); 13404bac471dSRadion Mirchevsky } 13414bac471dSRadion Mirchevsky 13424bac471dSRadion Mirchevsky static void 13434bac471dSRadion Mirchevsky icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) 13444bac471dSRadion Mirchevsky { 13454bac471dSRadion Mirchevsky const struct icm_tr_event_xdomain_disconnected *pkg = 13464bac471dSRadion Mirchevsky (const struct icm_tr_event_xdomain_disconnected *)hdr; 13474bac471dSRadion Mirchevsky struct tb_xdomain *xd; 13484bac471dSRadion Mirchevsky u64 route; 13494bac471dSRadion Mirchevsky 13504bac471dSRadion Mirchevsky route = get_route(pkg->route_hi, pkg->route_lo); 13514bac471dSRadion Mirchevsky 13524bac471dSRadion Mirchevsky xd = tb_xdomain_find_by_route(tb, route); 13534bac471dSRadion Mirchevsky if (xd) { 13544bac471dSRadion Mirchevsky remove_xdomain(xd); 13554bac471dSRadion Mirchevsky tb_xdomain_put(xd); 13564bac471dSRadion Mirchevsky } 13574bac471dSRadion Mirchevsky } 13584bac471dSRadion Mirchevsky 1359f67cf491SMika Westerberg static struct pci_dev *get_upstream_port(struct pci_dev *pdev) 1360f67cf491SMika Westerberg { 1361f67cf491SMika Westerberg struct pci_dev *parent; 1362f67cf491SMika Westerberg 1363f67cf491SMika Westerberg parent = pci_upstream_bridge(pdev); 1364f67cf491SMika Westerberg while (parent) { 1365f67cf491SMika Westerberg if (!pci_is_pcie(parent)) 1366f67cf491SMika Westerberg return NULL; 1367f67cf491SMika Westerberg if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) 1368f67cf491SMika Westerberg break; 1369f67cf491SMika Westerberg parent = pci_upstream_bridge(parent); 1370f67cf491SMika Westerberg } 1371f67cf491SMika Westerberg 1372f67cf491SMika Westerberg if (!parent) 1373f67cf491SMika Westerberg return NULL; 1374f67cf491SMika Westerberg 1375f67cf491SMika Westerberg switch (parent->device) { 1376f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: 1377f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: 1378f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: 1379f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: 1380f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: 1381c4630d6aSMika Westerberg case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: 1382c4630d6aSMika Westerberg case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: 1383f67cf491SMika Westerberg return parent; 1384f67cf491SMika Westerberg } 1385f67cf491SMika Westerberg 1386f67cf491SMika Westerberg return NULL; 1387f67cf491SMika Westerberg } 1388f67cf491SMika Westerberg 1389f67cf491SMika Westerberg static bool icm_ar_is_supported(struct tb *tb) 1390f67cf491SMika Westerberg { 1391f67cf491SMika Westerberg struct pci_dev *upstream_port; 1392f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1393f67cf491SMika Westerberg 1394f67cf491SMika Westerberg /* 1395f67cf491SMika Westerberg * Starting from Alpine Ridge we can use ICM on Apple machines 1396f67cf491SMika Westerberg * as well. We just need to reset and re-enable it first. 1397354a7a77SMika Westerberg * However, only start it if explicitly asked by the user. 1398f67cf491SMika Westerberg */ 1399354a7a77SMika Westerberg if (icm_firmware_running(tb->nhi)) 1400f67cf491SMika Westerberg return true; 1401354a7a77SMika Westerberg if (!start_icm) 1402354a7a77SMika Westerberg return false; 1403f67cf491SMika Westerberg 1404f67cf491SMika Westerberg /* 1405f67cf491SMika Westerberg * Find the upstream PCIe port in case we need to do reset 1406f67cf491SMika Westerberg * through its vendor specific registers. 1407f67cf491SMika Westerberg */ 1408f67cf491SMika Westerberg upstream_port = get_upstream_port(tb->nhi->pdev); 1409f67cf491SMika Westerberg if (upstream_port) { 1410f67cf491SMika Westerberg int cap; 1411f67cf491SMika Westerberg 1412f67cf491SMika Westerberg cap = pci_find_ext_capability(upstream_port, 1413f67cf491SMika Westerberg PCI_EXT_CAP_ID_VNDR); 1414f67cf491SMika Westerberg if (cap > 0) { 1415f67cf491SMika Westerberg icm->upstream_port = upstream_port; 1416f67cf491SMika Westerberg icm->vnd_cap = cap; 1417f67cf491SMika Westerberg 1418f67cf491SMika Westerberg return true; 1419f67cf491SMika Westerberg } 1420f67cf491SMika Westerberg } 1421f67cf491SMika Westerberg 1422f67cf491SMika Westerberg return false; 1423f67cf491SMika Westerberg } 1424f67cf491SMika Westerberg 14250d53827dSMika Westerberg static int icm_ar_cio_reset(struct tb *tb) 14260d53827dSMika Westerberg { 14270d53827dSMika Westerberg return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x50, BIT(9)); 14280d53827dSMika Westerberg } 14290d53827dSMika Westerberg 1430f67cf491SMika Westerberg static int icm_ar_get_mode(struct tb *tb) 1431f67cf491SMika Westerberg { 1432f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1433e4be8c9bSMika Westerberg int retries = 60; 1434f67cf491SMika Westerberg u32 val; 1435f67cf491SMika Westerberg 1436f67cf491SMika Westerberg do { 1437f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1438f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1439f67cf491SMika Westerberg break; 1440e4be8c9bSMika Westerberg msleep(50); 1441f67cf491SMika Westerberg } while (--retries); 1442f67cf491SMika Westerberg 1443f67cf491SMika Westerberg if (!retries) { 1444f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "ICM firmware not authenticated\n"); 1445f67cf491SMika Westerberg return -ENODEV; 1446f67cf491SMika Westerberg } 1447f67cf491SMika Westerberg 1448f67cf491SMika Westerberg return nhi_mailbox_mode(nhi); 1449f67cf491SMika Westerberg } 1450f67cf491SMika Westerberg 14519aaa3b8bSMika Westerberg static int 14529aaa3b8bSMika Westerberg icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, 14532d8ff0b5SMika Westerberg size_t *nboot_acl, bool *rpm) 14549aaa3b8bSMika Westerberg { 14559aaa3b8bSMika Westerberg struct icm_ar_pkg_driver_ready_response reply; 14569aaa3b8bSMika Westerberg struct icm_pkg_driver_ready request = { 14579aaa3b8bSMika Westerberg .hdr.code = ICM_DRIVER_READY, 14589aaa3b8bSMika Westerberg }; 14599aaa3b8bSMika Westerberg int ret; 14609aaa3b8bSMika Westerberg 14619aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 14629aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 14639aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 14649aaa3b8bSMika Westerberg if (ret) 14659aaa3b8bSMika Westerberg return ret; 14669aaa3b8bSMika Westerberg 14679aaa3b8bSMika Westerberg if (security_level) 14689aaa3b8bSMika Westerberg *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK; 14699aaa3b8bSMika Westerberg if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED)) 14709aaa3b8bSMika Westerberg *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >> 14719aaa3b8bSMika Westerberg ICM_AR_INFO_BOOT_ACL_SHIFT; 14722d8ff0b5SMika Westerberg if (rpm) 14732d8ff0b5SMika Westerberg *rpm = !!(reply.hdr.flags & ICM_AR_FLAGS_RTD3); 14742d8ff0b5SMika Westerberg 14759aaa3b8bSMika Westerberg return 0; 14769aaa3b8bSMika Westerberg } 14779aaa3b8bSMika Westerberg 1478f67cf491SMika Westerberg static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 1479f67cf491SMika Westerberg { 1480f67cf491SMika Westerberg struct icm_ar_pkg_get_route_response reply; 1481f67cf491SMika Westerberg struct icm_ar_pkg_get_route request = { 1482f67cf491SMika Westerberg .hdr = { .code = ICM_GET_ROUTE }, 1483f67cf491SMika Westerberg .link_info = depth << ICM_LINK_INFO_DEPTH_SHIFT | link, 1484f67cf491SMika Westerberg }; 1485f67cf491SMika Westerberg int ret; 1486f67cf491SMika Westerberg 1487f67cf491SMika Westerberg memset(&reply, 0, sizeof(reply)); 1488f67cf491SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 1489f67cf491SMika Westerberg 1, ICM_TIMEOUT); 1490f67cf491SMika Westerberg if (ret) 1491f67cf491SMika Westerberg return ret; 1492f67cf491SMika Westerberg 1493f67cf491SMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 1494f67cf491SMika Westerberg return -EIO; 1495f67cf491SMika Westerberg 1496f67cf491SMika Westerberg *route = get_route(reply.route_hi, reply.route_lo); 1497f67cf491SMika Westerberg return 0; 1498f67cf491SMika Westerberg } 1499f67cf491SMika Westerberg 15009aaa3b8bSMika Westerberg static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) 15019aaa3b8bSMika Westerberg { 15029aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 15039aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 15049aaa3b8bSMika Westerberg .hdr = { .code = ICM_PREBOOT_ACL }, 15059aaa3b8bSMika Westerberg }; 15069aaa3b8bSMika Westerberg int ret, i; 15079aaa3b8bSMika Westerberg 15089aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 15099aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 15109aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 15119aaa3b8bSMika Westerberg if (ret) 15129aaa3b8bSMika Westerberg return ret; 15139aaa3b8bSMika Westerberg 15149aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 15159aaa3b8bSMika Westerberg return -EIO; 15169aaa3b8bSMika Westerberg 15179aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 15189aaa3b8bSMika Westerberg u32 *uuid = (u32 *)&uuids[i]; 15199aaa3b8bSMika Westerberg 15209aaa3b8bSMika Westerberg uuid[0] = reply.acl[i].uuid_lo; 15219aaa3b8bSMika Westerberg uuid[1] = reply.acl[i].uuid_hi; 15229aaa3b8bSMika Westerberg 15239aaa3b8bSMika Westerberg if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) { 15249aaa3b8bSMika Westerberg /* Map empty entries to null UUID */ 15259aaa3b8bSMika Westerberg uuid[0] = 0; 15269aaa3b8bSMika Westerberg uuid[1] = 0; 1527dd010bd7SMika Westerberg } else if (uuid[0] != 0 || uuid[1] != 0) { 15289aaa3b8bSMika Westerberg /* Upper two DWs are always one's */ 15299aaa3b8bSMika Westerberg uuid[2] = 0xffffffff; 15309aaa3b8bSMika Westerberg uuid[3] = 0xffffffff; 15319aaa3b8bSMika Westerberg } 15329aaa3b8bSMika Westerberg } 15339aaa3b8bSMika Westerberg 15349aaa3b8bSMika Westerberg return ret; 15359aaa3b8bSMika Westerberg } 15369aaa3b8bSMika Westerberg 15379aaa3b8bSMika Westerberg static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, 15389aaa3b8bSMika Westerberg size_t nuuids) 15399aaa3b8bSMika Westerberg { 15409aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl_response reply; 15419aaa3b8bSMika Westerberg struct icm_ar_pkg_preboot_acl request = { 15429aaa3b8bSMika Westerberg .hdr = { 15439aaa3b8bSMika Westerberg .code = ICM_PREBOOT_ACL, 15449aaa3b8bSMika Westerberg .flags = ICM_FLAGS_WRITE, 15459aaa3b8bSMika Westerberg }, 15469aaa3b8bSMika Westerberg }; 15479aaa3b8bSMika Westerberg int ret, i; 15489aaa3b8bSMika Westerberg 15499aaa3b8bSMika Westerberg for (i = 0; i < nuuids; i++) { 15509aaa3b8bSMika Westerberg const u32 *uuid = (const u32 *)&uuids[i]; 15519aaa3b8bSMika Westerberg 15529aaa3b8bSMika Westerberg if (uuid_is_null(&uuids[i])) { 15539aaa3b8bSMika Westerberg /* 15549aaa3b8bSMika Westerberg * Map null UUID to the empty (all one) entries 15559aaa3b8bSMika Westerberg * for ICM. 15569aaa3b8bSMika Westerberg */ 15579aaa3b8bSMika Westerberg request.acl[i].uuid_lo = 0xffffffff; 15589aaa3b8bSMika Westerberg request.acl[i].uuid_hi = 0xffffffff; 15599aaa3b8bSMika Westerberg } else { 15609aaa3b8bSMika Westerberg /* Two high DWs need to be set to all one */ 15619aaa3b8bSMika Westerberg if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff) 15629aaa3b8bSMika Westerberg return -EINVAL; 15639aaa3b8bSMika Westerberg 15649aaa3b8bSMika Westerberg request.acl[i].uuid_lo = uuid[0]; 15659aaa3b8bSMika Westerberg request.acl[i].uuid_hi = uuid[1]; 15669aaa3b8bSMika Westerberg } 15679aaa3b8bSMika Westerberg } 15689aaa3b8bSMika Westerberg 15699aaa3b8bSMika Westerberg memset(&reply, 0, sizeof(reply)); 15709aaa3b8bSMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 15719aaa3b8bSMika Westerberg 1, ICM_TIMEOUT); 15729aaa3b8bSMika Westerberg if (ret) 15739aaa3b8bSMika Westerberg return ret; 15749aaa3b8bSMika Westerberg 15759aaa3b8bSMika Westerberg if (reply.hdr.flags & ICM_FLAGS_ERROR) 15769aaa3b8bSMika Westerberg return -EIO; 15779aaa3b8bSMika Westerberg 15789aaa3b8bSMika Westerberg return 0; 15799aaa3b8bSMika Westerberg } 15809aaa3b8bSMika Westerberg 15813cdb9446SMika Westerberg static int 15823cdb9446SMika Westerberg icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, 15833cdb9446SMika Westerberg size_t *nboot_acl, bool *rpm) 15843cdb9446SMika Westerberg { 15853cdb9446SMika Westerberg struct icm_tr_pkg_driver_ready_response reply; 15863cdb9446SMika Westerberg struct icm_pkg_driver_ready request = { 15873cdb9446SMika Westerberg .hdr.code = ICM_DRIVER_READY, 15883cdb9446SMika Westerberg }; 15893cdb9446SMika Westerberg int ret; 15903cdb9446SMika Westerberg 15913cdb9446SMika Westerberg memset(&reply, 0, sizeof(reply)); 15923cdb9446SMika Westerberg ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 15933cdb9446SMika Westerberg 1, 20000); 15943cdb9446SMika Westerberg if (ret) 15953cdb9446SMika Westerberg return ret; 15963cdb9446SMika Westerberg 15973cdb9446SMika Westerberg /* Ice Lake always supports RTD3 */ 15983cdb9446SMika Westerberg if (rpm) 15993cdb9446SMika Westerberg *rpm = true; 16003cdb9446SMika Westerberg 16013cdb9446SMika Westerberg return 0; 16023cdb9446SMika Westerberg } 16033cdb9446SMika Westerberg 16043cdb9446SMika Westerberg static void icm_icl_set_uuid(struct tb *tb) 16053cdb9446SMika Westerberg { 16063cdb9446SMika Westerberg struct tb_nhi *nhi = tb->nhi; 16073cdb9446SMika Westerberg u32 uuid[4]; 16083cdb9446SMika Westerberg 16093cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]); 16103cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]); 16113cdb9446SMika Westerberg uuid[2] = 0xffffffff; 16123cdb9446SMika Westerberg uuid[3] = 0xffffffff; 16133cdb9446SMika Westerberg 16143cdb9446SMika Westerberg tb->root_switch->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL); 16153cdb9446SMika Westerberg } 16163cdb9446SMika Westerberg 16173cdb9446SMika Westerberg static void 16183cdb9446SMika Westerberg icm_icl_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 16193cdb9446SMika Westerberg { 16203cdb9446SMika Westerberg __icm_tr_device_connected(tb, hdr, true); 16213cdb9446SMika Westerberg } 16223cdb9446SMika Westerberg 16233cdb9446SMika Westerberg static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr) 16243cdb9446SMika Westerberg { 16253cdb9446SMika Westerberg const struct icm_icl_event_rtd3_veto *pkg = 16263cdb9446SMika Westerberg (const struct icm_icl_event_rtd3_veto *)hdr; 16273cdb9446SMika Westerberg 16283cdb9446SMika Westerberg tb_dbg(tb, "ICM rtd3 veto=0x%08x\n", pkg->veto_reason); 16293cdb9446SMika Westerberg 16303cdb9446SMika Westerberg if (pkg->veto_reason) 16313cdb9446SMika Westerberg icm_veto_begin(tb); 16323cdb9446SMika Westerberg else 16333cdb9446SMika Westerberg icm_veto_end(tb); 16343cdb9446SMika Westerberg } 16353cdb9446SMika Westerberg 163657d8df68SMika Westerberg static bool icm_tgl_is_supported(struct tb *tb) 163757d8df68SMika Westerberg { 163857d8df68SMika Westerberg /* 163957d8df68SMika Westerberg * If the firmware is not running use software CM. This platform 164057d8df68SMika Westerberg * should fully support both. 164157d8df68SMika Westerberg */ 164257d8df68SMika Westerberg return icm_firmware_running(tb->nhi); 164357d8df68SMika Westerberg } 164457d8df68SMika Westerberg 1645f67cf491SMika Westerberg static void icm_handle_notification(struct work_struct *work) 1646f67cf491SMika Westerberg { 1647f67cf491SMika Westerberg struct icm_notification *n = container_of(work, typeof(*n), work); 1648f67cf491SMika Westerberg struct tb *tb = n->tb; 1649f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1650f67cf491SMika Westerberg 1651f67cf491SMika Westerberg mutex_lock(&tb->lock); 1652f67cf491SMika Westerberg 165386da809dSMika Westerberg /* 165486da809dSMika Westerberg * When the domain is stopped we flush its workqueue but before 165586da809dSMika Westerberg * that the root switch is removed. In that case we should treat 165686da809dSMika Westerberg * the queued events as being canceled. 165786da809dSMika Westerberg */ 165886da809dSMika Westerberg if (tb->root_switch) { 1659f67cf491SMika Westerberg switch (n->pkg->code) { 1660f67cf491SMika Westerberg case ICM_EVENT_DEVICE_CONNECTED: 1661f67cf491SMika Westerberg icm->device_connected(tb, n->pkg); 1662f67cf491SMika Westerberg break; 1663f67cf491SMika Westerberg case ICM_EVENT_DEVICE_DISCONNECTED: 1664f67cf491SMika Westerberg icm->device_disconnected(tb, n->pkg); 1665f67cf491SMika Westerberg break; 1666d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_CONNECTED: 1667d1ff7024SMika Westerberg icm->xdomain_connected(tb, n->pkg); 1668d1ff7024SMika Westerberg break; 1669d1ff7024SMika Westerberg case ICM_EVENT_XDOMAIN_DISCONNECTED: 1670d1ff7024SMika Westerberg icm->xdomain_disconnected(tb, n->pkg); 1671d1ff7024SMika Westerberg break; 16723cdb9446SMika Westerberg case ICM_EVENT_RTD3_VETO: 16733cdb9446SMika Westerberg icm->rtd3_veto(tb, n->pkg); 16743cdb9446SMika Westerberg break; 1675f67cf491SMika Westerberg } 167686da809dSMika Westerberg } 1677f67cf491SMika Westerberg 1678f67cf491SMika Westerberg mutex_unlock(&tb->lock); 1679f67cf491SMika Westerberg 1680f67cf491SMika Westerberg kfree(n->pkg); 1681f67cf491SMika Westerberg kfree(n); 1682f67cf491SMika Westerberg } 1683f67cf491SMika Westerberg 1684f67cf491SMika Westerberg static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, 1685f67cf491SMika Westerberg const void *buf, size_t size) 1686f67cf491SMika Westerberg { 1687f67cf491SMika Westerberg struct icm_notification *n; 1688f67cf491SMika Westerberg 1689f67cf491SMika Westerberg n = kmalloc(sizeof(*n), GFP_KERNEL); 1690f67cf491SMika Westerberg if (!n) 1691f67cf491SMika Westerberg return; 1692f67cf491SMika Westerberg 1693f67cf491SMika Westerberg INIT_WORK(&n->work, icm_handle_notification); 1694f67cf491SMika Westerberg n->pkg = kmemdup(buf, size, GFP_KERNEL); 1695f67cf491SMika Westerberg n->tb = tb; 1696f67cf491SMika Westerberg 1697f67cf491SMika Westerberg queue_work(tb->wq, &n->work); 1698f67cf491SMika Westerberg } 1699f67cf491SMika Westerberg 1700f67cf491SMika Westerberg static int 17019aaa3b8bSMika Westerberg __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, 17022d8ff0b5SMika Westerberg size_t *nboot_acl, bool *rpm) 1703f67cf491SMika Westerberg { 17043080e197SMika Westerberg struct icm *icm = tb_priv(tb); 170544b51bbbSMika Westerberg unsigned int retries = 50; 1706f67cf491SMika Westerberg int ret; 1707f67cf491SMika Westerberg 17082d8ff0b5SMika Westerberg ret = icm->driver_ready(tb, security_level, nboot_acl, rpm); 17093080e197SMika Westerberg if (ret) { 17103080e197SMika Westerberg tb_err(tb, "failed to send driver ready to ICM\n"); 1711f67cf491SMika Westerberg return ret; 17123080e197SMika Westerberg } 1713f67cf491SMika Westerberg 1714f67cf491SMika Westerberg /* 1715f67cf491SMika Westerberg * Hold on here until the switch config space is accessible so 1716f67cf491SMika Westerberg * that we can read root switch config successfully. 1717f67cf491SMika Westerberg */ 1718f67cf491SMika Westerberg do { 1719f67cf491SMika Westerberg struct tb_cfg_result res; 1720f67cf491SMika Westerberg u32 tmp; 1721f67cf491SMika Westerberg 1722f67cf491SMika Westerberg res = tb_cfg_read_raw(tb->ctl, &tmp, 0, 0, TB_CFG_SWITCH, 1723f67cf491SMika Westerberg 0, 1, 100); 1724f67cf491SMika Westerberg if (!res.err) 1725f67cf491SMika Westerberg return 0; 1726f67cf491SMika Westerberg 1727f67cf491SMika Westerberg msleep(50); 1728f67cf491SMika Westerberg } while (--retries); 1729f67cf491SMika Westerberg 173044b51bbbSMika Westerberg tb_err(tb, "failed to read root switch config space, giving up\n"); 1731f67cf491SMika Westerberg return -ETIMEDOUT; 1732f67cf491SMika Westerberg } 1733f67cf491SMika Westerberg 1734f67cf491SMika Westerberg static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) 1735f67cf491SMika Westerberg { 1736f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1737f67cf491SMika Westerberg u32 val; 1738f67cf491SMika Westerberg 1739ea9d7bb7SMika Westerberg if (!icm->upstream_port) 1740ea9d7bb7SMika Westerberg return -ENODEV; 1741ea9d7bb7SMika Westerberg 1742f67cf491SMika Westerberg /* Put ARC to wait for CIO reset event to happen */ 1743f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1744f67cf491SMika Westerberg val |= REG_FW_STS_CIO_RESET_REQ; 1745f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1746f67cf491SMika Westerberg 1747f67cf491SMika Westerberg /* Re-start ARC */ 1748f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1749f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_INVERT; 1750f67cf491SMika Westerberg val |= REG_FW_STS_ICM_EN_CPU; 1751f67cf491SMika Westerberg iowrite32(val, nhi->iobase + REG_FW_STS); 1752f67cf491SMika Westerberg 1753f67cf491SMika Westerberg /* Trigger CIO reset now */ 17540d53827dSMika Westerberg return icm->cio_reset(tb); 1755f67cf491SMika Westerberg } 1756f67cf491SMika Westerberg 1757f67cf491SMika Westerberg static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) 1758f67cf491SMika Westerberg { 1759f67cf491SMika Westerberg unsigned int retries = 10; 1760f67cf491SMika Westerberg int ret; 1761f67cf491SMika Westerberg u32 val; 1762f67cf491SMika Westerberg 1763f67cf491SMika Westerberg /* Check if the ICM firmware is already running */ 1764354a7a77SMika Westerberg if (icm_firmware_running(nhi)) 1765f67cf491SMika Westerberg return 0; 1766f67cf491SMika Westerberg 176762efe699SMika Westerberg dev_dbg(&nhi->pdev->dev, "starting ICM firmware\n"); 1768f67cf491SMika Westerberg 1769f67cf491SMika Westerberg ret = icm_firmware_reset(tb, nhi); 1770f67cf491SMika Westerberg if (ret) 1771f67cf491SMika Westerberg return ret; 1772f67cf491SMika Westerberg 1773f67cf491SMika Westerberg /* Wait until the ICM firmware tells us it is up and running */ 1774f67cf491SMika Westerberg do { 1775f67cf491SMika Westerberg /* Check that the ICM firmware is running */ 1776f67cf491SMika Westerberg val = ioread32(nhi->iobase + REG_FW_STS); 1777f67cf491SMika Westerberg if (val & REG_FW_STS_NVM_AUTH_DONE) 1778f67cf491SMika Westerberg return 0; 1779f67cf491SMika Westerberg 1780f67cf491SMika Westerberg msleep(300); 1781f67cf491SMika Westerberg } while (--retries); 1782f67cf491SMika Westerberg 1783f67cf491SMika Westerberg return -ETIMEDOUT; 1784f67cf491SMika Westerberg } 1785f67cf491SMika Westerberg 1786f67cf491SMika Westerberg static int icm_reset_phy_port(struct tb *tb, int phy_port) 1787f67cf491SMika Westerberg { 1788f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1789f67cf491SMika Westerberg u32 state0, state1; 1790f67cf491SMika Westerberg int port0, port1; 1791f67cf491SMika Westerberg u32 val0, val1; 1792f67cf491SMika Westerberg int ret; 1793f67cf491SMika Westerberg 1794f67cf491SMika Westerberg if (!icm->upstream_port) 1795f67cf491SMika Westerberg return 0; 1796f67cf491SMika Westerberg 1797f67cf491SMika Westerberg if (phy_port) { 1798f67cf491SMika Westerberg port0 = 3; 1799f67cf491SMika Westerberg port1 = 4; 1800f67cf491SMika Westerberg } else { 1801f67cf491SMika Westerberg port0 = 1; 1802f67cf491SMika Westerberg port1 = 2; 1803f67cf491SMika Westerberg } 1804f67cf491SMika Westerberg 1805f67cf491SMika Westerberg /* 1806f67cf491SMika Westerberg * Read link status of both null ports belonging to a single 1807f67cf491SMika Westerberg * physical port. 1808f67cf491SMika Westerberg */ 1809f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1810f67cf491SMika Westerberg if (ret) 1811f67cf491SMika Westerberg return ret; 1812f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1813f67cf491SMika Westerberg if (ret) 1814f67cf491SMika Westerberg return ret; 1815f67cf491SMika Westerberg 1816f67cf491SMika Westerberg state0 = val0 & PHY_PORT_CS1_LINK_STATE_MASK; 1817f67cf491SMika Westerberg state0 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1818f67cf491SMika Westerberg state1 = val1 & PHY_PORT_CS1_LINK_STATE_MASK; 1819f67cf491SMika Westerberg state1 >>= PHY_PORT_CS1_LINK_STATE_SHIFT; 1820f67cf491SMika Westerberg 1821f67cf491SMika Westerberg /* If they are both up we need to reset them now */ 1822f67cf491SMika Westerberg if (state0 != TB_PORT_UP || state1 != TB_PORT_UP) 1823f67cf491SMika Westerberg return 0; 1824f67cf491SMika Westerberg 1825f67cf491SMika Westerberg val0 |= PHY_PORT_CS1_LINK_DISABLE; 1826f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1827f67cf491SMika Westerberg if (ret) 1828f67cf491SMika Westerberg return ret; 1829f67cf491SMika Westerberg 1830f67cf491SMika Westerberg val1 |= PHY_PORT_CS1_LINK_DISABLE; 1831f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1832f67cf491SMika Westerberg if (ret) 1833f67cf491SMika Westerberg return ret; 1834f67cf491SMika Westerberg 1835f67cf491SMika Westerberg /* Wait a bit and then re-enable both ports */ 1836f67cf491SMika Westerberg usleep_range(10, 100); 1837f67cf491SMika Westerberg 1838f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, &val0); 1839f67cf491SMika Westerberg if (ret) 1840f67cf491SMika Westerberg return ret; 1841f67cf491SMika Westerberg ret = pcie2cio_read(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, &val1); 1842f67cf491SMika Westerberg if (ret) 1843f67cf491SMika Westerberg return ret; 1844f67cf491SMika Westerberg 1845f67cf491SMika Westerberg val0 &= ~PHY_PORT_CS1_LINK_DISABLE; 1846f67cf491SMika Westerberg ret = pcie2cio_write(icm, TB_CFG_PORT, port0, PHY_PORT_CS1, val0); 1847f67cf491SMika Westerberg if (ret) 1848f67cf491SMika Westerberg return ret; 1849f67cf491SMika Westerberg 1850f67cf491SMika Westerberg val1 &= ~PHY_PORT_CS1_LINK_DISABLE; 1851f67cf491SMika Westerberg return pcie2cio_write(icm, TB_CFG_PORT, port1, PHY_PORT_CS1, val1); 1852f67cf491SMika Westerberg } 1853f67cf491SMika Westerberg 1854f67cf491SMika Westerberg static int icm_firmware_init(struct tb *tb) 1855f67cf491SMika Westerberg { 1856f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 1857f67cf491SMika Westerberg struct tb_nhi *nhi = tb->nhi; 1858f67cf491SMika Westerberg int ret; 1859f67cf491SMika Westerberg 1860f67cf491SMika Westerberg ret = icm_firmware_start(tb, nhi); 1861f67cf491SMika Westerberg if (ret) { 1862f67cf491SMika Westerberg dev_err(&nhi->pdev->dev, "could not start ICM firmware\n"); 1863f67cf491SMika Westerberg return ret; 1864f67cf491SMika Westerberg } 1865f67cf491SMika Westerberg 1866f67cf491SMika Westerberg if (icm->get_mode) { 1867f67cf491SMika Westerberg ret = icm->get_mode(tb); 1868f67cf491SMika Westerberg 1869f67cf491SMika Westerberg switch (ret) { 1870e6b245ccSMika Westerberg case NHI_FW_SAFE_MODE: 1871e6b245ccSMika Westerberg icm->safe_mode = true; 1872e6b245ccSMika Westerberg break; 1873e6b245ccSMika Westerberg 1874f67cf491SMika Westerberg case NHI_FW_CM_MODE: 1875f67cf491SMika Westerberg /* Ask ICM to accept all Thunderbolt devices */ 1876f67cf491SMika Westerberg nhi_mailbox_cmd(nhi, NHI_MAILBOX_ALLOW_ALL_DEVS, 0); 1877f67cf491SMika Westerberg break; 1878f67cf491SMika Westerberg 1879f67cf491SMika Westerberg default: 1880e4be8c9bSMika Westerberg if (ret < 0) 1881e4be8c9bSMika Westerberg return ret; 1882e4be8c9bSMika Westerberg 1883f67cf491SMika Westerberg tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); 1884f67cf491SMika Westerberg return -ENODEV; 1885f67cf491SMika Westerberg } 1886f67cf491SMika Westerberg } 1887f67cf491SMika Westerberg 1888f67cf491SMika Westerberg /* 1889f67cf491SMika Westerberg * Reset both physical ports if there is anything connected to 1890f67cf491SMika Westerberg * them already. 1891f67cf491SMika Westerberg */ 1892f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 0); 1893f67cf491SMika Westerberg if (ret) 1894f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port0\n"); 1895f67cf491SMika Westerberg ret = icm_reset_phy_port(tb, 1); 1896f67cf491SMika Westerberg if (ret) 1897f67cf491SMika Westerberg dev_warn(&nhi->pdev->dev, "failed to reset links on port1\n"); 1898f67cf491SMika Westerberg 1899f67cf491SMika Westerberg return 0; 1900f67cf491SMika Westerberg } 1901f67cf491SMika Westerberg 1902f67cf491SMika Westerberg static int icm_driver_ready(struct tb *tb) 1903f67cf491SMika Westerberg { 1904e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 1905f67cf491SMika Westerberg int ret; 1906f67cf491SMika Westerberg 1907f67cf491SMika Westerberg ret = icm_firmware_init(tb); 1908f67cf491SMika Westerberg if (ret) 1909f67cf491SMika Westerberg return ret; 1910f67cf491SMika Westerberg 1911e6b245ccSMika Westerberg if (icm->safe_mode) { 1912e6b245ccSMika Westerberg tb_info(tb, "Thunderbolt host controller is in safe mode.\n"); 1913e6b245ccSMika Westerberg tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n"); 1914e6b245ccSMika Westerberg tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n"); 1915e6b245ccSMika Westerberg return 0; 1916e6b245ccSMika Westerberg } 1917e6b245ccSMika Westerberg 19182d8ff0b5SMika Westerberg ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl, 19192d8ff0b5SMika Westerberg &icm->rpm); 19209aaa3b8bSMika Westerberg if (ret) 19219aaa3b8bSMika Westerberg return ret; 19229aaa3b8bSMika Westerberg 19239aaa3b8bSMika Westerberg /* 19249aaa3b8bSMika Westerberg * Make sure the number of supported preboot ACL matches what we 19259aaa3b8bSMika Westerberg * expect or disable the whole feature. 19269aaa3b8bSMika Westerberg */ 19279aaa3b8bSMika Westerberg if (tb->nboot_acl > icm->max_boot_acl) 19289aaa3b8bSMika Westerberg tb->nboot_acl = 0; 19299aaa3b8bSMika Westerberg 19309aaa3b8bSMika Westerberg return 0; 1931f67cf491SMika Westerberg } 1932f67cf491SMika Westerberg 1933f67cf491SMika Westerberg static int icm_suspend(struct tb *tb) 1934f67cf491SMika Westerberg { 1935d04522faSMika Westerberg struct icm *icm = tb_priv(tb); 1936a684c5b1SRafael J. Wysocki 1937d04522faSMika Westerberg if (icm->save_devices) 1938d04522faSMika Westerberg icm->save_devices(tb); 1939a684c5b1SRafael J. Wysocki 1940d04522faSMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 1941a684c5b1SRafael J. Wysocki return 0; 1942f67cf491SMika Westerberg } 1943f67cf491SMika Westerberg 1944f67cf491SMika Westerberg /* 1945f67cf491SMika Westerberg * Mark all switches (except root switch) below this one unplugged. ICM 1946f67cf491SMika Westerberg * firmware will send us an updated list of switches after we have send 1947f67cf491SMika Westerberg * it driver ready command. If a switch is not in that list it will be 1948f67cf491SMika Westerberg * removed when we perform rescan. 1949f67cf491SMika Westerberg */ 1950f67cf491SMika Westerberg static void icm_unplug_children(struct tb_switch *sw) 1951f67cf491SMika Westerberg { 1952b433d010SMika Westerberg struct tb_port *port; 1953f67cf491SMika Westerberg 1954f67cf491SMika Westerberg if (tb_route(sw)) 1955f67cf491SMika Westerberg sw->is_unplugged = true; 1956f67cf491SMika Westerberg 1957b433d010SMika Westerberg tb_switch_for_each_port(sw, port) { 1958dfe40ca4SMika Westerberg if (port->xdomain) 1959d1ff7024SMika Westerberg port->xdomain->is_unplugged = true; 1960dfe40ca4SMika Westerberg else if (tb_port_has_remote(port)) 1961f67cf491SMika Westerberg icm_unplug_children(port->remote->sw); 1962f67cf491SMika Westerberg } 1963f67cf491SMika Westerberg } 1964f67cf491SMika Westerberg 19654f7c2e0dSMika Westerberg static int complete_rpm(struct device *dev, void *data) 19664f7c2e0dSMika Westerberg { 19674f7c2e0dSMika Westerberg struct tb_switch *sw = tb_to_switch(dev); 19684f7c2e0dSMika Westerberg 19694f7c2e0dSMika Westerberg if (sw) 19704f7c2e0dSMika Westerberg complete(&sw->rpm_complete); 19714f7c2e0dSMika Westerberg return 0; 19724f7c2e0dSMika Westerberg } 19734f7c2e0dSMika Westerberg 19744f7c2e0dSMika Westerberg static void remove_unplugged_switch(struct tb_switch *sw) 19754f7c2e0dSMika Westerberg { 19764f7c2e0dSMika Westerberg pm_runtime_get_sync(sw->dev.parent); 19774f7c2e0dSMika Westerberg 19784f7c2e0dSMika Westerberg /* 19794f7c2e0dSMika Westerberg * Signal this and switches below for rpm_complete because 19804f7c2e0dSMika Westerberg * tb_switch_remove() calls pm_runtime_get_sync() that then waits 19814f7c2e0dSMika Westerberg * for it. 19824f7c2e0dSMika Westerberg */ 19834f7c2e0dSMika Westerberg complete_rpm(&sw->dev, NULL); 19844f7c2e0dSMika Westerberg bus_for_each_dev(&tb_bus_type, &sw->dev, NULL, complete_rpm); 19854f7c2e0dSMika Westerberg tb_switch_remove(sw); 19864f7c2e0dSMika Westerberg 19874f7c2e0dSMika Westerberg pm_runtime_mark_last_busy(sw->dev.parent); 19884f7c2e0dSMika Westerberg pm_runtime_put_autosuspend(sw->dev.parent); 19894f7c2e0dSMika Westerberg } 19904f7c2e0dSMika Westerberg 1991f67cf491SMika Westerberg static void icm_free_unplugged_children(struct tb_switch *sw) 1992f67cf491SMika Westerberg { 1993b433d010SMika Westerberg struct tb_port *port; 1994f67cf491SMika Westerberg 1995b433d010SMika Westerberg tb_switch_for_each_port(sw, port) { 1996d1ff7024SMika Westerberg if (port->xdomain && port->xdomain->is_unplugged) { 1997d1ff7024SMika Westerberg tb_xdomain_remove(port->xdomain); 1998d1ff7024SMika Westerberg port->xdomain = NULL; 1999dfe40ca4SMika Westerberg } else if (tb_port_has_remote(port)) { 2000f67cf491SMika Westerberg if (port->remote->sw->is_unplugged) { 20014f7c2e0dSMika Westerberg remove_unplugged_switch(port->remote->sw); 2002f67cf491SMika Westerberg port->remote = NULL; 2003f67cf491SMika Westerberg } else { 2004f67cf491SMika Westerberg icm_free_unplugged_children(port->remote->sw); 2005f67cf491SMika Westerberg } 2006f67cf491SMika Westerberg } 2007f67cf491SMika Westerberg } 2008dfe40ca4SMika Westerberg } 2009f67cf491SMika Westerberg 2010f67cf491SMika Westerberg static void icm_rescan_work(struct work_struct *work) 2011f67cf491SMika Westerberg { 2012f67cf491SMika Westerberg struct icm *icm = container_of(work, struct icm, rescan_work.work); 2013f67cf491SMika Westerberg struct tb *tb = icm_to_tb(icm); 2014f67cf491SMika Westerberg 2015f67cf491SMika Westerberg mutex_lock(&tb->lock); 2016f67cf491SMika Westerberg if (tb->root_switch) 2017f67cf491SMika Westerberg icm_free_unplugged_children(tb->root_switch); 2018f67cf491SMika Westerberg mutex_unlock(&tb->lock); 2019f67cf491SMika Westerberg } 2020f67cf491SMika Westerberg 2021f67cf491SMika Westerberg static void icm_complete(struct tb *tb) 2022f67cf491SMika Westerberg { 2023f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 2024f67cf491SMika Westerberg 2025f67cf491SMika Westerberg if (tb->nhi->going_away) 2026f67cf491SMika Westerberg return; 2027f67cf491SMika Westerberg 20283cdb9446SMika Westerberg /* 20293cdb9446SMika Westerberg * If RTD3 was vetoed before we entered system suspend allow it 20303cdb9446SMika Westerberg * again now before driver ready is sent. Firmware sends a new RTD3 20313cdb9446SMika Westerberg * veto if it is still the case after we have sent it driver ready 20323cdb9446SMika Westerberg * command. 20333cdb9446SMika Westerberg */ 20343cdb9446SMika Westerberg icm_veto_end(tb); 2035f67cf491SMika Westerberg icm_unplug_children(tb->root_switch); 2036f67cf491SMika Westerberg 2037f67cf491SMika Westerberg /* 2038f67cf491SMika Westerberg * Now all existing children should be resumed, start events 2039f67cf491SMika Westerberg * from ICM to get updated status. 2040f67cf491SMika Westerberg */ 20412d8ff0b5SMika Westerberg __icm_driver_ready(tb, NULL, NULL, NULL); 2042f67cf491SMika Westerberg 2043f67cf491SMika Westerberg /* 2044f67cf491SMika Westerberg * We do not get notifications of devices that have been 2045f67cf491SMika Westerberg * unplugged during suspend so schedule rescan to clean them up 2046f67cf491SMika Westerberg * if any. 2047f67cf491SMika Westerberg */ 2048f67cf491SMika Westerberg queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500)); 2049f67cf491SMika Westerberg } 2050f67cf491SMika Westerberg 20512d8ff0b5SMika Westerberg static int icm_runtime_suspend(struct tb *tb) 20522d8ff0b5SMika Westerberg { 20532d8ff0b5SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 20542d8ff0b5SMika Westerberg return 0; 20552d8ff0b5SMika Westerberg } 20562d8ff0b5SMika Westerberg 20574f7c2e0dSMika Westerberg static int icm_runtime_suspend_switch(struct tb_switch *sw) 20584f7c2e0dSMika Westerberg { 20594f7c2e0dSMika Westerberg if (tb_route(sw)) 20604f7c2e0dSMika Westerberg reinit_completion(&sw->rpm_complete); 20614f7c2e0dSMika Westerberg return 0; 20624f7c2e0dSMika Westerberg } 20634f7c2e0dSMika Westerberg 20644f7c2e0dSMika Westerberg static int icm_runtime_resume_switch(struct tb_switch *sw) 20654f7c2e0dSMika Westerberg { 20664f7c2e0dSMika Westerberg if (tb_route(sw)) { 20674f7c2e0dSMika Westerberg if (!wait_for_completion_timeout(&sw->rpm_complete, 20684f7c2e0dSMika Westerberg msecs_to_jiffies(500))) { 20694f7c2e0dSMika Westerberg dev_dbg(&sw->dev, "runtime resuming timed out\n"); 20704f7c2e0dSMika Westerberg } 20714f7c2e0dSMika Westerberg } 20724f7c2e0dSMika Westerberg return 0; 20734f7c2e0dSMika Westerberg } 20744f7c2e0dSMika Westerberg 20752d8ff0b5SMika Westerberg static int icm_runtime_resume(struct tb *tb) 20762d8ff0b5SMika Westerberg { 20772d8ff0b5SMika Westerberg /* 20782d8ff0b5SMika Westerberg * We can reuse the same resume functionality than with system 20792d8ff0b5SMika Westerberg * suspend. 20802d8ff0b5SMika Westerberg */ 20812d8ff0b5SMika Westerberg icm_complete(tb); 20822d8ff0b5SMika Westerberg return 0; 20832d8ff0b5SMika Westerberg } 20842d8ff0b5SMika Westerberg 2085f67cf491SMika Westerberg static int icm_start(struct tb *tb) 2086f67cf491SMika Westerberg { 2087e6b245ccSMika Westerberg struct icm *icm = tb_priv(tb); 2088f67cf491SMika Westerberg int ret; 2089f67cf491SMika Westerberg 2090e6b245ccSMika Westerberg if (icm->safe_mode) 2091e6b245ccSMika Westerberg tb->root_switch = tb_switch_alloc_safe_mode(tb, &tb->dev, 0); 2092e6b245ccSMika Westerberg else 2093f67cf491SMika Westerberg tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); 2094444ac384SMika Westerberg if (IS_ERR(tb->root_switch)) 2095444ac384SMika Westerberg return PTR_ERR(tb->root_switch); 2096f67cf491SMika Westerberg 2097f437c24bSMika Westerberg tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm; 20982d8ff0b5SMika Westerberg tb->root_switch->rpm = icm->rpm; 2099e6b245ccSMika Westerberg 21003cdb9446SMika Westerberg if (icm->set_uuid) 21013cdb9446SMika Westerberg icm->set_uuid(tb); 21023cdb9446SMika Westerberg 2103f67cf491SMika Westerberg ret = tb_switch_add(tb->root_switch); 2104d1ff7024SMika Westerberg if (ret) { 2105f67cf491SMika Westerberg tb_switch_put(tb->root_switch); 2106d1ff7024SMika Westerberg tb->root_switch = NULL; 2107d1ff7024SMika Westerberg } 2108f67cf491SMika Westerberg 2109f67cf491SMika Westerberg return ret; 2110f67cf491SMika Westerberg } 2111f67cf491SMika Westerberg 2112f67cf491SMika Westerberg static void icm_stop(struct tb *tb) 2113f67cf491SMika Westerberg { 2114f67cf491SMika Westerberg struct icm *icm = tb_priv(tb); 2115f67cf491SMika Westerberg 2116f67cf491SMika Westerberg cancel_delayed_work(&icm->rescan_work); 2117f67cf491SMika Westerberg tb_switch_remove(tb->root_switch); 2118f67cf491SMika Westerberg tb->root_switch = NULL; 2119f67cf491SMika Westerberg nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0); 2120f67cf491SMika Westerberg } 2121f67cf491SMika Westerberg 2122e6b245ccSMika Westerberg static int icm_disconnect_pcie_paths(struct tb *tb) 2123e6b245ccSMika Westerberg { 2124e6b245ccSMika Westerberg return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); 2125e6b245ccSMika Westerberg } 2126e6b245ccSMika Westerberg 21279aaa3b8bSMika Westerberg /* Falcon Ridge */ 2128f67cf491SMika Westerberg static const struct tb_cm_ops icm_fr_ops = { 2129f67cf491SMika Westerberg .driver_ready = icm_driver_ready, 2130f67cf491SMika Westerberg .start = icm_start, 2131f67cf491SMika Westerberg .stop = icm_stop, 2132f67cf491SMika Westerberg .suspend = icm_suspend, 2133f67cf491SMika Westerberg .complete = icm_complete, 2134f67cf491SMika Westerberg .handle_event = icm_handle_event, 2135f67cf491SMika Westerberg .approve_switch = icm_fr_approve_switch, 2136f67cf491SMika Westerberg .add_switch_key = icm_fr_add_switch_key, 2137f67cf491SMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 2138e6b245ccSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 2139d1ff7024SMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 2140d1ff7024SMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 2141f67cf491SMika Westerberg }; 2142f67cf491SMika Westerberg 21439aaa3b8bSMika Westerberg /* Alpine Ridge */ 21449aaa3b8bSMika Westerberg static const struct tb_cm_ops icm_ar_ops = { 21459aaa3b8bSMika Westerberg .driver_ready = icm_driver_ready, 21469aaa3b8bSMika Westerberg .start = icm_start, 21479aaa3b8bSMika Westerberg .stop = icm_stop, 21489aaa3b8bSMika Westerberg .suspend = icm_suspend, 21499aaa3b8bSMika Westerberg .complete = icm_complete, 21502d8ff0b5SMika Westerberg .runtime_suspend = icm_runtime_suspend, 21512d8ff0b5SMika Westerberg .runtime_resume = icm_runtime_resume, 21524f7c2e0dSMika Westerberg .runtime_suspend_switch = icm_runtime_suspend_switch, 21534f7c2e0dSMika Westerberg .runtime_resume_switch = icm_runtime_resume_switch, 21549aaa3b8bSMika Westerberg .handle_event = icm_handle_event, 21559aaa3b8bSMika Westerberg .get_boot_acl = icm_ar_get_boot_acl, 21569aaa3b8bSMika Westerberg .set_boot_acl = icm_ar_set_boot_acl, 21579aaa3b8bSMika Westerberg .approve_switch = icm_fr_approve_switch, 21589aaa3b8bSMika Westerberg .add_switch_key = icm_fr_add_switch_key, 21599aaa3b8bSMika Westerberg .challenge_switch_key = icm_fr_challenge_switch_key, 21609aaa3b8bSMika Westerberg .disconnect_pcie_paths = icm_disconnect_pcie_paths, 21619aaa3b8bSMika Westerberg .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 21629aaa3b8bSMika Westerberg .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 21639aaa3b8bSMika Westerberg }; 21649aaa3b8bSMika Westerberg 21654bac471dSRadion Mirchevsky /* Titan Ridge */ 21664bac471dSRadion Mirchevsky static const struct tb_cm_ops icm_tr_ops = { 21674bac471dSRadion Mirchevsky .driver_ready = icm_driver_ready, 21684bac471dSRadion Mirchevsky .start = icm_start, 21694bac471dSRadion Mirchevsky .stop = icm_stop, 21704bac471dSRadion Mirchevsky .suspend = icm_suspend, 21714bac471dSRadion Mirchevsky .complete = icm_complete, 21722d8ff0b5SMika Westerberg .runtime_suspend = icm_runtime_suspend, 21732d8ff0b5SMika Westerberg .runtime_resume = icm_runtime_resume, 21744f7c2e0dSMika Westerberg .runtime_suspend_switch = icm_runtime_suspend_switch, 21754f7c2e0dSMika Westerberg .runtime_resume_switch = icm_runtime_resume_switch, 21764bac471dSRadion Mirchevsky .handle_event = icm_handle_event, 21774bac471dSRadion Mirchevsky .get_boot_acl = icm_ar_get_boot_acl, 21784bac471dSRadion Mirchevsky .set_boot_acl = icm_ar_set_boot_acl, 21794bac471dSRadion Mirchevsky .approve_switch = icm_tr_approve_switch, 21804bac471dSRadion Mirchevsky .add_switch_key = icm_tr_add_switch_key, 21814bac471dSRadion Mirchevsky .challenge_switch_key = icm_tr_challenge_switch_key, 21824bac471dSRadion Mirchevsky .disconnect_pcie_paths = icm_disconnect_pcie_paths, 21834bac471dSRadion Mirchevsky .approve_xdomain_paths = icm_tr_approve_xdomain_paths, 21844bac471dSRadion Mirchevsky .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, 21854bac471dSRadion Mirchevsky }; 21864bac471dSRadion Mirchevsky 21873cdb9446SMika Westerberg /* Ice Lake */ 21883cdb9446SMika Westerberg static const struct tb_cm_ops icm_icl_ops = { 21893cdb9446SMika Westerberg .driver_ready = icm_driver_ready, 21903cdb9446SMika Westerberg .start = icm_start, 21913cdb9446SMika Westerberg .stop = icm_stop, 21923cdb9446SMika Westerberg .complete = icm_complete, 21933cdb9446SMika Westerberg .runtime_suspend = icm_runtime_suspend, 21943cdb9446SMika Westerberg .runtime_resume = icm_runtime_resume, 21953cdb9446SMika Westerberg .handle_event = icm_handle_event, 21963cdb9446SMika Westerberg .approve_xdomain_paths = icm_tr_approve_xdomain_paths, 21973cdb9446SMika Westerberg .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, 21983cdb9446SMika Westerberg }; 21993cdb9446SMika Westerberg 2200f67cf491SMika Westerberg struct tb *icm_probe(struct tb_nhi *nhi) 2201f67cf491SMika Westerberg { 2202f67cf491SMika Westerberg struct icm *icm; 2203f67cf491SMika Westerberg struct tb *tb; 2204f67cf491SMika Westerberg 2205f67cf491SMika Westerberg tb = tb_domain_alloc(nhi, sizeof(struct icm)); 2206f67cf491SMika Westerberg if (!tb) 2207f67cf491SMika Westerberg return NULL; 2208f67cf491SMika Westerberg 2209f67cf491SMika Westerberg icm = tb_priv(tb); 2210f67cf491SMika Westerberg INIT_DELAYED_WORK(&icm->rescan_work, icm_rescan_work); 2211f67cf491SMika Westerberg mutex_init(&icm->request_lock); 2212f67cf491SMika Westerberg 2213f67cf491SMika Westerberg switch (nhi->pdev->device) { 2214f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: 2215f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: 2216f437c24bSMika Westerberg icm->can_upgrade_nvm = true; 2217f67cf491SMika Westerberg icm->is_supported = icm_fr_is_supported; 2218f67cf491SMika Westerberg icm->get_route = icm_fr_get_route; 2219d04522faSMika Westerberg icm->save_devices = icm_fr_save_devices; 22203080e197SMika Westerberg icm->driver_ready = icm_fr_driver_ready; 2221f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 2222f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 2223d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 2224d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 2225f67cf491SMika Westerberg tb->cm_ops = &icm_fr_ops; 2226f67cf491SMika Westerberg break; 2227f67cf491SMika Westerberg 2228f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI: 2229f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI: 2230f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: 2231f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: 2232f67cf491SMika Westerberg case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: 22339aaa3b8bSMika Westerberg icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 2234f437c24bSMika Westerberg /* 2235f437c24bSMika Westerberg * NVM upgrade has not been tested on Apple systems and 2236f437c24bSMika Westerberg * they don't provide images publicly either. To be on 2237f437c24bSMika Westerberg * the safe side prevent root switch NVM upgrade on Macs 2238f437c24bSMika Westerberg * for now. 2239f437c24bSMika Westerberg */ 2240f437c24bSMika Westerberg icm->can_upgrade_nvm = !x86_apple_machine; 2241f67cf491SMika Westerberg icm->is_supported = icm_ar_is_supported; 22420d53827dSMika Westerberg icm->cio_reset = icm_ar_cio_reset; 2243f67cf491SMika Westerberg icm->get_mode = icm_ar_get_mode; 2244f67cf491SMika Westerberg icm->get_route = icm_ar_get_route; 2245d04522faSMika Westerberg icm->save_devices = icm_fr_save_devices; 22469aaa3b8bSMika Westerberg icm->driver_ready = icm_ar_driver_ready; 2247f67cf491SMika Westerberg icm->device_connected = icm_fr_device_connected; 2248f67cf491SMika Westerberg icm->device_disconnected = icm_fr_device_disconnected; 2249d1ff7024SMika Westerberg icm->xdomain_connected = icm_fr_xdomain_connected; 2250d1ff7024SMika Westerberg icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 22519aaa3b8bSMika Westerberg tb->cm_ops = &icm_ar_ops; 2252f67cf491SMika Westerberg break; 22534bac471dSRadion Mirchevsky 22544bac471dSRadion Mirchevsky case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI: 22554bac471dSRadion Mirchevsky case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI: 22564bac471dSRadion Mirchevsky icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 2257f437c24bSMika Westerberg icm->can_upgrade_nvm = !x86_apple_machine; 22584bac471dSRadion Mirchevsky icm->is_supported = icm_ar_is_supported; 22590d53827dSMika Westerberg icm->cio_reset = icm_tr_cio_reset; 22604bac471dSRadion Mirchevsky icm->get_mode = icm_ar_get_mode; 22614bac471dSRadion Mirchevsky icm->driver_ready = icm_tr_driver_ready; 22624bac471dSRadion Mirchevsky icm->device_connected = icm_tr_device_connected; 22634bac471dSRadion Mirchevsky icm->device_disconnected = icm_tr_device_disconnected; 22644bac471dSRadion Mirchevsky icm->xdomain_connected = icm_tr_xdomain_connected; 22654bac471dSRadion Mirchevsky icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 22664bac471dSRadion Mirchevsky tb->cm_ops = &icm_tr_ops; 22674bac471dSRadion Mirchevsky break; 22683cdb9446SMika Westerberg 22693cdb9446SMika Westerberg case PCI_DEVICE_ID_INTEL_ICL_NHI0: 22703cdb9446SMika Westerberg case PCI_DEVICE_ID_INTEL_ICL_NHI1: 2271354a7a77SMika Westerberg icm->is_supported = icm_fr_is_supported; 22723cdb9446SMika Westerberg icm->driver_ready = icm_icl_driver_ready; 22733cdb9446SMika Westerberg icm->set_uuid = icm_icl_set_uuid; 22743cdb9446SMika Westerberg icm->device_connected = icm_icl_device_connected; 22753cdb9446SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 22763cdb9446SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 22773cdb9446SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 22783cdb9446SMika Westerberg icm->rtd3_veto = icm_icl_rtd3_veto; 22793cdb9446SMika Westerberg tb->cm_ops = &icm_icl_ops; 22803cdb9446SMika Westerberg break; 228157d8df68SMika Westerberg 228257d8df68SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_NHI0: 228357d8df68SMika Westerberg case PCI_DEVICE_ID_INTEL_TGL_NHI1: 228457d8df68SMika Westerberg icm->is_supported = icm_tgl_is_supported; 228557d8df68SMika Westerberg icm->driver_ready = icm_icl_driver_ready; 228657d8df68SMika Westerberg icm->set_uuid = icm_icl_set_uuid; 228757d8df68SMika Westerberg icm->device_connected = icm_icl_device_connected; 228857d8df68SMika Westerberg icm->device_disconnected = icm_tr_device_disconnected; 228957d8df68SMika Westerberg icm->xdomain_connected = icm_tr_xdomain_connected; 229057d8df68SMika Westerberg icm->xdomain_disconnected = icm_tr_xdomain_disconnected; 229157d8df68SMika Westerberg icm->rtd3_veto = icm_icl_rtd3_veto; 229257d8df68SMika Westerberg tb->cm_ops = &icm_icl_ops; 229357d8df68SMika Westerberg break; 2294f67cf491SMika Westerberg } 2295f67cf491SMika Westerberg 2296f67cf491SMika Westerberg if (!icm->is_supported || !icm->is_supported(tb)) { 2297f67cf491SMika Westerberg dev_dbg(&nhi->pdev->dev, "ICM not supported on this controller\n"); 2298f67cf491SMika Westerberg tb_domain_put(tb); 2299f67cf491SMika Westerberg return NULL; 2300f67cf491SMika Westerberg } 2301f67cf491SMika Westerberg 2302f67cf491SMika Westerberg return tb; 2303f67cf491SMika Westerberg } 2304