1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * NHI specific operations 4 * 5 * Copyright (C) 2019, Intel Corporation 6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com> 7 */ 8 9 #include <linux/delay.h> 10 #include <linux/suspend.h> 11 12 #include "nhi.h" 13 #include "nhi_regs.h" 14 #include "tb.h" 15 16 /* Ice Lake specific NHI operations */ 17 18 #define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */ 19 20 static int check_for_device(struct device *dev, void *data) 21 { 22 return tb_is_switch(dev); 23 } 24 25 static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) 26 { 27 struct tb *tb = pci_get_drvdata(nhi->pdev); 28 int ret; 29 30 ret = device_for_each_child(&tb->root_switch->dev, NULL, 31 check_for_device); 32 return ret > 0; 33 } 34 35 static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) 36 { 37 u32 vs_cap; 38 39 /* 40 * The Thunderbolt host controller is present always in Ice Lake 41 * but the firmware may not be loaded and running (depending 42 * whether there is device connected and so on). Each time the 43 * controller is used we need to "Force Power" it first and wait 44 * for the firmware to indicate it is up and running. This "Force 45 * Power" is really not about actually powering on/off the 46 * controller so it is accessible even if "Force Power" is off. 47 * 48 * The actual power management happens inside shared ACPI power 49 * resources using standard ACPI methods. 50 */ 51 pci_read_config_dword(nhi->pdev, VS_CAP_22, &vs_cap); 52 if (power) { 53 vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; 54 vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; 55 vs_cap |= VS_CAP_22_FORCE_POWER; 56 } else { 57 vs_cap &= ~VS_CAP_22_FORCE_POWER; 58 } 59 pci_write_config_dword(nhi->pdev, VS_CAP_22, vs_cap); 60 61 if (power) { 62 unsigned int retries = 350; 63 u32 val; 64 65 /* Wait until the firmware tells it is up and running */ 66 do { 67 pci_read_config_dword(nhi->pdev, VS_CAP_9, &val); 68 if (val & VS_CAP_9_FW_READY) 69 return 0; 70 usleep_range(3000, 3100); 71 } while (--retries); 72 73 return -ETIMEDOUT; 74 } 75 76 return 0; 77 } 78 79 static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) 80 { 81 u32 data; 82 83 data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; 84 pci_write_config_dword(nhi->pdev, VS_CAP_19, data | VS_CAP_19_VALID); 85 } 86 87 static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) 88 { 89 unsigned long end; 90 u32 data; 91 92 if (!timeout) 93 goto clear; 94 95 end = jiffies + msecs_to_jiffies(timeout); 96 do { 97 pci_read_config_dword(nhi->pdev, VS_CAP_18, &data); 98 if (data & VS_CAP_18_DONE) 99 goto clear; 100 usleep_range(1000, 1100); 101 } while (time_before(jiffies, end)); 102 103 return -ETIMEDOUT; 104 105 clear: 106 /* Clear the valid bit */ 107 pci_write_config_dword(nhi->pdev, VS_CAP_19, 0); 108 return 0; 109 } 110 111 static void icl_nhi_set_ltr(struct tb_nhi *nhi) 112 { 113 u32 max_ltr, ltr; 114 115 pci_read_config_dword(nhi->pdev, VS_CAP_16, &max_ltr); 116 max_ltr &= 0xffff; 117 /* Program the same value for both snoop and no-snoop */ 118 ltr = max_ltr << 16 | max_ltr; 119 pci_write_config_dword(nhi->pdev, VS_CAP_15, ltr); 120 } 121 122 static int icl_nhi_suspend(struct tb_nhi *nhi) 123 { 124 struct tb *tb = pci_get_drvdata(nhi->pdev); 125 int ret; 126 127 if (icl_nhi_is_device_connected(nhi)) 128 return 0; 129 130 if (tb_switch_is_icm(tb->root_switch)) { 131 /* 132 * If there is no device connected we need to perform 133 * both: a handshake through LC mailbox and force power 134 * down before entering D3. 135 */ 136 icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); 137 ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 138 if (ret) 139 return ret; 140 } 141 142 return icl_nhi_force_power(nhi, false); 143 } 144 145 static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) 146 { 147 struct tb *tb = pci_get_drvdata(nhi->pdev); 148 enum icl_lc_mailbox_cmd cmd; 149 150 if (!pm_suspend_via_firmware()) 151 return icl_nhi_suspend(nhi); 152 153 if (!tb_switch_is_icm(tb->root_switch)) 154 return 0; 155 156 cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; 157 icl_nhi_lc_mailbox_cmd(nhi, cmd); 158 return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 159 } 160 161 static int icl_nhi_resume(struct tb_nhi *nhi) 162 { 163 int ret; 164 165 ret = icl_nhi_force_power(nhi, true); 166 if (ret) 167 return ret; 168 169 icl_nhi_set_ltr(nhi); 170 return 0; 171 } 172 173 static void icl_nhi_shutdown(struct tb_nhi *nhi) 174 { 175 icl_nhi_force_power(nhi, false); 176 } 177 178 const struct tb_nhi_ops icl_nhi_ops = { 179 .init = icl_nhi_resume, 180 .suspend_noirq = icl_nhi_suspend_noirq, 181 .resume_noirq = icl_nhi_resume, 182 .runtime_suspend = icl_nhi_suspend, 183 .runtime_resume = icl_nhi_resume, 184 .shutdown = icl_nhi_shutdown, 185 }; 186