summaryrefslogtreecommitdiff
path: root/SOURCES/linux-surface.patch
diff options
context:
space:
mode:
authorJan200101 <sentrycraft123@gmail.com>2024-02-08 22:07:04 +0100
committerJan200101 <sentrycraft123@gmail.com>2024-02-08 22:07:04 +0100
commita76b9e93d4de9f14a7e4aaa6d19fc721fc2e17d3 (patch)
treec77f07714c04275e3aa69b885a4ef7673985245d /SOURCES/linux-surface.patch
parent32a4026b609472b5b278fb1a9c2e5d740782edd2 (diff)
downloadkernel-fsync-a76b9e93d4de9f14a7e4aaa6d19fc721fc2e17d3.tar.gz
kernel-fsync-a76b9e93d4de9f14a7e4aaa6d19fc721fc2e17d3.zip
kernel 6.7.3
Diffstat (limited to 'SOURCES/linux-surface.patch')
-rw-r--r--SOURCES/linux-surface.patch3612
1 files changed, 1568 insertions, 2044 deletions
diff --git a/SOURCES/linux-surface.patch b/SOURCES/linux-surface.patch
index 2de6bab..087417f 100644
--- a/SOURCES/linux-surface.patch
+++ b/SOURCES/linux-surface.patch
@@ -1,4 +1,4 @@
-From da55b6ffe4a98a4af6ced4074317ba9d026f84dd Mon Sep 17 00:00:00 2001
+From c9479d2ee549e4b5392c5f788d9905244404e207 Mon Sep 17 00:00:00 2001
From: Tsuchiya Yuto <kitakar@gmail.com>
Date: Sun, 18 Oct 2020 16:42:44 +0900
Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI
@@ -40,7 +40,7 @@ Patchset: surface3-oemb
3 files changed, 24 insertions(+)
diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c
-index ca4602bcc7dea..490b9731068ae 100644
+index c15ed7a12784..1ec8edb5aafa 100644
--- a/drivers/platform/surface/surface3-wmi.c
+++ b/drivers/platform/surface/surface3-wmi.c
@@ -37,6 +37,13 @@ static const struct dmi_system_id surface3_dmi_table[] = {
@@ -58,10 +58,10 @@ index ca4602bcc7dea..490b9731068ae 100644
{ }
};
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
-index 7938b52d741d8..2d5f83b0cdb0b 100644
+index edcb85bd8ea7..cea19fa3fa56 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
-@@ -3746,6 +3746,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
+@@ -3753,6 +3753,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&intel_braswell_platform_data,
},
@@ -78,7 +78,7 @@ index 7938b52d741d8..2d5f83b0cdb0b 100644
/*
* Match for the GPDwin which unfortunately uses somewhat
diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
-index cdcbf04b8832f..958305779b125 100644
+index 5e2ec60e2954..207868c699f2 100644
--- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c
@@ -27,6 +27,14 @@ static const struct dmi_system_id cht_table[] = {
@@ -97,9 +97,9 @@ index cdcbf04b8832f..958305779b125 100644
};
--
-2.42.0
+2.43.0
-From 35b3c5195c9fc191de6b5a6e4361762aa37edad2 Mon Sep 17 00:00:00 2001
+From 38181ea8d1f9130ce6d677d306f819d2fa3b5f57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Tue, 3 Nov 2020 13:28:04 +0100
Subject: [PATCH] mwifiex: Add quirk resetting the PCI bridge on MS Surface
@@ -133,11 +133,11 @@ Patchset: mwifiex
3 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
-index 6697132ecc977..f06b4ebc5bd8e 100644
+index 5f997becdbaa..9a9929424513 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
-@@ -1771,9 +1771,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
- static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
+@@ -1702,9 +1702,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
+ static void mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
{
struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
@@ -156,10 +156,10 @@ index 6697132ecc977..f06b4ebc5bd8e 100644
+ pci_reset_function(parent_pdev);
+
/* Write the RX ring read pointer in to reg->rx_rdptr */
- if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr |
- tx_wrap)) {
+ mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | tx_wrap);
+ }
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
-index dd6d21f1dbfd7..f46b06f8d6435 100644
+index dd6d21f1dbfd..f46b06f8d643 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
@@ -13,7 +13,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
@@ -252,7 +252,7 @@ index dd6d21f1dbfd7..f46b06f8d6435 100644
static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev)
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
-index d6ff964aec5bf..5d30ae39d65ec 100644
+index d6ff964aec5b..5d30ae39d65e 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
@@ -4,6 +4,7 @@
@@ -264,9 +264,9 @@ index d6ff964aec5bf..5d30ae39d65ec 100644
void mwifiex_initialize_quirks(struct pcie_service_card *card);
int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev);
--
-2.42.0
+2.43.0
-From 241da24644ea2f5b8119019448b638aa8df6ab26 Mon Sep 17 00:00:00 2001
+From 86149f1c99b17f67d717419af83f3ec76315e35b Mon Sep 17 00:00:00 2001
From: Tsuchiya Yuto <kitakar@gmail.com>
Date: Sun, 4 Oct 2020 00:11:49 +0900
Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+
@@ -288,10 +288,10 @@ Patchset: mwifiex
3 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
-index f06b4ebc5bd8e..07f13b52ddb92 100644
+index 9a9929424513..2273e3029776 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
-@@ -370,6 +370,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+@@ -377,6 +377,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct pcie_service_card *card;
@@ -299,7 +299,7 @@ index f06b4ebc5bd8e..07f13b52ddb92 100644
int ret;
pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n",
-@@ -411,6 +412,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
+@@ -418,6 +419,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
return -1;
}
@@ -313,7 +313,7 @@ index f06b4ebc5bd8e..07f13b52ddb92 100644
}
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
-index f46b06f8d6435..99b024ecbadea 100644
+index f46b06f8d643..99b024ecbade 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c
@@ -14,7 +14,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = {
@@ -407,7 +407,7 @@ index f46b06f8d6435..99b024ecbadea 100644
static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev)
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
-index 5d30ae39d65ec..c14eb56eb9118 100644
+index 5d30ae39d65e..c14eb56eb911 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
+++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h
@@ -5,6 +5,7 @@
@@ -419,9 +419,9 @@ index 5d30ae39d65ec..c14eb56eb9118 100644
void mwifiex_initialize_quirks(struct pcie_service_card *card);
int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev);
--
-2.42.0
+2.43.0
-From d20b58f9e2ccec57c66864e79c291c2618ab2dbe Mon Sep 17 00:00:00 2001
+From 23775dc0be26e58d04574ab75768cedd8b0076f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Thu, 25 Mar 2021 11:33:02 +0100
Subject: [PATCH] Bluetooth: btusb: Lower passive lescan interval on Marvell
@@ -457,7 +457,7 @@ Patchset: mwifiex
1 file changed, 15 insertions(+)
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
-index 499f4809fcdf3..2d442e080ca28 100644
+index b8e9de887b5d..66a418ae9584 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -65,6 +65,7 @@ static struct usb_driver btusb_driver;
@@ -476,7 +476,7 @@ index 499f4809fcdf3..2d442e080ca28 100644
/* Intel Bluetooth devices */
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED },
-@@ -4388,6 +4390,19 @@ static int btusb_probe(struct usb_interface *intf,
+@@ -4399,6 +4401,19 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_MARVELL)
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
@@ -497,9 +497,9 @@ index 499f4809fcdf3..2d442e080ca28 100644
(id->driver_info & BTUSB_MEDIATEK)) {
hdev->setup = btusb_mtk_setup;
--
-2.42.0
+2.43.0
-From c6f0985fae241ed43ea1245c9e5861e2c728e21e Mon Sep 17 00:00:00 2001
+From 825328cce718ba6de0fce529e8fd1f4cd6b94dde Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 27 Feb 2021 00:45:52 +0100
Subject: [PATCH] ath10k: Add module parameters to override board files
@@ -521,7 +521,7 @@ Patchset: ath10k
1 file changed, 58 insertions(+)
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
-index 6cdb225b7eacc..19c036751fb16 100644
+index 6cdb225b7eac..19c036751fb1 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -38,6 +38,9 @@ static bool fw_diag_log;
@@ -618,9 +618,9 @@ index 6cdb225b7eacc..19c036751fb16 100644
ret = firmware_request_nowarn(&fw, filename, ar->dev);
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n",
--
-2.42.0
+2.43.0
-From 986fe56f682f93925b2964f59fe78c7043758e47 Mon Sep 17 00:00:00 2001
+From f4e5ac291e877f3e7e5d888f4965310eb85379f5 Mon Sep 17 00:00:00 2001
From: Dorian Stoll <dorian.stoll@tmsp.io>
Date: Thu, 30 Jul 2020 13:21:53 +0200
Subject: [PATCH] misc: mei: Add missing IPTS device IDs
@@ -632,7 +632,7 @@ Patchset: ipts
2 files changed, 2 insertions(+)
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
-index bdc65d50b945f..08723c01d7275 100644
+index 961e5d53a27a..860f99b6ecd6 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -92,6 +92,7 @@
@@ -644,7 +644,7 @@ index bdc65d50b945f..08723c01d7275 100644
#define MEI_DEV_ID_JSP_N 0x4DE0 /* Jasper Lake Point N */
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
-index 676d566f38ddf..6b37dd1f8b2a3 100644
+index 676d566f38dd..6b37dd1f8b2a 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -97,6 +97,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
@@ -656,9 +656,9 @@ index 676d566f38ddf..6b37dd1f8b2a3 100644
{MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)},
--
-2.42.0
+2.43.0
-From 72ee1cbf26ccc575dbfbaee5e7305ab13e1aeb1e Mon Sep 17 00:00:00 2001
+From 4c91dcde022856325e3babe1a1b9e01fcc21ab0f Mon Sep 17 00:00:00 2001
From: Liban Hannan <liban.p@gmail.com>
Date: Tue, 12 Apr 2022 23:31:12 +0100
Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
@@ -680,7 +680,7 @@ Patchset: ipts
1 file changed, 24 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
-index 3685ba90ec88e..5a627e081797c 100644
+index 897159dba47d..cc6569613255 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -38,6 +38,8 @@
@@ -692,7 +692,7 @@ index 3685ba90ec88e..5a627e081797c 100644
#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
#define IOAPIC_RANGE_START (0xfee00000)
-@@ -292,12 +294,14 @@ int intel_iommu_enabled = 0;
+@@ -291,12 +293,14 @@ int intel_iommu_enabled = 0;
EXPORT_SYMBOL_GPL(intel_iommu_enabled);
static int dmar_map_gfx = 1;
@@ -706,8 +706,8 @@ index 3685ba90ec88e..5a627e081797c 100644
+#define IDENTMAP_IPTS 16
const struct iommu_ops intel_iommu_ops;
-
-@@ -2542,6 +2546,9 @@ static int device_def_domain_type(struct device *dev)
+ static const struct iommu_dirty_ops intel_dirty_ops;
+@@ -2548,6 +2552,9 @@ static int device_def_domain_type(struct device *dev)
if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
return IOMMU_DOMAIN_IDENTITY;
@@ -717,7 +717,7 @@ index 3685ba90ec88e..5a627e081797c 100644
}
return 0;
-@@ -2849,6 +2856,9 @@ static int __init init_dmars(void)
+@@ -2855,6 +2862,9 @@ static int __init init_dmars(void)
if (!dmar_map_gfx)
iommu_identity_mapping |= IDENTMAP_GFX;
@@ -727,7 +727,7 @@ index 3685ba90ec88e..5a627e081797c 100644
check_tylersburg_isoch();
ret = si_domain_init(hw_pass_through);
-@@ -4828,6 +4838,17 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
+@@ -4977,6 +4987,17 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
dmar_map_gfx = 0;
}
@@ -745,7 +745,7 @@ index 3685ba90ec88e..5a627e081797c 100644
/* G4x/GM45 integrated gfx dmar support is totally busted. */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
-@@ -4863,6 +4884,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
+@@ -5012,6 +5033,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
@@ -756,9 +756,9 @@ index 3685ba90ec88e..5a627e081797c 100644
{
if (risky_device(dev))
--
-2.42.0
+2.43.0
-From 8330f9f39ce8c9796259a8aeffe919fa950e18f5 Mon Sep 17 00:00:00 2001
+From 7a9591af425eafbb76700f7ab1ab3ae0c3a08e4c Mon Sep 17 00:00:00 2001
From: Dorian Stoll <dorian.stoll@tmsp.io>
Date: Sun, 11 Dec 2022 12:00:59 +0100
Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
@@ -825,10 +825,10 @@ Patchset: ipts
create mode 100644 drivers/hid/ipts/thread.h
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index 790aa908e2a78..0b9d245d10e54 100644
+index 4ce74af79657..86c6c815bd5b 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
-@@ -1345,4 +1345,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
+@@ -1341,4 +1341,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
source "drivers/hid/surface-hid/Kconfig"
@@ -836,7 +836,7 @@ index 790aa908e2a78..0b9d245d10e54 100644
+
endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
-index 8a06d0f840bcb..2ef21b257d0b5 100644
+index 8a06d0f840bc..2ef21b257d0b 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -169,3 +169,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
@@ -847,7 +847,7 @@ index 8a06d0f840bcb..2ef21b257d0b5 100644
+obj-$(CONFIG_HID_IPTS) += ipts/
diff --git a/drivers/hid/ipts/Kconfig b/drivers/hid/ipts/Kconfig
new file mode 100644
-index 0000000000000..297401bd388dd
+index 000000000000..297401bd388d
--- /dev/null
+++ b/drivers/hid/ipts/Kconfig
@@ -0,0 +1,14 @@
@@ -867,7 +867,7 @@ index 0000000000000..297401bd388dd
+ module will be called ipts.
diff --git a/drivers/hid/ipts/Makefile b/drivers/hid/ipts/Makefile
new file mode 100644
-index 0000000000000..883896f68e6ad
+index 000000000000..883896f68e6a
--- /dev/null
+++ b/drivers/hid/ipts/Makefile
@@ -0,0 +1,16 @@
@@ -889,7 +889,7 @@ index 0000000000000..883896f68e6ad
+ipts-objs += thread.o
diff --git a/drivers/hid/ipts/cmd.c b/drivers/hid/ipts/cmd.c
new file mode 100644
-index 0000000000000..63a4934bbc5fa
+index 000000000000..63a4934bbc5f
--- /dev/null
+++ b/drivers/hid/ipts/cmd.c
@@ -0,0 +1,61 @@
@@ -956,7 +956,7 @@ index 0000000000000..63a4934bbc5fa
+}
diff --git a/drivers/hid/ipts/cmd.h b/drivers/hid/ipts/cmd.h
new file mode 100644
-index 0000000000000..2b4079075b642
+index 000000000000..2b4079075b64
--- /dev/null
+++ b/drivers/hid/ipts/cmd.h
@@ -0,0 +1,60 @@
@@ -1022,7 +1022,7 @@ index 0000000000000..2b4079075b642
+#endif /* IPTS_CMD_H */
diff --git a/drivers/hid/ipts/context.h b/drivers/hid/ipts/context.h
new file mode 100644
-index 0000000000000..ba33259f1f7c5
+index 000000000000..ba33259f1f7c
--- /dev/null
+++ b/drivers/hid/ipts/context.h
@@ -0,0 +1,52 @@
@@ -1080,7 +1080,7 @@ index 0000000000000..ba33259f1f7c5
+#endif /* IPTS_CONTEXT_H */
diff --git a/drivers/hid/ipts/control.c b/drivers/hid/ipts/control.c
new file mode 100644
-index 0000000000000..5360842d260ba
+index 000000000000..5360842d260b
--- /dev/null
+++ b/drivers/hid/ipts/control.c
@@ -0,0 +1,486 @@
@@ -1572,7 +1572,7 @@ index 0000000000000..5360842d260ba
+}
diff --git a/drivers/hid/ipts/control.h b/drivers/hid/ipts/control.h
new file mode 100644
-index 0000000000000..26629c5144edb
+index 000000000000..26629c5144ed
--- /dev/null
+++ b/drivers/hid/ipts/control.h
@@ -0,0 +1,126 @@
@@ -1704,7 +1704,7 @@ index 0000000000000..26629c5144edb
+#endif /* IPTS_CONTROL_H */
diff --git a/drivers/hid/ipts/desc.h b/drivers/hid/ipts/desc.h
new file mode 100644
-index 0000000000000..307438c7c80cd
+index 000000000000..307438c7c80c
--- /dev/null
+++ b/drivers/hid/ipts/desc.h
@@ -0,0 +1,80 @@
@@ -1790,7 +1790,7 @@ index 0000000000000..307438c7c80cd
+#endif /* IPTS_DESC_H */
diff --git a/drivers/hid/ipts/eds1.c b/drivers/hid/ipts/eds1.c
new file mode 100644
-index 0000000000000..ecbb3a8bdaf60
+index 000000000000..ecbb3a8bdaf6
--- /dev/null
+++ b/drivers/hid/ipts/eds1.c
@@ -0,0 +1,103 @@
@@ -1899,7 +1899,7 @@ index 0000000000000..ecbb3a8bdaf60
+}
diff --git a/drivers/hid/ipts/eds1.h b/drivers/hid/ipts/eds1.h
new file mode 100644
-index 0000000000000..eeeb6575e3e89
+index 000000000000..eeeb6575e3e8
--- /dev/null
+++ b/drivers/hid/ipts/eds1.h
@@ -0,0 +1,35 @@
@@ -1940,7 +1940,7 @@ index 0000000000000..eeeb6575e3e89
+ enum hid_report_type report_type, enum hid_class_request request_type);
diff --git a/drivers/hid/ipts/eds2.c b/drivers/hid/ipts/eds2.c
new file mode 100644
-index 0000000000000..198dc65d78876
+index 000000000000..198dc65d7887
--- /dev/null
+++ b/drivers/hid/ipts/eds2.c
@@ -0,0 +1,144 @@
@@ -2090,7 +2090,7 @@ index 0000000000000..198dc65d78876
+}
diff --git a/drivers/hid/ipts/eds2.h b/drivers/hid/ipts/eds2.h
new file mode 100644
-index 0000000000000..064e3716907ab
+index 000000000000..064e3716907a
--- /dev/null
+++ b/drivers/hid/ipts/eds2.h
@@ -0,0 +1,35 @@
@@ -2131,7 +2131,7 @@ index 0000000000000..064e3716907ab
+ enum hid_report_type report_type, enum hid_class_request request_type);
diff --git a/drivers/hid/ipts/hid.c b/drivers/hid/ipts/hid.c
new file mode 100644
-index 0000000000000..e34a1a4f9fa77
+index 000000000000..e34a1a4f9fa7
--- /dev/null
+++ b/drivers/hid/ipts/hid.c
@@ -0,0 +1,225 @@
@@ -2362,7 +2362,7 @@ index 0000000000000..e34a1a4f9fa77
+}
diff --git a/drivers/hid/ipts/hid.h b/drivers/hid/ipts/hid.h
new file mode 100644
-index 0000000000000..1ebe77447903a
+index 000000000000..1ebe77447903
--- /dev/null
+++ b/drivers/hid/ipts/hid.h
@@ -0,0 +1,24 @@
@@ -2392,7 +2392,7 @@ index 0000000000000..1ebe77447903a
+#endif /* IPTS_HID_H */
diff --git a/drivers/hid/ipts/main.c b/drivers/hid/ipts/main.c
new file mode 100644
-index 0000000000000..fb5b5c13ee3ea
+index 000000000000..fb5b5c13ee3e
--- /dev/null
+++ b/drivers/hid/ipts/main.c
@@ -0,0 +1,126 @@
@@ -2524,7 +2524,7 @@ index 0000000000000..fb5b5c13ee3ea
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/ipts/mei.c b/drivers/hid/ipts/mei.c
new file mode 100644
-index 0000000000000..1e0395ceae4a4
+index 000000000000..1e0395ceae4a
--- /dev/null
+++ b/drivers/hid/ipts/mei.c
@@ -0,0 +1,188 @@
@@ -2718,7 +2718,7 @@ index 0000000000000..1e0395ceae4a4
+}
diff --git a/drivers/hid/ipts/mei.h b/drivers/hid/ipts/mei.h
new file mode 100644
-index 0000000000000..973bade6b0fdd
+index 000000000000..973bade6b0fd
--- /dev/null
+++ b/drivers/hid/ipts/mei.h
@@ -0,0 +1,66 @@
@@ -2790,7 +2790,7 @@ index 0000000000000..973bade6b0fdd
+#endif /* IPTS_MEI_H */
diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c
new file mode 100644
-index 0000000000000..ef66c3c9db807
+index 000000000000..ef66c3c9db80
--- /dev/null
+++ b/drivers/hid/ipts/receiver.c
@@ -0,0 +1,250 @@
@@ -3046,7 +3046,7 @@ index 0000000000000..ef66c3c9db807
+}
diff --git a/drivers/hid/ipts/receiver.h b/drivers/hid/ipts/receiver.h
new file mode 100644
-index 0000000000000..3de7da62d40c1
+index 000000000000..3de7da62d40c
--- /dev/null
+++ b/drivers/hid/ipts/receiver.h
@@ -0,0 +1,16 @@
@@ -3068,7 +3068,7 @@ index 0000000000000..3de7da62d40c1
+#endif /* IPTS_RECEIVER_H */
diff --git a/drivers/hid/ipts/resources.c b/drivers/hid/ipts/resources.c
new file mode 100644
-index 0000000000000..cc14653b2a9f5
+index 000000000000..cc14653b2a9f
--- /dev/null
+++ b/drivers/hid/ipts/resources.c
@@ -0,0 +1,131 @@
@@ -3205,7 +3205,7 @@ index 0000000000000..cc14653b2a9f5
+}
diff --git a/drivers/hid/ipts/resources.h b/drivers/hid/ipts/resources.h
new file mode 100644
-index 0000000000000..2068e13285f0e
+index 000000000000..2068e13285f0
--- /dev/null
+++ b/drivers/hid/ipts/resources.h
@@ -0,0 +1,41 @@
@@ -3252,7 +3252,7 @@ index 0000000000000..2068e13285f0e
+#endif /* IPTS_RESOURCES_H */
diff --git a/drivers/hid/ipts/spec-data.h b/drivers/hid/ipts/spec-data.h
new file mode 100644
-index 0000000000000..e8dd98895a7ee
+index 000000000000..e8dd98895a7e
--- /dev/null
+++ b/drivers/hid/ipts/spec-data.h
@@ -0,0 +1,100 @@
@@ -3358,7 +3358,7 @@ index 0000000000000..e8dd98895a7ee
+#endif /* IPTS_SPEC_DATA_H */
diff --git a/drivers/hid/ipts/spec-device.h b/drivers/hid/ipts/spec-device.h
new file mode 100644
-index 0000000000000..41845f9d90257
+index 000000000000..41845f9d9025
--- /dev/null
+++ b/drivers/hid/ipts/spec-device.h
@@ -0,0 +1,290 @@
@@ -3654,7 +3654,7 @@ index 0000000000000..41845f9d90257
+#endif /* IPTS_SPEC_DEVICE_H */
diff --git a/drivers/hid/ipts/spec-hid.h b/drivers/hid/ipts/spec-hid.h
new file mode 100644
-index 0000000000000..5a58d4a0a610f
+index 000000000000..5a58d4a0a610
--- /dev/null
+++ b/drivers/hid/ipts/spec-hid.h
@@ -0,0 +1,34 @@
@@ -3694,7 +3694,7 @@ index 0000000000000..5a58d4a0a610f
+#endif /* IPTS_SPEC_HID_H */
diff --git a/drivers/hid/ipts/thread.c b/drivers/hid/ipts/thread.c
new file mode 100644
-index 0000000000000..355e92bea26f8
+index 000000000000..355e92bea26f
--- /dev/null
+++ b/drivers/hid/ipts/thread.c
@@ -0,0 +1,84 @@
@@ -3784,7 +3784,7 @@ index 0000000000000..355e92bea26f8
+}
diff --git a/drivers/hid/ipts/thread.h b/drivers/hid/ipts/thread.h
new file mode 100644
-index 0000000000000..1f966b8b32c45
+index 000000000000..1f966b8b32c4
--- /dev/null
+++ b/drivers/hid/ipts/thread.h
@@ -0,0 +1,59 @@
@@ -3848,9 +3848,9 @@ index 0000000000000..1f966b8b32c45
+
+#endif /* IPTS_THREAD_H */
--
-2.42.0
+2.43.0
-From 033de13abc9653b2d773f06182465e03d5d6463b Mon Sep 17 00:00:00 2001
+From 8aadfc38967cb2804446c8bdae851377651e6248 Mon Sep 17 00:00:00 2001
From: Dorian Stoll <dorian.stoll@tmsp.io>
Date: Sun, 11 Dec 2022 12:03:38 +0100
Subject: [PATCH] iommu: intel: Disable source id verification for ITHC
@@ -3862,7 +3862,7 @@ Patchset: ithc
1 file changed, 16 insertions(+)
diff --git a/drivers/iommu/intel/irq_remapping.c b/drivers/iommu/intel/irq_remapping.c
-index 29b9e55dcf26c..986e91c813ae1 100644
+index 29b9e55dcf26..986e91c813ae 100644
--- a/drivers/iommu/intel/irq_remapping.c
+++ b/drivers/iommu/intel/irq_remapping.c
@@ -386,6 +386,22 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
@@ -3889,14 +3889,14 @@ index 29b9e55dcf26c..986e91c813ae1 100644
* DMA alias provides us with a PCI device and alias. The only case
* where the it will return an alias on a different bus than the
--
-2.42.0
+2.43.0
-From 0dd32bcfb70f9e36cfa009d94cd6c86a4839cff3 Mon Sep 17 00:00:00 2001
-From: Dorian Stoll <dorian.stoll@tmsp.io>
+From fe08b40d122fdb102c2cc4876d2d68ac19d74ae3 Mon Sep 17 00:00:00 2001
+From: quo <tuple@list.ru>
Date: Sun, 11 Dec 2022 12:10:54 +0100
Subject: [PATCH] hid: Add support for Intel Touch Host Controller
-Based on quo/ithc-linux@55803a2
+Based on quo/ithc-linux@0b8b45d
Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
Patchset: ithc
@@ -3905,14 +3905,14 @@ Patchset: ithc
drivers/hid/Makefile | 1 +
drivers/hid/ithc/Kbuild | 6 +
drivers/hid/ithc/Kconfig | 12 +
- drivers/hid/ithc/ithc-debug.c | 96 ++++++
- drivers/hid/ithc/ithc-dma.c | 258 ++++++++++++++++
- drivers/hid/ithc/ithc-dma.h | 67 +++++
- drivers/hid/ithc/ithc-main.c | 534 ++++++++++++++++++++++++++++++++++
- drivers/hid/ithc/ithc-regs.c | 64 ++++
- drivers/hid/ithc/ithc-regs.h | 186 ++++++++++++
- drivers/hid/ithc/ithc.h | 60 ++++
- 11 files changed, 1286 insertions(+)
+ drivers/hid/ithc/ithc-debug.c | 130 ++++++
+ drivers/hid/ithc/ithc-dma.c | 373 +++++++++++++++++
+ drivers/hid/ithc/ithc-dma.h | 69 ++++
+ drivers/hid/ithc/ithc-main.c | 728 ++++++++++++++++++++++++++++++++++
+ drivers/hid/ithc/ithc-regs.c | 96 +++++
+ drivers/hid/ithc/ithc-regs.h | 189 +++++++++
+ drivers/hid/ithc/ithc.h | 67 ++++
+ 11 files changed, 1673 insertions(+)
create mode 100644 drivers/hid/ithc/Kbuild
create mode 100644 drivers/hid/ithc/Kconfig
create mode 100644 drivers/hid/ithc/ithc-debug.c
@@ -3924,10 +3924,10 @@ Patchset: ithc
create mode 100644 drivers/hid/ithc/ithc.h
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index 0b9d245d10e54..8ba1c309228be 100644
+index 86c6c815bd5b..a87c3c6911fb 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
-@@ -1347,4 +1347,6 @@ source "drivers/hid/surface-hid/Kconfig"
+@@ -1343,4 +1343,6 @@ source "drivers/hid/surface-hid/Kconfig"
source "drivers/hid/ipts/Kconfig"
@@ -3935,7 +3935,7 @@ index 0b9d245d10e54..8ba1c309228be 100644
+
endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
-index 2ef21b257d0b5..e94b79727b489 100644
+index 2ef21b257d0b..e94b79727b48 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -171,3 +171,4 @@ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
@@ -3945,7 +3945,7 @@ index 2ef21b257d0b5..e94b79727b489 100644
+obj-$(CONFIG_HID_ITHC) += ithc/
diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild
new file mode 100644
-index 0000000000000..aea83f2ac07b4
+index 000000000000..aea83f2ac07b
--- /dev/null
+++ b/drivers/hid/ithc/Kbuild
@@ -0,0 +1,6 @@
@@ -3957,7 +3957,7 @@ index 0000000000000..aea83f2ac07b4
+
diff --git a/drivers/hid/ithc/Kconfig b/drivers/hid/ithc/Kconfig
new file mode 100644
-index 0000000000000..ede7130236096
+index 000000000000..ede713023609
--- /dev/null
+++ b/drivers/hid/ithc/Kconfig
@@ -0,0 +1,12 @@
@@ -3975,17 +3975,21 @@ index 0000000000000..ede7130236096
+ module will be called ithc.
diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c
new file mode 100644
-index 0000000000000..57bf125c45bd5
+index 000000000000..1f1f1e33f2e5
--- /dev/null
+++ b/drivers/hid/ithc/ithc-debug.c
-@@ -0,0 +1,96 @@
+@@ -0,0 +1,130 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
+#include "ithc.h"
+
-+void ithc_log_regs(struct ithc *ithc) {
-+ if (!ithc->prev_regs) return;
-+ u32 __iomem *cur = (__iomem void*)ithc->regs;
-+ u32 *prev = (void*)ithc->prev_regs;
-+ for (int i = 1024; i < sizeof *ithc->regs / 4; i++) {
++void ithc_log_regs(struct ithc *ithc)
++{
++ if (!ithc->prev_regs)
++ return;
++ u32 __iomem *cur = (__iomem void *)ithc->regs;
++ u32 *prev = (void *)ithc->prev_regs;
++ for (int i = 1024; i < sizeof(*ithc->regs) / 4; i++) {
+ u32 x = readl(cur + i);
+ if (x != prev[i]) {
+ pci_info(ithc->pci, "reg %04x: %08x -> %08x\n", i * 4, prev[i], x);
@@ -3994,55 +3998,79 @@ index 0000000000000..57bf125c45bd5
+ }
+}
+
-+static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, loff_t *offset) {
++static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len,
++ loff_t *offset)
++{
++ // Debug commands consist of a single letter followed by a list of numbers (decimal or
++ // hexadecimal, space-separated).
+ struct ithc *ithc = file_inode(f)->i_private;
+ char cmd[256];
-+ if (!ithc || !ithc->pci) return -ENODEV;
-+ if (!len) return -EINVAL;
-+ if (len >= sizeof cmd) return -EINVAL;
-+ if (copy_from_user(cmd, buf, len)) return -EFAULT;
++ if (!ithc || !ithc->pci)
++ return -ENODEV;
++ if (!len)
++ return -EINVAL;
++ if (len >= sizeof(cmd))
++ return -EINVAL;
++ if (copy_from_user(cmd, buf, len))
++ return -EFAULT;
+ cmd[len] = 0;
-+ if (cmd[len-1] == '\n') cmd[len-1] = 0;
++ if (cmd[len-1] == '\n')
++ cmd[len-1] = 0;
+ pci_info(ithc->pci, "debug command: %s\n", cmd);
++
++ // Parse the list of arguments into a u32 array.
+ u32 n = 0;
+ const char *s = cmd + 1;
+ u32 a[32];
+ while (*s && *s != '\n') {
-+ if (n >= ARRAY_SIZE(a)) return -EINVAL;
-+ if (*s++ != ' ') return -EINVAL;
++ if (n >= ARRAY_SIZE(a))
++ return -EINVAL;
++ if (*s++ != ' ')
++ return -EINVAL;
+ char *e;
+ a[n++] = simple_strtoul(s, &e, 0);
-+ if (e == s) return -EINVAL;
++ if (e == s)
++ return -EINVAL;
+ s = e;
+ }
+ ithc_log_regs(ithc);
-+ switch(cmd[0]) {
++
++ // Execute the command.
++ switch (cmd[0]) {
+ case 'x': // reset
+ ithc_reset(ithc);
+ break;
+ case 'w': // write register: offset mask value
-+ if (n != 3 || (a[0] & 3)) return -EINVAL;
-+ pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", a[0], a[2], a[1]);
++ if (n != 3 || (a[0] & 3))
++ return -EINVAL;
++ pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n",
++ a[0], a[2], a[1]);
+ bitsl(((__iomem u32 *)ithc->regs) + a[0] / 4, a[1], a[2]);
+ break;
+ case 'r': // read register: offset
-+ if (n != 1 || (a[0] & 3)) return -EINVAL;
-+ pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], readl(((__iomem u32 *)ithc->regs) + a[0] / 4));
++ if (n != 1 || (a[0] & 3))
++ return -EINVAL;
++ pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0],
++ readl(((__iomem u32 *)ithc->regs) + a[0] / 4));
+ break;
+ case 's': // spi command: cmd offset len data...
+ // read config: s 4 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ // set touch cfg: s 6 12 4 XX
-+ if (n < 3 || a[2] > (n - 3) * 4) return -EINVAL;
++ if (n < 3 || a[2] > (n - 3) * 4)
++ return -EINVAL;
+ pci_info(ithc->pci, "debug spi command %u with %u bytes of data\n", a[0], a[2]);
+ if (!CHECK(ithc_spi_command, ithc, a[0], a[1], a[2], a + 3))
-+ for (u32 i = 0; i < (a[2] + 3) / 4; i++) pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]);
++ for (u32 i = 0; i < (a[2] + 3) / 4; i++)
++ pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]);
+ break;
+ case 'd': // dma command: cmd len data...
+ // get report descriptor: d 7 8 0 0
+ // enable multitouch: d 3 2 0x0105
-+ if (n < 2 || a[1] > (n - 2) * 4) return -EINVAL;
++ if (n < 2 || a[1] > (n - 2) * 4)
++ return -EINVAL;
+ pci_info(ithc->pci, "debug dma command %u with %u bytes of data\n", a[0], a[1]);
-+ if (ithc_dma_tx(ithc, a[0], a[1], a + 2)) pci_err(ithc->pci, "dma tx failed\n");
++ if (ithc_dma_tx(ithc, a[0], a[1], a + 2))
++ pci_err(ithc->pci, "dma tx failed\n");
+ break;
+ default:
+ return -EINVAL;
@@ -4056,87 +4084,125 @@ index 0000000000000..57bf125c45bd5
+ .write = ithc_debugfs_cmd_write,
+};
+
-+static void ithc_debugfs_devres_release(struct device *dev, void *res) {
++static void ithc_debugfs_devres_release(struct device *dev, void *res)
++{
+ struct dentry **dbgm = res;
-+ if (*dbgm) debugfs_remove_recursive(*dbgm);
++ if (*dbgm)
++ debugfs_remove_recursive(*dbgm);
+}
+
-+int ithc_debug_init(struct ithc *ithc) {
-+ struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof *dbgm, GFP_KERNEL);
-+ if (!dbgm) return -ENOMEM;
++int ithc_debug_init(struct ithc *ithc)
++{
++ struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof(*dbgm), GFP_KERNEL);
++ if (!dbgm)
++ return -ENOMEM;
+ devres_add(&ithc->pci->dev, dbgm);
+ struct dentry *dbg = debugfs_create_dir(DEVNAME, NULL);
-+ if (IS_ERR(dbg)) return PTR_ERR(dbg);
++ if (IS_ERR(dbg))
++ return PTR_ERR(dbg);
+ *dbgm = dbg;
+
+ struct dentry *cmd = debugfs_create_file("cmd", 0220, dbg, ithc, &ithc_debugfops_cmd);
-+ if (IS_ERR(cmd)) return PTR_ERR(cmd);
++ if (IS_ERR(cmd))
++ return PTR_ERR(cmd);
+
+ return 0;
+}
+
diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c
new file mode 100644
-index 0000000000000..7e89b3496918d
+index 000000000000..ffb8689b8a78
--- /dev/null
+++ b/drivers/hid/ithc/ithc-dma.c
-@@ -0,0 +1,258 @@
+@@ -0,0 +1,373 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
+#include "ithc.h"
+
-+static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, unsigned num_buffers, unsigned num_pages, enum dma_data_direction dir) {
++// The THC uses tables of PRDs (physical region descriptors) to describe the TX and RX data buffers.
++// Each PRD contains the DMA address and size of a block of DMA memory, and some status flags.
++// This allows each data buffer to consist of multiple non-contiguous blocks of memory.
++
++static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p,
++ unsigned int num_buffers, unsigned int num_pages, enum dma_data_direction dir)
++{
+ p->num_pages = num_pages;
+ p->dir = dir;
++ // We allocate enough space to have one PRD per data buffer page, however if the data
++ // buffer pages happen to be contiguous, we can describe the buffer using fewer PRDs, so
++ // some will remain unused (which is fine).
+ p->size = round_up(num_buffers * num_pages * sizeof(struct ithc_phys_region_desc), PAGE_SIZE);
+ p->addr = dmam_alloc_coherent(&ithc->pci->dev, p->size, &p->dma_addr, GFP_KERNEL);
-+ if (!p->addr) return -ENOMEM;
-+ if (p->dma_addr & (PAGE_SIZE - 1)) return -EFAULT;
++ if (!p->addr)
++ return -ENOMEM;
++ if (p->dma_addr & (PAGE_SIZE - 1))
++ return -EFAULT;
+ return 0;
+}
+
++// Devres managed sg_table wrapper.
+struct ithc_sg_table {
+ void *addr;
+ struct sg_table sgt;
+ enum dma_data_direction dir;
+};
-+static void ithc_dma_sgtable_free(struct sg_table *sgt) {
++static void ithc_dma_sgtable_free(struct sg_table *sgt)
++{
+ struct scatterlist *sg;
+ int i;
+ for_each_sgtable_sg(sgt, sg, i) {
+ struct page *p = sg_page(sg);
-+ if (p) __free_page(p);
++ if (p)
++ __free_page(p);
+ }
+ sg_free_table(sgt);
+}
-+static void ithc_dma_data_devres_release(struct device *dev, void *res) {
++static void ithc_dma_data_devres_release(struct device *dev, void *res)
++{
+ struct ithc_sg_table *sgt = res;
-+ if (sgt->addr) vunmap(sgt->addr);
++ if (sgt->addr)
++ vunmap(sgt->addr);
+ dma_unmap_sgtable(dev, &sgt->sgt, sgt->dir, 0);
+ ithc_dma_sgtable_free(&sgt->sgt);
+}
+
-+static int ithc_dma_data_alloc(struct ithc* ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b) {
-+ // We don't use dma_alloc_coherent for data buffers, because they don't have to be contiguous (we can use one PRD per page) or coherent (they are unidirectional).
-+ // Instead we use an sg_table of individually allocated pages (5.13 has dma_alloc_noncontiguous for this, but we'd like to support 5.10 for now).
++static int ithc_dma_data_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *prds,
++ struct ithc_dma_data_buffer *b)
++{
++ // We don't use dma_alloc_coherent() for data buffers, because they don't have to be
++ // coherent (they are unidirectional) or contiguous (we can use one PRD per page).
++ // We could use dma_alloc_noncontiguous(), however this still always allocates a single
++ // DMA mapped segment, which is more restrictive than what we need.
++ // Instead we use an sg_table of individually allocated pages.
+ struct page *pages[16];
-+ if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) return -EINVAL;
++ if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages))
++ return -EINVAL;
+ b->active_idx = -1;
-+ struct ithc_sg_table *sgt = devres_alloc(ithc_dma_data_devres_release, sizeof *sgt, GFP_KERNEL);
-+ if (!sgt) return -ENOMEM;
++ struct ithc_sg_table *sgt = devres_alloc(
++ ithc_dma_data_devres_release, sizeof(*sgt), GFP_KERNEL);
++ if (!sgt)
++ return -ENOMEM;
+ sgt->dir = prds->dir;
++
+ if (!sg_alloc_table(&sgt->sgt, prds->num_pages, GFP_KERNEL)) {
+ struct scatterlist *sg;
+ int i;
+ bool ok = true;
+ for_each_sgtable_sg(&sgt->sgt, sg, i) {
-+ struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); // don't need __GFP_DMA for PCI DMA
-+ if (!p) { ok = false; break; }
++ // NOTE: don't need __GFP_DMA for PCI DMA
++ struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
++ if (!p) {
++ ok = false;
++ break;
++ }
+ sg_set_page(sg, p, PAGE_SIZE, 0);
+ }
+ if (ok && !dma_map_sgtable(&ithc->pci->dev, &sgt->sgt, prds->dir, 0)) {
+ devres_add(&ithc->pci->dev, sgt);
+ b->sgt = &sgt->sgt;
+ b->addr = sgt->addr = vmap(pages, prds->num_pages, 0, PAGE_KERNEL);
-+ if (!b->addr) return -ENOMEM;
++ if (!b->addr)
++ return -ENOMEM;
+ return 0;
+ }
+ ithc_dma_sgtable_free(&sgt->sgt);
@@ -4145,17 +4211,29 @@ index 0000000000000..7e89b3496918d
+ return -ENOMEM;
+}
+
-+static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) {
++static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds,
++ struct ithc_dma_data_buffer *b, unsigned int idx)
++{
++ // Give a buffer to the THC.
+ struct ithc_phys_region_desc *prd = prds->addr;
+ prd += idx * prds->num_pages;
-+ if (b->active_idx >= 0) { pci_err(ithc->pci, "buffer already active\n"); return -EINVAL; }
++ if (b->active_idx >= 0) {
++ pci_err(ithc->pci, "buffer already active\n");
++ return -EINVAL;
++ }
+ b->active_idx = idx;
+ if (prds->dir == DMA_TO_DEVICE) {
-+ if (b->data_size > PAGE_SIZE) return -EINVAL;
++ // TX buffer: Caller should have already filled the data buffer, so just fill
++ // the PRD and flush.
++ // (TODO: Support multi-page TX buffers. So far no device seems to use or need
++ // these though.)
++ if (b->data_size > PAGE_SIZE)
++ return -EINVAL;
+ prd->addr = sg_dma_address(b->sgt->sgl) >> 10;
+ prd->size = b->data_size | PRD_FLAG_END;
+ flush_kernel_vmap_range(b->addr, b->data_size);
+ } else if (prds->dir == DMA_FROM_DEVICE) {
++ // RX buffer: Reset PRDs.
+ struct scatterlist *sg;
+ int i;
+ for_each_sgtable_dma_sg(b->sgt, sg, i) {
@@ -4170,21 +4248,34 @@ index 0000000000000..7e89b3496918d
+ return 0;
+}
+
-+static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) {
++static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds,
++ struct ithc_dma_data_buffer *b, unsigned int idx)
++{
++ // Take a buffer from the THC.
+ struct ithc_phys_region_desc *prd = prds->addr;
+ prd += idx * prds->num_pages;
-+ if (b->active_idx != idx) { pci_err(ithc->pci, "wrong buffer index\n"); return -EINVAL; }
++ // This is purely a sanity check. We don't strictly need the idx parameter for this
++ // function, because it should always be the same as active_idx, unless we have a bug.
++ if (b->active_idx != idx) {
++ pci_err(ithc->pci, "wrong buffer index\n");
++ return -EINVAL;
++ }
+ b->active_idx = -1;
+ if (prds->dir == DMA_FROM_DEVICE) {
++ // RX buffer: Calculate actual received data size from PRDs.
+ dma_rmb(); // for the prds
+ b->data_size = 0;
+ struct scatterlist *sg;
+ int i;
+ for_each_sgtable_dma_sg(b->sgt, sg, i) {
-+ unsigned size = prd->size;
++ unsigned int size = prd->size;
+ b->data_size += size & PRD_SIZE_MASK;
-+ if (size & PRD_FLAG_END) break;
-+ if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { pci_err(ithc->pci, "truncated prd\n"); break; }
++ if (size & PRD_FLAG_END)
++ break;
++ if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) {
++ pci_err(ithc->pci, "truncated prd\n");
++ break;
++ }
+ prd++;
+ }
+ invalidate_kernel_vmap_range(b->addr, b->data_size);
@@ -4193,93 +4284,139 @@ index 0000000000000..7e89b3496918d
+ return 0;
+}
+
-+int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname) {
++int ithc_dma_rx_init(struct ithc *ithc, u8 channel)
++{
+ struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
+ mutex_init(&rx->mutex);
++
++ // Allocate buffers.
+ u32 buf_size = DEVCFG_DMA_RX_SIZE(ithc->config.dma_buf_sizes);
-+ unsigned num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE;
-+ pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", NUM_RX_BUF, buf_size, num_pages);
++ unsigned int num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE;
++ pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n",
++ NUM_RX_BUF, buf_size, num_pages);
+ CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE);
-+ for (unsigned i = 0; i < NUM_RX_BUF; i++)
++ for (unsigned int i = 0; i < NUM_RX_BUF; i++)
+ CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]);
++
++ // Init registers.
+ writeb(DMA_RX_CONTROL2_RESET, &ithc->regs->dma_rx[channel].control2);
+ lo_hi_writeq(rx->prds.dma_addr, &ithc->regs->dma_rx[channel].addr);
+ writeb(NUM_RX_BUF - 1, &ithc->regs->dma_rx[channel].num_bufs);
+ writeb(num_pages - 1, &ithc->regs->dma_rx[channel].num_prds);
+ u8 head = readb(&ithc->regs->dma_rx[channel].head);
-+ if (head) { pci_err(ithc->pci, "head is nonzero (%u)\n", head); return -EIO; }
-+ for (unsigned i = 0; i < NUM_RX_BUF; i++)
++ if (head) {
++ pci_err(ithc->pci, "head is nonzero (%u)\n", head);
++ return -EIO;
++ }
++
++ // Init buffers.
++ for (unsigned int i = 0; i < NUM_RX_BUF; i++)
+ CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, &rx->bufs[i], i);
++
+ writeb(head ^ DMA_RX_WRAP_FLAG, &ithc->regs->dma_rx[channel].tail);
+ return 0;
+}
-+void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) {
-+ bitsb_set(&ithc->regs->dma_rx[channel].control, DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA);
-+ CHECK(waitl, ithc, &ithc->regs->dma_rx[1].status, DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED);
++
++void ithc_dma_rx_enable(struct ithc *ithc, u8 channel)
++{
++ bitsb_set(&ithc->regs->dma_rx[channel].control,
++ DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA);
++ CHECK(waitl, ithc, &ithc->regs->dma_rx[channel].status,
++ DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED);
+}
+
-+int ithc_dma_tx_init(struct ithc *ithc) {
++int ithc_dma_tx_init(struct ithc *ithc)
++{
+ struct ithc_dma_tx *tx = &ithc->dma_tx;
+ mutex_init(&tx->mutex);
++
++ // Allocate buffers.
+ tx->max_size = DEVCFG_DMA_TX_SIZE(ithc->config.dma_buf_sizes);
-+ unsigned num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE;
-+ pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", tx->max_size, num_pages);
++ unsigned int num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE;
++ pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n",
++ tx->max_size, num_pages);
+ CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE);
+ CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf);
++
++ // Init registers.
+ lo_hi_writeq(tx->prds.dma_addr, &ithc->regs->dma_tx.addr);
+ writeb(num_pages - 1, &ithc->regs->dma_tx.num_prds);
++
++ // Init buffers.
+ CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
+ return 0;
+}
+
-+static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data, u8 channel, u8 buf) {
++static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data,
++ u8 channel, u8 buf)
++{
+ if (buf >= NUM_RX_BUF) {
+ pci_err(ithc->pci, "invalid dma ringbuffer index\n");
+ return -EINVAL;
+ }
-+ ithc_set_active(ithc);
+ u32 len = data->data_size;
+ struct ithc_dma_rx_header *hdr = data->addr;
+ u8 *hiddata = (void *)(hdr + 1);
-+ if (len >= sizeof *hdr && hdr->code == DMA_RX_CODE_RESET) {
++ if (len >= sizeof(*hdr) && hdr->code == DMA_RX_CODE_RESET) {
++ // The THC sends a reset request when we need to reinitialize the device.
++ // This usually only happens if we send an invalid command or put the device
++ // in a bad state.
+ CHECK(ithc_reset, ithc);
-+ } else if (len < sizeof *hdr || len != sizeof *hdr + hdr->data_size) {
++ } else if (len < sizeof(*hdr) || len != sizeof(*hdr) + hdr->data_size) {
+ if (hdr->code == DMA_RX_CODE_INPUT_REPORT) {
-+ // When the CPU enters a low power state during DMA, we can get truncated messages.
-+ // Typically this will be a single touch HID report that is only 1 byte, or a multitouch report that is 257 bytes.
++ // When the CPU enters a low power state during DMA, we can get truncated
++ // messages. For Surface devices, this will typically be a single touch
++ // report that is only 1 byte, or a multitouch report that is 257 bytes.
+ // See also ithc_set_active().
+ } else {
-+ pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n", channel, buf, len, hdr->code, hdr->data_size);
-+ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0);
++ pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n",
++ channel, buf, len, hdr->code, hdr->data_size);
++ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1,
++ hdr, min(len, 0x400u), 0);
+ }
+ } else if (hdr->code == DMA_RX_CODE_REPORT_DESCRIPTOR && hdr->data_size > 8) {
++ // Response to a 'get report descriptor' request.
++ // The actual descriptor is preceded by 8 nul bytes.
+ CHECK(hid_parse_report, ithc->hid, hiddata + 8, hdr->data_size - 8);
+ WRITE_ONCE(ithc->hid_parse_done, true);
+ wake_up(&ithc->wait_hid_parse);
+ } else if (hdr->code == DMA_RX_CODE_INPUT_REPORT) {
++ // Standard HID input report containing touch data.
+ CHECK(hid_input_report, ithc->hid, HID_INPUT_REPORT, hiddata, hdr->data_size, 1);
+ } else if (hdr->code == DMA_RX_CODE_FEATURE_REPORT) {
++ // Response to a 'get feature' request.
+ bool done = false;
+ mutex_lock(&ithc->hid_get_feature_mutex);
+ if (ithc->hid_get_feature_buf) {
-+ if (hdr->data_size < ithc->hid_get_feature_size) ithc->hid_get_feature_size = hdr->data_size;
++ if (hdr->data_size < ithc->hid_get_feature_size)
++ ithc->hid_get_feature_size = hdr->data_size;
+ memcpy(ithc->hid_get_feature_buf, hiddata, ithc->hid_get_feature_size);
+ ithc->hid_get_feature_buf = NULL;
+ done = true;
+ }
+ mutex_unlock(&ithc->hid_get_feature_mutex);
-+ if (done) wake_up(&ithc->wait_hid_get_feature);
-+ else CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT, hiddata, hdr->data_size, 1);
++ if (done) {
++ wake_up(&ithc->wait_hid_get_feature);
++ } else {
++ // Received data without a matching request, or the request already
++ // timed out. (XXX What's the correct thing to do here?)
++ CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT,
++ hiddata, hdr->data_size, 1);
++ }
+ } else {
-+ pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n", channel, buf, len, hdr->code);
-+ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0);
++ pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n",
++ channel, buf, len, hdr->code);
++ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1,
++ hdr, min(len, 0x400u), 0);
+ }
+ return 0;
+}
+
-+static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) {
++static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel)
++{
++ // Process all filled RX buffers from the ringbuffer.
+ struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
-+ unsigned n = rx->num_received;
++ unsigned int n = rx->num_received;
+ u8 head_wrap = readb(&ithc->regs->dma_rx[channel].head);
+ while (1) {
+ u8 tail = n % NUM_RX_BUF;
@@ -4287,7 +4424,8 @@ index 0000000000000..7e89b3496918d
+ writeb(tail_wrap, &ithc->regs->dma_rx[channel].tail);
+ // ringbuffer is full if tail_wrap == head_wrap
+ // ringbuffer is empty if tail_wrap == head_wrap ^ WRAP_FLAG
-+ if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) return 0;
++ if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG))
++ return 0;
+
+ // take the buffer that the device just filled
+ struct ithc_dma_data_buffer *b = &rx->bufs[n % NUM_RX_BUF];
@@ -4301,7 +4439,8 @@ index 0000000000000..7e89b3496918d
+ CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail);
+ }
+}
-+int ithc_dma_rx(struct ithc *ithc, u8 channel) {
++int ithc_dma_rx(struct ithc *ithc, u8 channel)
++{
+ struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
+ mutex_lock(&rx->mutex);
+ int ret = ithc_dma_rx_unlocked(ithc, channel);
@@ -4309,14 +4448,21 @@ index 0000000000000..7e89b3496918d
+ return ret;
+}
+
-+static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) {
++static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data)
++{
++ ithc_set_active(ithc, 100 * USEC_PER_MSEC);
++
++ // Send a single TX buffer to the THC.
+ pci_dbg(ithc->pci, "dma tx command %u, size %u\n", cmdcode, datasize);
+ struct ithc_dma_tx_header *hdr;
++ // Data must be padded to next 4-byte boundary.
+ u8 padding = datasize & 3 ? 4 - (datasize & 3) : 0;
-+ unsigned fullsize = sizeof *hdr + datasize + padding;
-+ if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE) return -EINVAL;
++ unsigned int fullsize = sizeof(*hdr) + datasize + padding;
++ if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE)
++ return -EINVAL;
+ CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
+
++ // Fill the TX buffer with header and data.
+ ithc->dma_tx.buf.data_size = fullsize;
+ hdr = ithc->dma_tx.buf.addr;
+ hdr->code = cmdcode;
@@ -4324,15 +4470,18 @@ index 0000000000000..7e89b3496918d
+ u8 *dest = (void *)(hdr + 1);
+ memcpy(dest, data, datasize);
+ dest += datasize;
-+ for (u8 p = 0; p < padding; p++) *dest++ = 0;
++ for (u8 p = 0; p < padding; p++)
++ *dest++ = 0;
+ CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
+
++ // Let the THC process the buffer.
+ bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND);
+ CHECK_RET(waitb, ithc, &ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0);
+ writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status);
+ return 0;
+}
-+int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) {
++int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data)
++{
+ mutex_lock(&ithc->dma_tx.mutex);
+ int ret = ithc_dma_tx_unlocked(ithc, cmdcode, datasize, data);
+ mutex_unlock(&ithc->dma_tx.mutex);
@@ -4341,10 +4490,12 @@ index 0000000000000..7e89b3496918d
+
diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h
new file mode 100644
-index 0000000000000..d9f2c19a13f3a
+index 000000000000..93652e4476bf
--- /dev/null
+++ b/drivers/hid/ithc/ithc-dma.h
-@@ -0,0 +1,67 @@
+@@ -0,0 +1,69 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
+#define PRD_SIZE_MASK 0xffffff
+#define PRD_FLAG_END 0x1000000
+#define PRD_FLAG_SUCCESS 0x2000000
@@ -4406,7 +4557,7 @@ index 0000000000000..d9f2c19a13f3a
+ struct ithc_dma_data_buffer bufs[NUM_RX_BUF];
+};
+
-+int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname);
++int ithc_dma_rx_init(struct ithc *ithc, u8 channel);
+void ithc_dma_rx_enable(struct ithc *ithc, u8 channel);
+int ithc_dma_tx_init(struct ithc *ithc);
+int ithc_dma_rx(struct ithc *ithc, u8 channel);
@@ -4414,10 +4565,12 @@ index 0000000000000..d9f2c19a13f3a
+
diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c
new file mode 100644
-index 0000000000000..09512b9cb4d31
+index 000000000000..87ed4aa70fda
--- /dev/null
+++ b/drivers/hid/ithc/ithc-main.c
-@@ -0,0 +1,534 @@
+@@ -0,0 +1,728 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
+#include "ithc.h"
+
+MODULE_DESCRIPTION("Intel Touch Host Controller driver");
@@ -4462,6 +4615,9 @@ index 0000000000000..09512b9cb4d31
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT2) },
++ // XXX So far the THC seems to be the only Intel PCI device with PCI_CLASS_INPUT_PEN,
++ // so instead of the device list we could just do:
++ // { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .class = PCI_CLASS_INPUT_PEN, .class_mask = ~0, },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, ithc_pci_tbl);
@@ -4472,6 +4628,7 @@ index 0000000000000..09512b9cb4d31
+module_param_named(poll, ithc_use_polling, bool, 0);
+MODULE_PARM_DESC(poll, "Use polling instead of interrupts");
+
++// Since all known devices seem to use only channel 1, by default we disable channel 0.
+static bool ithc_use_rx0 = false;
+module_param_named(rx0, ithc_use_rx0, bool, 0);
+MODULE_PARM_DESC(rx0, "Use DMA RX channel 0");
@@ -4480,37 +4637,56 @@ index 0000000000000..09512b9cb4d31
+module_param_named(rx1, ithc_use_rx1, bool, 0);
+MODULE_PARM_DESC(rx1, "Use DMA RX channel 1");
+
++// Values below 250 seem to work well on the SP7+. If this is set too high, you may observe cursor stuttering.
++static int ithc_dma_latency_us = 200;
++module_param_named(dma_latency_us, ithc_dma_latency_us, int, 0);
++MODULE_PARM_DESC(dma_latency_us, "Determines the CPU latency QoS value for DMA transfers (in microseconds), -1 to disable latency QoS");
++
++// Values above 1700 seem to work well on the SP7+. If this is set too low, you may observe cursor stuttering.
++static unsigned int ithc_dma_early_us = 2000;
++module_param_named(dma_early_us, ithc_dma_early_us, uint, 0);
++MODULE_PARM_DESC(dma_early_us, "Determines how early the CPU latency QoS value is applied before the next expected IRQ (in microseconds)");
++
+static bool ithc_log_regs_enabled = false;
+module_param_named(logregs, ithc_log_regs_enabled, bool, 0);
+MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)");
+
+// Sysfs attributes
+
-+static bool ithc_is_config_valid(struct ithc *ithc) {
++static bool ithc_is_config_valid(struct ithc *ithc)
++{
+ return ithc->config.device_id == DEVCFG_DEVICE_ID_TIC;
+}
+
-+static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) {
++static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
+ struct ithc *ithc = dev_get_drvdata(dev);
-+ if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++ if (!ithc || !ithc_is_config_valid(ithc))
++ return -ENODEV;
+ return sprintf(buf, "0x%04x", ithc->config.vendor_id);
+}
+static DEVICE_ATTR_RO(vendor);
-+static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) {
++static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
+ struct ithc *ithc = dev_get_drvdata(dev);
-+ if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++ if (!ithc || !ithc_is_config_valid(ithc))
++ return -ENODEV;
+ return sprintf(buf, "0x%04x", ithc->config.product_id);
+}
+static DEVICE_ATTR_RO(product);
-+static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) {
++static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
+ struct ithc *ithc = dev_get_drvdata(dev);
-+ if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++ if (!ithc || !ithc_is_config_valid(ithc))
++ return -ENODEV;
+ return sprintf(buf, "%u", ithc->config.revision);
+}
+static DEVICE_ATTR_RO(revision);
-+static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) {
++static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
+ struct ithc *ithc = dev_get_drvdata(dev);
-+ if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
++ if (!ithc || !ithc_is_config_valid(ithc))
++ return -ENODEV;
+ u32 v = ithc->config.fw_version;
+ return sprintf(buf, "%i.%i.%i.%i", v >> 24, v >> 16 & 0xff, v >> 8 & 0xff, v & 0xff);
+}
@@ -4537,45 +4713,75 @@ index 0000000000000..09512b9cb4d31
+static int ithc_hid_open(struct hid_device *hdev) { return 0; }
+static void ithc_hid_close(struct hid_device *hdev) { }
+
-+static int ithc_hid_parse(struct hid_device *hdev) {
++static int ithc_hid_parse(struct hid_device *hdev)
++{
+ struct ithc *ithc = hdev->driver_data;
+ u64 val = 0;
+ WRITE_ONCE(ithc->hid_parse_done, false);
-+ CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof val, &val);
-+ if (!wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done), msecs_to_jiffies(1000))) return -ETIMEDOUT;
-+ return 0;
++ for (int retries = 0; ; retries++) {
++ CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof(val), &val);
++ if (wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done),
++ msecs_to_jiffies(200)))
++ return 0;
++ if (retries > 5) {
++ pci_err(ithc->pci, "failed to read report descriptor\n");
++ return -ETIMEDOUT;
++ }
++ pci_warn(ithc->pci, "failed to read report descriptor, retrying\n");
++ }
+}
+
-+static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype, int reqtype) {
++static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf,
++ size_t len, unsigned char rtype, int reqtype)
++{
+ struct ithc *ithc = hdev->driver_data;
-+ if (!buf || !len) return -EINVAL;
++ if (!buf || !len)
++ return -EINVAL;
+ u32 code;
-+ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_OUTPUT_REPORT;
-+ else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_SET_FEATURE;
-+ else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) code = DMA_TX_CODE_GET_FEATURE;
-+ else {
-+ pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", rtype, reqtype, reportnum);
++ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) {
++ code = DMA_TX_CODE_OUTPUT_REPORT;
++ } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) {
++ code = DMA_TX_CODE_SET_FEATURE;
++ } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) {
++ code = DMA_TX_CODE_GET_FEATURE;
++ } else {
++ pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n",
++ rtype, reqtype, reportnum);
+ return -EINVAL;
+ }
+ buf[0] = reportnum;
++
+ if (reqtype == HID_REQ_GET_REPORT) {
++ // Prepare for response.
+ mutex_lock(&ithc->hid_get_feature_mutex);
+ ithc->hid_get_feature_buf = buf;
+ ithc->hid_get_feature_size = len;
+ mutex_unlock(&ithc->hid_get_feature_mutex);
++
++ // Transmit 'get feature' request.
+ int r = CHECK(ithc_dma_tx, ithc, code, 1, buf);
+ if (!r) {
-+ r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature, !ithc->hid_get_feature_buf, msecs_to_jiffies(1000));
-+ if (!r) r = -ETIMEDOUT;
-+ else if (r < 0) r = -EINTR;
-+ else r = 0;
++ r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature,
++ !ithc->hid_get_feature_buf, msecs_to_jiffies(1000));
++ if (!r)
++ r = -ETIMEDOUT;
++ else if (r < 0)
++ r = -EINTR;
++ else
++ r = 0;
+ }
++
++ // If everything went ok, the buffer has been filled with the response data.
++ // Return the response size.
+ mutex_lock(&ithc->hid_get_feature_mutex);
+ ithc->hid_get_feature_buf = NULL;
-+ if (!r) r = ithc->hid_get_feature_size;
++ if (!r)
++ r = ithc->hid_get_feature_size;
+ mutex_unlock(&ithc->hid_get_feature_mutex);
+ return r;
+ }
++
++ // 'Set feature', or 'output report'. These don't have a response.
+ CHECK_RET(ithc_dma_tx, ithc, code, len, buf);
+ return 0;
+}
@@ -4589,17 +4795,22 @@ index 0000000000000..09512b9cb4d31
+ .raw_request = ithc_hid_raw_request,
+};
+
-+static void ithc_hid_devres_release(struct device *dev, void *res) {
++static void ithc_hid_devres_release(struct device *dev, void *res)
++{
+ struct hid_device **hidm = res;
-+ if (*hidm) hid_destroy_device(*hidm);
++ if (*hidm)
++ hid_destroy_device(*hidm);
+}
+
-+static int ithc_hid_init(struct ithc *ithc) {
-+ struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof *hidm, GFP_KERNEL);
-+ if (!hidm) return -ENOMEM;
++static int ithc_hid_init(struct ithc *ithc)
++{
++ struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL);
++ if (!hidm)
++ return -ENOMEM;
+ devres_add(&ithc->pci->dev, hidm);
+ struct hid_device *hid = hid_allocate_device();
-+ if (IS_ERR(hid)) return PTR_ERR(hid);
++ if (IS_ERR(hid))
++ return PTR_ERR(hid);
+ *hidm = hid;
+
+ strscpy(hid->name, DEVFULLNAME, sizeof(hid->name));
@@ -4618,27 +4829,45 @@ index 0000000000000..09512b9cb4d31
+
+// Interrupts/polling
+
-+static void ithc_activity_timer_callback(struct timer_list *t) {
-+ struct ithc *ithc = container_of(t, struct ithc, activity_timer);
++static enum hrtimer_restart ithc_activity_start_timer_callback(struct hrtimer *t)
++{
++ struct ithc *ithc = container_of(t, struct ithc, activity_start_timer);
++ ithc_set_active(ithc, ithc_dma_early_us * 2 + USEC_PER_MSEC);
++ return HRTIMER_NORESTART;
++}
++
++static enum hrtimer_restart ithc_activity_end_timer_callback(struct hrtimer *t)
++{
++ struct ithc *ithc = container_of(t, struct ithc, activity_end_timer);
+ cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
++ return HRTIMER_NORESTART;
+}
+
-+void ithc_set_active(struct ithc *ithc) {
++void ithc_set_active(struct ithc *ithc, unsigned int duration_us)
++{
++ if (ithc_dma_latency_us < 0)
++ return;
+ // When CPU usage is very low, the CPU can enter various low power states (C2-C10).
-+ // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_UNKNOWN_12 will be set when this happens.
-+ // The amount of truncated messages can become very high, resulting in user-visible effects (laggy/stuttering cursor).
-+ // To avoid this, we use a CPU latency QoS request to prevent the CPU from entering low power states during touch interactions.
-+ cpu_latency_qos_update_request(&ithc->activity_qos, 0);
-+ mod_timer(&ithc->activity_timer, jiffies + msecs_to_jiffies(1000));
++ // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_RX_TIMEOUT will be
++ // set when this happens. The amount of truncated messages can become very high, resulting
++ // in user-visible effects (laggy/stuttering cursor). To avoid this, we use a CPU latency
++ // QoS request to prevent the CPU from entering low power states during touch interactions.
++ cpu_latency_qos_update_request(&ithc->activity_qos, ithc_dma_latency_us);
++ hrtimer_start_range_ns(&ithc->activity_end_timer,
++ ns_to_ktime(duration_us * NSEC_PER_USEC), duration_us * NSEC_PER_USEC, HRTIMER_MODE_REL);
+}
+
-+static int ithc_set_device_enabled(struct ithc *ithc, bool enable) {
-+ u32 x = ithc->config.touch_cfg = (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2
-+ | (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0);
-+ return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, offsetof(struct ithc_device_config, touch_cfg), sizeof x, &x);
++static int ithc_set_device_enabled(struct ithc *ithc, bool enable)
++{
++ u32 x = ithc->config.touch_cfg =
++ (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2 |
++ (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0);
++ return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE,
++ offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x);
+}
+
-+static void ithc_disable_interrupts(struct ithc *ithc) {
++static void ithc_disable_interrupts(struct ithc *ithc)
++{
+ writel(0, &ithc->regs->error_control);
+ bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0);
+ bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0);
@@ -4646,43 +4875,85 @@ index 0000000000000..09512b9cb4d31
+ bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0);
+}
+
-+static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned channel) {
-+ writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA, &ithc->regs->dma_rx[channel].status);
++static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned int channel)
++{
++ writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA,
++ &ithc->regs->dma_rx[channel].status);
+}
+
-+static void ithc_clear_interrupts(struct ithc *ithc) {
++static void ithc_clear_interrupts(struct ithc *ithc)
++{
+ writel(0xffffffff, &ithc->regs->error_flags);
+ writel(ERROR_STATUS_DMA | ERROR_STATUS_SPI, &ithc->regs->error_status);
+ writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
+ ithc_clear_dma_rx_interrupts(ithc, 0);
+ ithc_clear_dma_rx_interrupts(ithc, 1);
-+ writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, &ithc->regs->dma_tx.status);
++ writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2,
++ &ithc->regs->dma_tx.status);
+}
+
-+static void ithc_process(struct ithc *ithc) {
++static void ithc_process(struct ithc *ithc)
++{
+ ithc_log_regs(ithc);
+
-+ // read and clear error bits
++ bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0;
++ bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0;
++
++ // Track time between DMA rx transfers, so we can try to predict when we need to enable CPU latency QoS for the next transfer
++ ktime_t t = ktime_get();
++ ktime_t dt = ktime_sub(t, ithc->last_rx_time);
++ if (rx0 || rx1) {
++ ithc->last_rx_time = t;
++ if (dt > ms_to_ktime(100)) {
++ ithc->cur_rx_seq_count = 0;
++ ithc->cur_rx_seq_errors = 0;
++ }
++ ithc->cur_rx_seq_count++;
++ if (!ithc_use_polling && ithc_dma_latency_us >= 0) {
++ // Disable QoS, since the DMA transfer has completed (we re-enable it after a delay below)
++ cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
++ hrtimer_try_to_cancel(&ithc->activity_end_timer);
++ }
++ }
++
++ // Read and clear error bits
+ u32 err = readl(&ithc->regs->error_flags);
+ if (err) {
-+ if (err & ~ERROR_FLAG_DMA_UNKNOWN_12) pci_err(ithc->pci, "error flags: 0x%08x\n", err);
+ writel(err, &ithc->regs->error_flags);
++ if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT)
++ pci_err(ithc->pci, "error flags: 0x%08x\n", err);
++ if (err & ERROR_FLAG_DMA_RX_TIMEOUT) {
++ // Only log an error if we see a significant number of these errors.
++ ithc->cur_rx_seq_errors++;
++ if (ithc->cur_rx_seq_errors && ithc->cur_rx_seq_errors % 50 == 0 && ithc->cur_rx_seq_errors > ithc->cur_rx_seq_count / 10)
++ pci_err(ithc->pci, "High number of DMA RX timeouts/errors (%u/%u, dt=%lldus). Try adjusting dma_early_us and/or dma_latency_us.\n",
++ ithc->cur_rx_seq_errors, ithc->cur_rx_seq_count, ktime_to_us(dt));
++ }
+ }
+
-+ // process DMA rx
++ // Process DMA rx
+ if (ithc_use_rx0) {
+ ithc_clear_dma_rx_interrupts(ithc, 0);
-+ ithc_dma_rx(ithc, 0);
++ if (rx0)
++ ithc_dma_rx(ithc, 0);
+ }
+ if (ithc_use_rx1) {
+ ithc_clear_dma_rx_interrupts(ithc, 1);
-+ ithc_dma_rx(ithc, 1);
++ if (rx1)
++ ithc_dma_rx(ithc, 1);
++ }
++
++ // Start timer to re-enable QoS for next rx, but only if we've seen an ERROR_FLAG_DMA_RX_TIMEOUT
++ if ((rx0 || rx1) && !ithc_use_polling && ithc_dma_latency_us >= 0 && ithc->cur_rx_seq_errors > 0) {
++ ktime_t expires = ktime_add(t, ktime_sub_us(dt, ithc_dma_early_us));
++ hrtimer_start_range_ns(&ithc->activity_start_timer, expires, 10 * NSEC_PER_USEC, HRTIMER_MODE_ABS);
+ }
+
+ ithc_log_regs(ithc);
+}
+
-+static irqreturn_t ithc_interrupt_thread(int irq, void *arg) {
++static irqreturn_t ithc_interrupt_thread(int irq, void *arg)
++{
+ struct ithc *ithc = arg;
+ pci_dbg(ithc->pci, "IRQ! err=%08x/%08x/%08x, cmd=%02x/%08x, rx0=%02x/%08x, rx1=%02x/%08x, tx=%02x/%08x\n",
+ readl(&ithc->regs->error_control), readl(&ithc->regs->error_status), readl(&ithc->regs->error_flags),
@@ -4694,14 +4965,21 @@ index 0000000000000..09512b9cb4d31
+ return IRQ_HANDLED;
+}
+
-+static int ithc_poll_thread(void *arg) {
++static int ithc_poll_thread(void *arg)
++{
+ struct ithc *ithc = arg;
-+ unsigned sleep = 100;
++ unsigned int sleep = 100;
+ while (!kthread_should_stop()) {
+ u32 n = ithc->dma_rx[1].num_received;
+ ithc_process(ithc);
-+ if (n != ithc->dma_rx[1].num_received) sleep = 20;
-+ else sleep = min(200u, sleep + (sleep >> 4) + 1);
++ // Decrease polling interval to 20ms if we received data, otherwise slowly
++ // increase it up to 200ms.
++ if (n != ithc->dma_rx[1].num_received) {
++ ithc_set_active(ithc, 100 * USEC_PER_MSEC);
++ sleep = 20;
++ } else {
++ sleep = min(200u, sleep + (sleep >> 4) + 1);
++ }
+ msleep_interruptible(sleep);
+ }
+ return 0;
@@ -4709,7 +4987,8 @@ index 0000000000000..09512b9cb4d31
+
+// Device initialization and shutdown
+
-+static void ithc_disable(struct ithc *ithc) {
++static void ithc_disable(struct ithc *ithc)
++{
+ bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE);
+ CHECK(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED);
+ bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0);
@@ -4721,81 +5000,112 @@ index 0000000000000..09512b9cb4d31
+ ithc_clear_interrupts(ithc);
+}
+
-+static int ithc_init_device(struct ithc *ithc) {
++static int ithc_init_device(struct ithc *ithc)
++{
+ ithc_log_regs(ithc);
+ bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0;
+ ithc_disable(ithc);
+ CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY);
++
++ // Since we don't yet know which SPI config the device wants, use default speed and mode
++ // initially for reading config data.
+ ithc_set_spi_config(ithc, 10, 0);
-+ bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000); // seems to help with reading config
+
-+ if (was_enabled) if (msleep_interruptible(100)) return -EINTR;
++ // Setting the following bit seems to make reading the config more reliable.
++ bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000);
++
++ // If the device was previously enabled, wait a bit to make sure it's fully shut down.
++ if (was_enabled)
++ if (msleep_interruptible(100))
++ return -EINTR;
++
++ // Take the touch device out of reset.
+ bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0);
+ CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0);
+ for (int retries = 0; ; retries++) {
+ ithc_log_regs(ithc);
+ bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET);
-+ if (!waitl(ithc, &ithc->regs->state, 0xf, 2)) break;
++ if (!waitl(ithc, &ithc->regs->state, 0xf, 2))
++ break;
+ if (retries > 5) {
-+ pci_err(ithc->pci, "too many retries, failed to reset device\n");
++ pci_err(ithc->pci, "failed to reset device, state = 0x%08x\n", readl(&ithc->regs->state));
+ return -ETIMEDOUT;
+ }
-+ pci_err(ithc->pci, "invalid state, retrying reset\n");
++ pci_warn(ithc->pci, "invalid state, retrying reset\n");
+ bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0);
-+ if (msleep_interruptible(1000)) return -EINTR;
++ if (msleep_interruptible(1000))
++ return -EINTR;
+ }
+ ithc_log_regs(ithc);
+
++ // Waiting for the following status bit makes reading config much more reliable,
++ // however the official driver does not seem to do this...
+ CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_UNKNOWN_4, DMA_RX_STATUS_UNKNOWN_4);
+
-+ // read config
++ // Read configuration data.
+ for (int retries = 0; ; retries++) {
+ ithc_log_regs(ithc);
-+ memset(&ithc->config, 0, sizeof ithc->config);
-+ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof ithc->config, &ithc->config);
++ memset(&ithc->config, 0, sizeof(ithc->config));
++ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(ithc->config), &ithc->config);
+ u32 *p = (void *)&ithc->config;
+ pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
-+ if (ithc_is_config_valid(ithc)) break;
++ if (ithc_is_config_valid(ithc))
++ break;
+ if (retries > 10) {
-+ pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", ithc->config.device_id);
++ pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n",
++ ithc->config.device_id);
+ return -EIO;
+ }
-+ pci_err(ithc->pci, "failed to read config, retrying\n");
-+ if (msleep_interruptible(100)) return -EINTR;
++ pci_warn(ithc->pci, "failed to read config, retrying\n");
++ if (msleep_interruptible(100))
++ return -EINTR;
+ }
+ ithc_log_regs(ithc);
+
-+ CHECK_RET(ithc_set_spi_config, ithc, DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config), DEVCFG_SPI_MODE(ithc->config.spi_config));
++ // Apply SPI config and enable touch device.
++ CHECK_RET(ithc_set_spi_config, ithc,
++ DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config),
++ DEVCFG_SPI_MODE(ithc->config.spi_config));
+ CHECK_RET(ithc_set_device_enabled, ithc, true);
+ ithc_log_regs(ithc);
+ return 0;
+}
+
-+int ithc_reset(struct ithc *ithc) {
-+ // FIXME This should probably do devres_release_group()+ithc_start(). But because this is called during DMA
-+ // processing, that would have to be done asynchronously (schedule_work()?). And with extra locking?
++int ithc_reset(struct ithc *ithc)
++{
++ // FIXME This should probably do devres_release_group()+ithc_start().
++ // But because this is called during DMA processing, that would have to be done
++ // asynchronously (schedule_work()?). And with extra locking?
+ pci_err(ithc->pci, "reset\n");
+ CHECK(ithc_init_device, ithc);
-+ if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0);
-+ if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1);
++ if (ithc_use_rx0)
++ ithc_dma_rx_enable(ithc, 0);
++ if (ithc_use_rx1)
++ ithc_dma_rx_enable(ithc, 1);
+ ithc_log_regs(ithc);
+ pci_dbg(ithc->pci, "reset completed\n");
+ return 0;
+}
+
-+static void ithc_stop(void *res) {
++static void ithc_stop(void *res)
++{
+ struct ithc *ithc = res;
+ pci_dbg(ithc->pci, "stopping\n");
+ ithc_log_regs(ithc);
-+ if (ithc->poll_thread) CHECK(kthread_stop, ithc->poll_thread);
-+ if (ithc->irq >= 0) disable_irq(ithc->irq);
++
++ if (ithc->poll_thread)
++ CHECK(kthread_stop, ithc->poll_thread);
++ if (ithc->irq >= 0)
++ disable_irq(ithc->irq);
+ CHECK(ithc_set_device_enabled, ithc, false);
+ ithc_disable(ithc);
-+ del_timer_sync(&ithc->activity_timer);
++ hrtimer_cancel(&ithc->activity_start_timer);
++ hrtimer_cancel(&ithc->activity_end_timer);
+ cpu_latency_qos_remove_request(&ithc->activity_qos);
-+ // clear dma config
-+ for(unsigned i = 0; i < 2; i++) {
++
++ // Clear DMA config.
++ for (unsigned int i = 0; i < 2; i++) {
+ CHECK(waitl, ithc, &ithc->regs->dma_rx[i].status, DMA_RX_STATUS_ENABLED, 0);
+ lo_hi_writeq(0, &ithc->regs->dma_rx[i].addr);
+ writeb(0, &ithc->regs->dma_rx[i].num_bufs);
@@ -4803,35 +5113,43 @@ index 0000000000000..09512b9cb4d31
+ }
+ lo_hi_writeq(0, &ithc->regs->dma_tx.addr);
+ writeb(0, &ithc->regs->dma_tx.num_prds);
++
+ ithc_log_regs(ithc);
+ pci_dbg(ithc->pci, "stopped\n");
+}
+
-+static void ithc_clear_drvdata(void *res) {
++static void ithc_clear_drvdata(void *res)
++{
+ struct pci_dev *pci = res;
+ pci_set_drvdata(pci, NULL);
+}
+
-+static int ithc_start(struct pci_dev *pci) {
++static int ithc_start(struct pci_dev *pci)
++{
+ pci_dbg(pci, "starting\n");
+ if (pci_get_drvdata(pci)) {
+ pci_err(pci, "device already initialized\n");
+ return -EINVAL;
+ }
-+ if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) return -ENOMEM;
++ if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL))
++ return -ENOMEM;
+
-+ struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof *ithc, GFP_KERNEL);
-+ if (!ithc) return -ENOMEM;
++ // Allocate/init main driver struct.
++ struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof(*ithc), GFP_KERNEL);
++ if (!ithc)
++ return -ENOMEM;
+ ithc->irq = -1;
+ ithc->pci = pci;
-+ snprintf(ithc->phys, sizeof ithc->phys, "pci-%s/" DEVNAME, pci_name(pci));
++ snprintf(ithc->phys, sizeof(ithc->phys), "pci-%s/" DEVNAME, pci_name(pci));
+ init_waitqueue_head(&ithc->wait_hid_parse);
+ init_waitqueue_head(&ithc->wait_hid_get_feature);
+ mutex_init(&ithc->hid_get_feature_mutex);
+ pci_set_drvdata(pci, ithc);
+ CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci);
-+ if (ithc_log_regs_enabled) ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof *ithc->prev_regs, GFP_KERNEL);
++ if (ithc_log_regs_enabled)
++ ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof(*ithc->prev_regs), GFP_KERNEL);
+
++ // PCI initialization.
+ CHECK_RET(pcim_enable_device, pci);
+ pci_set_master(pci);
+ CHECK_RET(pcim_iomap_regions, pci, BIT(0), DEVNAME " regs");
@@ -4839,29 +5157,39 @@ index 0000000000000..09512b9cb4d31
+ CHECK_RET(pci_set_power_state, pci, PCI_D0);
+ ithc->regs = pcim_iomap_table(pci)[0];
+
++ // Allocate IRQ.
+ if (!ithc_use_polling) {
+ CHECK_RET(pci_alloc_irq_vectors, pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
+ ithc->irq = CHECK(pci_irq_vector, pci, 0);
-+ if (ithc->irq < 0) return ithc->irq;
++ if (ithc->irq < 0)
++ return ithc->irq;
+ }
+
++ // Initialize THC and touch device.
+ CHECK_RET(ithc_init_device, ithc);
+ CHECK(devm_device_add_groups, &pci->dev, ithc_attribute_groups);
-+ if (ithc_use_rx0) CHECK_RET(ithc_dma_rx_init, ithc, 0, ithc_use_rx1 ? DEVNAME "0" : DEVNAME);
-+ if (ithc_use_rx1) CHECK_RET(ithc_dma_rx_init, ithc, 1, ithc_use_rx0 ? DEVNAME "1" : DEVNAME);
++ if (ithc_use_rx0)
++ CHECK_RET(ithc_dma_rx_init, ithc, 0);
++ if (ithc_use_rx1)
++ CHECK_RET(ithc_dma_rx_init, ithc, 1);
+ CHECK_RET(ithc_dma_tx_init, ithc);
+
-+ CHECK_RET(ithc_hid_init, ithc);
-+
+ cpu_latency_qos_add_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
-+ timer_setup(&ithc->activity_timer, ithc_activity_timer_callback, 0);
++ hrtimer_init(&ithc->activity_start_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
++ ithc->activity_start_timer.function = ithc_activity_start_timer_callback;
++ hrtimer_init(&ithc->activity_end_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ ithc->activity_end_timer.function = ithc_activity_end_timer_callback;
+
-+ // add ithc_stop callback AFTER setting up DMA buffers, so that polling/irqs/DMA are disabled BEFORE the buffers are freed
++ // Add ithc_stop() callback AFTER setting up DMA buffers, so that polling/irqs/DMA are
++ // disabled BEFORE the buffers are freed.
+ CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc);
+
++ CHECK_RET(ithc_hid_init, ithc);
++
++ // Start polling/IRQ.
+ if (ithc_use_polling) {
+ pci_info(pci, "using polling instead of irq\n");
-+ // use a thread instead of simple timer because we want to be able to sleep
++ // Use a thread instead of simple timer because we want to be able to sleep.
+ ithc->poll_thread = kthread_run(ithc_poll_thread, ithc, DEVNAME "poll");
+ if (IS_ERR(ithc->poll_thread)) {
+ int err = PTR_ERR(ithc->poll_thread);
@@ -4869,13 +5197,17 @@ index 0000000000000..09512b9cb4d31
+ return err;
+ }
+ } else {
-+ CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc);
++ CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL,
++ ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc);
+ }
+
-+ if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0);
-+ if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1);
++ if (ithc_use_rx0)
++ ithc_dma_rx_enable(ithc, 0);
++ if (ithc_use_rx1)
++ ithc_dma_rx_enable(ithc, 1);
+
-+ // hid_add_device can only be called after irq/polling is started and DMA is enabled, because it calls ithc_hid_parse which reads the report descriptor via DMA
++ // hid_add_device() can only be called after irq/polling is started and DMA is enabled,
++ // because it calls ithc_hid_parse() which reads the report descriptor via DMA.
+ CHECK_RET(hid_add_device, ithc->hid);
+
+ CHECK(ithc_debug_init, ithc);
@@ -4884,43 +5216,54 @@ index 0000000000000..09512b9cb4d31
+ return 0;
+}
+
-+static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) {
++static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id)
++{
+ pci_dbg(pci, "device probe\n");
+ return ithc_start(pci);
+}
+
-+static void ithc_remove(struct pci_dev *pci) {
++static void ithc_remove(struct pci_dev *pci)
++{
+ pci_dbg(pci, "device remove\n");
+ // all cleanup is handled by devres
+}
+
-+static int ithc_suspend(struct device *dev) {
++// For suspend/resume, we just deinitialize and reinitialize everything.
++// TODO It might be cleaner to keep the HID device around, however we would then have to signal
++// to userspace that the touch device has lost state and userspace needs to e.g. resend 'set
++// feature' requests. Hidraw does not seem to have a facility to do that.
++static int ithc_suspend(struct device *dev)
++{
+ struct pci_dev *pci = to_pci_dev(dev);
+ pci_dbg(pci, "pm suspend\n");
+ devres_release_group(dev, ithc_start);
+ return 0;
+}
+
-+static int ithc_resume(struct device *dev) {
++static int ithc_resume(struct device *dev)
++{
+ struct pci_dev *pci = to_pci_dev(dev);
+ pci_dbg(pci, "pm resume\n");
+ return ithc_start(pci);
+}
+
-+static int ithc_freeze(struct device *dev) {
++static int ithc_freeze(struct device *dev)
++{
+ struct pci_dev *pci = to_pci_dev(dev);
+ pci_dbg(pci, "pm freeze\n");
+ devres_release_group(dev, ithc_start);
+ return 0;
+}
+
-+static int ithc_thaw(struct device *dev) {
++static int ithc_thaw(struct device *dev)
++{
+ struct pci_dev *pci = to_pci_dev(dev);
+ pci_dbg(pci, "pm thaw\n");
+ return ithc_start(pci);
+}
+
-+static int ithc_restore(struct device *dev) {
++static int ithc_restore(struct device *dev)
++{
+ struct pci_dev *pci = to_pci_dev(dev);
+ pci_dbg(pci, "pm restore\n");
+ return ithc_start(pci);
@@ -4941,11 +5284,13 @@ index 0000000000000..09512b9cb4d31
+ //.dev_groups = ithc_attribute_groups, // could use this (since 5.14), however the attributes won't have valid values until config has been read anyway
+};
+
-+static int __init ithc_init(void) {
++static int __init ithc_init(void)
++{
+ return pci_register_driver(&ithc_driver);
+}
+
-+static void __exit ithc_exit(void) {
++static void __exit ithc_exit(void)
++{
+ pci_unregister_driver(&ithc_driver);
+}
+
@@ -4954,80 +5299,114 @@ index 0000000000000..09512b9cb4d31
+
diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c
new file mode 100644
-index 0000000000000..85d567b05761f
+index 000000000000..e058721886e3
--- /dev/null
+++ b/drivers/hid/ithc/ithc-regs.c
-@@ -0,0 +1,64 @@
+@@ -0,0 +1,96 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++
+#include "ithc.h"
+
+#define reg_num(r) (0x1fff & (u16)(__force u64)(r))
+
-+void bitsl(__iomem u32 *reg, u32 mask, u32 val) {
-+ if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask);
++void bitsl(__iomem u32 *reg, u32 mask, u32 val)
++{
++ if (val & ~mask)
++ pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n",
++ reg_num(reg), val, mask);
+ writel((readl(reg) & ~mask) | (val & mask), reg);
+}
+
-+void bitsb(__iomem u8 *reg, u8 mask, u8 val) {
-+ if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask);
++void bitsb(__iomem u8 *reg, u8 mask, u8 val)
++{
++ if (val & ~mask)
++ pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n",
++ reg_num(reg), val, mask);
+ writeb((readb(reg) & ~mask) | (val & mask), reg);
+}
+
-+int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) {
-+ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val);
++int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val)
++{
++ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n",
++ reg_num(reg), mask, val);
+ u32 x;
+ if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) {
-+ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val);
++ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n",
++ reg_num(reg), mask, val);
+ return -ETIMEDOUT;
+ }
+ pci_dbg(ithc->pci, "done waiting\n");
+ return 0;
+}
+
-+int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) {
-+ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val);
++int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val)
++{
++ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n",
++ reg_num(reg), mask, val);
+ u8 x;
+ if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) {
-+ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val);
++ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n",
++ reg_num(reg), mask, val);
+ return -ETIMEDOUT;
+ }
+ pci_dbg(ithc->pci, "done waiting\n");
+ return 0;
+}
+
-+int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode) {
++int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode)
++{
+ pci_dbg(ithc->pci, "setting SPI speed to %i, mode %i\n", speed, mode);
-+ if (mode == 3) mode = 2;
++ if (mode == 3)
++ mode = 2;
+ bitsl(&ithc->regs->spi_config,
+ SPI_CONFIG_MODE(0xff) | SPI_CONFIG_SPEED(0xff) | SPI_CONFIG_UNKNOWN_18(0xff) | SPI_CONFIG_SPEED2(0xff),
+ SPI_CONFIG_MODE(mode) | SPI_CONFIG_SPEED(speed) | SPI_CONFIG_UNKNOWN_18(0) | SPI_CONFIG_SPEED2(speed));
+ return 0;
+}
+
-+int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) {
++int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data)
++{
+ pci_dbg(ithc->pci, "SPI command %u, size %u, offset %u\n", command, size, offset);
-+ if (size > sizeof ithc->regs->spi_cmd.data) return -EINVAL;
++ if (size > sizeof(ithc->regs->spi_cmd.data))
++ return -EINVAL;
++
++ // Wait if the device is still busy.
+ CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0);
++ // Clear result flags.
+ writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
++
++ // Init SPI command data.
+ writeb(command, &ithc->regs->spi_cmd.code);
+ writew(size, &ithc->regs->spi_cmd.size);
+ writel(offset, &ithc->regs->spi_cmd.offset);
+ u32 *p = data, n = (size + 3) / 4;
-+ for (u32 i = 0; i < n; i++) writel(p[i], &ithc->regs->spi_cmd.data[i]);
++ for (u32 i = 0; i < n; i++)
++ writel(p[i], &ithc->regs->spi_cmd.data[i]);
++
++ // Start transmission.
+ bitsb_set(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND);
+ CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0);
-+ if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) return -EIO;
-+ if (readw(&ithc->regs->spi_cmd.size) != size) return -EMSGSIZE;
-+ for (u32 i = 0; i < n; i++) p[i] = readl(&ithc->regs->spi_cmd.data[i]);
++
++ // Read response.
++ if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE)
++ return -EIO;
++ if (readw(&ithc->regs->spi_cmd.size) != size)
++ return -EMSGSIZE;
++ for (u32 i = 0; i < n; i++)
++ p[i] = readl(&ithc->regs->spi_cmd.data[i]);
++
+ writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
+ return 0;
+}
+
diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h
new file mode 100644
-index 0000000000000..1a96092ed7eed
+index 000000000000..d4007d9e2bac
--- /dev/null
+++ b/drivers/hid/ithc/ithc-regs.h
-@@ -0,0 +1,186 @@
+@@ -0,0 +1,189 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
+#define CONTROL_QUIESCE BIT(1)
+#define CONTROL_IS_QUIESCED BIT(2)
+#define CONTROL_NRESET BIT(3)
@@ -5054,7 +5433,7 @@ index 0000000000000..1a96092ed7eed
+
+#define ERROR_FLAG_DMA_UNKNOWN_9 BIT(9)
+#define ERROR_FLAG_DMA_UNKNOWN_10 BIT(10)
-+#define ERROR_FLAG_DMA_UNKNOWN_12 BIT(12) // set when we receive a truncated DMA message
++#define ERROR_FLAG_DMA_RX_TIMEOUT BIT(12) // set when we receive a truncated DMA message
+#define ERROR_FLAG_DMA_UNKNOWN_13 BIT(13)
+#define ERROR_FLAG_SPI_BUS_TURNAROUND BIT(16)
+#define ERROR_FLAG_SPI_RESPONSE_TIMEOUT BIT(17)
@@ -5097,6 +5476,7 @@ index 0000000000000..1a96092ed7eed
+#define DMA_RX_STATUS_HAVE_DATA BIT(5)
+#define DMA_RX_STATUS_ENABLED BIT(8)
+
++// COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC.
+#define COUNTER_RESET BIT(31)
+
+struct ithc_registers {
@@ -5177,15 +5557,15 @@ index 0000000000000..1a96092ed7eed
+#define DEVCFG_SPI_MAX_FREQ(x) (((x) >> 1) & 0xf) // high bit = use high speed mode?
+#define DEVCFG_SPI_MODE(x) (((x) >> 6) & 3)
+#define DEVCFG_SPI_UNKNOWN_8(x) (((x) >> 8) & 0x3f)
-+#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20)
-+#define DEVCFG_SPI_HEARTBEAT_INTERVAL (((x) >> 21) & 7)
++#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat
++#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7)
+#define DEVCFG_SPI_UNKNOWN_25 BIT(25)
+#define DEVCFG_SPI_UNKNOWN_26 BIT(26)
+#define DEVCFG_SPI_UNKNOWN_27 BIT(27)
-+#define DEVCFG_SPI_DELAY (((x) >> 28) & 7)
-+#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31)
++#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this
++#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this?
+
-+struct ithc_device_config {
++struct ithc_device_config { // (Example values are from an SP7+.)
+ u32 _unknown_00; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET)
+ u32 _unknown_04; // 04 = 0x00000000
+ u32 dma_buf_sizes; // 08 = 0x000a00ff
@@ -5196,9 +5576,9 @@ index 0000000000000..1a96092ed7eed
+ u16 vendor_id; // 1c = 0x045e = Microsoft Corp.
+ u16 product_id; // 1e = 0x0c1a
+ u32 revision; // 20 = 0x00000001
-+ u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139
++ u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices)
+ u32 _unknown_28; // 28 = 0x00000000
-+ u32 fw_mode; // 2c = 0x00000000
++ u32 fw_mode; // 2c = 0x00000000 (for fw update?)
+ u32 _unknown_30; // 30 = 0x00000000
+ u32 _unknown_34; // 34 = 0x0404035e (u8,u8,u8,u8 = version?)
+ u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET)
@@ -5216,10 +5596,12 @@ index 0000000000000..1a96092ed7eed
+
diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h
new file mode 100644
-index 0000000000000..6a9b0d480bc15
+index 000000000000..028e55a4ec53
--- /dev/null
+++ b/drivers/hid/ithc/ithc.h
-@@ -0,0 +1,60 @@
+@@ -0,0 +1,67 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
++
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/hid.h>
@@ -5243,7 +5625,7 @@ index 0000000000000..6a9b0d480bc15
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define CHECK(fn, ...) ({ int r = fn(__VA_ARGS__); if (r < 0) pci_err(ithc->pci, "%s: %s failed with %i\n", __func__, #fn, r); r; })
-+#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while(0)
++#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while (0)
+
+#define NUM_RX_BUF 16
+
@@ -5257,8 +5639,13 @@ index 0000000000000..6a9b0d480bc15
+ struct pci_dev *pci;
+ int irq;
+ struct task_struct *poll_thread;
++
+ struct pm_qos_request activity_qos;
-+ struct timer_list activity_timer;
++ struct hrtimer activity_start_timer;
++ struct hrtimer activity_end_timer;
++ ktime_t last_rx_time;
++ unsigned int cur_rx_seq_count;
++ unsigned int cur_rx_seq_errors;
+
+ struct hid_device *hid;
+ bool hid_parse_done;
@@ -5276,1605 +5663,14 @@ index 0000000000000..6a9b0d480bc15
+};
+
+int ithc_reset(struct ithc *ithc);
-+void ithc_set_active(struct ithc *ithc);
++void ithc_set_active(struct ithc *ithc, unsigned int duration_us);
+int ithc_debug_init(struct ithc *ithc);
+void ithc_log_regs(struct ithc *ithc);
+
--
-2.42.0
-
-From 9f8d2a0f4012644f56ed8dfd322e575b57e1c208 Mon Sep 17 00:00:00 2001
-From: quo <tuple@list.ru>
-Date: Mon, 23 Oct 2023 10:15:29 +0200
-Subject: [PATCH] Update ITHC from module repo
-
-Changes:
- - Added some comments and fixed a few checkpatch warnings
- - Improved CPU latency QoS handling
- - Retry reading the report descriptor on error / timeout
-
-Based on https://github.com/quo/ithc-linux/commit/0b8b45d9775e756d6bd3a699bfaf9f5bd7b9b10b
-
-Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
-Patchset: ithc
----
- drivers/hid/ithc/ithc-debug.c | 94 +++++---
- drivers/hid/ithc/ithc-dma.c | 231 +++++++++++++-----
- drivers/hid/ithc/ithc-dma.h | 4 +-
- drivers/hid/ithc/ithc-main.c | 430 ++++++++++++++++++++++++----------
- drivers/hid/ithc/ithc-regs.c | 68 ++++--
- drivers/hid/ithc/ithc-regs.h | 19 +-
- drivers/hid/ithc/ithc.h | 13 +-
- 7 files changed, 623 insertions(+), 236 deletions(-)
-
-diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c
-index 57bf125c45bd5..1f1f1e33f2e5a 100644
---- a/drivers/hid/ithc/ithc-debug.c
-+++ b/drivers/hid/ithc/ithc-debug.c
-@@ -1,10 +1,14 @@
-+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
-+
- #include "ithc.h"
-
--void ithc_log_regs(struct ithc *ithc) {
-- if (!ithc->prev_regs) return;
-- u32 __iomem *cur = (__iomem void*)ithc->regs;
-- u32 *prev = (void*)ithc->prev_regs;
-- for (int i = 1024; i < sizeof *ithc->regs / 4; i++) {
-+void ithc_log_regs(struct ithc *ithc)
-+{
-+ if (!ithc->prev_regs)
-+ return;
-+ u32 __iomem *cur = (__iomem void *)ithc->regs;
-+ u32 *prev = (void *)ithc->prev_regs;
-+ for (int i = 1024; i < sizeof(*ithc->regs) / 4; i++) {
- u32 x = readl(cur + i);
- if (x != prev[i]) {
- pci_info(ithc->pci, "reg %04x: %08x -> %08x\n", i * 4, prev[i], x);
-@@ -13,55 +17,79 @@ void ithc_log_regs(struct ithc *ithc) {
- }
- }
-
--static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, loff_t *offset) {
-+static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len,
-+ loff_t *offset)
-+{
-+ // Debug commands consist of a single letter followed by a list of numbers (decimal or
-+ // hexadecimal, space-separated).
- struct ithc *ithc = file_inode(f)->i_private;
- char cmd[256];
-- if (!ithc || !ithc->pci) return -ENODEV;
-- if (!len) return -EINVAL;
-- if (len >= sizeof cmd) return -EINVAL;
-- if (copy_from_user(cmd, buf, len)) return -EFAULT;
-+ if (!ithc || !ithc->pci)
-+ return -ENODEV;
-+ if (!len)
-+ return -EINVAL;
-+ if (len >= sizeof(cmd))
-+ return -EINVAL;
-+ if (copy_from_user(cmd, buf, len))
-+ return -EFAULT;
- cmd[len] = 0;
-- if (cmd[len-1] == '\n') cmd[len-1] = 0;
-+ if (cmd[len-1] == '\n')
-+ cmd[len-1] = 0;
- pci_info(ithc->pci, "debug command: %s\n", cmd);
-+
-+ // Parse the list of arguments into a u32 array.
- u32 n = 0;
- const char *s = cmd + 1;
- u32 a[32];
- while (*s && *s != '\n') {
-- if (n >= ARRAY_SIZE(a)) return -EINVAL;
-- if (*s++ != ' ') return -EINVAL;
-+ if (n >= ARRAY_SIZE(a))
-+ return -EINVAL;
-+ if (*s++ != ' ')
-+ return -EINVAL;
- char *e;
- a[n++] = simple_strtoul(s, &e, 0);
-- if (e == s) return -EINVAL;
-+ if (e == s)
-+ return -EINVAL;
- s = e;
- }
- ithc_log_regs(ithc);
-- switch(cmd[0]) {
-+
-+ // Execute the command.
-+ switch (cmd[0]) {
- case 'x': // reset
- ithc_reset(ithc);
- break;
- case 'w': // write register: offset mask value
-- if (n != 3 || (a[0] & 3)) return -EINVAL;
-- pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", a[0], a[2], a[1]);
-+ if (n != 3 || (a[0] & 3))
-+ return -EINVAL;
-+ pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n",
-+ a[0], a[2], a[1]);
- bitsl(((__iomem u32 *)ithc->regs) + a[0] / 4, a[1], a[2]);
- break;
- case 'r': // read register: offset
-- if (n != 1 || (a[0] & 3)) return -EINVAL;
-- pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], readl(((__iomem u32 *)ithc->regs) + a[0] / 4));
-+ if (n != 1 || (a[0] & 3))
-+ return -EINVAL;
-+ pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0],
-+ readl(((__iomem u32 *)ithc->regs) + a[0] / 4));
- break;
- case 's': // spi command: cmd offset len data...
- // read config: s 4 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- // set touch cfg: s 6 12 4 XX
-- if (n < 3 || a[2] > (n - 3) * 4) return -EINVAL;
-+ if (n < 3 || a[2] > (n - 3) * 4)
-+ return -EINVAL;
- pci_info(ithc->pci, "debug spi command %u with %u bytes of data\n", a[0], a[2]);
- if (!CHECK(ithc_spi_command, ithc, a[0], a[1], a[2], a + 3))
-- for (u32 i = 0; i < (a[2] + 3) / 4; i++) pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]);
-+ for (u32 i = 0; i < (a[2] + 3) / 4; i++)
-+ pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]);
- break;
- case 'd': // dma command: cmd len data...
- // get report descriptor: d 7 8 0 0
- // enable multitouch: d 3 2 0x0105
-- if (n < 2 || a[1] > (n - 2) * 4) return -EINVAL;
-+ if (n < 2 || a[1] > (n - 2) * 4)
-+ return -EINVAL;
- pci_info(ithc->pci, "debug dma command %u with %u bytes of data\n", a[0], a[1]);
-- if (ithc_dma_tx(ithc, a[0], a[1], a + 2)) pci_err(ithc->pci, "dma tx failed\n");
-+ if (ithc_dma_tx(ithc, a[0], a[1], a + 2))
-+ pci_err(ithc->pci, "dma tx failed\n");
- break;
- default:
- return -EINVAL;
-@@ -75,21 +103,27 @@ static const struct file_operations ithc_debugfops_cmd = {
- .write = ithc_debugfs_cmd_write,
- };
-
--static void ithc_debugfs_devres_release(struct device *dev, void *res) {
-+static void ithc_debugfs_devres_release(struct device *dev, void *res)
-+{
- struct dentry **dbgm = res;
-- if (*dbgm) debugfs_remove_recursive(*dbgm);
-+ if (*dbgm)
-+ debugfs_remove_recursive(*dbgm);
- }
-
--int ithc_debug_init(struct ithc *ithc) {
-- struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof *dbgm, GFP_KERNEL);
-- if (!dbgm) return -ENOMEM;
-+int ithc_debug_init(struct ithc *ithc)
-+{
-+ struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof(*dbgm), GFP_KERNEL);
-+ if (!dbgm)
-+ return -ENOMEM;
- devres_add(&ithc->pci->dev, dbgm);
- struct dentry *dbg = debugfs_create_dir(DEVNAME, NULL);
-- if (IS_ERR(dbg)) return PTR_ERR(dbg);
-+ if (IS_ERR(dbg))
-+ return PTR_ERR(dbg);
- *dbgm = dbg;
-
- struct dentry *cmd = debugfs_create_file("cmd", 0220, dbg, ithc, &ithc_debugfops_cmd);
-- if (IS_ERR(cmd)) return PTR_ERR(cmd);
-+ if (IS_ERR(cmd))
-+ return PTR_ERR(cmd);
-
- return 0;
- }
-diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c
-index 7e89b3496918d..ffb8689b8a780 100644
---- a/drivers/hid/ithc/ithc-dma.c
-+++ b/drivers/hid/ithc/ithc-dma.c
-@@ -1,59 +1,91 @@
-+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
-+
- #include "ithc.h"
-
--static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, unsigned num_buffers, unsigned num_pages, enum dma_data_direction dir) {
-+// The THC uses tables of PRDs (physical region descriptors) to describe the TX and RX data buffers.
-+// Each PRD contains the DMA address and size of a block of DMA memory, and some status flags.
-+// This allows each data buffer to consist of multiple non-contiguous blocks of memory.
-+
-+static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p,
-+ unsigned int num_buffers, unsigned int num_pages, enum dma_data_direction dir)
-+{
- p->num_pages = num_pages;
- p->dir = dir;
-+ // We allocate enough space to have one PRD per data buffer page, however if the data
-+ // buffer pages happen to be contiguous, we can describe the buffer using fewer PRDs, so
-+ // some will remain unused (which is fine).
- p->size = round_up(num_buffers * num_pages * sizeof(struct ithc_phys_region_desc), PAGE_SIZE);
- p->addr = dmam_alloc_coherent(&ithc->pci->dev, p->size, &p->dma_addr, GFP_KERNEL);
-- if (!p->addr) return -ENOMEM;
-- if (p->dma_addr & (PAGE_SIZE - 1)) return -EFAULT;
-+ if (!p->addr)
-+ return -ENOMEM;
-+ if (p->dma_addr & (PAGE_SIZE - 1))
-+ return -EFAULT;
- return 0;
- }
-
-+// Devres managed sg_table wrapper.
- struct ithc_sg_table {
- void *addr;
- struct sg_table sgt;
- enum dma_data_direction dir;
- };
--static void ithc_dma_sgtable_free(struct sg_table *sgt) {
-+static void ithc_dma_sgtable_free(struct sg_table *sgt)
-+{
- struct scatterlist *sg;
- int i;
- for_each_sgtable_sg(sgt, sg, i) {
- struct page *p = sg_page(sg);
-- if (p) __free_page(p);
-+ if (p)
-+ __free_page(p);
- }
- sg_free_table(sgt);
- }
--static void ithc_dma_data_devres_release(struct device *dev, void *res) {
-+static void ithc_dma_data_devres_release(struct device *dev, void *res)
-+{
- struct ithc_sg_table *sgt = res;
-- if (sgt->addr) vunmap(sgt->addr);
-+ if (sgt->addr)
-+ vunmap(sgt->addr);
- dma_unmap_sgtable(dev, &sgt->sgt, sgt->dir, 0);
- ithc_dma_sgtable_free(&sgt->sgt);
- }
-
--static int ithc_dma_data_alloc(struct ithc* ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b) {
-- // We don't use dma_alloc_coherent for data buffers, because they don't have to be contiguous (we can use one PRD per page) or coherent (they are unidirectional).
-- // Instead we use an sg_table of individually allocated pages (5.13 has dma_alloc_noncontiguous for this, but we'd like to support 5.10 for now).
-+static int ithc_dma_data_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *prds,
-+ struct ithc_dma_data_buffer *b)
-+{
-+ // We don't use dma_alloc_coherent() for data buffers, because they don't have to be
-+ // coherent (they are unidirectional) or contiguous (we can use one PRD per page).
-+ // We could use dma_alloc_noncontiguous(), however this still always allocates a single
-+ // DMA mapped segment, which is more restrictive than what we need.
-+ // Instead we use an sg_table of individually allocated pages.
- struct page *pages[16];
-- if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) return -EINVAL;
-+ if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages))
-+ return -EINVAL;
- b->active_idx = -1;
-- struct ithc_sg_table *sgt = devres_alloc(ithc_dma_data_devres_release, sizeof *sgt, GFP_KERNEL);
-- if (!sgt) return -ENOMEM;
-+ struct ithc_sg_table *sgt = devres_alloc(
-+ ithc_dma_data_devres_release, sizeof(*sgt), GFP_KERNEL);
-+ if (!sgt)
-+ return -ENOMEM;
- sgt->dir = prds->dir;
-+
- if (!sg_alloc_table(&sgt->sgt, prds->num_pages, GFP_KERNEL)) {
- struct scatterlist *sg;
- int i;
- bool ok = true;
- for_each_sgtable_sg(&sgt->sgt, sg, i) {
-- struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); // don't need __GFP_DMA for PCI DMA
-- if (!p) { ok = false; break; }
-+ // NOTE: don't need __GFP_DMA for PCI DMA
-+ struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
-+ if (!p) {
-+ ok = false;
-+ break;
-+ }
- sg_set_page(sg, p, PAGE_SIZE, 0);
- }
- if (ok && !dma_map_sgtable(&ithc->pci->dev, &sgt->sgt, prds->dir, 0)) {
- devres_add(&ithc->pci->dev, sgt);
- b->sgt = &sgt->sgt;
- b->addr = sgt->addr = vmap(pages, prds->num_pages, 0, PAGE_KERNEL);
-- if (!b->addr) return -ENOMEM;
-+ if (!b->addr)
-+ return -ENOMEM;
- return 0;
- }
- ithc_dma_sgtable_free(&sgt->sgt);
-@@ -62,17 +94,29 @@ static int ithc_dma_data_alloc(struct ithc* ithc, struct ithc_dma_prd_buffer *pr
- return -ENOMEM;
- }
-
--static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) {
-+static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds,
-+ struct ithc_dma_data_buffer *b, unsigned int idx)
-+{
-+ // Give a buffer to the THC.
- struct ithc_phys_region_desc *prd = prds->addr;
- prd += idx * prds->num_pages;
-- if (b->active_idx >= 0) { pci_err(ithc->pci, "buffer already active\n"); return -EINVAL; }
-+ if (b->active_idx >= 0) {
-+ pci_err(ithc->pci, "buffer already active\n");
-+ return -EINVAL;
-+ }
- b->active_idx = idx;
- if (prds->dir == DMA_TO_DEVICE) {
-- if (b->data_size > PAGE_SIZE) return -EINVAL;
-+ // TX buffer: Caller should have already filled the data buffer, so just fill
-+ // the PRD and flush.
-+ // (TODO: Support multi-page TX buffers. So far no device seems to use or need
-+ // these though.)
-+ if (b->data_size > PAGE_SIZE)
-+ return -EINVAL;
- prd->addr = sg_dma_address(b->sgt->sgl) >> 10;
- prd->size = b->data_size | PRD_FLAG_END;
- flush_kernel_vmap_range(b->addr, b->data_size);
- } else if (prds->dir == DMA_FROM_DEVICE) {
-+ // RX buffer: Reset PRDs.
- struct scatterlist *sg;
- int i;
- for_each_sgtable_dma_sg(b->sgt, sg, i) {
-@@ -87,21 +131,34 @@ static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffe
- return 0;
- }
-
--static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, struct ithc_dma_data_buffer *b, unsigned idx) {
-+static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds,
-+ struct ithc_dma_data_buffer *b, unsigned int idx)
-+{
-+ // Take a buffer from the THC.
- struct ithc_phys_region_desc *prd = prds->addr;
- prd += idx * prds->num_pages;
-- if (b->active_idx != idx) { pci_err(ithc->pci, "wrong buffer index\n"); return -EINVAL; }
-+ // This is purely a sanity check. We don't strictly need the idx parameter for this
-+ // function, because it should always be the same as active_idx, unless we have a bug.
-+ if (b->active_idx != idx) {
-+ pci_err(ithc->pci, "wrong buffer index\n");
-+ return -EINVAL;
-+ }
- b->active_idx = -1;
- if (prds->dir == DMA_FROM_DEVICE) {
-+ // RX buffer: Calculate actual received data size from PRDs.
- dma_rmb(); // for the prds
- b->data_size = 0;
- struct scatterlist *sg;
- int i;
- for_each_sgtable_dma_sg(b->sgt, sg, i) {
-- unsigned size = prd->size;
-+ unsigned int size = prd->size;
- b->data_size += size & PRD_SIZE_MASK;
-- if (size & PRD_FLAG_END) break;
-- if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { pci_err(ithc->pci, "truncated prd\n"); break; }
-+ if (size & PRD_FLAG_END)
-+ break;
-+ if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) {
-+ pci_err(ithc->pci, "truncated prd\n");
-+ break;
-+ }
- prd++;
- }
- invalidate_kernel_vmap_range(b->addr, b->data_size);
-@@ -110,93 +167,139 @@ static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffe
- return 0;
- }
-
--int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname) {
-+int ithc_dma_rx_init(struct ithc *ithc, u8 channel)
-+{
- struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
- mutex_init(&rx->mutex);
-+
-+ // Allocate buffers.
- u32 buf_size = DEVCFG_DMA_RX_SIZE(ithc->config.dma_buf_sizes);
-- unsigned num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE;
-- pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", NUM_RX_BUF, buf_size, num_pages);
-+ unsigned int num_pages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE;
-+ pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n",
-+ NUM_RX_BUF, buf_size, num_pages);
- CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE);
-- for (unsigned i = 0; i < NUM_RX_BUF; i++)
-+ for (unsigned int i = 0; i < NUM_RX_BUF; i++)
- CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]);
-+
-+ // Init registers.
- writeb(DMA_RX_CONTROL2_RESET, &ithc->regs->dma_rx[channel].control2);
- lo_hi_writeq(rx->prds.dma_addr, &ithc->regs->dma_rx[channel].addr);
- writeb(NUM_RX_BUF - 1, &ithc->regs->dma_rx[channel].num_bufs);
- writeb(num_pages - 1, &ithc->regs->dma_rx[channel].num_prds);
- u8 head = readb(&ithc->regs->dma_rx[channel].head);
-- if (head) { pci_err(ithc->pci, "head is nonzero (%u)\n", head); return -EIO; }
-- for (unsigned i = 0; i < NUM_RX_BUF; i++)
-+ if (head) {
-+ pci_err(ithc->pci, "head is nonzero (%u)\n", head);
-+ return -EIO;
-+ }
-+
-+ // Init buffers.
-+ for (unsigned int i = 0; i < NUM_RX_BUF; i++)
- CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, &rx->bufs[i], i);
-+
- writeb(head ^ DMA_RX_WRAP_FLAG, &ithc->regs->dma_rx[channel].tail);
- return 0;
- }
--void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) {
-- bitsb_set(&ithc->regs->dma_rx[channel].control, DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA);
-- CHECK(waitl, ithc, &ithc->regs->dma_rx[1].status, DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED);
-+
-+void ithc_dma_rx_enable(struct ithc *ithc, u8 channel)
-+{
-+ bitsb_set(&ithc->regs->dma_rx[channel].control,
-+ DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA);
-+ CHECK(waitl, ithc, &ithc->regs->dma_rx[channel].status,
-+ DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED);
- }
-
--int ithc_dma_tx_init(struct ithc *ithc) {
-+int ithc_dma_tx_init(struct ithc *ithc)
-+{
- struct ithc_dma_tx *tx = &ithc->dma_tx;
- mutex_init(&tx->mutex);
-+
-+ // Allocate buffers.
- tx->max_size = DEVCFG_DMA_TX_SIZE(ithc->config.dma_buf_sizes);
-- unsigned num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE;
-- pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", tx->max_size, num_pages);
-+ unsigned int num_pages = (tx->max_size + PAGE_SIZE - 1) / PAGE_SIZE;
-+ pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n",
-+ tx->max_size, num_pages);
- CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE);
- CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf);
-+
-+ // Init registers.
- lo_hi_writeq(tx->prds.dma_addr, &ithc->regs->dma_tx.addr);
- writeb(num_pages - 1, &ithc->regs->dma_tx.num_prds);
-+
-+ // Init buffers.
- CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
- return 0;
- }
-
--static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data, u8 channel, u8 buf) {
-+static int ithc_dma_rx_process_buf(struct ithc *ithc, struct ithc_dma_data_buffer *data,
-+ u8 channel, u8 buf)
-+{
- if (buf >= NUM_RX_BUF) {
- pci_err(ithc->pci, "invalid dma ringbuffer index\n");
- return -EINVAL;
- }
-- ithc_set_active(ithc);
- u32 len = data->data_size;
- struct ithc_dma_rx_header *hdr = data->addr;
- u8 *hiddata = (void *)(hdr + 1);
-- if (len >= sizeof *hdr && hdr->code == DMA_RX_CODE_RESET) {
-+ if (len >= sizeof(*hdr) && hdr->code == DMA_RX_CODE_RESET) {
-+ // The THC sends a reset request when we need to reinitialize the device.
-+ // This usually only happens if we send an invalid command or put the device
-+ // in a bad state.
- CHECK(ithc_reset, ithc);
-- } else if (len < sizeof *hdr || len != sizeof *hdr + hdr->data_size) {
-+ } else if (len < sizeof(*hdr) || len != sizeof(*hdr) + hdr->data_size) {
- if (hdr->code == DMA_RX_CODE_INPUT_REPORT) {
-- // When the CPU enters a low power state during DMA, we can get truncated messages.
-- // Typically this will be a single touch HID report that is only 1 byte, or a multitouch report that is 257 bytes.
-+ // When the CPU enters a low power state during DMA, we can get truncated
-+ // messages. For Surface devices, this will typically be a single touch
-+ // report that is only 1 byte, or a multitouch report that is 257 bytes.
- // See also ithc_set_active().
- } else {
-- pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n", channel, buf, len, hdr->code, hdr->data_size);
-- print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0);
-+ pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u, code %u, data size %u\n",
-+ channel, buf, len, hdr->code, hdr->data_size);
-+ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1,
-+ hdr, min(len, 0x400u), 0);
- }
- } else if (hdr->code == DMA_RX_CODE_REPORT_DESCRIPTOR && hdr->data_size > 8) {
-+ // Response to a 'get report descriptor' request.
-+ // The actual descriptor is preceded by 8 nul bytes.
- CHECK(hid_parse_report, ithc->hid, hiddata + 8, hdr->data_size - 8);
- WRITE_ONCE(ithc->hid_parse_done, true);
- wake_up(&ithc->wait_hid_parse);
- } else if (hdr->code == DMA_RX_CODE_INPUT_REPORT) {
-+ // Standard HID input report containing touch data.
- CHECK(hid_input_report, ithc->hid, HID_INPUT_REPORT, hiddata, hdr->data_size, 1);
- } else if (hdr->code == DMA_RX_CODE_FEATURE_REPORT) {
-+ // Response to a 'get feature' request.
- bool done = false;
- mutex_lock(&ithc->hid_get_feature_mutex);
- if (ithc->hid_get_feature_buf) {
-- if (hdr->data_size < ithc->hid_get_feature_size) ithc->hid_get_feature_size = hdr->data_size;
-+ if (hdr->data_size < ithc->hid_get_feature_size)
-+ ithc->hid_get_feature_size = hdr->data_size;
- memcpy(ithc->hid_get_feature_buf, hiddata, ithc->hid_get_feature_size);
- ithc->hid_get_feature_buf = NULL;
- done = true;
- }
- mutex_unlock(&ithc->hid_get_feature_mutex);
-- if (done) wake_up(&ithc->wait_hid_get_feature);
-- else CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT, hiddata, hdr->data_size, 1);
-+ if (done) {
-+ wake_up(&ithc->wait_hid_get_feature);
-+ } else {
-+ // Received data without a matching request, or the request already
-+ // timed out. (XXX What's the correct thing to do here?)
-+ CHECK(hid_input_report, ithc->hid, HID_FEATURE_REPORT,
-+ hiddata, hdr->data_size, 1);
-+ }
- } else {
-- pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n", channel, buf, len, hdr->code);
-- print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, hdr, min(len, 0x400u), 0);
-+ pci_dbg(ithc->pci, "unhandled dma rx data! channel %u, buffer %u, size %u, code %u\n",
-+ channel, buf, len, hdr->code);
-+ print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1,
-+ hdr, min(len, 0x400u), 0);
- }
- return 0;
- }
-
--static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) {
-+static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel)
-+{
-+ // Process all filled RX buffers from the ringbuffer.
- struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
-- unsigned n = rx->num_received;
-+ unsigned int n = rx->num_received;
- u8 head_wrap = readb(&ithc->regs->dma_rx[channel].head);
- while (1) {
- u8 tail = n % NUM_RX_BUF;
-@@ -204,7 +307,8 @@ static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) {
- writeb(tail_wrap, &ithc->regs->dma_rx[channel].tail);
- // ringbuffer is full if tail_wrap == head_wrap
- // ringbuffer is empty if tail_wrap == head_wrap ^ WRAP_FLAG
-- if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) return 0;
-+ if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG))
-+ return 0;
-
- // take the buffer that the device just filled
- struct ithc_dma_data_buffer *b = &rx->bufs[n % NUM_RX_BUF];
-@@ -218,7 +322,8 @@ static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) {
- CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail);
- }
- }
--int ithc_dma_rx(struct ithc *ithc, u8 channel) {
-+int ithc_dma_rx(struct ithc *ithc, u8 channel)
-+{
- struct ithc_dma_rx *rx = &ithc->dma_rx[channel];
- mutex_lock(&rx->mutex);
- int ret = ithc_dma_rx_unlocked(ithc, channel);
-@@ -226,14 +331,21 @@ int ithc_dma_rx(struct ithc *ithc, u8 channel) {
- return ret;
- }
-
--static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) {
-+static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data)
-+{
-+ ithc_set_active(ithc, 100 * USEC_PER_MSEC);
-+
-+ // Send a single TX buffer to the THC.
- pci_dbg(ithc->pci, "dma tx command %u, size %u\n", cmdcode, datasize);
- struct ithc_dma_tx_header *hdr;
-+ // Data must be padded to next 4-byte boundary.
- u8 padding = datasize & 3 ? 4 - (datasize & 3) : 0;
-- unsigned fullsize = sizeof *hdr + datasize + padding;
-- if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE) return -EINVAL;
-+ unsigned int fullsize = sizeof(*hdr) + datasize + padding;
-+ if (fullsize > ithc->dma_tx.max_size || fullsize > PAGE_SIZE)
-+ return -EINVAL;
- CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
-
-+ // Fill the TX buffer with header and data.
- ithc->dma_tx.buf.data_size = fullsize;
- hdr = ithc->dma_tx.buf.addr;
- hdr->code = cmdcode;
-@@ -241,15 +353,18 @@ static int ithc_dma_tx_unlocked(struct ithc *ithc, u32 cmdcode, u32 datasize, vo
- u8 *dest = (void *)(hdr + 1);
- memcpy(dest, data, datasize);
- dest += datasize;
-- for (u8 p = 0; p < padding; p++) *dest++ = 0;
-+ for (u8 p = 0; p < padding; p++)
-+ *dest++ = 0;
- CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0);
-
-+ // Let the THC process the buffer.
- bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND);
- CHECK_RET(waitb, ithc, &ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0);
- writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status);
- return 0;
- }
--int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data) {
-+int ithc_dma_tx(struct ithc *ithc, u32 cmdcode, u32 datasize, void *data)
-+{
- mutex_lock(&ithc->dma_tx.mutex);
- int ret = ithc_dma_tx_unlocked(ithc, cmdcode, datasize, data);
- mutex_unlock(&ithc->dma_tx.mutex);
-diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h
-index d9f2c19a13f3a..93652e4476bf8 100644
---- a/drivers/hid/ithc/ithc-dma.h
-+++ b/drivers/hid/ithc/ithc-dma.h
-@@ -1,3 +1,5 @@
-+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
-+
- #define PRD_SIZE_MASK 0xffffff
- #define PRD_FLAG_END 0x1000000
- #define PRD_FLAG_SUCCESS 0x2000000
-@@ -59,7 +61,7 @@ struct ithc_dma_rx {
- struct ithc_dma_data_buffer bufs[NUM_RX_BUF];
- };
-
--int ithc_dma_rx_init(struct ithc *ithc, u8 channel, const char *devname);
-+int ithc_dma_rx_init(struct ithc *ithc, u8 channel);
- void ithc_dma_rx_enable(struct ithc *ithc, u8 channel);
- int ithc_dma_tx_init(struct ithc *ithc);
- int ithc_dma_rx(struct ithc *ithc, u8 channel);
-diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c
-index 09512b9cb4d31..87ed4aa70fda0 100644
---- a/drivers/hid/ithc/ithc-main.c
-+++ b/drivers/hid/ithc/ithc-main.c
-@@ -1,3 +1,5 @@
-+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
-+
- #include "ithc.h"
-
- MODULE_DESCRIPTION("Intel Touch Host Controller driver");
-@@ -42,6 +44,9 @@ static const struct pci_device_id ithc_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT1) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_MTL_PORT2) },
-+ // XXX So far the THC seems to be the only Intel PCI device with PCI_CLASS_INPUT_PEN,
-+ // so instead of the device list we could just do:
-+ // { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .class = PCI_CLASS_INPUT_PEN, .class_mask = ~0, },
- {}
- };
- MODULE_DEVICE_TABLE(pci, ithc_pci_tbl);
-@@ -52,6 +57,7 @@ static bool ithc_use_polling = false;
- module_param_named(poll, ithc_use_polling, bool, 0);
- MODULE_PARM_DESC(poll, "Use polling instead of interrupts");
-
-+// Since all known devices seem to use only channel 1, by default we disable channel 0.
- static bool ithc_use_rx0 = false;
- module_param_named(rx0, ithc_use_rx0, bool, 0);
- MODULE_PARM_DESC(rx0, "Use DMA RX channel 0");
-@@ -60,37 +66,56 @@ static bool ithc_use_rx1 = true;
- module_param_named(rx1, ithc_use_rx1, bool, 0);
- MODULE_PARM_DESC(rx1, "Use DMA RX channel 1");
-
-+// Values below 250 seem to work well on the SP7+. If this is set too high, you may observe cursor stuttering.
-+static int ithc_dma_latency_us = 200;
-+module_param_named(dma_latency_us, ithc_dma_latency_us, int, 0);
-+MODULE_PARM_DESC(dma_latency_us, "Determines the CPU latency QoS value for DMA transfers (in microseconds), -1 to disable latency QoS");
-+
-+// Values above 1700 seem to work well on the SP7+. If this is set too low, you may observe cursor stuttering.
-+static unsigned int ithc_dma_early_us = 2000;
-+module_param_named(dma_early_us, ithc_dma_early_us, uint, 0);
-+MODULE_PARM_DESC(dma_early_us, "Determines how early the CPU latency QoS value is applied before the next expected IRQ (in microseconds)");
-+
- static bool ithc_log_regs_enabled = false;
- module_param_named(logregs, ithc_log_regs_enabled, bool, 0);
- MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)");
-
- // Sysfs attributes
-
--static bool ithc_is_config_valid(struct ithc *ithc) {
-+static bool ithc_is_config_valid(struct ithc *ithc)
-+{
- return ithc->config.device_id == DEVCFG_DEVICE_ID_TIC;
- }
-
--static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) {
-+static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
- struct ithc *ithc = dev_get_drvdata(dev);
-- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
-+ if (!ithc || !ithc_is_config_valid(ithc))
-+ return -ENODEV;
- return sprintf(buf, "0x%04x", ithc->config.vendor_id);
- }
- static DEVICE_ATTR_RO(vendor);
--static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf) {
-+static ssize_t product_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
- struct ithc *ithc = dev_get_drvdata(dev);
-- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
-+ if (!ithc || !ithc_is_config_valid(ithc))
-+ return -ENODEV;
- return sprintf(buf, "0x%04x", ithc->config.product_id);
- }
- static DEVICE_ATTR_RO(product);
--static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) {
-+static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
- struct ithc *ithc = dev_get_drvdata(dev);
-- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
-+ if (!ithc || !ithc_is_config_valid(ithc))
-+ return -ENODEV;
- return sprintf(buf, "%u", ithc->config.revision);
- }
- static DEVICE_ATTR_RO(revision);
--static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) {
-+static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
- struct ithc *ithc = dev_get_drvdata(dev);
-- if (!ithc || !ithc_is_config_valid(ithc)) return -ENODEV;
-+ if (!ithc || !ithc_is_config_valid(ithc))
-+ return -ENODEV;
- u32 v = ithc->config.fw_version;
- return sprintf(buf, "%i.%i.%i.%i", v >> 24, v >> 16 & 0xff, v >> 8 & 0xff, v & 0xff);
- }
-@@ -117,45 +142,75 @@ static void ithc_hid_stop(struct hid_device *hdev) { }
- static int ithc_hid_open(struct hid_device *hdev) { return 0; }
- static void ithc_hid_close(struct hid_device *hdev) { }
-
--static int ithc_hid_parse(struct hid_device *hdev) {
-+static int ithc_hid_parse(struct hid_device *hdev)
-+{
- struct ithc *ithc = hdev->driver_data;
- u64 val = 0;
- WRITE_ONCE(ithc->hid_parse_done, false);
-- CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof val, &val);
-- if (!wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done), msecs_to_jiffies(1000))) return -ETIMEDOUT;
-- return 0;
-+ for (int retries = 0; ; retries++) {
-+ CHECK_RET(ithc_dma_tx, ithc, DMA_TX_CODE_GET_REPORT_DESCRIPTOR, sizeof(val), &val);
-+ if (wait_event_timeout(ithc->wait_hid_parse, READ_ONCE(ithc->hid_parse_done),
-+ msecs_to_jiffies(200)))
-+ return 0;
-+ if (retries > 5) {
-+ pci_err(ithc->pci, "failed to read report descriptor\n");
-+ return -ETIMEDOUT;
-+ }
-+ pci_warn(ithc->pci, "failed to read report descriptor, retrying\n");
-+ }
- }
-
--static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype, int reqtype) {
-+static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf,
-+ size_t len, unsigned char rtype, int reqtype)
-+{
- struct ithc *ithc = hdev->driver_data;
-- if (!buf || !len) return -EINVAL;
-+ if (!buf || !len)
-+ return -EINVAL;
- u32 code;
-- if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_OUTPUT_REPORT;
-- else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) code = DMA_TX_CODE_SET_FEATURE;
-- else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) code = DMA_TX_CODE_GET_FEATURE;
-- else {
-- pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", rtype, reqtype, reportnum);
-+ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) {
-+ code = DMA_TX_CODE_OUTPUT_REPORT;
-+ } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) {
-+ code = DMA_TX_CODE_SET_FEATURE;
-+ } else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) {
-+ code = DMA_TX_CODE_GET_FEATURE;
-+ } else {
-+ pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n",
-+ rtype, reqtype, reportnum);
- return -EINVAL;
- }
- buf[0] = reportnum;
-+
- if (reqtype == HID_REQ_GET_REPORT) {
-+ // Prepare for response.
- mutex_lock(&ithc->hid_get_feature_mutex);
- ithc->hid_get_feature_buf = buf;
- ithc->hid_get_feature_size = len;
- mutex_unlock(&ithc->hid_get_feature_mutex);
-+
-+ // Transmit 'get feature' request.
- int r = CHECK(ithc_dma_tx, ithc, code, 1, buf);
- if (!r) {
-- r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature, !ithc->hid_get_feature_buf, msecs_to_jiffies(1000));
-- if (!r) r = -ETIMEDOUT;
-- else if (r < 0) r = -EINTR;
-- else r = 0;
-+ r = wait_event_interruptible_timeout(ithc->wait_hid_get_feature,
-+ !ithc->hid_get_feature_buf, msecs_to_jiffies(1000));
-+ if (!r)
-+ r = -ETIMEDOUT;
-+ else if (r < 0)
-+ r = -EINTR;
-+ else
-+ r = 0;
- }
-+
-+ // If everything went ok, the buffer has been filled with the response data.
-+ // Return the response size.
- mutex_lock(&ithc->hid_get_feature_mutex);
- ithc->hid_get_feature_buf = NULL;
-- if (!r) r = ithc->hid_get_feature_size;
-+ if (!r)
-+ r = ithc->hid_get_feature_size;
- mutex_unlock(&ithc->hid_get_feature_mutex);
- return r;
- }
-+
-+ // 'Set feature', or 'output report'. These don't have a response.
- CHECK_RET(ithc_dma_tx, ithc, code, len, buf);
- return 0;
- }
-@@ -169,17 +224,22 @@ static struct hid_ll_driver ithc_ll_driver = {
- .raw_request = ithc_hid_raw_request,
- };
-
--static void ithc_hid_devres_release(struct device *dev, void *res) {
-+static void ithc_hid_devres_release(struct device *dev, void *res)
-+{
- struct hid_device **hidm = res;
-- if (*hidm) hid_destroy_device(*hidm);
-+ if (*hidm)
-+ hid_destroy_device(*hidm);
- }
-
--static int ithc_hid_init(struct ithc *ithc) {
-- struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof *hidm, GFP_KERNEL);
-- if (!hidm) return -ENOMEM;
-+static int ithc_hid_init(struct ithc *ithc)
-+{
-+ struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL);
-+ if (!hidm)
-+ return -ENOMEM;
- devres_add(&ithc->pci->dev, hidm);
- struct hid_device *hid = hid_allocate_device();
-- if (IS_ERR(hid)) return PTR_ERR(hid);
-+ if (IS_ERR(hid))
-+ return PTR_ERR(hid);
- *hidm = hid;
-
- strscpy(hid->name, DEVFULLNAME, sizeof(hid->name));
-@@ -198,27 +258,45 @@ static int ithc_hid_init(struct ithc *ithc) {
-
- // Interrupts/polling
-
--static void ithc_activity_timer_callback(struct timer_list *t) {
-- struct ithc *ithc = container_of(t, struct ithc, activity_timer);
-- cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
-+static enum hrtimer_restart ithc_activity_start_timer_callback(struct hrtimer *t)
-+{
-+ struct ithc *ithc = container_of(t, struct ithc, activity_start_timer);
-+ ithc_set_active(ithc, ithc_dma_early_us * 2 + USEC_PER_MSEC);
-+ return HRTIMER_NORESTART;
- }
-
--void ithc_set_active(struct ithc *ithc) {
-- // When CPU usage is very low, the CPU can enter various low power states (C2-C10).
-- // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_UNKNOWN_12 will be set when this happens.
-- // The amount of truncated messages can become very high, resulting in user-visible effects (laggy/stuttering cursor).
-- // To avoid this, we use a CPU latency QoS request to prevent the CPU from entering low power states during touch interactions.
-- cpu_latency_qos_update_request(&ithc->activity_qos, 0);
-- mod_timer(&ithc->activity_timer, jiffies + msecs_to_jiffies(1000));
--}
--
--static int ithc_set_device_enabled(struct ithc *ithc, bool enable) {
-- u32 x = ithc->config.touch_cfg = (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2
-- | (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0);
-- return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, offsetof(struct ithc_device_config, touch_cfg), sizeof x, &x);
-+static enum hrtimer_restart ithc_activity_end_timer_callback(struct hrtimer *t)
-+{
-+ struct ithc *ithc = container_of(t, struct ithc, activity_end_timer);
-+ cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
-+ return HRTIMER_NORESTART;
- }
-
--static void ithc_disable_interrupts(struct ithc *ithc) {
-+void ithc_set_active(struct ithc *ithc, unsigned int duration_us)
-+{
-+ if (ithc_dma_latency_us < 0)
-+ return;
-+ // When CPU usage is very low, the CPU can enter various low power states (C2-C10).
-+ // This disrupts DMA, causing truncated DMA messages. ERROR_FLAG_DMA_RX_TIMEOUT will be
-+ // set when this happens. The amount of truncated messages can become very high, resulting
-+ // in user-visible effects (laggy/stuttering cursor). To avoid this, we use a CPU latency
-+ // QoS request to prevent the CPU from entering low power states during touch interactions.
-+ cpu_latency_qos_update_request(&ithc->activity_qos, ithc_dma_latency_us);
-+ hrtimer_start_range_ns(&ithc->activity_end_timer,
-+ ns_to_ktime(duration_us * NSEC_PER_USEC), duration_us * NSEC_PER_USEC, HRTIMER_MODE_REL);
-+}
-+
-+static int ithc_set_device_enabled(struct ithc *ithc, bool enable)
-+{
-+ u32 x = ithc->config.touch_cfg =
-+ (ithc->config.touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | DEVCFG_TOUCH_UNKNOWN_2 |
-+ (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_UNKNOWN_3 | DEVCFG_TOUCH_UNKNOWN_4 : 0);
-+ return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE,
-+ offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x);
-+}
-+
-+static void ithc_disable_interrupts(struct ithc *ithc)
-+{
- writel(0, &ithc->regs->error_control);
- bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0);
- bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_UNKNOWN_4 | DMA_RX_CONTROL_IRQ_DATA, 0);
-@@ -226,43 +304,85 @@ static void ithc_disable_interrupts(struct ithc *ithc) {
- bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0);
- }
-
--static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned channel) {
-- writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA, &ithc->regs->dma_rx[channel].status);
-+static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned int channel)
-+{
-+ writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_UNKNOWN_4 | DMA_RX_STATUS_HAVE_DATA,
-+ &ithc->regs->dma_rx[channel].status);
- }
-
--static void ithc_clear_interrupts(struct ithc *ithc) {
-+static void ithc_clear_interrupts(struct ithc *ithc)
-+{
- writel(0xffffffff, &ithc->regs->error_flags);
- writel(ERROR_STATUS_DMA | ERROR_STATUS_SPI, &ithc->regs->error_status);
- writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
- ithc_clear_dma_rx_interrupts(ithc, 0);
- ithc_clear_dma_rx_interrupts(ithc, 1);
-- writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, &ithc->regs->dma_tx.status);
-+ writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2,
-+ &ithc->regs->dma_tx.status);
- }
-
--static void ithc_process(struct ithc *ithc) {
-+static void ithc_process(struct ithc *ithc)
-+{
- ithc_log_regs(ithc);
-
-- // read and clear error bits
-+ bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0;
-+ bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0;
-+
-+ // Track time between DMA rx transfers, so we can try to predict when we need to enable CPU latency QoS for the next transfer
-+ ktime_t t = ktime_get();
-+ ktime_t dt = ktime_sub(t, ithc->last_rx_time);
-+ if (rx0 || rx1) {
-+ ithc->last_rx_time = t;
-+ if (dt > ms_to_ktime(100)) {
-+ ithc->cur_rx_seq_count = 0;
-+ ithc->cur_rx_seq_errors = 0;
-+ }
-+ ithc->cur_rx_seq_count++;
-+ if (!ithc_use_polling && ithc_dma_latency_us >= 0) {
-+ // Disable QoS, since the DMA transfer has completed (we re-enable it after a delay below)
-+ cpu_latency_qos_update_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
-+ hrtimer_try_to_cancel(&ithc->activity_end_timer);
-+ }
-+ }
-+
-+ // Read and clear error bits
- u32 err = readl(&ithc->regs->error_flags);
- if (err) {
-- if (err & ~ERROR_FLAG_DMA_UNKNOWN_12) pci_err(ithc->pci, "error flags: 0x%08x\n", err);
- writel(err, &ithc->regs->error_flags);
-+ if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT)
-+ pci_err(ithc->pci, "error flags: 0x%08x\n", err);
-+ if (err & ERROR_FLAG_DMA_RX_TIMEOUT) {
-+ // Only log an error if we see a significant number of these errors.
-+ ithc->cur_rx_seq_errors++;
-+ if (ithc->cur_rx_seq_errors && ithc->cur_rx_seq_errors % 50 == 0 && ithc->cur_rx_seq_errors > ithc->cur_rx_seq_count / 10)
-+ pci_err(ithc->pci, "High number of DMA RX timeouts/errors (%u/%u, dt=%lldus). Try adjusting dma_early_us and/or dma_latency_us.\n",
-+ ithc->cur_rx_seq_errors, ithc->cur_rx_seq_count, ktime_to_us(dt));
-+ }
- }
-
-- // process DMA rx
-+ // Process DMA rx
- if (ithc_use_rx0) {
- ithc_clear_dma_rx_interrupts(ithc, 0);
-- ithc_dma_rx(ithc, 0);
-+ if (rx0)
-+ ithc_dma_rx(ithc, 0);
- }
- if (ithc_use_rx1) {
- ithc_clear_dma_rx_interrupts(ithc, 1);
-- ithc_dma_rx(ithc, 1);
-+ if (rx1)
-+ ithc_dma_rx(ithc, 1);
-+ }
-+
-+ // Start timer to re-enable QoS for next rx, but only if we've seen an ERROR_FLAG_DMA_RX_TIMEOUT
-+ if ((rx0 || rx1) && !ithc_use_polling && ithc_dma_latency_us >= 0 && ithc->cur_rx_seq_errors > 0) {
-+ ktime_t expires = ktime_add(t, ktime_sub_us(dt, ithc_dma_early_us));
-+ hrtimer_start_range_ns(&ithc->activity_start_timer, expires, 10 * NSEC_PER_USEC, HRTIMER_MODE_ABS);
- }
-
- ithc_log_regs(ithc);
- }
-
--static irqreturn_t ithc_interrupt_thread(int irq, void *arg) {
-+static irqreturn_t ithc_interrupt_thread(int irq, void *arg)
-+{
- struct ithc *ithc = arg;
- pci_dbg(ithc->pci, "IRQ! err=%08x/%08x/%08x, cmd=%02x/%08x, rx0=%02x/%08x, rx1=%02x/%08x, tx=%02x/%08x\n",
- readl(&ithc->regs->error_control), readl(&ithc->regs->error_status), readl(&ithc->regs->error_flags),
-@@ -274,14 +394,21 @@ static irqreturn_t ithc_interrupt_thread(int irq, void *arg) {
- return IRQ_HANDLED;
- }
-
--static int ithc_poll_thread(void *arg) {
-+static int ithc_poll_thread(void *arg)
-+{
- struct ithc *ithc = arg;
-- unsigned sleep = 100;
-+ unsigned int sleep = 100;
- while (!kthread_should_stop()) {
- u32 n = ithc->dma_rx[1].num_received;
- ithc_process(ithc);
-- if (n != ithc->dma_rx[1].num_received) sleep = 20;
-- else sleep = min(200u, sleep + (sleep >> 4) + 1);
-+ // Decrease polling interval to 20ms if we received data, otherwise slowly
-+ // increase it up to 200ms.
-+ if (n != ithc->dma_rx[1].num_received) {
-+ ithc_set_active(ithc, 100 * USEC_PER_MSEC);
-+ sleep = 20;
-+ } else {
-+ sleep = min(200u, sleep + (sleep >> 4) + 1);
-+ }
- msleep_interruptible(sleep);
- }
- return 0;
-@@ -289,7 +416,8 @@ static int ithc_poll_thread(void *arg) {
-
- // Device initialization and shutdown
-
--static void ithc_disable(struct ithc *ithc) {
-+static void ithc_disable(struct ithc *ithc)
-+{
- bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE);
- CHECK(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED);
- bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0);
-@@ -301,81 +429,112 @@ static void ithc_disable(struct ithc *ithc) {
- ithc_clear_interrupts(ithc);
- }
-
--static int ithc_init_device(struct ithc *ithc) {
-+static int ithc_init_device(struct ithc *ithc)
-+{
- ithc_log_regs(ithc);
- bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0;
- ithc_disable(ithc);
- CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY);
-+
-+ // Since we don't yet know which SPI config the device wants, use default speed and mode
-+ // initially for reading config data.
- ithc_set_spi_config(ithc, 10, 0);
-- bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000); // seems to help with reading config
-
-- if (was_enabled) if (msleep_interruptible(100)) return -EINTR;
-+ // Setting the following bit seems to make reading the config more reliable.
-+ bitsl_set(&ithc->regs->dma_rx[0].unknown_init_bits, 0x80000000);
-+
-+ // If the device was previously enabled, wait a bit to make sure it's fully shut down.
-+ if (was_enabled)
-+ if (msleep_interruptible(100))
-+ return -EINTR;
-+
-+ // Take the touch device out of reset.
- bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0);
- CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0);
- for (int retries = 0; ; retries++) {
- ithc_log_regs(ithc);
- bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET);
-- if (!waitl(ithc, &ithc->regs->state, 0xf, 2)) break;
-+ if (!waitl(ithc, &ithc->regs->state, 0xf, 2))
-+ break;
- if (retries > 5) {
-- pci_err(ithc->pci, "too many retries, failed to reset device\n");
-+ pci_err(ithc->pci, "failed to reset device, state = 0x%08x\n", readl(&ithc->regs->state));
- return -ETIMEDOUT;
- }
-- pci_err(ithc->pci, "invalid state, retrying reset\n");
-+ pci_warn(ithc->pci, "invalid state, retrying reset\n");
- bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0);
-- if (msleep_interruptible(1000)) return -EINTR;
-+ if (msleep_interruptible(1000))
-+ return -EINTR;
- }
- ithc_log_regs(ithc);
-
-+ // Waiting for the following status bit makes reading config much more reliable,
-+ // however the official driver does not seem to do this...
- CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_UNKNOWN_4, DMA_RX_STATUS_UNKNOWN_4);
-
-- // read config
-+ // Read configuration data.
- for (int retries = 0; ; retries++) {
- ithc_log_regs(ithc);
-- memset(&ithc->config, 0, sizeof ithc->config);
-- CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof ithc->config, &ithc->config);
-+ memset(&ithc->config, 0, sizeof(ithc->config));
-+ CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(ithc->config), &ithc->config);
- u32 *p = (void *)&ithc->config;
- pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
- p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
-- if (ithc_is_config_valid(ithc)) break;
-+ if (ithc_is_config_valid(ithc))
-+ break;
- if (retries > 10) {
-- pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", ithc->config.device_id);
-+ pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n",
-+ ithc->config.device_id);
- return -EIO;
- }
-- pci_err(ithc->pci, "failed to read config, retrying\n");
-- if (msleep_interruptible(100)) return -EINTR;
-+ pci_warn(ithc->pci, "failed to read config, retrying\n");
-+ if (msleep_interruptible(100))
-+ return -EINTR;
- }
- ithc_log_regs(ithc);
-
-- CHECK_RET(ithc_set_spi_config, ithc, DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config), DEVCFG_SPI_MODE(ithc->config.spi_config));
-+ // Apply SPI config and enable touch device.
-+ CHECK_RET(ithc_set_spi_config, ithc,
-+ DEVCFG_SPI_MAX_FREQ(ithc->config.spi_config),
-+ DEVCFG_SPI_MODE(ithc->config.spi_config));
- CHECK_RET(ithc_set_device_enabled, ithc, true);
- ithc_log_regs(ithc);
- return 0;
- }
-
--int ithc_reset(struct ithc *ithc) {
-- // FIXME This should probably do devres_release_group()+ithc_start(). But because this is called during DMA
-- // processing, that would have to be done asynchronously (schedule_work()?). And with extra locking?
-+int ithc_reset(struct ithc *ithc)
-+{
-+ // FIXME This should probably do devres_release_group()+ithc_start().
-+ // But because this is called during DMA processing, that would have to be done
-+ // asynchronously (schedule_work()?). And with extra locking?
- pci_err(ithc->pci, "reset\n");
- CHECK(ithc_init_device, ithc);
-- if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0);
-- if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1);
-+ if (ithc_use_rx0)
-+ ithc_dma_rx_enable(ithc, 0);
-+ if (ithc_use_rx1)
-+ ithc_dma_rx_enable(ithc, 1);
- ithc_log_regs(ithc);
- pci_dbg(ithc->pci, "reset completed\n");
- return 0;
- }
-
--static void ithc_stop(void *res) {
-+static void ithc_stop(void *res)
-+{
- struct ithc *ithc = res;
- pci_dbg(ithc->pci, "stopping\n");
- ithc_log_regs(ithc);
-- if (ithc->poll_thread) CHECK(kthread_stop, ithc->poll_thread);
-- if (ithc->irq >= 0) disable_irq(ithc->irq);
-+
-+ if (ithc->poll_thread)
-+ CHECK(kthread_stop, ithc->poll_thread);
-+ if (ithc->irq >= 0)
-+ disable_irq(ithc->irq);
- CHECK(ithc_set_device_enabled, ithc, false);
- ithc_disable(ithc);
-- del_timer_sync(&ithc->activity_timer);
-+ hrtimer_cancel(&ithc->activity_start_timer);
-+ hrtimer_cancel(&ithc->activity_end_timer);
- cpu_latency_qos_remove_request(&ithc->activity_qos);
-- // clear dma config
-- for(unsigned i = 0; i < 2; i++) {
-+
-+ // Clear DMA config.
-+ for (unsigned int i = 0; i < 2; i++) {
- CHECK(waitl, ithc, &ithc->regs->dma_rx[i].status, DMA_RX_STATUS_ENABLED, 0);
- lo_hi_writeq(0, &ithc->regs->dma_rx[i].addr);
- writeb(0, &ithc->regs->dma_rx[i].num_bufs);
-@@ -383,35 +542,43 @@ static void ithc_stop(void *res) {
- }
- lo_hi_writeq(0, &ithc->regs->dma_tx.addr);
- writeb(0, &ithc->regs->dma_tx.num_prds);
-+
- ithc_log_regs(ithc);
- pci_dbg(ithc->pci, "stopped\n");
- }
-
--static void ithc_clear_drvdata(void *res) {
-+static void ithc_clear_drvdata(void *res)
-+{
- struct pci_dev *pci = res;
- pci_set_drvdata(pci, NULL);
- }
-
--static int ithc_start(struct pci_dev *pci) {
-+static int ithc_start(struct pci_dev *pci)
-+{
- pci_dbg(pci, "starting\n");
- if (pci_get_drvdata(pci)) {
- pci_err(pci, "device already initialized\n");
- return -EINVAL;
- }
-- if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) return -ENOMEM;
-+ if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL))
-+ return -ENOMEM;
-
-- struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof *ithc, GFP_KERNEL);
-- if (!ithc) return -ENOMEM;
-+ // Allocate/init main driver struct.
-+ struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof(*ithc), GFP_KERNEL);
-+ if (!ithc)
-+ return -ENOMEM;
- ithc->irq = -1;
- ithc->pci = pci;
-- snprintf(ithc->phys, sizeof ithc->phys, "pci-%s/" DEVNAME, pci_name(pci));
-+ snprintf(ithc->phys, sizeof(ithc->phys), "pci-%s/" DEVNAME, pci_name(pci));
- init_waitqueue_head(&ithc->wait_hid_parse);
- init_waitqueue_head(&ithc->wait_hid_get_feature);
- mutex_init(&ithc->hid_get_feature_mutex);
- pci_set_drvdata(pci, ithc);
- CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci);
-- if (ithc_log_regs_enabled) ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof *ithc->prev_regs, GFP_KERNEL);
-+ if (ithc_log_regs_enabled)
-+ ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof(*ithc->prev_regs), GFP_KERNEL);
-
-+ // PCI initialization.
- CHECK_RET(pcim_enable_device, pci);
- pci_set_master(pci);
- CHECK_RET(pcim_iomap_regions, pci, BIT(0), DEVNAME " regs");
-@@ -419,29 +586,39 @@ static int ithc_start(struct pci_dev *pci) {
- CHECK_RET(pci_set_power_state, pci, PCI_D0);
- ithc->regs = pcim_iomap_table(pci)[0];
-
-+ // Allocate IRQ.
- if (!ithc_use_polling) {
- CHECK_RET(pci_alloc_irq_vectors, pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
- ithc->irq = CHECK(pci_irq_vector, pci, 0);
-- if (ithc->irq < 0) return ithc->irq;
-+ if (ithc->irq < 0)
-+ return ithc->irq;
- }
-
-+ // Initialize THC and touch device.
- CHECK_RET(ithc_init_device, ithc);
- CHECK(devm_device_add_groups, &pci->dev, ithc_attribute_groups);
-- if (ithc_use_rx0) CHECK_RET(ithc_dma_rx_init, ithc, 0, ithc_use_rx1 ? DEVNAME "0" : DEVNAME);
-- if (ithc_use_rx1) CHECK_RET(ithc_dma_rx_init, ithc, 1, ithc_use_rx0 ? DEVNAME "1" : DEVNAME);
-+ if (ithc_use_rx0)
-+ CHECK_RET(ithc_dma_rx_init, ithc, 0);
-+ if (ithc_use_rx1)
-+ CHECK_RET(ithc_dma_rx_init, ithc, 1);
- CHECK_RET(ithc_dma_tx_init, ithc);
-
-- CHECK_RET(ithc_hid_init, ithc);
--
- cpu_latency_qos_add_request(&ithc->activity_qos, PM_QOS_DEFAULT_VALUE);
-- timer_setup(&ithc->activity_timer, ithc_activity_timer_callback, 0);
-+ hrtimer_init(&ithc->activity_start_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
-+ ithc->activity_start_timer.function = ithc_activity_start_timer_callback;
-+ hrtimer_init(&ithc->activity_end_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-+ ithc->activity_end_timer.function = ithc_activity_end_timer_callback;
-
-- // add ithc_stop callback AFTER setting up DMA buffers, so that polling/irqs/DMA are disabled BEFORE the buffers are freed
-+ // Add ithc_stop() callback AFTER setting up DMA buffers, so that polling/irqs/DMA are
-+ // disabled BEFORE the buffers are freed.
- CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc);
-
-+ CHECK_RET(ithc_hid_init, ithc);
-+
-+ // Start polling/IRQ.
- if (ithc_use_polling) {
- pci_info(pci, "using polling instead of irq\n");
-- // use a thread instead of simple timer because we want to be able to sleep
-+ // Use a thread instead of simple timer because we want to be able to sleep.
- ithc->poll_thread = kthread_run(ithc_poll_thread, ithc, DEVNAME "poll");
- if (IS_ERR(ithc->poll_thread)) {
- int err = PTR_ERR(ithc->poll_thread);
-@@ -449,13 +626,17 @@ static int ithc_start(struct pci_dev *pci) {
- return err;
- }
- } else {
-- CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc);
-+ CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL,
-+ ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc);
- }
-
-- if (ithc_use_rx0) ithc_dma_rx_enable(ithc, 0);
-- if (ithc_use_rx1) ithc_dma_rx_enable(ithc, 1);
-+ if (ithc_use_rx0)
-+ ithc_dma_rx_enable(ithc, 0);
-+ if (ithc_use_rx1)
-+ ithc_dma_rx_enable(ithc, 1);
-
-- // hid_add_device can only be called after irq/polling is started and DMA is enabled, because it calls ithc_hid_parse which reads the report descriptor via DMA
-+ // hid_add_device() can only be called after irq/polling is started and DMA is enabled,
-+ // because it calls ithc_hid_parse() which reads the report descriptor via DMA.
- CHECK_RET(hid_add_device, ithc->hid);
-
- CHECK(ithc_debug_init, ithc);
-@@ -464,43 +645,54 @@ static int ithc_start(struct pci_dev *pci) {
- return 0;
- }
-
--static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) {
-+static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id)
-+{
- pci_dbg(pci, "device probe\n");
- return ithc_start(pci);
- }
-
--static void ithc_remove(struct pci_dev *pci) {
-+static void ithc_remove(struct pci_dev *pci)
-+{
- pci_dbg(pci, "device remove\n");
- // all cleanup is handled by devres
- }
-
--static int ithc_suspend(struct device *dev) {
-+// For suspend/resume, we just deinitialize and reinitialize everything.
-+// TODO It might be cleaner to keep the HID device around, however we would then have to signal
-+// to userspace that the touch device has lost state and userspace needs to e.g. resend 'set
-+// feature' requests. Hidraw does not seem to have a facility to do that.
-+static int ithc_suspend(struct device *dev)
-+{
- struct pci_dev *pci = to_pci_dev(dev);
- pci_dbg(pci, "pm suspend\n");
- devres_release_group(dev, ithc_start);
- return 0;
- }
-
--static int ithc_resume(struct device *dev) {
-+static int ithc_resume(struct device *dev)
-+{
- struct pci_dev *pci = to_pci_dev(dev);
- pci_dbg(pci, "pm resume\n");
- return ithc_start(pci);
- }
-
--static int ithc_freeze(struct device *dev) {
-+static int ithc_freeze(struct device *dev)
-+{
- struct pci_dev *pci = to_pci_dev(dev);
- pci_dbg(pci, "pm freeze\n");
- devres_release_group(dev, ithc_start);
- return 0;
- }
-
--static int ithc_thaw(struct device *dev) {
-+static int ithc_thaw(struct device *dev)
-+{
- struct pci_dev *pci = to_pci_dev(dev);
- pci_dbg(pci, "pm thaw\n");
- return ithc_start(pci);
- }
-
--static int ithc_restore(struct device *dev) {
-+static int ithc_restore(struct device *dev)
-+{
- struct pci_dev *pci = to_pci_dev(dev);
- pci_dbg(pci, "pm restore\n");
- return ithc_start(pci);
-@@ -521,11 +713,13 @@ static struct pci_driver ithc_driver = {
- //.dev_groups = ithc_attribute_groups, // could use this (since 5.14), however the attributes won't have valid values until config has been read anyway
- };
-
--static int __init ithc_init(void) {
-+static int __init ithc_init(void)
-+{
- return pci_register_driver(&ithc_driver);
- }
-
--static void __exit ithc_exit(void) {
-+static void __exit ithc_exit(void)
-+{
- pci_unregister_driver(&ithc_driver);
- }
-
-diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c
-index 85d567b05761f..e058721886e37 100644
---- a/drivers/hid/ithc/ithc-regs.c
-+++ b/drivers/hid/ithc/ithc-regs.c
-@@ -1,63 +1,95 @@
-+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
-+
- #include "ithc.h"
-
- #define reg_num(r) (0x1fff & (u16)(__force u64)(r))
-
--void bitsl(__iomem u32 *reg, u32 mask, u32 val) {
-- if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask);
-+void bitsl(__iomem u32 *reg, u32 mask, u32 val)
-+{
-+ if (val & ~mask)
-+ pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n",
-+ reg_num(reg), val, mask);
- writel((readl(reg) & ~mask) | (val & mask), reg);
- }
-
--void bitsb(__iomem u8 *reg, u8 mask, u8 val) {
-- if (val & ~mask) pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", reg_num(reg), val, mask);
-+void bitsb(__iomem u8 *reg, u8 mask, u8 val)
-+{
-+ if (val & ~mask)
-+ pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n",
-+ reg_num(reg), val, mask);
- writeb((readb(reg) & ~mask) | (val & mask), reg);
- }
-
--int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) {
-- pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val);
-+int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val)
-+{
-+ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n",
-+ reg_num(reg), mask, val);
- u32 x;
- if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) {
-- pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", reg_num(reg), mask, val);
-+ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n",
-+ reg_num(reg), mask, val);
- return -ETIMEDOUT;
- }
- pci_dbg(ithc->pci, "done waiting\n");
- return 0;
- }
-
--int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) {
-- pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val);
-+int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val)
-+{
-+ pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n",
-+ reg_num(reg), mask, val);
- u8 x;
- if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) {
-- pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", reg_num(reg), mask, val);
-+ pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n",
-+ reg_num(reg), mask, val);
- return -ETIMEDOUT;
- }
- pci_dbg(ithc->pci, "done waiting\n");
- return 0;
- }
-
--int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode) {
-+int ithc_set_spi_config(struct ithc *ithc, u8 speed, u8 mode)
-+{
- pci_dbg(ithc->pci, "setting SPI speed to %i, mode %i\n", speed, mode);
-- if (mode == 3) mode = 2;
-+ if (mode == 3)
-+ mode = 2;
- bitsl(&ithc->regs->spi_config,
- SPI_CONFIG_MODE(0xff) | SPI_CONFIG_SPEED(0xff) | SPI_CONFIG_UNKNOWN_18(0xff) | SPI_CONFIG_SPEED2(0xff),
- SPI_CONFIG_MODE(mode) | SPI_CONFIG_SPEED(speed) | SPI_CONFIG_UNKNOWN_18(0) | SPI_CONFIG_SPEED2(speed));
- return 0;
- }
-
--int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) {
-+int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data)
-+{
- pci_dbg(ithc->pci, "SPI command %u, size %u, offset %u\n", command, size, offset);
-- if (size > sizeof ithc->regs->spi_cmd.data) return -EINVAL;
-+ if (size > sizeof(ithc->regs->spi_cmd.data))
-+ return -EINVAL;
-+
-+ // Wait if the device is still busy.
- CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0);
-+ // Clear result flags.
- writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
-+
-+ // Init SPI command data.
- writeb(command, &ithc->regs->spi_cmd.code);
- writew(size, &ithc->regs->spi_cmd.size);
- writel(offset, &ithc->regs->spi_cmd.offset);
- u32 *p = data, n = (size + 3) / 4;
-- for (u32 i = 0; i < n; i++) writel(p[i], &ithc->regs->spi_cmd.data[i]);
-+ for (u32 i = 0; i < n; i++)
-+ writel(p[i], &ithc->regs->spi_cmd.data[i]);
-+
-+ // Start transmission.
- bitsb_set(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND);
- CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0);
-- if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) return -EIO;
-- if (readw(&ithc->regs->spi_cmd.size) != size) return -EMSGSIZE;
-- for (u32 i = 0; i < n; i++) p[i] = readl(&ithc->regs->spi_cmd.data[i]);
-+
-+ // Read response.
-+ if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE)
-+ return -EIO;
-+ if (readw(&ithc->regs->spi_cmd.size) != size)
-+ return -EMSGSIZE;
-+ for (u32 i = 0; i < n; i++)
-+ p[i] = readl(&ithc->regs->spi_cmd.data[i]);
-+
- writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status);
- return 0;
- }
-diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h
-index 1a96092ed7eed..d4007d9e2bacc 100644
---- a/drivers/hid/ithc/ithc-regs.h
-+++ b/drivers/hid/ithc/ithc-regs.h
-@@ -1,3 +1,5 @@
-+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
-+
- #define CONTROL_QUIESCE BIT(1)
- #define CONTROL_IS_QUIESCED BIT(2)
- #define CONTROL_NRESET BIT(3)
-@@ -24,7 +26,7 @@
-
- #define ERROR_FLAG_DMA_UNKNOWN_9 BIT(9)
- #define ERROR_FLAG_DMA_UNKNOWN_10 BIT(10)
--#define ERROR_FLAG_DMA_UNKNOWN_12 BIT(12) // set when we receive a truncated DMA message
-+#define ERROR_FLAG_DMA_RX_TIMEOUT BIT(12) // set when we receive a truncated DMA message
- #define ERROR_FLAG_DMA_UNKNOWN_13 BIT(13)
- #define ERROR_FLAG_SPI_BUS_TURNAROUND BIT(16)
- #define ERROR_FLAG_SPI_RESPONSE_TIMEOUT BIT(17)
-@@ -67,6 +69,7 @@
- #define DMA_RX_STATUS_HAVE_DATA BIT(5)
- #define DMA_RX_STATUS_ENABLED BIT(8)
-
-+// COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC.
- #define COUNTER_RESET BIT(31)
-
- struct ithc_registers {
-@@ -147,15 +150,15 @@ static_assert(sizeof(struct ithc_registers) == 0x1300);
- #define DEVCFG_SPI_MAX_FREQ(x) (((x) >> 1) & 0xf) // high bit = use high speed mode?
- #define DEVCFG_SPI_MODE(x) (((x) >> 6) & 3)
- #define DEVCFG_SPI_UNKNOWN_8(x) (((x) >> 8) & 0x3f)
--#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20)
--#define DEVCFG_SPI_HEARTBEAT_INTERVAL (((x) >> 21) & 7)
-+#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat
-+#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7)
- #define DEVCFG_SPI_UNKNOWN_25 BIT(25)
- #define DEVCFG_SPI_UNKNOWN_26 BIT(26)
- #define DEVCFG_SPI_UNKNOWN_27 BIT(27)
--#define DEVCFG_SPI_DELAY (((x) >> 28) & 7)
--#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31)
-+#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this
-+#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this?
-
--struct ithc_device_config {
-+struct ithc_device_config { // (Example values are from an SP7+.)
- u32 _unknown_00; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET)
- u32 _unknown_04; // 04 = 0x00000000
- u32 dma_buf_sizes; // 08 = 0x000a00ff
-@@ -166,9 +169,9 @@ struct ithc_device_config {
- u16 vendor_id; // 1c = 0x045e = Microsoft Corp.
- u16 product_id; // 1e = 0x0c1a
- u32 revision; // 20 = 0x00000001
-- u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139
-+ u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices)
- u32 _unknown_28; // 28 = 0x00000000
-- u32 fw_mode; // 2c = 0x00000000
-+ u32 fw_mode; // 2c = 0x00000000 (for fw update?)
- u32 _unknown_30; // 30 = 0x00000000
- u32 _unknown_34; // 34 = 0x0404035e (u8,u8,u8,u8 = version?)
- u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET)
-diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h
-index 6a9b0d480bc15..028e55a4ec53e 100644
---- a/drivers/hid/ithc/ithc.h
-+++ b/drivers/hid/ithc/ithc.h
-@@ -1,3 +1,5 @@
-+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
-+
- #include <linux/module.h>
- #include <linux/input.h>
- #include <linux/hid.h>
-@@ -21,7 +23,7 @@
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
- #define CHECK(fn, ...) ({ int r = fn(__VA_ARGS__); if (r < 0) pci_err(ithc->pci, "%s: %s failed with %i\n", __func__, #fn, r); r; })
--#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while(0)
-+#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while (0)
-
- #define NUM_RX_BUF 16
-
-@@ -35,8 +37,13 @@ struct ithc {
- struct pci_dev *pci;
- int irq;
- struct task_struct *poll_thread;
-+
- struct pm_qos_request activity_qos;
-- struct timer_list activity_timer;
-+ struct hrtimer activity_start_timer;
-+ struct hrtimer activity_end_timer;
-+ ktime_t last_rx_time;
-+ unsigned int cur_rx_seq_count;
-+ unsigned int cur_rx_seq_errors;
-
- struct hid_device *hid;
- bool hid_parse_done;
-@@ -54,7 +61,7 @@ struct ithc {
- };
-
- int ithc_reset(struct ithc *ithc);
--void ithc_set_active(struct ithc *ithc);
-+void ithc_set_active(struct ithc *ithc, unsigned int duration_us);
- int ithc_debug_init(struct ithc *ithc);
- void ithc_log_regs(struct ithc *ithc);
-
---
-2.42.0
+2.43.0
-From c4cbbcd24ea10e6558753174ae6dabcc9b54e438 Mon Sep 17 00:00:00 2001
+From fb7e9294f3970a450b891c2cc7b2195861d454e3 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sun, 22 Oct 2023 14:57:11 +0200
Subject: [PATCH] platform/surface: aggregator_registry: Add support for
@@ -6891,7 +5687,7 @@ Patchset: surface-sam
1 file changed, 3 insertions(+)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index 0fe5be5396525..0d8c8395c5886 100644
+index aeb3feae40ff..2bc4977037fc 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
@@ -367,6 +367,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = {
@@ -6905,9 +5701,9 @@ index 0fe5be5396525..0d8c8395c5886 100644
{ "MSHW0123", (unsigned long)ssam_node_group_sls },
--
-2.42.0
+2.43.0
-From 0bb0adce3efad7a43fc3811f6cc24148c8c75253 Mon Sep 17 00:00:00 2001
+From 2de16abc5d0d2334e2935b1bdb3667a95d0009f2 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Mon, 20 Nov 2023 19:47:00 +0100
Subject: [PATCH] platform/surface: aggregator_registry: Add support for
@@ -6925,7 +5721,7 @@ Patchset: surface-sam
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
-index 0d8c8395c5886..530db4db71aba 100644
+index 2bc4977037fc..26cb6229ad16 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
@@ -247,8 +247,8 @@ static const struct software_node *ssam_node_group_sl5[] = {
@@ -6975,9 +5771,743 @@ index 0d8c8395c5886..530db4db71aba 100644
{ },
};
--
-2.42.0
+2.43.0
+
+From c06e370b5ed873b603aa0dc2faafe24a9e63b3e8 Mon Sep 17 00:00:00 2001
+From: Ivor Wanders <ivor@iwanders.net>
+Date: Mon, 18 Dec 2023 19:21:32 -0500
+Subject: [PATCH] platform/surface: aggregator_registry: add entry for fan
+ speed
+
+Add an entry for the fan speed function.
+Add this new entry to the Surface Pro 9 group.
+
+Signed-off-by: Ivor Wanders <ivor@iwanders.net>
+Link: https://github.com/linux-surface/kernel/pull/144
+Reviewed-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam
+---
+ drivers/platform/surface/surface_aggregator_registry.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
+index 26cb6229ad16..f02a933160ff 100644
+--- a/drivers/platform/surface/surface_aggregator_registry.c
++++ b/drivers/platform/surface/surface_aggregator_registry.c
+@@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
+ .parent = &ssam_node_root,
+ };
+
++/* Fan speed function. */
++static const struct software_node ssam_node_fan_speed = {
++ .name = "ssam:01:05:01:01:01",
++ .parent = &ssam_node_root,
++};
++
+ /* Tablet-mode switch via KIP subsystem. */
+ static const struct software_node ssam_node_kip_tablet_switch = {
+ .name = "ssam:01:0e:01:00:01",
+@@ -319,6 +325,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
+ &ssam_node_bat_ac,
+ &ssam_node_bat_main,
+ &ssam_node_tmp_pprof,
++ &ssam_node_fan_speed,
+ &ssam_node_pos_tablet_switch,
+ &ssam_node_hid_kip_keyboard,
+ &ssam_node_hid_kip_penstash,
+--
+2.43.0
+
+From 63dcbbcad69219e1487db46a5c26c1ebdd9ef6be Mon Sep 17 00:00:00 2001
+From: Ivor Wanders <ivor@iwanders.net>
+Date: Thu, 30 Nov 2023 20:20:24 -0500
+Subject: [PATCH] hwmon: add fan speed monitoring driver for Surface devices
+
+Adds a driver that provides read only access to the fan speed for Microsoft
+Surface Pro devices. The fan speed is always regulated by the EC and cannot
+be influenced directly.
+
+Signed-off-by: Ivor Wanders <ivor@iwanders.net>
+Link: https://github.com/linux-surface/kernel/pull/144
+Patchset: surface-sam
+---
+ Documentation/hwmon/index.rst | 1 +
+ Documentation/hwmon/surface_fan.rst | 25 ++++++++
+ MAINTAINERS | 8 +++
+ drivers/hwmon/Kconfig | 13 ++++
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/surface_fan.c | 93 +++++++++++++++++++++++++++++
+ 6 files changed, 141 insertions(+)
+ create mode 100644 Documentation/hwmon/surface_fan.rst
+ create mode 100644 drivers/hwmon/surface_fan.c
+
+diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
+index 72f4e6065bae..7c254562abd6 100644
+--- a/Documentation/hwmon/index.rst
++++ b/Documentation/hwmon/index.rst
+@@ -204,6 +204,7 @@ Hardware Monitoring Kernel Drivers
+ smsc47m1
+ sparx5-temp
+ stpddc60
++ surface_fan
+ sy7636a-hwmon
+ tc654
+ tc74
+diff --git a/Documentation/hwmon/surface_fan.rst b/Documentation/hwmon/surface_fan.rst
+new file mode 100644
+index 000000000000..07942574c4f0
+--- /dev/null
++++ b/Documentation/hwmon/surface_fan.rst
+@@ -0,0 +1,25 @@
++.. SPDX-License-Identifier: GPL-2.0-or-later
++
++Kernel driver surface_fan
++=========================
++
++Supported Devices:
++
++ * Microsoft Surface Pro 9
++
++Author: Ivor Wanders <ivor@iwanders.net>
++
++Description
++-----------
++
++This provides monitoring of the fan found in some Microsoft Surface Pro devices,
++like the Surface Pro 9. The fan is always controlled by the onboard controller.
++
++Sysfs interface
++---------------
++
++======================= ======= =========================================
++Name Perm Description
++======================= ======= =========================================
++``fan1_input`` RO Current fan speed in RPM.
++======================= ======= =========================================
+diff --git a/MAINTAINERS b/MAINTAINERS
+index a7c4cf8201e0..77eb076e77da 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -14331,6 +14331,14 @@ F: Documentation/driver-api/surface_aggregator/clients/dtx.rst
+ F: drivers/platform/surface/surface_dtx.c
+ F: include/uapi/linux/surface_aggregator/dtx.h
+
++MICROSOFT SURFACE SENSOR FAN DRIVER
++M: Maximilian Luz <luzmaximilian@gmail.com>
++M: Ivor Wanders <ivor@iwanders.net>
++L: linux-hwmon@vger.kernel.org
++S: Maintained
++F: Documentation/hwmon/surface_fan.rst
++F: drivers/hwmon/surface_fan.c
++
+ MICROSOFT SURFACE GPE LID SUPPORT DRIVER
+ M: Maximilian Luz <luzmaximilian@gmail.com>
+ L: platform-driver-x86@vger.kernel.org
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index cf27523eed5a..1cef428c79ea 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -1983,6 +1983,19 @@ config SENSORS_SFCTEMP
+ This driver can also be built as a module. If so, the module
+ will be called sfctemp.
+
++config SENSORS_SURFACE_FAN
++ tristate "Surface Fan Driver"
++ depends on SURFACE_AGGREGATOR
++ help
++ Driver that provides monitoring of the fan on Surface Pro devices that
++ have a fan, like the Surface Pro 9.
++
++ This makes the fan's current speed accessible through the hwmon
++ system. It does not provide control over the fan, the firmware is
++ responsible for that, this driver merely provides monitoring.
++
++ Select M or Y here, if you want to be able to read the fan's speed.
++
+ config SENSORS_ADC128D818
+ tristate "Texas Instruments ADC128D818"
+ depends on I2C
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index e84bd9685b5c..30a284fc5ab6 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -200,6 +200,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
+ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+ obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
+ obj-$(CONFIG_SENSORS_STTS751) += stts751.o
++obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
+ obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
+ obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
+ obj-$(CONFIG_SENSORS_TC74) += tc74.o
+diff --git a/drivers/hwmon/surface_fan.c b/drivers/hwmon/surface_fan.c
+new file mode 100644
+index 000000000000..7c2e3ae3eb40
+--- /dev/null
++++ b/drivers/hwmon/surface_fan.c
+@@ -0,0 +1,93 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Surface Fan driver for Surface System Aggregator Module. It provides access
++ * to the fan's rpm through the hwmon system.
++ *
++ * Copyright (C) 2023 Ivor Wanders <ivor@iwanders.net>
++ */
++
++#include <linux/hwmon.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/surface_aggregator/device.h>
++#include <linux/types.h>
++
++// SSAM
++SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_fan_rpm_get, __le16, {
++ .target_category = SSAM_SSH_TC_FAN,
++ .command_id = 0x01,
++});
++
++// hwmon
++umode_t surface_fan_hwmon_is_visible(const void *drvdata,
++ enum hwmon_sensor_types type, u32 attr,
++ int channel)
++{
++ return 0444;
++}
++
++static int surface_fan_hwmon_read(struct device *dev,
++ enum hwmon_sensor_types type, u32 attr,
++ int channel, long *val)
++{
++ struct ssam_device *sdev = dev_get_drvdata(dev);
++ int ret;
++ __le16 value;
++
++ ret = __ssam_fan_rpm_get(sdev, &value);
++ if (ret)
++ return ret;
++
++ *val = le16_to_cpu(value);
++
++ return ret;
++}
++
++static const struct hwmon_channel_info *const surface_fan_info[] = {
++ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
++ NULL
++};
++
++static const struct hwmon_ops surface_fan_hwmon_ops = {
++ .is_visible = surface_fan_hwmon_is_visible,
++ .read = surface_fan_hwmon_read,
++};
++
++static const struct hwmon_chip_info surface_fan_chip_info = {
++ .ops = &surface_fan_hwmon_ops,
++ .info = surface_fan_info,
++};
++
++static int surface_fan_probe(struct ssam_device *sdev)
++{
++ struct device *hdev;
++
++ hdev = devm_hwmon_device_register_with_info(&sdev->dev,
++ "surface_fan", sdev,
++ &surface_fan_chip_info,
++ NULL);
++ if (IS_ERR(hdev))
++ return PTR_ERR(hdev);
++
++ return 0;
++}
++
++static const struct ssam_device_id ssam_fan_match[] = {
++ { SSAM_SDEV(FAN, SAM, 0x01, 0x01) },
++ {},
++};
++MODULE_DEVICE_TABLE(ssam, ssam_fan_match);
++
++static struct ssam_device_driver surface_fan = {
++ .probe = surface_fan_probe,
++ .match_table = ssam_fan_match,
++ .driver = {
++ .name = "surface_fan",
++ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
++ },
++};
++module_ssam_device_driver(surface_fan);
++
++MODULE_AUTHOR("Ivor Wanders <ivor@iwanders.net>");
++MODULE_DESCRIPTION("Fan Driver for Surface System Aggregator Module");
++MODULE_LICENSE("GPL");
+--
+2.43.0
+
+From 5f549c253e4df330fa8f311fe151df80e199bec4 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 30 Dec 2023 18:07:54 +0100
+Subject: [PATCH] hwmon: Add thermal sensor driver for Surface Aggregator
+ Module
+
+Some of the newer Microsoft Surface devices (such as the Surface Book
+3 and Pro 9) have thermal sensors connected via the Surface Aggregator
+Module (the embedded controller on those devices). Add a basic driver
+to read out the temperature values of those sensors.
+
+Link: https://github.com/linux-surface/surface-aggregator-module/issues/59
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam
+---
+ drivers/hwmon/Kconfig | 10 +++
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/surface_temp.c | 165 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 176 insertions(+)
+ create mode 100644 drivers/hwmon/surface_temp.c
+
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 1cef428c79ea..ca20716911ad 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -1996,6 +1996,16 @@ config SENSORS_SURFACE_FAN
+
+ Select M or Y here, if you want to be able to read the fan's speed.
+
++config SENSORS_SURFACE_TEMP
++ tristate "Microsoft Surface Thermal Sensor Driver"
++ depends on SURFACE_AGGREGATOR
++ help
++ Driver for monitoring thermal sensors connected via the Surface
++ Aggregator Module (embedded controller) on Microsoft Surface devices.
++
++ This driver can also be built as a module. If so, the module
++ will be called surface_temp.
++
+ config SENSORS_ADC128D818
+ tristate "Texas Instruments ADC128D818"
+ depends on I2C
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index 30a284fc5ab6..a6bcde6b4843 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -201,6 +201,7 @@ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+ obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
+ obj-$(CONFIG_SENSORS_STTS751) += stts751.o
+ obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
++obj-$(CONFIG_SENSORS_SURFACE_TEMP)+= surface_temp.o
+ obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
+ obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
+ obj-$(CONFIG_SENSORS_TC74) += tc74.o
+diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c
+new file mode 100644
+index 000000000000..48c3e826713f
+--- /dev/null
++++ b/drivers/hwmon/surface_temp.c
+@@ -0,0 +1,165 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Thermal sensor subsystem driver for Surface System Aggregator Module (SSAM).
++ *
++ * Copyright (C) 2022-2023 Maximilian Luz <luzmaximilian@gmail.com>
++ */
++
++#include <linux/bitops.h>
++#include <linux/hwmon.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++
++#include <linux/surface_aggregator/controller.h>
++#include <linux/surface_aggregator/device.h>
++
++
++/* -- SAM interface. -------------------------------------------------------- */
++
++SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {
++ .target_category = SSAM_SSH_TC_TMP,
++ .command_id = 0x04,
++});
++
++SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {
++ .target_category = SSAM_SSH_TC_TMP,
++ .command_id = 0x01,
++});
++
++static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors)
++{
++ __le16 sensors_le;
++ int status;
++
++ status = __ssam_tmp_get_available_sensors(sdev, &sensors_le);
++ if (status)
++ return status;
++
++ *sensors = le16_to_cpu(sensors_le);
++ return 0;
++}
++
++static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temperature)
++{
++ __le16 temp_le;
++ int status;
++
++ status = __ssam_tmp_get_temperature(sdev->ctrl, sdev->uid.target, iid, &temp_le);
++ if (status)
++ return status;
++
++ /* Convert 1/10 °K to 1/1000 °C */
++ *temperature = (le16_to_cpu(temp_le) - 2731) * 100L;
++ return 0;
++}
++
++
++/* -- Driver.---------------------------------------------------------------- */
++
++struct ssam_temp {
++ struct ssam_device *sdev;
++ s16 sensors;
++};
++
++static umode_t ssam_temp_hwmon_is_visible(const void *data,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel)
++{
++ const struct ssam_temp *ssam_temp = data;
++
++ if (!(ssam_temp->sensors & BIT(channel)))
++ return 0;
++
++ return 0444;
++}
++
++static int ssam_temp_hwmon_read(struct device *dev,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel, long *value)
++{
++ const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);
++
++ return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value);
++}
++
++static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = {
++ HWMON_CHANNEL_INFO(chip,
++ HWMON_C_REGISTER_TZ),
++ /* We have at most 16 thermal sensor channels. */
++ HWMON_CHANNEL_INFO(temp,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT),
++ NULL
++};
++
++static const struct hwmon_ops ssam_temp_hwmon_ops = {
++ .is_visible = ssam_temp_hwmon_is_visible,
++ .read = ssam_temp_hwmon_read,
++};
++
++static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {
++ .ops = &ssam_temp_hwmon_ops,
++ .info = ssam_temp_hwmon_info,
++};
++
++static int ssam_temp_probe(struct ssam_device *sdev)
++{
++ struct ssam_temp *ssam_temp;
++ struct device *hwmon_dev;
++ s16 sensors;
++ int status;
++
++ status = ssam_tmp_get_available_sensors(sdev, &sensors);
++ if (status)
++ return status;
++
++ ssam_temp = devm_kzalloc(&sdev->dev, sizeof(*ssam_temp), GFP_KERNEL);
++ if (!ssam_temp)
++ return -ENOMEM;
++
++ ssam_temp->sdev = sdev;
++ ssam_temp->sensors = sensors;
++
++ hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev,
++ "surface_thermal", ssam_temp, &ssam_temp_hwmon_chip_info,
++ NULL);
++ if (IS_ERR(hwmon_dev))
++ return PTR_ERR(hwmon_dev);
++
++ return 0;
++}
++
++static const struct ssam_device_id ssam_temp_match[] = {
++ { SSAM_SDEV(TMP, SAM, 0x00, 0x02) },
++ { },
++};
++MODULE_DEVICE_TABLE(ssam, ssam_temp_match);
++
++static struct ssam_device_driver ssam_temp = {
++ .probe = ssam_temp_probe,
++ .match_table = ssam_temp_match,
++ .driver = {
++ .name = "surface_temp",
++ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
++ },
++};
++module_ssam_device_driver(ssam_temp);
++
++MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
++MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module");
++MODULE_LICENSE("GPL");
+--
+2.43.0
+
+From 3ccfa3b6be4794f247488f7e665ba91793ec09c7 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 30 Dec 2023 18:12:23 +0100
+Subject: [PATCH] hwmon: surface_temp: Add support for sensor names
+
+The thermal subsystem of the Surface Aggregator Module allows us to
+query the names of the respective thermal sensors. Forward those to
+userspace.
+
+Signed-off-by: Ivor Wanders <ivor@iwanders.net>
+Co-Developed-by: Maximilian Luz <luzmaximilian@gmail.com>
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam
+---
+ drivers/hwmon/surface_temp.c | 113 +++++++++++++++++++++++++++++------
+ 1 file changed, 96 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c
+index 48c3e826713f..4c08926139db 100644
+--- a/drivers/hwmon/surface_temp.c
++++ b/drivers/hwmon/surface_temp.c
+@@ -17,6 +17,27 @@
+
+ /* -- SAM interface. -------------------------------------------------------- */
+
++/*
++ * Available sensors are indicated by a 16-bit bitfield, where a 1 marks the
++ * presence of a sensor. So we have at most 16 possible sensors/channels.
++ */
++#define SSAM_TMP_SENSOR_MAX_COUNT 16
++
++/*
++ * All names observed so far are 6 characters long, but there's only
++ * zeros after the name, so perhaps they can be longer. This number reflects
++ * the maximum zero-padded space observed in the returned buffer.
++ */
++#define SSAM_TMP_SENSOR_NAME_LENGTH 18
++
++struct ssam_tmp_get_name_rsp {
++ __le16 unknown1;
++ char unknown2;
++ char name[SSAM_TMP_SENSOR_NAME_LENGTH];
++} __packed;
++
++static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21);
++
+ SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {
+ .target_category = SSAM_SSH_TC_TMP,
+ .command_id = 0x04,
+@@ -27,6 +48,11 @@ SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {
+ .command_id = 0x01,
+ });
+
++SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_name, struct ssam_tmp_get_name_rsp, {
++ .target_category = SSAM_SSH_TC_TMP,
++ .command_id = 0x0e,
++});
++
+ static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors)
+ {
+ __le16 sensors_le;
+@@ -54,12 +80,37 @@ static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temp
+ return 0;
+ }
+
++static int ssam_tmp_get_name(struct ssam_device *sdev, u8 iid, char *buf, size_t buf_len)
++{
++ struct ssam_tmp_get_name_rsp name_rsp;
++ int status;
++
++ status = __ssam_tmp_get_name(sdev->ctrl, sdev->uid.target, iid, &name_rsp);
++ if (status)
++ return status;
++
++ /*
++ * This should not fail unless the name in the returned struct is not
++ * null-terminated or someone changed something in the struct
++ * definitions above, since our buffer and struct have the same
++ * capacity by design. So if this fails blow this up with a warning.
++ * Since the more likely cause is that the returned string isn't
++ * null-terminated, we might have received garbage (as opposed to just
++ * an incomplete string), so also fail the function.
++ */
++ status = strscpy(buf, name_rsp.name, buf_len);
++ WARN_ON(status < 0);
++
++ return status < 0 ? status : 0;
++}
++
+
+ /* -- Driver.---------------------------------------------------------------- */
+
+ struct ssam_temp {
+ struct ssam_device *sdev;
+ s16 sensors;
++ char names[SSAM_TMP_SENSOR_MAX_COUNT][SSAM_TMP_SENSOR_NAME_LENGTH];
+ };
+
+ static umode_t ssam_temp_hwmon_is_visible(const void *data,
+@@ -83,33 +134,47 @@ static int ssam_temp_hwmon_read(struct device *dev,
+ return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value);
+ }
+
++static int ssam_temp_hwmon_read_string(struct device *dev,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel, const char **str)
++{
++ const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);
++
++ *str = ssam_temp->names[channel];
++ return 0;
++}
++
+ static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+- /* We have at most 16 thermal sensor channels. */
++ /*
++ * We have at most SSAM_TMP_SENSOR_MAX_COUNT = 16 thermal sensor
++ * channels.
++ */
+ HWMON_CHANNEL_INFO(temp,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT,
+- HWMON_T_INPUT),
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL,
++ HWMON_T_INPUT | HWMON_T_LABEL),
+ NULL
+ };
+
+ static const struct hwmon_ops ssam_temp_hwmon_ops = {
+ .is_visible = ssam_temp_hwmon_is_visible,
+ .read = ssam_temp_hwmon_read,
++ .read_string = ssam_temp_hwmon_read_string,
+ };
+
+ static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {
+@@ -122,6 +187,7 @@ static int ssam_temp_probe(struct ssam_device *sdev)
+ struct ssam_temp *ssam_temp;
+ struct device *hwmon_dev;
+ s16 sensors;
++ int channel;
+ int status;
+
+ status = ssam_tmp_get_available_sensors(sdev, &sensors);
+@@ -135,6 +201,19 @@ static int ssam_temp_probe(struct ssam_device *sdev)
+ ssam_temp->sdev = sdev;
+ ssam_temp->sensors = sensors;
+
++ /* Retrieve the name for each available sensor. */
++ for (channel = 0; channel < SSAM_TMP_SENSOR_MAX_COUNT; channel++)
++ {
++ if (!(sensors & BIT(channel)))
++ continue;
++
++ status = ssam_tmp_get_name(sdev, channel + 1,
++ ssam_temp->names[channel],
++ SSAM_TMP_SENSOR_NAME_LENGTH);
++ if (status)
++ return status;
++ }
++
+ hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev,
+ "surface_thermal", ssam_temp, &ssam_temp_hwmon_chip_info,
+ NULL);
+--
+2.43.0
+
+From 8ccf7b86ad270655bd1e8cd0ab8d2ff475ad0ea7 Mon Sep 17 00:00:00 2001
+From: Maximilian Luz <luzmaximilian@gmail.com>
+Date: Sat, 30 Dec 2023 18:21:12 +0100
+Subject: [PATCH] platform/surface: aggregator_registry: Add support for
+ thermal sensors on the Surface Pro 9
+
+The Surface Pro 9 has thermal sensors connected via the Surface
+Aggregator Module. Add a device node to support those.
+
+Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
+Patchset: surface-sam
+---
+ drivers/platform/surface/surface_aggregator_registry.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
+index f02a933160ff..67686042e009 100644
+--- a/drivers/platform/surface/surface_aggregator_registry.c
++++ b/drivers/platform/surface/surface_aggregator_registry.c
+@@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = {
+ .parent = &ssam_node_root,
+ };
+
++/* Thermal sensors. */
++static const struct software_node ssam_node_tmp_sensors = {
++ .name = "ssam:01:03:01:00:02",
++ .parent = &ssam_node_root,
++};
++
+ /* Fan speed function. */
+ static const struct software_node ssam_node_fan_speed = {
+ .name = "ssam:01:05:01:01:01",
+@@ -325,6 +331,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
+ &ssam_node_bat_ac,
+ &ssam_node_bat_main,
+ &ssam_node_tmp_pprof,
++ &ssam_node_tmp_sensors,
+ &ssam_node_fan_speed,
+ &ssam_node_pos_tablet_switch,
+ &ssam_node_hid_kip_keyboard,
+--
+2.43.0
-From 3772b511c710c369b737fd0a111fbda63b028f1d Mon Sep 17 00:00:00 2001
+From 38a76c85dee37facde40f245d994c4209ccddd15 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 25 Jul 2020 17:19:53 +0200
Subject: [PATCH] i2c: acpi: Implement RawBytes read access
@@ -7034,7 +6564,7 @@ Patchset: surface-sam-over-hid
1 file changed, 35 insertions(+)
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
-index d6037a3286690..a290ebc77aea2 100644
+index d6037a328669..a290ebc77aea 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -628,6 +628,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
@@ -7087,9 +6617,9 @@ index d6037a3286690..a290ebc77aea2 100644
dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
accessor_type, client->addr);
--
-2.42.0
+2.43.0
-From f45a16750118da615fca44e7214204c83631ee7f Mon Sep 17 00:00:00 2001
+From f4ad3e5c368c11503d8b7af6a703f3972ebd5e98 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 13 Feb 2021 16:41:18 +0100
Subject: [PATCH] platform/surface: Add driver for Surface Book 1 dGPU switch
@@ -7112,7 +6642,7 @@ Patchset: surface-sam-over-hid
create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c
diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
-index b629e82af97c0..68656e8f309ed 100644
+index b629e82af97c..68656e8f309e 100644
--- a/drivers/platform/surface/Kconfig
+++ b/drivers/platform/surface/Kconfig
@@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH
@@ -7130,7 +6660,7 @@ index b629e82af97c0..68656e8f309ed 100644
tristate "Surface DTX (Detachment System) Driver"
depends on SURFACE_AGGREGATOR
diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
-index 53344330939bf..7efcd0cdb5329 100644
+index 53344330939b..7efcd0cdb532 100644
--- a/drivers/platform/surface/Makefile
+++ b/drivers/platform/surface/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
@@ -7143,7 +6673,7 @@ index 53344330939bf..7efcd0cdb5329 100644
obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c
new file mode 100644
-index 0000000000000..8b816ed8f35c6
+index 000000000000..8b816ed8f35c
--- /dev/null
+++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c
@@ -0,0 +1,162 @@
@@ -7310,9 +6840,9 @@ index 0000000000000..8b816ed8f35c6
+MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1");
+MODULE_LICENSE("GPL");
--
-2.42.0
+2.43.0
-From a5d9cf4762a27e2bf7f38c0d5a223b9df8b4ba8a Mon Sep 17 00:00:00 2001
+From 96cb53fd556f88f97d61b237c6015cec946865d5 Mon Sep 17 00:00:00 2001
From: Sachi King <nakato@nakato.io>
Date: Tue, 5 Oct 2021 00:05:09 +1100
Subject: [PATCH] Input: soc_button_array - support AMD variant Surface devices
@@ -7334,10 +6864,10 @@ Patchset: surface-button
1 file changed, 8 insertions(+), 25 deletions(-)
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
-index e79f5497948b8..2bddbe6e9ea4d 100644
+index f6d060377d18..b8603f74eb28 100644
--- a/drivers/input/misc/soc_button_array.c
+++ b/drivers/input/misc/soc_button_array.c
-@@ -537,8 +537,8 @@ static const struct soc_device_data soc_device_MSHW0028 = {
+@@ -540,8 +540,8 @@ static const struct soc_device_data soc_device_MSHW0028 = {
* Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
* devices use MSHW0040 for power and volume buttons, however the way they
* have to be addressed differs. Make sure that we only load this drivers
@@ -7348,7 +6878,7 @@ index e79f5497948b8..2bddbe6e9ea4d 100644
*/
#define MSHW0040_DSM_REVISION 0x01
#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision
-@@ -549,31 +549,14 @@ static const guid_t MSHW0040_DSM_UUID =
+@@ -552,31 +552,14 @@ static const guid_t MSHW0040_DSM_UUID =
static int soc_device_check_MSHW0040(struct device *dev)
{
acpi_handle handle = ACPI_HANDLE(dev);
@@ -7387,9 +6917,9 @@ index e79f5497948b8..2bddbe6e9ea4d 100644
/*
--
-2.42.0
+2.43.0
-From 66f0a34801ad81ff08cc3ae0e175e0958959c461 Mon Sep 17 00:00:00 2001
+From 7909f30b15796e8df43a6d4ea32cbbd40627c410 Mon Sep 17 00:00:00 2001
From: Sachi King <nakato@nakato.io>
Date: Tue, 5 Oct 2021 00:22:57 +1100
Subject: [PATCH] platform/surface: surfacepro3_button: don't load on amd
@@ -7410,7 +6940,7 @@ Patchset: surface-button
1 file changed, 6 insertions(+), 24 deletions(-)
diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c
-index 2755601f979cd..4240c98ca2265 100644
+index 2755601f979c..4240c98ca226 100644
--- a/drivers/platform/surface/surfacepro3_button.c
+++ b/drivers/platform/surface/surfacepro3_button.c
@@ -149,7 +149,8 @@ static int surface_button_resume(struct device *dev)
@@ -7459,9 +6989,9 @@ index 2755601f979cd..4240c98ca2265 100644
--
-2.42.0
+2.43.0
-From a55587ce4f5065bedb604f9031082ad47612a163 Mon Sep 17 00:00:00 2001
+From 28ea3660b6680bfd528ca05c543e69b8a2ad412c Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sat, 18 Feb 2023 01:02:49 +0100
Subject: [PATCH] USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3
@@ -7486,7 +7016,7 @@ Patchset: surface-typecover
1 file changed, 3 insertions(+)
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
-index 15e9bd180a1d2..0d70461d01e16 100644
+index 15e9bd180a1d..0d70461d01e1 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -220,6 +220,9 @@ static const struct usb_device_id usb_quirk_list[] = {
@@ -7500,9 +7030,9 @@ index 15e9bd180a1d2..0d70461d01e16 100644
{ USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME },
--
-2.42.0
+2.43.0
-From 678999792d6b1c72e56c6b63fc3909b93db47b32 Mon Sep 17 00:00:00 2001
+From 039ed906cfe0578e78c40d786433e9b144c56785 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Thu, 5 Nov 2020 13:09:45 +0100
Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
@@ -7538,7 +7068,7 @@ Patchset: surface-typecover
1 file changed, 98 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index 8db4ae05febc8..99a5efef45258 100644
+index fd5b0637dad6..0f49d8fa6333 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -34,7 +34,10 @@
@@ -7712,7 +7242,7 @@ index 8db4ae05febc8..99a5efef45258 100644
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
if (ret)
-@@ -1842,6 +1932,7 @@ static void mt_remove(struct hid_device *hdev)
+@@ -1840,6 +1930,7 @@ static void mt_remove(struct hid_device *hdev)
{
struct mt_device *td = hid_get_drvdata(hdev);
@@ -7720,7 +7250,7 @@ index 8db4ae05febc8..99a5efef45258 100644
del_timer_sync(&td->release_timer);
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
-@@ -2223,6 +2314,11 @@ static const struct hid_device_id mt_devices[] = {
+@@ -2226,6 +2317,11 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_CSR2) },
@@ -7733,9 +7263,9 @@ index 8db4ae05febc8..99a5efef45258 100644
{ .driver_data = MT_CLS_GOOGLE,
HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
--
-2.42.0
+2.43.0
-From 12427f01e38ebf653ccf44faefdcb92110c43c20 Mon Sep 17 00:00:00 2001
+From 97419c9be08b7b3e4311aa989fa8f91d0549a469 Mon Sep 17 00:00:00 2001
From: PJungkamp <p.jungkamp@gmail.com>
Date: Fri, 25 Feb 2022 12:04:25 +0100
Subject: [PATCH] hid/multitouch: Add support for surface pro type cover tablet
@@ -7764,7 +7294,7 @@ Patchset: surface-typecover
1 file changed, 122 insertions(+), 26 deletions(-)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index 99a5efef45258..6ae43ea90bcd5 100644
+index 0f49d8fa6333..1fad1199775b 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -77,6 +77,7 @@ MODULE_LICENSE("GPL");
@@ -7974,7 +7504,7 @@ index 99a5efef45258..6ae43ea90bcd5 100644
hid_err(hdev, "couldn't find backlight field\n");
goto out;
}
-@@ -1909,13 +1975,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
+@@ -1908,13 +1974,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
static int mt_reset_resume(struct hid_device *hdev)
{
@@ -7999,7 +7529,7 @@ index 99a5efef45258..6ae43ea90bcd5 100644
/* Some Elan legacy devices require SET_IDLE to be set on resume.
* It should be safe to send it to other devices too.
* Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
-@@ -1924,6 +2001,10 @@ static int mt_resume(struct hid_device *hdev)
+@@ -1923,12 +2000,31 @@ static int mt_resume(struct hid_device *hdev)
mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
@@ -8009,8 +7539,7 @@ index 99a5efef45258..6ae43ea90bcd5 100644
+
return 0;
}
- #endif
-@@ -1931,6 +2012,21 @@ static int mt_resume(struct hid_device *hdev)
+
static void mt_remove(struct hid_device *hdev)
{
struct mt_device *td = hid_get_drvdata(hdev);
@@ -8033,9 +7562,9 @@ index 99a5efef45258..6ae43ea90bcd5 100644
unregister_pm_notifier(&td->pm_notifier);
del_timer_sync(&td->release_timer);
--
-2.42.0
+2.43.0
-From 151f9dba2f3d6d066d160128da109a0173a3ff4c Mon Sep 17 00:00:00 2001
+From 3854d7e575b1091a994c58ef8ee2a89f4efced12 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sun, 19 Feb 2023 22:12:24 +0100
Subject: [PATCH] PCI: Add quirk to prevent calling shutdown mehtod
@@ -8060,7 +7589,7 @@ Patchset: surface-shutdown
3 files changed, 40 insertions(+)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
-index 51ec9e7e784f0..40554890d7211 100644
+index 51ec9e7e784f..40554890d721 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -507,6 +507,9 @@ static void pci_device_shutdown(struct device *dev)
@@ -8074,13 +7603,14 @@ index 51ec9e7e784f0..40554890d7211 100644
if (drv && drv->shutdown)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
-index e3e915329510f..666ff1e9b6d7b 100644
+index d55a3ffae4b8..e8614d8476fe 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
-@@ -6212,6 +6212,42 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5020, of_pci_make_dev_node);
- DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5021, of_pci_make_dev_node);
- DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REDHAT, 0x0005, of_pci_make_dev_node);
-
+@@ -6227,3 +6227,39 @@ static void pci_fixup_d3cold_delay_1sec(struct pci_dev *pdev)
+ pdev->d3cold_delay = 1000;
+ }
+ DECLARE_PCI_FIXUP_FINAL(0x5555, 0x0004, pci_fixup_d3cold_delay_1sec);
++
+static const struct dmi_system_id no_shutdown_dmi_table[] = {
+ /*
+ * Systems on which some devices should not be touched during shutdown.
@@ -8116,13 +7646,8 @@ index e3e915329510f..666ff1e9b6d7b 100644
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown); // Thunderbolt 4 NHI
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown); // GPU
-+
- /*
- * Devices known to require a longer delay before first config space access
- * after reset recovery or resume from D3cold:
-
diff --git a/include/linux/pci.h b/include/linux/pci.h
-index 8c7c2c3c6c652..0c223b04dff91 100644
+index bc80960fad7c..eec5704d1000 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -465,6 +465,7 @@ struct pci_dev {
@@ -8134,9 +7659,9 @@ index 8c7c2c3c6c652..0c223b04dff91 100644
atomic_t enable_cnt; /* pci_enable_device has been called */
--
-2.42.0
+2.43.0
-From 912e956823b3cadd7203d3ce94418d162ff701be Mon Sep 17 00:00:00 2001
+From d9ddc9ae99c11ebc912a1a8dde46d783e873508b Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Sun, 12 Mar 2023 01:41:57 +0100
Subject: [PATCH] platform/surface: gpe: Add support for Surface Pro 9
@@ -8150,7 +7675,7 @@ Patchset: surface-gpe
1 file changed, 17 insertions(+)
diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c
-index c219b840d491a..69c4352e8406b 100644
+index 62fd4004db31..103fc4468262 100644
--- a/drivers/platform/surface/surface_gpe.c
+++ b/drivers/platform/surface/surface_gpe.c
@@ -41,6 +41,11 @@ static const struct property_entry lid_device_props_l4F[] = {
@@ -8185,9 +7710,9 @@ index c219b840d491a..69c4352e8406b 100644
.ident = "Surface Book 1",
.matches = {
--
-2.42.0
+2.43.0
-From df083025f8c63824279c19de8ec3339440f819c9 Mon Sep 17 00:00:00 2001
+From 5fdcd780891777ef73585adf610593e6e097e6d6 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Sun, 10 Oct 2021 20:56:57 +0200
Subject: [PATCH] ACPI: delay enumeration of devices with a _DEP pointing to an
@@ -8247,10 +7772,10 @@ Patchset: cameras
1 file changed, 3 insertions(+)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
-index 691d4b7686ee7..9283217689279 100644
+index 02bb2cce423f..b123138d3dc0 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
-@@ -2108,6 +2108,9 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
+@@ -2114,6 +2114,9 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
static void acpi_default_enumeration(struct acpi_device *device)
{
@@ -8261,9 +7786,9 @@ index 691d4b7686ee7..9283217689279 100644
* Do not enumerate devices with enumeration_by_parent flag set as
* they will be enumerated by their respective parents.
--
-2.42.0
+2.43.0
-From 87650a001d3068a8b614fd688e21bb87c2d3a3e6 Mon Sep 17 00:00:00 2001
+From eb19f5e13f14a8973920d406125f205945558fb9 Mon Sep 17 00:00:00 2001
From: zouxiaoh <xiaohong.zou@intel.com>
Date: Fri, 25 Jun 2021 08:52:59 +0800
Subject: [PATCH] iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs
@@ -8289,7 +7814,7 @@ Patchset: cameras
1 file changed, 30 insertions(+)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
-index 5a627e081797c..da866ac6b30ba 100644
+index cc6569613255..8a532d32efdd 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -38,6 +38,12 @@
@@ -8305,7 +7830,7 @@ index 5a627e081797c..da866ac6b30ba 100644
#define IS_IPTS(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \
((pdev)->device == 0x9d3e))
#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
-@@ -295,12 +301,14 @@ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
+@@ -294,12 +300,14 @@ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
static int dmar_map_gfx = 1;
static int dmar_map_ipts = 1;
@@ -8320,7 +7845,7 @@ index 5a627e081797c..da866ac6b30ba 100644
#define IDENTMAP_IPTS 16
const struct iommu_ops intel_iommu_ops;
-@@ -2547,6 +2555,9 @@ static int device_def_domain_type(struct device *dev)
+@@ -2553,6 +2561,9 @@ static int device_def_domain_type(struct device *dev)
if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
return IOMMU_DOMAIN_IDENTITY;
@@ -8330,7 +7855,7 @@ index 5a627e081797c..da866ac6b30ba 100644
if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
return IOMMU_DOMAIN_IDENTITY;
}
-@@ -2856,6 +2867,9 @@ static int __init init_dmars(void)
+@@ -2862,6 +2873,9 @@ static int __init init_dmars(void)
if (!dmar_map_gfx)
iommu_identity_mapping |= IDENTMAP_GFX;
@@ -8340,7 +7865,7 @@ index 5a627e081797c..da866ac6b30ba 100644
if (!dmar_map_ipts)
iommu_identity_mapping |= IDENTMAP_IPTS;
-@@ -4838,6 +4852,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
+@@ -4987,6 +5001,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
dmar_map_gfx = 0;
}
@@ -8359,7 +7884,7 @@ index 5a627e081797c..da866ac6b30ba 100644
static void quirk_iommu_ipts(struct pci_dev *dev)
{
if (!IS_IPTS(dev))
-@@ -4849,6 +4875,7 @@ static void quirk_iommu_ipts(struct pci_dev *dev)
+@@ -4998,6 +5024,7 @@ static void quirk_iommu_ipts(struct pci_dev *dev)
pci_info(dev, "Passthrough IOMMU for IPTS\n");
dmar_map_ipts = 0;
}
@@ -8367,7 +7892,7 @@ index 5a627e081797c..da866ac6b30ba 100644
/* G4x/GM45 integrated gfx dmar support is totally busted. */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
-@@ -4884,6 +4911,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
+@@ -5033,6 +5060,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
@@ -8378,9 +7903,9 @@ index 5a627e081797c..da866ac6b30ba 100644
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
--
-2.42.0
+2.43.0
-From 76fec27d978bf7708a60862d4aab2e1fe7ec3f27 Mon Sep 17 00:00:00 2001
+From 1b16e7cbcbf699e4d841424568e0de1cee048d93 Mon Sep 17 00:00:00 2001
From: Daniel Scally <djrscally@gmail.com>
Date: Sun, 10 Oct 2021 20:57:02 +0200
Subject: [PATCH] platform/x86: int3472: Enable I2c daisy chain
@@ -8397,7 +7922,7 @@ Patchset: cameras
1 file changed, 7 insertions(+)
diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
-index 1e107fd49f828..e3e1696e7f0ee 100644
+index 1e107fd49f82..e3e1696e7f0e 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -46,6 +46,13 @@ static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
@@ -8415,9 +7940,9 @@ index 1e107fd49f828..e3e1696e7f0ee 100644
return 0;
--
-2.42.0
+2.43.0
-From 232a0f88ecc21141c6f0d94cc74eb63c7869c217 Mon Sep 17 00:00:00 2001
+From a856e6ec1aa1ce0e88abdd423a151f2bbddb8134 Mon Sep 17 00:00:00 2001
From: Daniel Scally <dan.scally@ideasonboard.com>
Date: Thu, 2 Mar 2023 12:59:39 +0000
Subject: [PATCH] platform/x86: int3472: Remap reset GPIO for INT347E
@@ -8439,20 +7964,19 @@ Patchset: cameras
1 file changed, 14 insertions(+)
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
-index e33c2d75975cf..c0c90ae66b705 100644
+index 07b302e09340..1d3097bc7e48 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
-@@ -57,6 +57,9 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
+@@ -83,12 +83,26 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
const char *func, u32 polarity)
{
- char *path = agpio->resource_source.string_ptr;
+ int ret;
+ const struct acpi_device_id ov7251_ids[] = {
+ { "INT347E" },
+ };
- struct gpiod_lookup *table_entry;
- struct acpi_device *adev;
- acpi_handle handle;
-@@ -67,6 +70,17 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
+
+ if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
+ dev_warn(int3472->dev, "Too many GPIOs mapped\n");
return -EINVAL;
}
@@ -8467,13 +7991,13 @@ index e33c2d75975cf..c0c90ae66b705 100644
+ polarity = GPIO_ACTIVE_HIGH;
+ }
+
- status = acpi_get_handle(NULL, path, &handle);
- if (ACPI_FAILURE(status))
- return -EINVAL;
+ ret = skl_int3472_fill_gpiod_lookup(&int3472->gpios.table[int3472->n_sensor_gpios],
+ agpio, func, polarity);
+ if (ret)
--
-2.42.0
+2.43.0
-From 0cfd5c05a675388bbb2edfa87423dc5ad931cc97 Mon Sep 17 00:00:00 2001
+From a7a10c4493fe0a381f12fd6a20a024e7797bd37c Mon Sep 17 00:00:00 2001
From: Daniel Scally <dan.scally@ideasonboard.com>
Date: Tue, 21 Mar 2023 13:45:26 +0000
Subject: [PATCH] media: i2c: Clarify that gain is Analogue gain in OV7251
@@ -8488,7 +8012,7 @@ Patchset: cameras
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
-index 675fb37a6feae..43b30db08c9e4 100644
+index 6582cc0e2384..fd0796b6e07e 100644
--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -1051,7 +1051,7 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -8500,7 +8024,7 @@ index 675fb37a6feae..43b30db08c9e4 100644
ret = ov7251_set_gain(ov7251, ctrl->val);
break;
case V4L2_CID_TEST_PATTERN:
-@@ -1551,7 +1551,7 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251)
+@@ -1553,7 +1553,7 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251)
ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 32, 1, 32);
ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
@@ -8510,9 +8034,9 @@ index 675fb37a6feae..43b30db08c9e4 100644
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
--
-2.42.0
+2.43.0
-From 18fa273c21f1dd86160f18242a81947392272443 Mon Sep 17 00:00:00 2001
+From e96fa67c9172fac9aa6e68199cf7e29d074c21e6 Mon Sep 17 00:00:00 2001
From: Daniel Scally <dan.scally@ideasonboard.com>
Date: Wed, 22 Mar 2023 11:01:42 +0000
Subject: [PATCH] media: v4l2-core: Acquire privacy led in
@@ -8531,7 +8055,7 @@ Patchset: cameras
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
-index 091e8cf4114ba..cca10f5355844 100644
+index 8cfd593d293d..c32f0d1b29d4 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -796,6 +796,10 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
@@ -8546,7 +8070,7 @@ index 091e8cf4114ba..cca10f5355844 100644
* No reference taken. The reference is held by the device (struct
* v4l2_subdev.dev), and async sub-device does not exist independently
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
-index 7f181fbbb1407..1c0347de4e216 100644
+index 7f181fbbb140..1c0347de4e21 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -1217,10 +1217,6 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
@@ -8561,9 +8085,9 @@ index 7f181fbbb1407..1c0347de4e216 100644
if (ret < 0)
goto out_cleanup;
--
-2.42.0
+2.43.0
-From 07e01113f2641afab78b155d42e9d9d399a9e164 Mon Sep 17 00:00:00 2001
+From 68dac72bec1c99890d35d6bfd1b1f66e0cf8789c Mon Sep 17 00:00:00 2001
From: Kate Hsuan <hpa@redhat.com>
Date: Tue, 21 Mar 2023 23:37:16 +0800
Subject: [PATCH] platform: x86: int3472: Add MFD cell for tps68470 LED
@@ -8579,7 +8103,7 @@ Patchset: cameras
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
-index e3e1696e7f0ee..423dc555093f7 100644
+index e3e1696e7f0e..423dc555093f 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -17,7 +17,7 @@
@@ -8602,9 +8126,9 @@ index e3e1696e7f0ee..423dc555093f7 100644
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
--
-2.42.0
+2.43.0
-From a704bf822539e09b00015110b48bc997692c92ce Mon Sep 17 00:00:00 2001
+From 3f446f24aecaba808693f0173e28972e651fa87d Mon Sep 17 00:00:00 2001
From: Kate Hsuan <hpa@redhat.com>
Date: Tue, 21 Mar 2023 23:37:17 +0800
Subject: [PATCH] include: mfd: tps68470: Add masks for LEDA and LEDB
@@ -8622,7 +8146,7 @@ Patchset: cameras
1 file changed, 5 insertions(+)
diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h
-index 7807fa329db00..2d2abb25b944f 100644
+index 7807fa329db0..2d2abb25b944 100644
--- a/include/linux/mfd/tps68470.h
+++ b/include/linux/mfd/tps68470.h
@@ -34,6 +34,7 @@
@@ -8643,9 +8167,9 @@ index 7807fa329db00..2d2abb25b944f 100644
+
#endif /* __LINUX_MFD_TPS68470_H */
--
-2.42.0
+2.43.0
-From c8a6ce96be3a4dca7e9e99613b28494d10b4ade0 Mon Sep 17 00:00:00 2001
+From a0fe4ec438c5edb9f4360c8a2a5f5269d05c44ef Mon Sep 17 00:00:00 2001
From: Kate Hsuan <hpa@redhat.com>
Date: Tue, 21 Mar 2023 23:37:18 +0800
Subject: [PATCH] leds: tps68470: Add LED control for tps68470
@@ -8668,10 +8192,10 @@ Patchset: cameras
create mode 100644 drivers/leds/leds-tps68470.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
-index b92208eccdea9..312c0c21cc5ef 100644
+index a3a9ac5b5338..0bc6845b5d29 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
-@@ -873,6 +873,18 @@ config LEDS_TPS6105X
+@@ -875,6 +875,18 @@ config LEDS_TPS6105X
It is a single boost converter primarily for white LEDs and
audio amplifiers.
@@ -8691,7 +8215,7 @@ index b92208eccdea9..312c0c21cc5ef 100644
tristate "LED support for SGI Octane machines"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
-index d7348e8bc019a..10caea4e7c614 100644
+index d7348e8bc019..10caea4e7c61 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
@@ -8704,7 +8228,7 @@ index d7348e8bc019a..10caea4e7c614 100644
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
diff --git a/drivers/leds/leds-tps68470.c b/drivers/leds/leds-tps68470.c
new file mode 100644
-index 0000000000000..35aeb5db89c8f
+index 000000000000..35aeb5db89c8
--- /dev/null
+++ b/drivers/leds/leds-tps68470.c
@@ -0,0 +1,185 @@
@@ -8894,9 +8418,9 @@ index 0000000000000..35aeb5db89c8f
+MODULE_DESCRIPTION("LED driver for TPS68470 PMIC");
+MODULE_LICENSE("GPL v2");
--
-2.42.0
+2.43.0
-From 82252c3764ecee6c09218077759072f15001f9ee Mon Sep 17 00:00:00 2001
+From 04069751b144350632ec45b5b25c2cc01d5f34ef Mon Sep 17 00:00:00 2001
From: Sachi King <nakato@nakato.io>
Date: Sat, 29 May 2021 17:47:38 +1000
Subject: [PATCH] ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7
@@ -8919,7 +8443,7 @@ Patchset: amd-gpio
1 file changed, 17 insertions(+)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
-index c55c0ef47a187..f29740cf89ff6 100644
+index 85a3ce2a3666..2c0e04a3a697 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -22,6 +22,7 @@
@@ -8930,7 +8454,7 @@ index c55c0ef47a187..f29740cf89ff6 100644
#include <asm/e820/api.h>
#include <asm/irqdomain.h>
-@@ -1255,6 +1256,17 @@ static void __init mp_config_acpi_legacy_irqs(void)
+@@ -1251,6 +1252,17 @@ static void __init mp_config_acpi_legacy_irqs(void)
}
}
@@ -8948,7 +8472,7 @@ index c55c0ef47a187..f29740cf89ff6 100644
/*
* Parse IOAPIC related entries in MADT
* returns 0 on success, < 0 on error
-@@ -1310,6 +1322,11 @@ static int __init acpi_parse_madt_ioapic_entries(void)
+@@ -1306,6 +1318,11 @@ static int __init acpi_parse_madt_ioapic_entries(void)
acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0,
acpi_gbl_FADT.sci_interrupt);
@@ -8961,9 +8485,9 @@ index c55c0ef47a187..f29740cf89ff6 100644
mp_config_acpi_legacy_irqs();
--
-2.42.0
+2.43.0
-From 52e3f50633128a93bf99ca5c97f98929da66a9ed Mon Sep 17 00:00:00 2001
+From 8e2f2b852776fca1dd0ab8728be2303051cb19e1 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Thu, 3 Jun 2021 14:04:26 +0200
Subject: [PATCH] ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override
@@ -8978,10 +8502,10 @@ Patchset: amd-gpio
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
-index f29740cf89ff6..247d2a8bcdf4b 100644
+index 2c0e04a3a697..b0e1dab3d2ec 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
-@@ -1258,12 +1258,19 @@ static void __init mp_config_acpi_legacy_irqs(void)
+@@ -1254,12 +1254,19 @@ static void __init mp_config_acpi_legacy_irqs(void)
static const struct dmi_system_id surface_quirk[] __initconst = {
{
@@ -9003,9 +8527,9 @@ index f29740cf89ff6..247d2a8bcdf4b 100644
};
--
-2.42.0
+2.43.0
-From 8cd23b1bb3a8b7a3ef7cec2c37e7e46e6397a858 Mon Sep 17 00:00:00 2001
+From 4e36132e272de3d84833b799be56c2b460db08b6 Mon Sep 17 00:00:00 2001
From: "Bart Groeneveld | GPX Solutions B.V" <bart@gpxbv.nl>
Date: Mon, 5 Dec 2022 16:08:46 +0100
Subject: [PATCH] acpi: allow usage of acpi_tad on HW-reduced platforms
@@ -9028,7 +8552,7 @@ Patchset: rtc
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
-index 33c3b16af556b..900445d06623d 100644
+index 33c3b16af556..900445d06623 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -432,6 +432,14 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
@@ -9113,5 +8637,5 @@ index 33c3b16af556b..900445d06623d 100644
ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
if (ret)
--
-2.42.0
+2.43.0