diff options
Diffstat (limited to 'SOURCES/linux-surface.patch')
-rw-r--r-- | SOURCES/linux-surface.patch | 3612 |
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 |