xref: /openbmc/linux/drivers/thunderbolt/nhi_ops.c (revision 3cdb9446)
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