13cdb9446SMika Westerberg // SPDX-License-Identifier: GPL-2.0 23cdb9446SMika Westerberg /* 33cdb9446SMika Westerberg * NHI specific operations 43cdb9446SMika Westerberg * 53cdb9446SMika Westerberg * Copyright (C) 2019, Intel Corporation 63cdb9446SMika Westerberg * Author: Mika Westerberg <mika.westerberg@linux.intel.com> 73cdb9446SMika Westerberg */ 83cdb9446SMika Westerberg 93cdb9446SMika Westerberg #include <linux/delay.h> 103cdb9446SMika Westerberg #include <linux/suspend.h> 113cdb9446SMika Westerberg 123cdb9446SMika Westerberg #include "nhi.h" 133cdb9446SMika Westerberg #include "nhi_regs.h" 143cdb9446SMika Westerberg #include "tb.h" 153cdb9446SMika Westerberg 163cdb9446SMika Westerberg /* Ice Lake specific NHI operations */ 173cdb9446SMika Westerberg 183cdb9446SMika Westerberg #define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */ 193cdb9446SMika Westerberg 203cdb9446SMika Westerberg static int check_for_device(struct device *dev, void *data) 213cdb9446SMika Westerberg { 223cdb9446SMika Westerberg return tb_is_switch(dev); 233cdb9446SMika Westerberg } 243cdb9446SMika Westerberg 253cdb9446SMika Westerberg static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) 263cdb9446SMika Westerberg { 273cdb9446SMika Westerberg struct tb *tb = pci_get_drvdata(nhi->pdev); 283cdb9446SMika Westerberg int ret; 293cdb9446SMika Westerberg 303cdb9446SMika Westerberg ret = device_for_each_child(&tb->root_switch->dev, NULL, 313cdb9446SMika Westerberg check_for_device); 323cdb9446SMika Westerberg return ret > 0; 333cdb9446SMika Westerberg } 343cdb9446SMika Westerberg 353cdb9446SMika Westerberg static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) 363cdb9446SMika Westerberg { 373cdb9446SMika Westerberg u32 vs_cap; 383cdb9446SMika Westerberg 393cdb9446SMika Westerberg /* 403cdb9446SMika Westerberg * The Thunderbolt host controller is present always in Ice Lake 413cdb9446SMika Westerberg * but the firmware may not be loaded and running (depending 423cdb9446SMika Westerberg * whether there is device connected and so on). Each time the 433cdb9446SMika Westerberg * controller is used we need to "Force Power" it first and wait 443cdb9446SMika Westerberg * for the firmware to indicate it is up and running. This "Force 453cdb9446SMika Westerberg * Power" is really not about actually powering on/off the 463cdb9446SMika Westerberg * controller so it is accessible even if "Force Power" is off. 473cdb9446SMika Westerberg * 483cdb9446SMika Westerberg * The actual power management happens inside shared ACPI power 493cdb9446SMika Westerberg * resources using standard ACPI methods. 503cdb9446SMika Westerberg */ 513cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_22, &vs_cap); 523cdb9446SMika Westerberg if (power) { 533cdb9446SMika Westerberg vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; 543cdb9446SMika Westerberg vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; 553cdb9446SMika Westerberg vs_cap |= VS_CAP_22_FORCE_POWER; 563cdb9446SMika Westerberg } else { 573cdb9446SMika Westerberg vs_cap &= ~VS_CAP_22_FORCE_POWER; 583cdb9446SMika Westerberg } 593cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_22, vs_cap); 603cdb9446SMika Westerberg 613cdb9446SMika Westerberg if (power) { 623cdb9446SMika Westerberg unsigned int retries = 10; 633cdb9446SMika Westerberg u32 val; 643cdb9446SMika Westerberg 653cdb9446SMika Westerberg /* Wait until the firmware tells it is up and running */ 663cdb9446SMika Westerberg do { 673cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_9, &val); 683cdb9446SMika Westerberg if (val & VS_CAP_9_FW_READY) 693cdb9446SMika Westerberg return 0; 703cdb9446SMika Westerberg msleep(250); 713cdb9446SMika Westerberg } while (--retries); 723cdb9446SMika Westerberg 733cdb9446SMika Westerberg return -ETIMEDOUT; 743cdb9446SMika Westerberg } 753cdb9446SMika Westerberg 763cdb9446SMika Westerberg return 0; 773cdb9446SMika Westerberg } 783cdb9446SMika Westerberg 793cdb9446SMika Westerberg static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) 803cdb9446SMika Westerberg { 813cdb9446SMika Westerberg u32 data; 823cdb9446SMika Westerberg 833cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_19, &data); 843cdb9446SMika Westerberg data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; 853cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_19, data | VS_CAP_19_VALID); 863cdb9446SMika Westerberg } 873cdb9446SMika Westerberg 883cdb9446SMika Westerberg static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) 893cdb9446SMika Westerberg { 903cdb9446SMika Westerberg unsigned long end; 913cdb9446SMika Westerberg u32 data; 923cdb9446SMika Westerberg 933cdb9446SMika Westerberg if (!timeout) 943cdb9446SMika Westerberg goto clear; 953cdb9446SMika Westerberg 963cdb9446SMika Westerberg end = jiffies + msecs_to_jiffies(timeout); 973cdb9446SMika Westerberg do { 983cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_18, &data); 993cdb9446SMika Westerberg if (data & VS_CAP_18_DONE) 1003cdb9446SMika Westerberg goto clear; 1013cdb9446SMika Westerberg msleep(100); 1023cdb9446SMika Westerberg } while (time_before(jiffies, end)); 1033cdb9446SMika Westerberg 1043cdb9446SMika Westerberg return -ETIMEDOUT; 1053cdb9446SMika Westerberg 1063cdb9446SMika Westerberg clear: 1073cdb9446SMika Westerberg /* Clear the valid bit */ 1083cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_19, 0); 1093cdb9446SMika Westerberg return 0; 1103cdb9446SMika Westerberg } 1113cdb9446SMika Westerberg 1123cdb9446SMika Westerberg static void icl_nhi_set_ltr(struct tb_nhi *nhi) 1133cdb9446SMika Westerberg { 1143cdb9446SMika Westerberg u32 max_ltr, ltr; 1153cdb9446SMika Westerberg 1163cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_16, &max_ltr); 1173cdb9446SMika Westerberg max_ltr &= 0xffff; 1183cdb9446SMika Westerberg /* Program the same value for both snoop and no-snoop */ 1193cdb9446SMika Westerberg ltr = max_ltr << 16 | max_ltr; 1203cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_15, ltr); 1213cdb9446SMika Westerberg } 1223cdb9446SMika Westerberg 1233cdb9446SMika Westerberg static int icl_nhi_suspend(struct tb_nhi *nhi) 1243cdb9446SMika Westerberg { 1253cdb9446SMika Westerberg int ret; 1263cdb9446SMika Westerberg 1273cdb9446SMika Westerberg if (icl_nhi_is_device_connected(nhi)) 1283cdb9446SMika Westerberg return 0; 1293cdb9446SMika Westerberg 1303cdb9446SMika Westerberg /* 1313cdb9446SMika Westerberg * If there is no device connected we need to perform both: a 1323cdb9446SMika Westerberg * handshake through LC mailbox and force power down before 1333cdb9446SMika Westerberg * entering D3. 1343cdb9446SMika Westerberg */ 1353cdb9446SMika Westerberg icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); 1363cdb9446SMika Westerberg ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 1373cdb9446SMika Westerberg if (ret) 1383cdb9446SMika Westerberg return ret; 1393cdb9446SMika Westerberg 1403cdb9446SMika Westerberg return icl_nhi_force_power(nhi, false); 1413cdb9446SMika Westerberg } 1423cdb9446SMika Westerberg 1433cdb9446SMika Westerberg static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) 1443cdb9446SMika Westerberg { 1453cdb9446SMika Westerberg enum icl_lc_mailbox_cmd cmd; 1463cdb9446SMika Westerberg 1473cdb9446SMika Westerberg if (!pm_suspend_via_firmware()) 1483cdb9446SMika Westerberg return icl_nhi_suspend(nhi); 1493cdb9446SMika Westerberg 1503cdb9446SMika Westerberg cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; 1513cdb9446SMika Westerberg icl_nhi_lc_mailbox_cmd(nhi, cmd); 1523cdb9446SMika Westerberg return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 1533cdb9446SMika Westerberg } 1543cdb9446SMika Westerberg 1553cdb9446SMika Westerberg static int icl_nhi_resume(struct tb_nhi *nhi) 1563cdb9446SMika Westerberg { 1573cdb9446SMika Westerberg int ret; 1583cdb9446SMika Westerberg 1593cdb9446SMika Westerberg ret = icl_nhi_force_power(nhi, true); 1603cdb9446SMika Westerberg if (ret) 1613cdb9446SMika Westerberg return ret; 1623cdb9446SMika Westerberg 1633cdb9446SMika Westerberg icl_nhi_set_ltr(nhi); 1643cdb9446SMika Westerberg return 0; 1653cdb9446SMika Westerberg } 1663cdb9446SMika Westerberg 1673cdb9446SMika Westerberg static void icl_nhi_shutdown(struct tb_nhi *nhi) 1683cdb9446SMika Westerberg { 1693cdb9446SMika Westerberg icl_nhi_force_power(nhi, false); 1703cdb9446SMika Westerberg } 1713cdb9446SMika Westerberg 1723cdb9446SMika Westerberg const struct tb_nhi_ops icl_nhi_ops = { 1733cdb9446SMika Westerberg .init = icl_nhi_resume, 1743cdb9446SMika Westerberg .suspend_noirq = icl_nhi_suspend_noirq, 1753cdb9446SMika Westerberg .resume_noirq = icl_nhi_resume, 1763cdb9446SMika Westerberg .runtime_suspend = icl_nhi_suspend, 1773cdb9446SMika Westerberg .runtime_resume = icl_nhi_resume, 1783cdb9446SMika Westerberg .shutdown = icl_nhi_shutdown, 1793cdb9446SMika Westerberg }; 180