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) { 62dc4c4bf1SRajmohan Mani unsigned int retries = 350; 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; 70dc4c4bf1SRajmohan Mani usleep_range(3000, 3100); 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 data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; 843cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_19, data | VS_CAP_19_VALID); 853cdb9446SMika Westerberg } 863cdb9446SMika Westerberg 873cdb9446SMika Westerberg static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) 883cdb9446SMika Westerberg { 893cdb9446SMika Westerberg unsigned long end; 903cdb9446SMika Westerberg u32 data; 913cdb9446SMika Westerberg 923cdb9446SMika Westerberg if (!timeout) 933cdb9446SMika Westerberg goto clear; 943cdb9446SMika Westerberg 953cdb9446SMika Westerberg end = jiffies + msecs_to_jiffies(timeout); 963cdb9446SMika Westerberg do { 973cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_18, &data); 983cdb9446SMika Westerberg if (data & VS_CAP_18_DONE) 993cdb9446SMika Westerberg goto clear; 1006651c91dSRajmohan Mani usleep_range(1000, 1100); 1013cdb9446SMika Westerberg } while (time_before(jiffies, end)); 1023cdb9446SMika Westerberg 1033cdb9446SMika Westerberg return -ETIMEDOUT; 1043cdb9446SMika Westerberg 1053cdb9446SMika Westerberg clear: 1063cdb9446SMika Westerberg /* Clear the valid bit */ 1073cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_19, 0); 1083cdb9446SMika Westerberg return 0; 1093cdb9446SMika Westerberg } 1103cdb9446SMika Westerberg 1113cdb9446SMika Westerberg static void icl_nhi_set_ltr(struct tb_nhi *nhi) 1123cdb9446SMika Westerberg { 1133cdb9446SMika Westerberg u32 max_ltr, ltr; 1143cdb9446SMika Westerberg 1153cdb9446SMika Westerberg pci_read_config_dword(nhi->pdev, VS_CAP_16, &max_ltr); 1163cdb9446SMika Westerberg max_ltr &= 0xffff; 1173cdb9446SMika Westerberg /* Program the same value for both snoop and no-snoop */ 1183cdb9446SMika Westerberg ltr = max_ltr << 16 | max_ltr; 1193cdb9446SMika Westerberg pci_write_config_dword(nhi->pdev, VS_CAP_15, ltr); 1203cdb9446SMika Westerberg } 1213cdb9446SMika Westerberg 1223cdb9446SMika Westerberg static int icl_nhi_suspend(struct tb_nhi *nhi) 1233cdb9446SMika Westerberg { 1243cdb9446SMika Westerberg int ret; 1253cdb9446SMika Westerberg 1263cdb9446SMika Westerberg if (icl_nhi_is_device_connected(nhi)) 1273cdb9446SMika Westerberg return 0; 1283cdb9446SMika Westerberg 1293cdb9446SMika Westerberg /* 1303cdb9446SMika Westerberg * If there is no device connected we need to perform both: a 1313cdb9446SMika Westerberg * handshake through LC mailbox and force power down before 1323cdb9446SMika Westerberg * entering D3. 1333cdb9446SMika Westerberg */ 1343cdb9446SMika Westerberg icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); 1353cdb9446SMika Westerberg ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 1363cdb9446SMika Westerberg if (ret) 1373cdb9446SMika Westerberg return ret; 1383cdb9446SMika Westerberg 1393cdb9446SMika Westerberg return icl_nhi_force_power(nhi, false); 1403cdb9446SMika Westerberg } 1413cdb9446SMika Westerberg 1423cdb9446SMika Westerberg static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) 1433cdb9446SMika Westerberg { 1443cdb9446SMika Westerberg enum icl_lc_mailbox_cmd cmd; 1453cdb9446SMika Westerberg 1463cdb9446SMika Westerberg if (!pm_suspend_via_firmware()) 1473cdb9446SMika Westerberg return icl_nhi_suspend(nhi); 1483cdb9446SMika Westerberg 1493cdb9446SMika Westerberg cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; 1503cdb9446SMika Westerberg icl_nhi_lc_mailbox_cmd(nhi, cmd); 1513cdb9446SMika Westerberg return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 1523cdb9446SMika Westerberg } 1533cdb9446SMika Westerberg 1543cdb9446SMika Westerberg static int icl_nhi_resume(struct tb_nhi *nhi) 1553cdb9446SMika Westerberg { 1563cdb9446SMika Westerberg int ret; 1573cdb9446SMika Westerberg 1583cdb9446SMika Westerberg ret = icl_nhi_force_power(nhi, true); 1593cdb9446SMika Westerberg if (ret) 1603cdb9446SMika Westerberg return ret; 1613cdb9446SMika Westerberg 1623cdb9446SMika Westerberg icl_nhi_set_ltr(nhi); 1633cdb9446SMika Westerberg return 0; 1643cdb9446SMika Westerberg } 1653cdb9446SMika Westerberg 1663cdb9446SMika Westerberg static void icl_nhi_shutdown(struct tb_nhi *nhi) 1673cdb9446SMika Westerberg { 1683cdb9446SMika Westerberg icl_nhi_force_power(nhi, false); 1693cdb9446SMika Westerberg } 1703cdb9446SMika Westerberg 1713cdb9446SMika Westerberg const struct tb_nhi_ops icl_nhi_ops = { 1723cdb9446SMika Westerberg .init = icl_nhi_resume, 1733cdb9446SMika Westerberg .suspend_noirq = icl_nhi_suspend_noirq, 1743cdb9446SMika Westerberg .resume_noirq = icl_nhi_resume, 1753cdb9446SMika Westerberg .runtime_suspend = icl_nhi_suspend, 1763cdb9446SMika Westerberg .runtime_resume = icl_nhi_resume, 1773cdb9446SMika Westerberg .shutdown = icl_nhi_shutdown, 1783cdb9446SMika Westerberg }; 179