From 7c0e669f67d2e8b708ea9b673861929faa8b4a18 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 22 Oct 2020 18:27:17 +0200 Subject: [PATCH] Update v5.8 patches Changes: - SAM: - Update DTX driver state after resume. - Add DTX Documentation, misc. fixes, and cleanup. - IPTS: This implements a new and refined UAPI interface that should improve stability during suspend and move some responsibility into userspace, making the driver simpler. It also fixes some sleep issues due to improper shutdown of the device. Shortlog: 0a4a44c Add missing include 31ae03d Improve error handling of ipts_control_* functions 287dea0 Prevent lockups if stop is called from the receiver thread b737a9c On remove, wait until CLEAR_MEM_WINDOW returned. c5b66a5 Add GET_DEVICE_READY ioctl af0f84a Seperate UAPI initialization and device probing 4ae7674 Patch the MEI bus to allow sending commands on remove 27772bc Just a few refactorings... Links: - SAM: https://github.com/linux-surface/surface-aggregator-module/commit/af4bb01042d8ab707d8a73d4ee7ff770223a1c2f - IPTS: https://github.com/linux-surface/intel-precise-touch/commit/0a4a44c2a9b676bd25d1cd916118dcfe3f447849 - kernel: https://github.com/linux-surface/kernel/commit/6e8bb10ad83dee8c6f5f02dc08b2e2d7e5ea3b19 --- patches/5.8/0001-surface3-oemb.patch | 38 +- patches/5.8/0002-wifi.patch | 1446 +++++++-- patches/5.8/0003-ipts.patch | 2730 +++++++++-------- ...rface-gpe.patch => 0004-surface-gpe.patch} | 45 +- patches/5.8/0005-surface-sam-over-hid.patch | 97 +- ...rface-sam.patch => 0006-surface-sam.patch} | 1632 ++++++++-- ...-acpi_walk_dep_device_list-getting-c.patch | 6 +- pkg/arch/kernel/0004-surface-gpe.patch | 1 + pkg/arch/kernel/0004-surface-sam.patch | 1 - pkg/arch/kernel/0006-surface-gpe.patch | 1 - pkg/arch/kernel/0006-surface-sam.patch | 1 + pkg/arch/kernel/PKGBUILD | 18 +- 12 files changed, 4030 insertions(+), 1986 deletions(-) rename patches/5.8/{0006-surface-gpe.patch => 0004-surface-gpe.patch} (84%) rename patches/5.8/{0004-surface-sam.patch => 0006-surface-sam.patch} (93%) create mode 120000 pkg/arch/kernel/0004-surface-gpe.patch delete mode 120000 pkg/arch/kernel/0004-surface-sam.patch delete mode 120000 pkg/arch/kernel/0006-surface-gpe.patch create mode 120000 pkg/arch/kernel/0006-surface-sam.patch diff --git a/patches/5.8/0001-surface3-oemb.patch b/patches/5.8/0001-surface3-oemb.patch index 130b4815f..c27864a9e 100644 --- a/patches/5.8/0001-surface3-oemb.patch +++ b/patches/5.8/0001-surface3-oemb.patch @@ -1,8 +1,38 @@ -From 463107b4f3a4b794406270c8333e66619bdadf05 Mon Sep 17 00:00:00 2001 -From: Chih-Wei Huang -Date: Tue, 18 Sep 2018 11:01:37 +0800 -Subject: [PATCH 1/7] surface3-oemb +From 783117ef972d0dcc29d1065e4609690c9093a8c3 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Sun, 18 Oct 2020 16:42:44 +0900 +Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI + table +On some Surface 3, the DMI table gets corrupted for unknown reasons +and breaks existing DMI matching used for device-specific quirks. + +This commit adds the (broken) DMI data into dmi_system_id tables used +for quirks so that each driver can enable quirks even on the affected +systems. + +On affected systems, DMI data will look like this: + $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ + chassis_vendor,product_name,sys_vendor} + /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. + /sys/devices/virtual/dmi/id/board_name:OEMB + /sys/devices/virtual/dmi/id/board_vendor:OEMB + /sys/devices/virtual/dmi/id/chassis_vendor:OEMB + /sys/devices/virtual/dmi/id/product_name:OEMB + /sys/devices/virtual/dmi/id/sys_vendor:OEMB + +Expected: + $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ + chassis_vendor,product_name,sys_vendor} + /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. + /sys/devices/virtual/dmi/id/board_name:Surface 3 + /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation + /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation + /sys/devices/virtual/dmi/id/product_name:Surface 3 + /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation + +Signed-off-by: Tsuchiya Yuto +Patchset: surface3-oemb --- drivers/platform/x86/surface3-wmi.c | 7 +++++++ sound/soc/codecs/rt5645.c | 9 +++++++++ diff --git a/patches/5.8/0002-wifi.patch b/patches/5.8/0002-wifi.patch index 551c61aad..dc2adfc9d 100644 --- a/patches/5.8/0002-wifi.patch +++ b/patches/5.8/0002-wifi.patch @@ -1,248 +1,116 @@ -From e9076b84d446d05265717b73de4f77e302e78598 Mon Sep 17 00:00:00 2001 -From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> -Date: Thu, 20 Feb 2020 16:51:11 +0900 -Subject: [PATCH 2/7] wifi +From c6be0add7574727c5ab9081ff4b46b4f6ef1fdf5 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Thu, 24 Sep 2020 18:02:06 +0900 +Subject: [PATCH] mwifiex: pcie: skip cancel_work_sync() on reset failure path +If a reset is performed, but even the reset fails for some reasons (e.g., +on Surface devices, the fw reset requires another quirks), +cancel_work_sync() hangs in mwifiex_cleanup_pcie(). + + # reset performed after firmware went into bad state + kernel: mwifiex_pcie 0000:01:00.0: WLAN FW already running! Skip FW dnld + kernel: mwifiex_pcie 0000:01:00.0: WLAN FW is active + # but even the reset failed + kernel: mwifiex_pcie 0000:01:00.0: mwifiex_cmd_timeout_func: Timeout cmd id = 0xfa, act = 0xa000 + kernel: mwifiex_pcie 0000:01:00.0: num_data_h2c_failure = 0 + kernel: mwifiex_pcie 0000:01:00.0: num_cmd_h2c_failure = 0 + kernel: mwifiex_pcie 0000:01:00.0: is_cmd_timedout = 1 + kernel: mwifiex_pcie 0000:01:00.0: num_tx_timeout = 0 + kernel: mwifiex_pcie 0000:01:00.0: last_cmd_index = 2 + kernel: mwifiex_pcie 0000:01:00.0: last_cmd_id: 16 00 a4 00 fa 00 a4 00 7f 00 + kernel: mwifiex_pcie 0000:01:00.0: last_cmd_act: 00 00 00 00 00 a0 00 00 00 00 + kernel: mwifiex_pcie 0000:01:00.0: last_cmd_resp_index = 0 + kernel: mwifiex_pcie 0000:01:00.0: last_cmd_resp_id: 16 80 7f 80 16 80 a4 80 7f 80 + kernel: mwifiex_pcie 0000:01:00.0: last_event_index = 1 + kernel: mwifiex_pcie 0000:01:00.0: last_event: 58 00 58 00 58 00 58 00 58 00 + kernel: mwifiex_pcie 0000:01:00.0: data_sent=0 cmd_sent=1 + kernel: mwifiex_pcie 0000:01:00.0: ps_mode=0 ps_state=0 + kernel: mwifiex_pcie 0000:01:00.0: info: _mwifiex_fw_dpc: unregister device + # mwifiex_pcie_work hanged + kernel: INFO: task kworker/0:0:24857 blocked for more than 122 seconds. + kernel: Tainted: G W OE 5.3.11-arch1-1 #1 + kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. + kernel: kworker/0:0 D 0 24857 2 0x80004000 + kernel: Workqueue: events mwifiex_pcie_work [mwifiex_pcie] + kernel: Call Trace: + kernel: ? __schedule+0x27f/0x6d0 + kernel: schedule+0x43/0xd0 + kernel: schedule_timeout+0x299/0x3d0 + kernel: ? __switch_to_asm+0x40/0x70 + kernel: wait_for_common+0xeb/0x190 + kernel: ? wake_up_q+0x60/0x60 + kernel: __flush_work+0x130/0x1e0 + kernel: ? flush_workqueue_prep_pwqs+0x130/0x130 + kernel: __cancel_work_timer+0x123/0x1b0 + kernel: mwifiex_cleanup_pcie+0x28/0xd0 [mwifiex_pcie] + kernel: mwifiex_free_adapter+0x24/0xe0 [mwifiex] + kernel: _mwifiex_fw_dpc+0x28d/0x520 [mwifiex] + kernel: mwifiex_reinit_sw+0x15d/0x2c0 [mwifiex] + kernel: mwifiex_pcie_reset_done+0x50/0x80 [mwifiex_pcie] + kernel: pci_try_reset_function+0x38/0x70 + kernel: process_one_work+0x1d1/0x3a0 + kernel: worker_thread+0x4a/0x3d0 + kernel: kthread+0xfb/0x130 + kernel: ? process_one_work+0x3a0/0x3a0 + kernel: ? kthread_park+0x80/0x80 + kernel: ret_from_fork+0x35/0x40 + +This is a deadlock caused by calling cancel_work_sync() in +mwifiex_cleanup_pcie(): + +- Device resets are done via mwifiex_pcie_card_reset() +- which schedules card->work to call mwifiex_pcie_card_reset_work() +- which calls pci_try_reset_function(). +- This leads to mwifiex_pcie_reset_done() be called on the same workqueue, + which in turn calls +- mwifiex_reinit_sw() and that calls +- _mwifiex_fw_dpc(). + +The problem is now that _mwifiex_fw_dpc() calls mwifiex_free_adapter() +in case firmware initialization fails. That ends up calling +mwifiex_cleanup_pcie(). + +Note that all those calls are still running on the workqueue. So when +mwifiex_cleanup_pcie() now calls cancel_work_sync(), it's really waiting +on itself to complete, causing a deadlock. + +This commit fixes the deadlock by skipping cancel_work_sync() on a reset +failure path. + +After this commit, when reset fails, the following output is +expected to be shown: + + kernel: mwifiex_pcie 0000:03:00.0: info: _mwifiex_fw_dpc: unregister device + kernel: mwifiex: Failed to bring up adapter: -5 + kernel: mwifiex_pcie 0000:03:00.0: reinit failed: -5 + +To reproduce this issue, for example, try putting the root port of wifi +into D3 (replace "00:1d.3" with your setup). + + # put into D3 (root port) + sudo setpci -v -s 00:1d.3 CAP_PM+4.b=0b + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi --- - drivers/net/wireless/marvell/mwifiex/Makefile | 1 + - .../net/wireless/marvell/mwifiex/cfg80211.c | 26 ++ - drivers/net/wireless/marvell/mwifiex/main.c | 6 +- - drivers/net/wireless/marvell/mwifiex/pcie.c | 84 ++++-- - drivers/net/wireless/marvell/mwifiex/pcie.h | 3 + - .../wireless/marvell/mwifiex/pcie_quirks.c | 255 ++++++++++++++++++ - .../wireless/marvell/mwifiex/pcie_quirks.h | 17 ++ - .../net/wireless/marvell/mwifiex/sta_cmd.c | 14 +- - 8 files changed, 374 insertions(+), 32 deletions(-) - create mode 100644 drivers/net/wireless/marvell/mwifiex/pcie_quirks.c - create mode 100644 drivers/net/wireless/marvell/mwifiex/pcie_quirks.h + drivers/net/wireless/marvell/mwifiex/pcie.c | 18 +++++++++++++++++- + drivers/net/wireless/marvell/mwifiex/pcie.h | 2 ++ + 2 files changed, 19 insertions(+), 1 deletion(-) -diff --git a/drivers/net/wireless/marvell/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile -index fdfd9bf15ed4..8a1e7c5b9c6e 100644 ---- a/drivers/net/wireless/marvell/mwifiex/Makefile -+++ b/drivers/net/wireless/marvell/mwifiex/Makefile -@@ -49,6 +49,7 @@ mwifiex_sdio-y += sdio.o - obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o - - mwifiex_pcie-y += pcie.o -+mwifiex_pcie-y += pcie_quirks.o - obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o - - mwifiex_usb-y += usb.o -diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index 4e4f59c17ded..528eedfbf41c 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c -+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -@@ -25,6 +25,11 @@ - static char *reg_alpha2; - module_param(reg_alpha2, charp, 0); - -+static bool allow_ps_mode; -+module_param(allow_ps_mode, bool, 0444); -+MODULE_PARM_DESC(allow_ps_mode, -+ "allow WiFi power management to be enabled. (default: disallowed)"); -+ - static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { - { - .max = 3, .types = BIT(NL80211_IFTYPE_STATION) | -@@ -434,6 +439,27 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, - - ps_mode = enabled; - -+ /* Allow ps_mode to be enabled only when allow_ps_mode is set -+ * (but always allow ps_mode to be disabled in case it gets enabled -+ * for unknown reason and you want to disable it) */ -+ if (ps_mode && !allow_ps_mode) { -+ dev_info(priv->adapter->dev, -+ "Request to enable ps_mode received but it's disallowed " -+ "by module parameter. Rejecting the request.\n"); -+ -+ /* Return negative value to inform userspace tools that setting -+ * power_save to be enabled is not permitted. */ -+ return -1; -+ } -+ -+ if (ps_mode) -+ dev_warn(priv->adapter->dev, -+ "WARN: Request to enable ps_mode received. Enabling it. " -+ "Disable it if you encounter connection instability.\n"); -+ else -+ dev_info(priv->adapter->dev, -+ "Request to disable ps_mode received. Disabling it.\n"); -+ - return mwifiex_drv_set_power(priv, &ps_mode); - } - -diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c -index 529099137644..a26eb66865e2 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.c -+++ b/drivers/net/wireless/marvell/mwifiex/main.c -@@ -1453,7 +1453,7 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) - } - - /* -- * This function gets called during PCIe function level reset. -+ * This function can be used for shutting down the adapter SW. - */ - int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) - { -@@ -1469,6 +1469,8 @@ int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - mwifiex_deauthenticate(priv, NULL); - -+ mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); -+ - mwifiex_uninit_sw(adapter); - adapter->is_up = false; - -@@ -1479,7 +1481,7 @@ int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) - } - EXPORT_SYMBOL_GPL(mwifiex_shutdown_sw); - --/* This function gets called during PCIe function level reset. Required -+/* This function can be used for reinitting the adapter SW. Required - * code is extracted from mwifiex_add_card() - */ - int diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index 87b4ccca4b9a..696fa48c1ef5 100644 +index 87b4ccca4b9a..00138d6129f4 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -27,12 +27,18 @@ - #include "wmm.h" - #include "11n.h" - #include "pcie.h" -+#include "pcie_quirks.h" - - #define PCIE_VERSION "1.0" - #define DRV_NAME "Marvell mwifiex PCIe" - - static struct mwifiex_if_ops pcie_ops; - -+static bool enable_device_dump; -+module_param(enable_device_dump, bool, 0644); -+MODULE_PARM_DESC(enable_device_dump, -+ "enable device_dump (default: disabled)"); -+ - static const struct of_device_id mwifiex_pcie_of_match_table[] = { - { .compatible = "pci11ab,2b42" }, - { .compatible = "pci1b4b,2b42" }, -@@ -144,8 +150,7 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * -- * If already not suspended, this function allocates and sends a host -- * sleep activate request to the firmware and turns off the traffic. -+ * This function shuts down the adapter. - */ - static int mwifiex_pcie_suspend(struct device *dev) - { -@@ -153,31 +158,21 @@ static int mwifiex_pcie_suspend(struct device *dev) - struct pcie_service_card *card = dev_get_drvdata(dev); - - -- /* Might still be loading firmware */ -- wait_for_completion(&card->fw_done); -- - adapter = card->adapter; - if (!adapter) { - dev_err(dev, "adapter is not valid\n"); - return 0; - } - -- mwifiex_enable_wake(adapter); -- -- /* Enable the Host Sleep */ -- if (!mwifiex_enable_hs(adapter)) { -+ /* Shut down SW */ -+ if (mwifiex_shutdown_sw(adapter)) { - mwifiex_dbg(adapter, ERROR, - "cmd: failed to suspend\n"); -- clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); -- mwifiex_disable_wake(adapter); - return -EFAULT; - } - -- flush_workqueue(adapter->workqueue); -- - /* Indicate device suspended */ - set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); -- clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); - - return 0; - } -@@ -187,13 +182,13 @@ static int mwifiex_pcie_suspend(struct device *dev) - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * -- * If already not resumed, this function turns on the traffic and -- * sends a host sleep cancel request to the firmware. -+ * If already not resumed, this function reinits the adapter. - */ - static int mwifiex_pcie_resume(struct device *dev) - { - struct mwifiex_adapter *adapter; - struct pcie_service_card *card = dev_get_drvdata(dev); -+ int ret; - - - if (!card->adapter) { -@@ -211,9 +206,11 @@ static int mwifiex_pcie_resume(struct device *dev) - - clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); - -- mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), -- MWIFIEX_ASYNC_CMD); -- mwifiex_disable_wake(adapter); -+ ret = mwifiex_reinit_sw(adapter); -+ if (ret) -+ dev_err(dev, "reinit failed: %d\n", ret); -+ else -+ mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); - - return 0; - } -@@ -229,8 +226,13 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) - { - struct pcie_service_card *card; -+ struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); - int ret; - -+ /* disable bridge_d3 to fix driver crashing after suspend on gen4+ -+ * Surface devices */ -+ parent_pdev->bridge_d3 = false; -+ - pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", - pdev->vendor, pdev->device, pdev->revision); - -@@ -261,6 +263,9 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, - return ret; - } - -+ /* check quirks */ -+ mwifiex_initialize_quirks(card); -+ - if (mwifiex_add_card(card, &card->fw_done, &pcie_ops, - MWIFIEX_PCIE, &pdev->dev)) { - pr_err("%s failed\n", __func__); -@@ -376,7 +381,16 @@ static void mwifiex_pcie_reset_prepare(struct pci_dev *pdev) - mwifiex_shutdown_sw(adapter); +@@ -377,6 +377,8 @@ static void mwifiex_pcie_reset_prepare(struct pci_dev *pdev) clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); -+ -+ /* For Surface gen4+ devices, we need to put wifi into D3cold right -+ * before performing FLR -+ */ -+ if (card->quirks & QUIRK_FW_RST_D3COLD) -+ mwifiex_pcie_reset_d3cold_quirk(pdev); -+ mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); + + card->pci_reset_ongoing = true; } /* -@@ -405,6 +419,8 @@ static void mwifiex_pcie_reset_done(struct pci_dev *pdev) +@@ -405,6 +407,8 @@ static void mwifiex_pcie_reset_done(struct pci_dev *pdev) dev_err(&pdev->dev, "reinit failed: %d\n", ret); else mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); @@ -251,37 +119,7 @@ index 87b4ccca4b9a..696fa48c1ef5 100644 } static const struct pci_error_handlers mwifiex_pcie_err_handler = { -@@ -2785,6 +2801,12 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) - - static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) - { -+ if (!enable_device_dump) { -+ mwifiex_dbg(adapter, MSG, -+ "device_dump is disabled by module parameter\n"); -+ return; -+ } -+ - adapter->devdump_data = vzalloc(MWIFIEX_FW_DUMP_SIZE); - if (!adapter->devdump_data) { - mwifiex_dbg(adapter, ERROR, -@@ -2802,6 +2824,16 @@ static void mwifiex_pcie_card_reset_work(struct mwifiex_adapter *adapter) - { - struct pcie_service_card *card = adapter->card; - -+ /* On Surface 3, reset_wsid method removes then re-probes card by -+ * itself. So, need to place it here and skip performing any other -+ * reset-related works. -+ */ -+ if (card->quirks & QUIRK_FW_RST_WSID_S3) { -+ mwifiex_pcie_reset_wsid_quirk(card->dev); -+ /* skip performing any other reset-related works */ -+ return; -+ } -+ - /* We can't afford to wait here; remove() might be waiting on us. If we - * can't grab the device lock, maybe we'll get another chance later. - */ -@@ -2995,7 +3027,19 @@ static void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter) +@@ -2995,7 +2999,19 @@ static void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter) int ret; u32 fw_status; @@ -303,25 +141,95 @@ index 87b4ccca4b9a..696fa48c1ef5 100644 ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status); if (fw_status == FIRMWARE_READY_PCIE) { diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h -index fc59b522f670..51566380f8da 100644 +index fc59b522f670..048f4db6027a 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h -@@ -391,6 +391,9 @@ struct pcie_service_card { +@@ -391,6 +391,8 @@ struct pcie_service_card { struct mwifiex_msix_context share_irq_ctx; struct work_struct work; unsigned long work_flags; + + bool pci_reset_ongoing; + }; + + static inline int +-- +2.28.0 + +From 2b463669a76bc7cc2146481fd131cbc1233b9711 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Mon, 28 Sep 2020 17:46:49 +0900 +Subject: [PATCH] mwifiex: pcie: add DMI-based quirk impl for Surface devices + +This commit adds quirk implementation based on DMI matching with DMI +table for Surface devices. + +This implementation can be used for quirks later. + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/Makefile | 1 + + drivers/net/wireless/marvell/mwifiex/pcie.c | 4 + + drivers/net/wireless/marvell/mwifiex/pcie.h | 1 + + .../wireless/marvell/mwifiex/pcie_quirks.c | 114 ++++++++++++++++++ + .../wireless/marvell/mwifiex/pcie_quirks.h | 11 ++ + 5 files changed, 131 insertions(+) + create mode 100644 drivers/net/wireless/marvell/mwifiex/pcie_quirks.c + create mode 100644 drivers/net/wireless/marvell/mwifiex/pcie_quirks.h + +diff --git a/drivers/net/wireless/marvell/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile +index fdfd9bf15ed4..8a1e7c5b9c6e 100644 +--- a/drivers/net/wireless/marvell/mwifiex/Makefile ++++ b/drivers/net/wireless/marvell/mwifiex/Makefile +@@ -49,6 +49,7 @@ mwifiex_sdio-y += sdio.o + obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o + + mwifiex_pcie-y += pcie.o ++mwifiex_pcie-y += pcie_quirks.o + obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o + + mwifiex_usb-y += usb.o +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index 00138d6129f4..899ce2657880 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -27,6 +27,7 @@ + #include "wmm.h" + #include "11n.h" + #include "pcie.h" ++#include "pcie_quirks.h" + + #define PCIE_VERSION "1.0" + #define DRV_NAME "Marvell mwifiex PCIe" +@@ -261,6 +262,9 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, + return ret; + } + ++ /* check quirks */ ++ mwifiex_initialize_quirks(card); ++ + if (mwifiex_add_card(card, &card->fw_done, &pcie_ops, + MWIFIEX_PCIE, &pdev->dev)) { + pr_err("%s failed\n", __func__); +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h +index 048f4db6027a..51566380f8da 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.h ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.h +@@ -393,6 +393,7 @@ struct pcie_service_card { + unsigned long work_flags; + + bool pci_reset_ongoing; + unsigned long quirks; }; static inline int diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c new file mode 100644 -index 000000000000..34dcd84f02a6 +index 000000000000..929aee2b0a60 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c -@@ -0,0 +1,255 @@ +@@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * File for PCIe quirks. @@ -333,21 +241,10 @@ index 000000000000..34dcd84f02a6 + * down, or causes NULL ptr deref). + */ + -+#include +#include + +#include "pcie_quirks.h" + -+/* For reset_wsid quirk */ -+#define ACPI_WSID_PATH "\\_SB.WSID" -+#define WSID_REV 0x0 -+#define WSID_FUNC_WIFI_PWR_OFF 0x1 -+#define WSID_FUNC_WIFI_PWR_ON 0x2 -+/* WSID _DSM UUID: "534ea3bf-fcc2-4e7a-908f-a13978f0c7ef" */ -+static const guid_t wsid_dsm_guid = -+ GUID_INIT(0x534ea3bf, 0xfcc2, 0x4e7a, -+ 0x90, 0x8f, 0xa1, 0x39, 0x78, 0xf0, 0xc7, 0xef); -+ +/* quirk table based on DMI matching */ +static const struct dmi_system_id mwifiex_quirk_table[] = { + { @@ -356,7 +253,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Pro 5", @@ -365,7 +262,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Pro 5 (LTE)", @@ -374,7 +271,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Pro 6", @@ -382,7 +279,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Book 1", @@ -390,7 +287,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Book 2", @@ -398,7 +295,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Laptop 1", @@ -406,7 +303,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface Laptop 2", @@ -414,7 +311,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = 0, + }, + { + .ident = "Surface 3", @@ -422,16 +319,7 @@ index 000000000000..34dcd84f02a6 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, -+ .driver_data = (void *)QUIRK_FW_RST_WSID_S3, -+ }, -+ { -+ .ident = "Surface 3", -+ .matches = { -+ DMI_EXACT_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), -+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "OEMB"), -+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OEMB"), -+ }, -+ .driver_data = (void *)QUIRK_FW_RST_WSID_S3, ++ .driver_data = 0, + }, + { + .ident = "Surface Pro 3", @@ -455,11 +343,156 @@ index 000000000000..34dcd84f02a6 + + if (!card->quirks) + dev_info(&pdev->dev, "no quirks enabled\n"); ++} +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +new file mode 100644 +index 000000000000..5326ae7e5671 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +@@ -0,0 +1,11 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Header file for PCIe quirks. ++ */ ++ ++#include "pcie.h" ++ ++/* quirks */ ++// quirk flags can be added here ++ ++void mwifiex_initialize_quirks(struct pcie_service_card *card); +-- +2.28.0 + +From e344e3aa7d35ea66edb2d0792bda98ef311a9f98 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Tue, 29 Sep 2020 17:25:22 +0900 +Subject: [PATCH] mwifiex: pcie: add reset_d3cold quirk for Surface gen4+ + devices + +To reset mwifiex on Surface gen4+ (Pro 4 or later gen) devices, it +seems that putting the wifi device into D3cold is required according +to errata.inf file on Windows installation (Windows/INF/errata.inf). + +This patch adds a function that performs power-cycle (put into D3cold +then D0) and call the function at the end of reset_prepare(). + +Note: Need to also reset the parent device (bridge) of wifi on SB1; +it might be because the bridge of wifi always reports it's in D3hot. +When I tried to reset only the wifi device (not touching parent), it gave +the following error and the reset failed: + + acpi device:4b: Cannot transition to power state D0 for parent in D3hot + mwifiex_pcie 0000:03:00.0: can't change power state from D3cold to D0 (config space inaccessible) + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 7 ++ + .../wireless/marvell/mwifiex/pcie_quirks.c | 73 +++++++++++++++++-- + .../wireless/marvell/mwifiex/pcie_quirks.h | 3 +- + 3 files changed, 74 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index 899ce2657880..45488c2bc1c1 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -380,6 +380,13 @@ static void mwifiex_pcie_reset_prepare(struct pci_dev *pdev) + mwifiex_shutdown_sw(adapter); + clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); + clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); ++ ++ /* For Surface gen4+ devices, we need to put wifi into D3cold right ++ * before performing FLR ++ */ ++ if (card->quirks & QUIRK_FW_RST_D3COLD) ++ mwifiex_pcie_reset_d3cold_quirk(pdev); ++ + mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); + + card->pci_reset_ongoing = true; +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +index 929aee2b0a60..edc739c542fe 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +@@ -21,7 +21,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Pro 5", +@@ -30,7 +30,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Pro 5 (LTE)", +@@ -39,7 +39,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Pro 6", +@@ -47,7 +47,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Book 1", +@@ -55,7 +55,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Book 2", +@@ -63,7 +63,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Laptop 1", +@@ -71,7 +71,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface Laptop 2", +@@ -79,7 +79,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, + }, + { + .ident = "Surface 3", +@@ -111,4 +111,61 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) + + if (!card->quirks) + dev_info(&pdev->dev, "no quirks enabled\n"); + if (card->quirks & QUIRK_FW_RST_D3COLD) + dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); -+ if (card->quirks & QUIRK_FW_RST_WSID_S3) -+ dev_info(&pdev->dev, -+ "quirk reset_wsid for Surface 3 enabled\n"); +} + +static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) @@ -515,7 +548,122 @@ index 000000000000..34dcd84f02a6 + return ret; + + return 0; -+} + } +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +index 5326ae7e5671..8b9dcb5070d8 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +@@ -6,6 +6,7 @@ + #include "pcie.h" + + /* quirks */ +-// quirk flags can be added here ++#define QUIRK_FW_RST_D3COLD BIT(0) + + void mwifiex_initialize_quirks(struct pcie_service_card *card); ++int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); +-- +2.28.0 + +From a835d3d17471990f1c33e8a566876b1774856ac9 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Tue, 29 Sep 2020 17:32:22 +0900 +Subject: [PATCH] mwifiex: pcie: add reset_wsid quirk for Surface 3 + +This commit adds reset_wsid quirk and uses this quirk for Surface 3 on +card reset. + +To reset mwifiex on Surface 3, it seems that calling the _DSM method +exists in \_SB.WSID [1] device is required. + +On Surface 3, calling the _DSM method removes/re-probes the card by +itself. So, need to place the reset function before performing FLR and +skip performing any other reset-related works. + +Note that Surface Pro 3 also has the WSID device [2], but it seems to need +more work. This commit only supports Surface 3 yet. + +[1] https://github.com/linux-surface/acpidumps/blob/05cba925f3a515f222acb5b3551a032ddde958fe/surface_3/dsdt.dsl#L11947-L12011 +[2] https://github.com/linux-surface/acpidumps/blob/05cba925f3a515f222acb5b3551a032ddde958fe/surface_pro_3/dsdt.dsl#L12164-L12216 + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 10 +++ + .../wireless/marvell/mwifiex/pcie_quirks.c | 77 ++++++++++++++++++- + .../wireless/marvell/mwifiex/pcie_quirks.h | 5 ++ + 3 files changed, 91 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index 45488c2bc1c1..daae572ce94e 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -2817,6 +2817,16 @@ static void mwifiex_pcie_card_reset_work(struct mwifiex_adapter *adapter) + { + struct pcie_service_card *card = adapter->card; + ++ /* On Surface 3, reset_wsid method removes then re-probes card by ++ * itself. So, need to place it here and skip performing any other ++ * reset-related works. ++ */ ++ if (card->quirks & QUIRK_FW_RST_WSID_S3) { ++ mwifiex_pcie_reset_wsid_quirk(card->dev); ++ /* skip performing any other reset-related works */ ++ return; ++ } ++ + /* We can't afford to wait here; remove() might be waiting on us. If we + * can't grab the device lock, maybe we'll get another chance later. + */ +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +index edc739c542fe..f0a6fa0a7ae5 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +@@ -9,10 +9,21 @@ + * down, or causes NULL ptr deref). + */ + ++#include + #include + + #include "pcie_quirks.h" + ++/* For reset_wsid quirk */ ++#define ACPI_WSID_PATH "\\_SB.WSID" ++#define WSID_REV 0x0 ++#define WSID_FUNC_WIFI_PWR_OFF 0x1 ++#define WSID_FUNC_WIFI_PWR_ON 0x2 ++/* WSID _DSM UUID: "534ea3bf-fcc2-4e7a-908f-a13978f0c7ef" */ ++static const guid_t wsid_dsm_guid = ++ GUID_INIT(0x534ea3bf, 0xfcc2, 0x4e7a, ++ 0x90, 0x8f, 0xa1, 0x39, 0x78, 0xf0, 0xc7, 0xef); ++ + /* quirk table based on DMI matching */ + static const struct dmi_system_id mwifiex_quirk_table[] = { + { +@@ -87,7 +98,7 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, +- .driver_data = 0, ++ .driver_data = (void *)QUIRK_FW_RST_WSID_S3, + }, + { + .ident = "Surface Pro 3", +@@ -113,6 +124,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) + dev_info(&pdev->dev, "no quirks enabled\n"); + if (card->quirks & QUIRK_FW_RST_D3COLD) + dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); ++ if (card->quirks & QUIRK_FW_RST_WSID_S3) ++ dev_info(&pdev->dev, ++ "quirk reset_wsid for Surface 3 enabled\n"); + } + + static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) +@@ -169,3 +183,64 @@ int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev) + + return 0; + } + +int mwifiex_pcie_reset_wsid_quirk(struct pci_dev *pdev) +{ @@ -578,53 +726,671 @@ index 000000000000..34dcd84f02a6 + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -new file mode 100644 -index 000000000000..3ef7440418e3 ---- /dev/null +index 8b9dcb5070d8..3ef7440418e3 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h -@@ -0,0 +1,17 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Header file for PCIe quirks. -+ */ -+ -+#include "pcie.h" -+ -+/* quirks */ -+#define QUIRK_FW_RST_D3COLD BIT(0) +@@ -7,6 +7,11 @@ + + /* quirks */ + #define QUIRK_FW_RST_D3COLD BIT(0) +/* Surface 3 and Surface Pro 3 have the same _DSM method but need to + * be handled differently. Currently, only S3 is supported. + */ +#define QUIRK_FW_RST_WSID_S3 BIT(1) -+ -+void mwifiex_initialize_quirks(struct pcie_service_card *card); -+int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); + + void mwifiex_initialize_quirks(struct pcie_service_card *card); + int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); +int mwifiex_pcie_reset_wsid_quirk(struct pci_dev *pdev); -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -index 8bd355d7974e..484ba60b51ab 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -@@ -2332,16 +2332,10 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) - if (ret) - return -1; - -- if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { -- /* Enable IEEE PS by default */ -- priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; -- ret = mwifiex_send_cmd(priv, -- HostCmd_CMD_802_11_PS_MODE_ENH, -- EN_AUTO_PS, BITMAP_STA_PS, NULL, -- true); -- if (ret) -- return -1; -- } -+ /* Not enabling ps_mode (IEEE power_save) by default. Enabling -+ * this causes connection instability, especially on 5GHz APs -+ * and eventually causes "firmware wakeup failed". Therefore, -+ * the relevant code was removed from here. */ - - if (drcs) { - adapter->drcs_enabled = true; +-- +2.28.0 + +From 1a027d9e20179b26a9a89cbc67570dc57decc30e Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Wed, 30 Sep 2020 18:08:24 +0900 +Subject: [PATCH] mwifiex: pcie: (OEMB) add quirk for Surface 3 with broken DMI + table + +(made referring to http://git.osdn.net/view?p=android-x86/kernel.git;a=commitdiff;h=18e2e857c57633b25b3b4120f212224a108cd883) + +On some Surface 3, the DMI table gets corrupted for unknown reasons +and breaks existing DMI matching used for device-specific quirks. + +This commit adds the (broken) DMI info for the affected Surface 3. + +On affected systems, DMI info will look like this: + $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ + chassis_vendor,product_name,sys_vendor} + /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. + /sys/devices/virtual/dmi/id/board_name:OEMB + /sys/devices/virtual/dmi/id/board_vendor:OEMB + /sys/devices/virtual/dmi/id/chassis_vendor:OEMB + /sys/devices/virtual/dmi/id/product_name:OEMB + /sys/devices/virtual/dmi/id/sys_vendor:OEMB + +Expected: + $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ + chassis_vendor,product_name,sys_vendor} + /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. + /sys/devices/virtual/dmi/id/board_name:Surface 3 + /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation + /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation + /sys/devices/virtual/dmi/id/product_name:Surface 3 + /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/pcie_quirks.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +index f0a6fa0a7ae5..34dcd84f02a6 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +@@ -100,6 +100,15 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + }, + .driver_data = (void *)QUIRK_FW_RST_WSID_S3, + }, ++ { ++ .ident = "Surface 3", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "OEMB"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OEMB"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_WSID_S3, ++ }, + { + .ident = "Surface Pro 3", + .matches = { +-- +2.28.0 + +From b83d011dd7c568c2db41c520d3fdaadc4ff8954a Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Thu, 24 Sep 2020 01:56:29 +0900 +Subject: [PATCH] mwifiex: fix mwifiex_shutdown_sw() causing sw reset failure + +When FLR is performed but without fw reset for some reasons (e.g. on +Surface devices, fw reset requires another quirk), it fails to reset +properly. You can trigger the issue on such devices via debugfs entry +for reset: + + $ echo 1 | sudo tee /sys/kernel/debug/mwifiex/mlan0/reset + +and the resulting dmesg log: + + mwifiex_pcie 0000:03:00.0: Resetting per request + mwifiex_pcie 0000:03:00.0: info: successfully disconnected from [BSSID]: reason code 3 + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: info: shutdown mwifiex... + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: PREP_CMD: card is removed + mwifiex_pcie 0000:03:00.0: WLAN FW already running! Skip FW dnld + mwifiex_pcie 0000:03:00.0: WLAN FW is active + mwifiex_pcie 0000:03:00.0: Unknown api_id: 4 + mwifiex_pcie 0000:03:00.0: info: MWIFIEX VERSION: mwifiex 1.0 (15.68.19.p21) + mwifiex_pcie 0000:03:00.0: driver_version = mwifiex 1.0 (15.68.19.p21) + mwifiex_pcie 0000:03:00.0: info: trying to associate to '[SSID]' bssid [BSSID] + mwifiex_pcie 0000:03:00.0: info: associated to bssid [BSSID] successfully + mwifiex_pcie 0000:03:00.0: cmd_wait_q terminated: -110 + mwifiex_pcie 0000:03:00.0: info: successfully disconnected from [BSSID]: reason code 15 + mwifiex_pcie 0000:03:00.0: cmd_wait_q terminated: -110 + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: cmd_wait_q terminated: -110 + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + mwifiex_pcie 0000:03:00.0: cmd_wait_q terminated: -110 + mwifiex_pcie 0000:03:00.0: deleting the crypto keys + [...] + +When comparing mwifiex_shutdown_sw() with mwifiex_pcie_remove(), it +lacks mwifiex_init_shutdown_fw(). + +This commit fixes mwifiex_shutdown_sw() by adding the missing +mwifiex_init_shutdown_fw(). + +Fixes: 4c5dae59d2e9 ("mwifiex: add PCIe function level reset support") +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/main.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c +index 529099137644..c21f916c05a3 100644 +--- a/drivers/net/wireless/marvell/mwifiex/main.c ++++ b/drivers/net/wireless/marvell/mwifiex/main.c +@@ -1469,6 +1469,8 @@ int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + mwifiex_deauthenticate(priv, NULL); + ++ mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); ++ + mwifiex_uninit_sw(adapter); + adapter->is_up = false; + +-- +2.28.0 + +From 919ef1f9830f57b14ad8af2b812bb25ec7a5dba0 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Thu, 24 Sep 2020 01:56:34 +0900 +Subject: [PATCH] mwifiex: pcie: use shutdown_sw()/reinit_sw() on + suspend()/resume() + +There are issues with S0ix achievement and AP scanning after suspend +with the current Host Sleep method. + +When using the Host Sleep method, it prevents the platform to reach S0ix +during suspend. Also, after suspend, sometimes AP scanning won't work, +resulting in non-working wifi. + +To fix such issues, perform shutdown_sw()/reinit_sw() instead of Host +Sleep. + +As a side effect, this patch disables wakeups (means that Wake-On-WLAN +can't be used anymore, if it was working before), and might also reset +some internal states. + +Note that suspend() no longer checks if it's already suspended. + +With the previous Host Sleep method, the check was done by looking at +adapter->hs_activated in mwifiex_enable_hs() [sta_ioctl.c], but not +MWIFIEX_IS_SUSPENDED. So, what the previous method checked was instead +Host Sleep state, not suspend itself. Therefore, there is no need to check +the suspend state now. + +Also removed comment for suspend state check at top of suspend() +accordingly. + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 29 +++++++-------------- + 1 file changed, 10 insertions(+), 19 deletions(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index daae572ce94e..b46d56389c3b 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -145,8 +145,7 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * +- * If already not suspended, this function allocates and sends a host +- * sleep activate request to the firmware and turns off the traffic. ++ * This function shuts down the adapter. + */ + static int mwifiex_pcie_suspend(struct device *dev) + { +@@ -154,31 +153,21 @@ static int mwifiex_pcie_suspend(struct device *dev) + struct pcie_service_card *card = dev_get_drvdata(dev); + + +- /* Might still be loading firmware */ +- wait_for_completion(&card->fw_done); +- + adapter = card->adapter; + if (!adapter) { + dev_err(dev, "adapter is not valid\n"); + return 0; + } + +- mwifiex_enable_wake(adapter); +- +- /* Enable the Host Sleep */ +- if (!mwifiex_enable_hs(adapter)) { ++ /* Shut down SW */ ++ if (mwifiex_shutdown_sw(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cmd: failed to suspend\n"); +- clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); +- mwifiex_disable_wake(adapter); + return -EFAULT; + } + +- flush_workqueue(adapter->workqueue); +- + /* Indicate device suspended */ + set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); +- clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); + + return 0; + } +@@ -188,13 +177,13 @@ static int mwifiex_pcie_suspend(struct device *dev) + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * +- * If already not resumed, this function turns on the traffic and +- * sends a host sleep cancel request to the firmware. ++ * If already not resumed, this function reinits the adapter. + */ + static int mwifiex_pcie_resume(struct device *dev) + { + struct mwifiex_adapter *adapter; + struct pcie_service_card *card = dev_get_drvdata(dev); ++ int ret; + + + if (!card->adapter) { +@@ -212,9 +201,11 @@ static int mwifiex_pcie_resume(struct device *dev) + + clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); + +- mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), +- MWIFIEX_ASYNC_CMD); +- mwifiex_disable_wake(adapter); ++ ret = mwifiex_reinit_sw(adapter); ++ if (ret) ++ dev_err(dev, "reinit failed: %d\n", ret); ++ else ++ mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); + + return 0; + } +-- +2.28.0 + +From d63ccbb60c210c361cf48612ea3fc8f882565da6 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Sun, 27 Sep 2020 00:51:38 +0900 +Subject: [PATCH] mwifiex: update comment for shutdown_sw()/reinit_sw() + reflecting current state + +The functions mwifiex_shutdown_sw() and mwifiex_reinit_sw() can be used +for more general purposes than the PCIe function level reset. Also, these +are even not PCIe-specific. + +So, let's update the comments at the top of each function accordingly. + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/main.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c +index c21f916c05a3..a26eb66865e2 100644 +--- a/drivers/net/wireless/marvell/mwifiex/main.c ++++ b/drivers/net/wireless/marvell/mwifiex/main.c +@@ -1453,7 +1453,7 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter) + } + + /* +- * This function gets called during PCIe function level reset. ++ * This function can be used for shutting down the adapter SW. + */ + int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) + { +@@ -1481,7 +1481,7 @@ int mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) + } + EXPORT_SYMBOL_GPL(mwifiex_shutdown_sw); + +-/* This function gets called during PCIe function level reset. Required ++/* This function can be used for reinitting the adapter SW. Required + * code is extracted from mwifiex_add_card() + */ + int +-- +2.28.0 + +From c7a3556e53d7053e3bfed69c23e4a80a12ec03d4 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Mon, 24 Aug 2020 17:11:35 +0900 +Subject: [PATCH] mwifiex: pcie: add enable_device_dump module parameter + +The devicve_dump may take a little bit long time and users may want to +disable the dump for daily usage. + +This commit adds a new module parameter and disables device_dump by +default. + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index b46d56389c3b..1847a0274991 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -34,6 +34,11 @@ + + static struct mwifiex_if_ops pcie_ops; + ++static bool enable_device_dump; ++module_param(enable_device_dump, bool, 0644); ++MODULE_PARM_DESC(enable_device_dump, ++ "enable device_dump (default: disabled)"); ++ + static const struct of_device_id mwifiex_pcie_of_match_table[] = { + { .compatible = "pci11ab,2b42" }, + { .compatible = "pci1b4b,2b42" }, +@@ -2791,6 +2796,12 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) + + static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) + { ++ if (!enable_device_dump) { ++ mwifiex_dbg(adapter, MSG, ++ "device_dump is disabled by module parameter\n"); ++ return; ++ } ++ + adapter->devdump_data = vzalloc(MWIFIEX_FW_DUMP_SIZE); + if (!adapter->devdump_data) { + mwifiex_dbg(adapter, ERROR, +-- +2.28.0 + +From bcf9d4b3af74128a676e6e5b79ac83e6e29fbbef Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Sun, 4 Oct 2020 00:11:49 +0900 +Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+ + +Currently, mwifiex fw will crash after suspend on recent kernel series. +On Windows, it seems that the root port of wifi will never enter D3 state +(stay on D0 state). And on Linux, disabling the D3 state for the +bridge fixes fw crashing after suspend. + +This commit disables the D3 state of root port on driver initialization +and fixes fw crashing after suspend. + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 7 +++++ + .../wireless/marvell/mwifiex/pcie_quirks.c | 27 +++++++++++++------ + .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + + 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 1847a0274991..3bd39d9ba3de 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -226,6 +226,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) + { + struct pcie_service_card *card; ++ struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); + int ret; + + pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", +@@ -267,6 +268,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, + return -1; + } + ++ /* disable bridge_d3 for Surface gen4+ devices to fix fw crashing ++ * after suspend ++ */ ++ if (card->quirks & QUIRK_NO_BRIDGE_D3) ++ parent_pdev->bridge_d3 = false; ++ + return 0; + } + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +index 34dcd84f02a6..a2aeb2af907e 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +@@ -32,7 +32,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Pro 5", +@@ -41,7 +42,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Pro 5 (LTE)", +@@ -50,7 +52,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Pro 6", +@@ -58,7 +61,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Book 1", +@@ -66,7 +70,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Book 2", +@@ -74,7 +79,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Laptop 1", +@@ -82,7 +88,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface Laptop 2", +@@ -90,7 +97,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), + }, +- .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ .driver_data = (void *)(QUIRK_FW_RST_D3COLD | ++ QUIRK_NO_BRIDGE_D3), + }, + { + .ident = "Surface 3", +@@ -136,6 +144,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) + if (card->quirks & QUIRK_FW_RST_WSID_S3) + dev_info(&pdev->dev, + "quirk reset_wsid for Surface 3 enabled\n"); ++ if (card->quirks & QUIRK_NO_BRIDGE_D3) ++ dev_info(&pdev->dev, ++ "quirk no_brigde_d3 enabled\n"); + } + + 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 3ef7440418e3..a95ebac06e13 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +@@ -11,6 +11,7 @@ + * be handled differently. Currently, only S3 is supported. + */ + #define QUIRK_FW_RST_WSID_S3 BIT(1) ++#define QUIRK_NO_BRIDGE_D3 BIT(2) + + void mwifiex_initialize_quirks(struct pcie_service_card *card); + int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); +-- +2.28.0 + +From 7dda35220e0c86d787ea4d1299b7ff645e992acb Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Sun, 4 Oct 2020 00:25:48 +0900 +Subject: [PATCH] mwifiex: add allow_ps_mode module parameter + +This commit adds the allow_ps_mode module parameter and set it false +(disallowed) by default, to make ps_mode (power_save) control easier. + +On some setups (e.g., with 5GHz AP), power_save causes connection +completely unstable. So, we need to disable it. However, userspace tools +may try to enable it. For this reason, we need to tell userspace that +power_save is disallowed by default. + +When this parameter is set to false, changing the power_save mode will +be disallowed like the following: + + $ sudo iw dev mlan0 set power_save on + command failed: Operation not permitted (-1) + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/cfg80211.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c +index 4e4f59c17ded..1074bcb2606b 100644 +--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c ++++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c +@@ -25,6 +25,11 @@ + static char *reg_alpha2; + module_param(reg_alpha2, charp, 0); + ++static bool allow_ps_mode; ++module_param(allow_ps_mode, bool, 0644); ++MODULE_PARM_DESC(allow_ps_mode, ++ "allow WiFi power management to be enabled. (default: disallowed)"); ++ + static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { + { + .max = 3, .types = BIT(NL80211_IFTYPE_STATION) | +@@ -434,6 +439,17 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, + + ps_mode = enabled; + ++ /* Allow ps_mode to be enabled only when allow_ps_mode is true */ ++ if (ps_mode && !allow_ps_mode) { ++ mwifiex_dbg(priv->adapter, MSG, ++ "Enabling ps_mode disallowed by modparam\n"); ++ ++ /* Return -EPERM to inform userspace tools that setting ++ * power_save to be enabled is not permitted. ++ */ ++ return -EPERM; ++ } ++ + return mwifiex_drv_set_power(priv, &ps_mode); + } + +-- +2.28.0 + +From 390fa12d3af6558f2b4bdb145c45b27129b29ba1 Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Sun, 4 Oct 2020 00:59:37 +0900 +Subject: [PATCH] mwifiex: disable ps_mode explicitly by default instead + +At least on Surface devices, the ps_mode causes connection unstable, +especially with 5GHz APs. Then, it eventually causes fw crashing. + +This commit disables ps_mode by default instead of enabling it. + +Required code is extracted from mwifiex_drv_set_power(). + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +index 8bd355d7974e..621519826685 100644 +--- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c ++++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +@@ -2333,14 +2333,19 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) + return -1; + + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { +- /* Enable IEEE PS by default */ +- priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; ++ /* Disable IEEE PS by default */ ++ priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_PS_MODE_ENH, +- EN_AUTO_PS, BITMAP_STA_PS, NULL, ++ DIS_AUTO_PS, BITMAP_STA_PS, NULL, + true); + if (ret) + return -1; ++ ret = mwifiex_send_cmd(priv, ++ HostCmd_CMD_802_11_PS_MODE_ENH, ++ GET_PS, 0, NULL, false); ++ if (ret) ++ return -1; + } + + if (drcs) { +-- +2.28.0 + +From 570c6d717002dd3734452e1d6db0ac8414418b7e Mon Sep 17 00:00:00 2001 +From: Tsuchiya Yuto +Date: Sun, 4 Oct 2020 00:38:48 +0900 +Subject: [PATCH] mwifiex: print message when changing ps_mode + +Users may want to know the ps_mode state change (e.g., diagnosing +connection issues). This commit adds the print when changing ps_mode. + +Signed-off-by: Tsuchiya Yuto +Patchset: wifi +--- + drivers/net/wireless/marvell/mwifiex/cfg80211.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c +index 1074bcb2606b..67800980a7f0 100644 +--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c ++++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c +@@ -450,6 +450,13 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, + return -EPERM; + } + ++ if (ps_mode) ++ mwifiex_dbg(priv->adapter, MSG, ++ "Enabling ps_mode, disable if unstable.\n"); ++ else ++ mwifiex_dbg(priv->adapter, MSG, ++ "Disabling ps_mode.\n"); ++ + return mwifiex_drv_set_power(priv, &ps_mode); + } + -- 2.28.0 diff --git a/patches/5.8/0003-ipts.patch b/patches/5.8/0003-ipts.patch index e59bf9df4..0142da4cf 100644 --- a/patches/5.8/0003-ipts.patch +++ b/patches/5.8/0003-ipts.patch @@ -1,1354 +1,43 @@ -From 64af858e6b6b7ae3f2c477119221da33344f24c7 Mon Sep 17 00:00:00 2001 +From 1fb79b576315c036f08e778546e71212230d9ddc Mon Sep 17 00:00:00 2001 +From: Dorian Stoll +Date: Fri, 25 Sep 2020 18:06:05 +0200 +Subject: [PATCH] mei: Remove client devices before shutting down + +Patchset: ipts +--- + drivers/misc/mei/init.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c +index bcee77768b91..21ed765003e1 100644 +--- a/drivers/misc/mei/init.c ++++ b/drivers/misc/mei/init.c +@@ -302,10 +302,10 @@ void mei_stop(struct mei_device *dev) + { + dev_dbg(dev->dev, "stopping the device.\n"); + ++ mei_cl_bus_remove_devices(dev); + mutex_lock(&dev->device_lock); + mei_set_devstate(dev, MEI_DEV_POWER_DOWN); + mutex_unlock(&dev->device_lock); +- mei_cl_bus_remove_devices(dev); + + mei_cancel_work(dev); + +-- +2.28.0 + +From b9b4c4f84f5111e3213225be406e43cc11623dbc Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 -Subject: [PATCH 3/7] ipts +Subject: [PATCH] misc: mei: Add missing IPTS device IDs +Patchset: ipts --- - drivers/misc/Kconfig | 1 + - drivers/misc/Makefile | 1 + - drivers/misc/ipts/Kconfig | 13 ++ - drivers/misc/ipts/Makefile | 11 ++ - drivers/misc/ipts/context.h | 125 ++++++++++++++ - drivers/misc/ipts/control.c | 63 ++++++++ - drivers/misc/ipts/control.h | 17 ++ - drivers/misc/ipts/init.c | 87 ++++++++++ - drivers/misc/ipts/protocol.h | 236 +++++++++++++++++++++++++++ - drivers/misc/ipts/receiver.c | 202 +++++++++++++++++++++++ - drivers/misc/ipts/receiver.h | 10 ++ - drivers/misc/ipts/resources.c | 133 +++++++++++++++ - drivers/misc/ipts/resources.h | 11 ++ - drivers/misc/ipts/uapi.c | 297 ++++++++++++++++++++++++++++++++++ - drivers/misc/ipts/uapi.h | 11 ++ - drivers/misc/mei/hw-me-regs.h | 3 + - drivers/misc/mei/pci-me.c | 3 + - 17 files changed, 1224 insertions(+) - create mode 100644 drivers/misc/ipts/Kconfig - create mode 100644 drivers/misc/ipts/Makefile - create mode 100644 drivers/misc/ipts/context.h - create mode 100644 drivers/misc/ipts/control.c - create mode 100644 drivers/misc/ipts/control.h - create mode 100644 drivers/misc/ipts/init.c - create mode 100644 drivers/misc/ipts/protocol.h - create mode 100644 drivers/misc/ipts/receiver.c - create mode 100644 drivers/misc/ipts/receiver.h - create mode 100644 drivers/misc/ipts/resources.c - create mode 100644 drivers/misc/ipts/resources.h - create mode 100644 drivers/misc/ipts/uapi.c - create mode 100644 drivers/misc/ipts/uapi.h + drivers/misc/mei/hw-me-regs.h | 3 +++ + drivers/misc/mei/pci-me.c | 3 +++ + 2 files changed, 6 insertions(+) -diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index e1b1ba5e2b92..be901ffc66fe 100644 ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -472,4 +472,5 @@ source "drivers/misc/ocxl/Kconfig" - source "drivers/misc/cardreader/Kconfig" - source "drivers/misc/habanalabs/Kconfig" - source "drivers/misc/uacce/Kconfig" -+source "drivers/misc/ipts/Kconfig" - endmenu -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index c7bd01ac6291..f97938d777e1 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -57,3 +57,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o - obj-$(CONFIG_HABANA_AI) += habanalabs/ - obj-$(CONFIG_UACCE) += uacce/ - obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o -+obj-$(CONFIG_MISC_IPTS) += ipts/ -diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig -new file mode 100644 -index 000000000000..7dce12245a4f ---- /dev/null -+++ b/drivers/misc/ipts/Kconfig -@@ -0,0 +1,13 @@ -+# SPDX-License-Identifier: GPL-2.0-or-later -+ -+config MISC_IPTS -+ tristate "Intel Precise Touch & Stylus" -+ depends on INTEL_MEI -+ help -+ Say Y here if your system has a touchscreen using Intels -+ Precise Touch & Stylus (IPTS) technology. -+ -+ If unsure say N. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called ipts. -diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile -new file mode 100644 -index 000000000000..a7232badd8b8 ---- /dev/null -+++ b/drivers/misc/ipts/Makefile -@@ -0,0 +1,11 @@ -+# SPDX-License-Identifier: GPL-2.0-or-later -+# -+# Makefile for the IPTS touchscreen driver -+# -+ -+obj-$(CONFIG_MISC_IPTS) += ipts.o -+ipts-objs := control.o -+ipts-objs += init.o -+ipts-objs += receiver.o -+ipts-objs += resources.o -+ipts-objs += uapi.o -diff --git a/drivers/misc/ipts/context.h b/drivers/misc/ipts/context.h -new file mode 100644 -index 000000000000..d24fd6ac026b ---- /dev/null -+++ b/drivers/misc/ipts/context.h -@@ -0,0 +1,125 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef _IPTS_CONTEXT_H_ -+#define _IPTS_CONTEXT_H_ -+ -+#include -+#include -+#include -+#include -+ -+#include "protocol.h" -+ -+/* -+ * enum ipts_host_states - States of the IPTS driver -+ * -+ * IPTS_HOST_STATUS_STOPPED: -+ * -+ * The driver was either shut down or encountered a fatal error, causing -+ * it to disable itself. In this state no messages from the ME will be read, -+ * and no data can be read by userspace. -+ * -+ * IPTS_HOST_STATUS_STARTING: -+ * -+ * The driver is currently going through the initialization sequence. -+ * ME messages will be read, but no data can be read by userspace. -+ * -+ * IPTS_HOST_STATUS_STARTED: -+ * -+ * The driver completely initialized the device and receives data from -+ * it. Userspace can now read data. -+ * -+ * IPTS_HOST_STATUS_RESTARTING: -+ * -+ * A sensor error triggered a restart. Restarting IPTS means to stash all -+ * current operations using QUIESCE_IO, and then rerun the initialization -+ * sequence after the command returned. Since the same command is also used -+ * during shutdown, this mode tells the response handler for QUIESCE_IO if -+ * it should start re-initialization. -+ */ -+enum ipts_host_status { -+ IPTS_HOST_STATUS_STOPPED, -+ IPTS_HOST_STATUS_STARTING, -+ IPTS_HOST_STATUS_STARTED, -+ IPTS_HOST_STATUS_RESTARTING -+}; -+ -+/* -+ * struct ipts_buffer_info - Buffer for passing data between ME and host. -+ * -+ * @address: The virtual kernelspace address for the host to access the buffer. -+ * @dma_address: The physical address for the ME to access the buffer. -+ */ -+struct ipts_buffer_info { -+ u8 *address; -+ dma_addr_t dma_address; -+}; -+ -+/* -+ * struct ipts_uapi - Context for the userspace interface -+ * -+ * @device: The character device that IPTS data can be read from. -+ * @doorbell_thread: Polls the doorbell value and signals changes to userspace. -+ * @doorbell: The last transaction that was passed to userspace. -+ * @active: Whether a client has activated and locked the data stream. -+ */ -+struct ipts_uapi { -+ struct miscdevice device; -+ struct task_struct *db_thread; -+ -+ u32 doorbell; -+ bool active; -+}; -+ -+/* -+ * struct ipts_context - Context for the IPTS driver -+ * -+ * @cldev: The MEI client device for IPTS. -+ * @dev: The Linux driver model device, used for logging. -+ * @device_info: Information about the device we are connected to. -+ * -+ * @status: Current state of the driver. -+ * @uapi: The context for the userspace interface. -+ * -+ * @data: The IPTS data buffers. They get filled with touch data that is -+ * forwarded to userspace and parsed into input events. -+ * -+ * @doorbell: An unsigned 32-bit integer that will be incremented after one -+ * data buffer has been filled up. Always corresponds to the data -+ * buffer that will be filled *next*. -+ * -+ * The following buffers are a leftover from when IPTS used binary firmware -+ * with GuC submission. They are not used by the host but they need to be -+ * allocated to ensure proper operation. -+ * -+ * @feedback: Buffers that contain payload data for the FEEDBACK command. -+ * The command works with an empty buffer, so these are not used. -+ * -+ * @workqueue: Buffer that was used to synchronize the ME with the firmware -+ * running on the GuC. Just like the GuC, this buffer is not -+ * used anymore. -+ * -+ * @host2me: A special channel for sending feedback that is not linked to one -+ * of the data buffers. It is identified by using IPTS_BUFFERS as -+ * the buffer index, instead of 0 < n < IPTS_BUFFERS. In theory it -+ * allows for advanced interaction with the sensor, but these -+ * usages were never used or documented by intel, therefor it -+ * cannot be used. -+ */ -+struct ipts_context { -+ struct mei_cl_device *cldev; -+ struct device *dev; -+ struct ipts_device_info device_info; -+ -+ enum ipts_host_status status; -+ struct ipts_uapi uapi; -+ -+ struct ipts_buffer_info data[IPTS_BUFFERS]; -+ struct ipts_buffer_info doorbell; -+ -+ struct ipts_buffer_info feedback[IPTS_BUFFERS]; -+ struct ipts_buffer_info workqueue; -+ struct ipts_buffer_info host2me; -+}; -+ -+#endif /* _IPTS_CONTEXT_H_ */ -diff --git a/drivers/misc/ipts/control.c b/drivers/misc/ipts/control.c -new file mode 100644 -index 000000000000..857bcf498752 ---- /dev/null -+++ b/drivers/misc/ipts/control.c -@@ -0,0 +1,63 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+#include -+ -+#include "context.h" -+#include "protocol.h" -+#include "resources.h" -+#include "uapi.h" -+ -+int ipts_control_send(struct ipts_context *ipts, -+ u32 cmd, void *data, u32 size) -+{ -+ int ret; -+ struct ipts_command msg; -+ -+ msg.code = cmd; -+ -+ // Copy message payload -+ if (data && size > 0) -+ memcpy(&msg.data, data, size); -+ -+ ret = mei_cldev_send(ipts->cldev, (u8 *)&msg, sizeof(msg.code) + size); -+ if (ret >= 0) -+ return 0; -+ -+ if (cmd == IPTS_CMD(FEEDBACK) && ret == -IPTS_ME_STATUS_NOT_READY) -+ return 0; -+ -+ dev_err(ipts->dev, "MEI error while sending: 0x%X:%d\n", cmd, ret); -+ -+ return ret; -+} -+ -+int ipts_control_start(struct ipts_context *ipts) -+{ -+ ipts->status = IPTS_HOST_STATUS_STARTING; -+ ipts_uapi_init(ipts); -+ -+ return ipts_control_send(ipts, IPTS_CMD(NOTIFY_DEV_READY), NULL, 0); -+} -+ -+void ipts_control_stop(struct ipts_context *ipts) -+{ -+ ipts->status = IPTS_HOST_STATUS_STOPPED; -+ -+ ipts_control_send(ipts, IPTS_CMD(QUIESCE_IO), NULL, 0); -+ ipts_control_send(ipts, IPTS_CMD(CLEAR_MEM_WINDOW), NULL, 0); -+ -+ ipts_uapi_free(ipts); -+ ipts_resources_free(ipts); -+} -+ -+int ipts_control_restart(struct ipts_context *ipts) -+{ -+ if (ipts->status == IPTS_HOST_STATUS_RESTARTING) -+ return 0; -+ -+ dev_info(ipts->dev, "Restarting IPTS\n"); -+ ipts->status = IPTS_HOST_STATUS_RESTARTING; -+ -+ return ipts_control_send(ipts, IPTS_CMD(QUIESCE_IO), NULL, 0); -+} -diff --git a/drivers/misc/ipts/control.h b/drivers/misc/ipts/control.h -new file mode 100644 -index 000000000000..718cde10dd2c ---- /dev/null -+++ b/drivers/misc/ipts/control.h -@@ -0,0 +1,17 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef _IPTS_CONTROL_H_ -+#define _IPTS_CONTROL_H_ -+ -+#include -+ -+#include "context.h" -+ -+int ipts_control_send(struct ipts_context *ipts, -+ u32 cmd, void *data, u32 size); -+ -+int ipts_control_start(struct ipts_context *ipts); -+void ipts_control_stop(struct ipts_context *ipts); -+int ipts_control_restart(struct ipts_context *ipts); -+ -+#endif /* _IPTS_CONTROL_H_ */ -diff --git a/drivers/misc/ipts/init.c b/drivers/misc/ipts/init.c -new file mode 100644 -index 000000000000..c2f237feed11 ---- /dev/null -+++ b/drivers/misc/ipts/init.c -@@ -0,0 +1,87 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+#include -+#include -+#include -+ -+#include "context.h" -+#include "control.h" -+#include "receiver.h" -+ -+#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ -+ 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) -+ -+static int ipts_init_probe(struct mei_cl_device *cldev, -+ const struct mei_cl_device_id *id) -+{ -+ int ret; -+ struct ipts_context *ipts = NULL; -+ -+ dev_info(&cldev->dev, "Probing IPTS\n"); -+ -+ // Setup the DMA bit mask -+ if (!dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64))) { -+ dev_info(&cldev->dev, "IPTS using DMA_BIT_MASK(64)\n"); -+ } else if (!dma_coerce_mask_and_coherent(&cldev->dev, -+ DMA_BIT_MASK(32))) { -+ dev_info(&cldev->dev, "IPTS using DMA_BIT_MASK(32)\n"); -+ } else { -+ dev_err(&cldev->dev, "No suitable DMA for IPTS available\n"); -+ return -EFAULT; -+ } -+ -+ ret = mei_cldev_enable(cldev); -+ if (ret) { -+ dev_err(&cldev->dev, "Cannot enable IPTS\n"); -+ return ret; -+ } -+ -+ ipts = devm_kzalloc(&cldev->dev, -+ sizeof(struct ipts_context), GFP_KERNEL); -+ if (!ipts) { -+ mei_cldev_disable(cldev); -+ return -ENOMEM; -+ } -+ -+ ipts->cldev = cldev; -+ ipts->dev = &cldev->dev; -+ -+ mei_cldev_set_drvdata(cldev, ipts); -+ mei_cldev_register_rx_cb(cldev, ipts_receiver_callback); -+ -+ ipts_control_start(ipts); -+ -+ return 0; -+} -+ -+static int ipts_init_remove(struct mei_cl_device *cldev) -+{ -+ struct ipts_context *ipts = mei_cldev_get_drvdata(cldev); -+ -+ dev_info(&cldev->dev, "Removing IPTS\n"); -+ -+ ipts_control_stop(ipts); -+ mei_cldev_disable(cldev); -+ -+ return 0; -+} -+ -+static struct mei_cl_device_id ipts_device_id[] = { -+ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(mei, ipts_device_id); -+ -+static struct mei_cl_driver ipts_driver = { -+ .id_table = ipts_device_id, -+ .name = "ipts", -+ .probe = ipts_init_probe, -+ .remove = ipts_init_remove, -+}; -+module_mei_cl_driver(ipts_driver); -+ -+MODULE_DESCRIPTION("IPTS touchscreen driver"); -+MODULE_AUTHOR("Dorian Stoll "); -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/ipts/protocol.h b/drivers/misc/ipts/protocol.h -new file mode 100644 -index 000000000000..c8b412899ec4 ---- /dev/null -+++ b/drivers/misc/ipts/protocol.h -@@ -0,0 +1,236 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef _IPTS_PROTOCOL_H_ -+#define _IPTS_PROTOCOL_H_ -+ -+#include -+#include -+ -+#define IPTS_WORKQUEUE_SIZE 8192 -+#define IPTS_WORKQUEUE_ITEM_SIZE 16 -+ -+/* -+ * How many data / feedback buffers IPTS uses -+ */ -+#define IPTS_BUFFERS 16 -+ -+/* -+ * Helpers to avoid writing boilerplate code. -+ * The response to a command code is always 0x8000000x, where x -+ * is the command code itself. Instead of writing two definitions, -+ * we use macros to calculate the value on the fly instead. -+ */ -+#define IPTS_CMD(COMMAND) IPTS_EVT_##COMMAND -+#define IPTS_RSP(COMMAND) (IPTS_CMD(COMMAND) + 0x80000000) -+ -+/* -+ * enum ipts_evt_code - Events that can be sent and received from the ME -+ * -+ * Events can describe either a command (sent from host to ME) or a -+ * response (sent from ME to host). These values should not be used -+ * directly, instead they should be wrapped with the appropreate -+ * IPTS_CMD / IPTS_RSP macro, to clearly document the wanted event type. -+ */ -+enum ipts_evt_code { -+ IPTS_EVT_GET_DEVICE_INFO = 1, -+ IPTS_EVT_SET_MODE, -+ IPTS_EVT_SET_MEM_WINDOW, -+ IPTS_EVT_QUIESCE_IO, -+ IPTS_EVT_READY_FOR_DATA, -+ IPTS_EVT_FEEDBACK, -+ IPTS_EVT_CLEAR_MEM_WINDOW, -+ IPTS_EVT_NOTIFY_DEV_READY, -+}; -+ -+/* -+ * enum ipts_me_status - Status codes returned in response to a command. -+ * -+ * These codes are returned by the ME to indicate whether a command was -+ * executed successfully. -+ * -+ * Some of these errors are less serious than others, and some need to be -+ * ignored to ensure proper operation. See also ipts_receiver_handle_error. -+ */ -+enum ipts_me_status { -+ IPTS_ME_STATUS_SUCCESS = 0, -+ IPTS_ME_STATUS_INVALID_PARAMS, -+ IPTS_ME_STATUS_ACCESS_DENIED, -+ IPTS_ME_STATUS_CMD_SIZE_ERROR, -+ IPTS_ME_STATUS_NOT_READY, -+ IPTS_ME_STATUS_REQUEST_OUTSTANDING, -+ IPTS_ME_STATUS_NO_SENSOR_FOUND, -+ IPTS_ME_STATUS_OUT_OF_MEMORY, -+ IPTS_ME_STATUS_INTERNAL_ERROR, -+ IPTS_ME_STATUS_SENSOR_DISABLED, -+ IPTS_ME_STATUS_COMPAT_CHECK_FAIL, -+ IPTS_ME_STATUS_SENSOR_EXPECTED_RESET, -+ IPTS_ME_STATUS_SENSOR_UNEXPECTED_RESET, -+ IPTS_ME_STATUS_RESET_FAILED, -+ IPTS_ME_STATUS_TIMEOUT, -+ IPTS_ME_STATUS_TEST_MODE_FAIL, -+ IPTS_ME_STATUS_SENSOR_FAIL_FATAL, -+ IPTS_ME_STATUS_SENSOR_FAIL_NONFATAL, -+ IPTS_ME_STATUS_INVALID_DEVICE_CAPS, -+ IPTS_ME_STATUS_QUIESCE_IO_IN_PROGRESS, -+ IPTS_ME_STATUS_MAX -+}; -+ -+/* -+ * enum ipts_sensor_mode - The sensor mode for IPTS to use -+ * -+ * IPTS_SENSOR_MODE_SINGLETOUCH: -+ * -+ * Singletouch mode is a fallback mode that does not support the stylus -+ * or more than one touch input. The data is received as a HID report with -+ * report ID 64. -+ * -+ * IPTS_SENSOR_MODE_MULTITOUCH: -+ * -+ * Multitouch mode is the proper operation mode for IPTS. It will return -+ * stylus data, as well as touch data as a raw heatmap directly from -+ * the sensor. This data needs to be processed before it can be used -+ * for input devices. -+ * -+ * This driver only supports multitouch mode. -+ */ -+enum ipts_sensor_mode { -+ IPTS_SENSOR_MODE_SINGLETOUCH = 0, -+ IPTS_SENSOR_MODE_MULTITOUCH, -+}; -+ -+/* -+ * struct ipts_set_mode_cmd - Parameters for the SET_MODE command. -+ * -+ * @sensor_mode: The mode which the touch sensor should operate in -+ * (from enum ipts_sensor_mode). -+ * -+ * On newer generations of IPTS (surface gen7) this command will only accept -+ * IPTS_SENSOR_MODE_MULTITOUCH, and fail if anything else is sent. -+ */ -+struct ipts_set_mode_cmd { -+ u32 sensor_mode; -+ u8 reserved[12]; -+} __packed; -+ -+static_assert(sizeof(struct ipts_set_mode_cmd) == 16); -+ -+/* -+ * struct ipts_set_mem_window_cmd - Parameters for the SET_MEM_WINDOW command. -+ * -+ * This passes the physical addresses of buffers to the ME, which are -+ * the used to exchange data between host and ME. -+ * -+ * Some of these buffers are not used by the host. They are a leftover from -+ * when IPTS used binary firmware with GuC submission. They need to be -+ * allocated and passed, otherwise the command will not return successfully. -+ * -+ * For a description of the various buffers, please check out the ipts_context -+ * struct and it's documentation. -+ */ -+struct ipts_set_mem_window_cmd { -+ u32 data_buffer_addr_lower[IPTS_BUFFERS]; -+ u32 data_buffer_addr_upper[IPTS_BUFFERS]; -+ u32 workqueue_addr_lower; -+ u32 workqueue_addr_upper; -+ u32 doorbell_addr_lower; -+ u32 doorbell_addr_upper; -+ u32 feedback_buffer_addr_lower[IPTS_BUFFERS]; -+ u32 feedback_buffer_addr_upper[IPTS_BUFFERS]; -+ u32 host2me_addr_lower; -+ u32 host2me_addr_upper; -+ u32 host2me_size; -+ u8 reserved1; -+ u8 workqueue_item_size; -+ u16 workqueue_size; -+ u8 reserved[32]; -+} __packed; -+ -+static_assert(sizeof(struct ipts_set_mem_window_cmd) == 320); -+ -+/* -+ * struct ipts_feedback_cmd - Parameters for the FEEDBACK command. -+ * -+ * This command is sent to indicate that the data in a buffer has been -+ * processed by the host, and that the ME can safely overwrite the data. -+ * -+ * @buffer: The buffer to be refilled -+ */ -+struct ipts_feedback_cmd { -+ u32 buffer; -+ u8 reserved[12]; -+} __packed; -+ -+static_assert(sizeof(struct ipts_feedback_cmd) == 16); -+ -+/* -+ * struct ipts_command - Describes a command sent from the host to the ME. -+ * -+ * @code: The command code. (IPTS_CMD(EVENT)) -+ * @set_mode: The parameters for the SET_MODE command -+ * @set_mem_window: The parameters for the SET_MEM_WINDOW command -+ * @feedback: The parameters for the FEEDBACK command. -+ * -+ * This struct should always be initialized with 0, to prevent the ME -+ * from interpreting random bytes as a parameter. -+ * -+ * The ME will react to a command by sending a response, indicating if -+ * the command was successfully, and returning queried data. -+ */ -+struct ipts_command { -+ u32 code; -+ union { -+ struct ipts_set_mode_cmd set_mode; -+ struct ipts_set_mem_window_cmd set_mem_window; -+ struct ipts_feedback_cmd feedback; -+ } data; -+} __packed; -+ -+static_assert(sizeof(struct ipts_command) == 324); -+ -+/* -+ * struct ipts_device_info - Returned by the GET_DEVICE_INFO command. -+ * -+ * @vendor_id: Vendor ID of the touch sensor -+ * @device_id: Device ID of the touch sensor -+ * @hw_rev: Hardware revision of the touch sensor -+ * @fw_rev: Firmware revision of the touch sensor -+ * @data_size: Required size of one data buffer -+ * @feedback_size: Required size of one feedback buffer -+ * @max_touch_points: The amount of concurrent touches supported by the sensor -+ */ -+struct ipts_device_info { -+ u16 vendor_id; -+ u16 device_id; -+ u32 hw_rev; -+ u32 fw_rev; -+ u32 data_size; -+ u32 feedback_size; -+ u8 reserved1[4]; -+ u8 max_touch_points; -+ u8 reserved[19]; -+} __packed; -+ -+static_assert(sizeof(struct ipts_device_info) == 44); -+ -+/* -+ * struct ipts_response - Describes the response from the ME to a command. -+ * -+ * @code: The response code. (0x80000000 + command code that was sent) -+ * @status: The return value of the command. (from enum ipts_me_status) -+ * @device_info: The data that was queried by the GET_DEVICE_INFO command. -+ * -+ * Theoretically all commands could return data but only the data from -+ * GET_DEVICE_INFO is relevant for the host. -+ */ -+struct ipts_response { -+ u32 code; -+ u32 status; -+ union { -+ struct ipts_device_info device_info; -+ u8 reserved[80]; -+ } data; -+} __packed; -+ -+static_assert(sizeof(struct ipts_response) == 88); -+ -+#endif /* _IPTS_PROTOCOL_H_ */ -diff --git a/drivers/misc/ipts/receiver.c b/drivers/misc/ipts/receiver.c -new file mode 100644 -index 000000000000..bf78b64249a5 ---- /dev/null -+++ b/drivers/misc/ipts/receiver.c -@@ -0,0 +1,202 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+#include -+ -+#include "context.h" -+#include "control.h" -+#include "protocol.h" -+#include "resources.h" -+#include "uapi.h" -+ -+static int ipts_receiver_handle_notify_dev_ready(struct ipts_context *ipts) -+{ -+ return ipts_control_send(ipts, IPTS_CMD(GET_DEVICE_INFO), NULL, 0); -+} -+ -+static int ipts_receiver_handle_get_device_info(struct ipts_context *ipts, -+ struct ipts_response *msg) -+{ -+ memcpy(&ipts->device_info, &msg->data.device_info, -+ sizeof(struct ipts_device_info)); -+ -+ dev_info(ipts->dev, "Device %04hX:%04hX found\n", -+ ipts->device_info.vendor_id, -+ ipts->device_info.device_id); -+ -+ return ipts_control_send(ipts, IPTS_CMD(CLEAR_MEM_WINDOW), NULL, 0); -+} -+ -+static int ipts_receiver_handle_clear_mem_window(struct ipts_context *ipts) -+{ -+ struct ipts_set_mode_cmd sensor_mode_cmd; -+ -+ memset(&sensor_mode_cmd, 0, sizeof(struct ipts_set_mode_cmd)); -+ sensor_mode_cmd.sensor_mode = IPTS_SENSOR_MODE_MULTITOUCH; -+ -+ return ipts_control_send(ipts, IPTS_CMD(SET_MODE), -+ &sensor_mode_cmd, sizeof(struct ipts_set_mode_cmd)); -+} -+ -+static int ipts_receiver_handle_set_mode(struct ipts_context *ipts) -+{ -+ int i, ret; -+ struct ipts_set_mem_window_cmd cmd; -+ -+ ret = ipts_resources_init(ipts); -+ if (ret) -+ return ret; -+ -+ memset(&cmd, 0, sizeof(struct ipts_set_mem_window_cmd)); -+ -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ cmd.data_buffer_addr_lower[i] = -+ lower_32_bits(ipts->data[i].dma_address); -+ -+ cmd.data_buffer_addr_upper[i] = -+ upper_32_bits(ipts->data[i].dma_address); -+ -+ cmd.feedback_buffer_addr_lower[i] = -+ lower_32_bits(ipts->feedback[i].dma_address); -+ -+ cmd.feedback_buffer_addr_upper[i] = -+ upper_32_bits(ipts->feedback[i].dma_address); -+ } -+ -+ cmd.workqueue_addr_lower = lower_32_bits(ipts->workqueue.dma_address); -+ cmd.workqueue_addr_upper = upper_32_bits(ipts->workqueue.dma_address); -+ -+ cmd.doorbell_addr_lower = lower_32_bits(ipts->doorbell.dma_address); -+ cmd.doorbell_addr_upper = upper_32_bits(ipts->doorbell.dma_address); -+ -+ cmd.host2me_addr_lower = lower_32_bits(ipts->host2me.dma_address); -+ cmd.host2me_addr_upper = upper_32_bits(ipts->host2me.dma_address); -+ cmd.host2me_size = ipts->device_info.data_size; -+ -+ cmd.workqueue_size = IPTS_WORKQUEUE_SIZE; -+ cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE; -+ -+ return ipts_control_send(ipts, IPTS_CMD(SET_MEM_WINDOW), -+ &cmd, sizeof(struct ipts_set_mem_window_cmd)); -+} -+ -+static int ipts_receiver_handle_set_mem_window(struct ipts_context *ipts) -+{ -+ ipts->status = IPTS_HOST_STATUS_STARTED; -+ dev_info(ipts->dev, "IPTS enabled\n"); -+ -+ return ipts_control_send(ipts, IPTS_CMD(READY_FOR_DATA), NULL, 0); -+} -+ -+static int ipts_receiver_handle_quiesce_io(struct ipts_context *ipts) -+{ -+ if (ipts->status != IPTS_HOST_STATUS_RESTARTING) -+ return 0; -+ -+ ipts_uapi_free(ipts); -+ ipts_resources_free(ipts); -+ -+ return ipts_control_start(ipts); -+} -+ -+static bool ipts_receiver_handle_error(struct ipts_context *ipts, -+ struct ipts_response *msg) -+{ -+ bool error; -+ bool restart; -+ -+ switch (msg->status) { -+ case IPTS_ME_STATUS_SUCCESS: -+ case IPTS_ME_STATUS_COMPAT_CHECK_FAIL: -+ error = false; -+ restart = false; -+ break; -+ case IPTS_ME_STATUS_INVALID_PARAMS: -+ error = msg->code != IPTS_RSP(FEEDBACK); -+ restart = false; -+ break; -+ case IPTS_ME_STATUS_SENSOR_DISABLED: -+ error = msg->code != IPTS_RSP(READY_FOR_DATA); -+ restart = false; -+ break; -+ case IPTS_ME_STATUS_TIMEOUT: -+ error = msg->code != IPTS_RSP(CLEAR_MEM_WINDOW); -+ restart = false; -+ break; -+ case IPTS_ME_STATUS_SENSOR_EXPECTED_RESET: -+ case IPTS_ME_STATUS_SENSOR_UNEXPECTED_RESET: -+ error = true; -+ restart = true; -+ break; -+ default: -+ error = true; -+ restart = false; -+ break; -+ } -+ -+ if (!error) -+ return false; -+ -+ dev_err(ipts->dev, "0x%08x failed: %d\n", msg->code, msg->status); -+ -+ if (restart) { -+ dev_err(ipts->dev, "Sensor reset: %d\n", msg->status); -+ ipts_control_restart(ipts); -+ } -+ -+ return true; -+} -+ -+static void ipts_receiver_handle_response(struct ipts_context *ipts, -+ struct ipts_response *msg) -+{ -+ int ret = 0; -+ -+ if (ipts_receiver_handle_error(ipts, msg)) -+ return; -+ -+ switch (msg->code) { -+ case IPTS_RSP(NOTIFY_DEV_READY): -+ ret = ipts_receiver_handle_notify_dev_ready(ipts); -+ break; -+ case IPTS_RSP(GET_DEVICE_INFO): -+ ret = ipts_receiver_handle_get_device_info(ipts, msg); -+ break; -+ case IPTS_RSP(CLEAR_MEM_WINDOW): -+ ret = ipts_receiver_handle_clear_mem_window(ipts); -+ break; -+ case IPTS_RSP(SET_MODE): -+ ret = ipts_receiver_handle_set_mode(ipts); -+ break; -+ case IPTS_RSP(SET_MEM_WINDOW): -+ ret = ipts_receiver_handle_set_mem_window(ipts); -+ break; -+ case IPTS_RSP(QUIESCE_IO): -+ ret = ipts_receiver_handle_quiesce_io(ipts); -+ break; -+ } -+ -+ if (!ret) -+ return; -+ -+ dev_err(ipts->dev, "Detected MEI bus error\n"); -+ dev_err(ipts->dev, "Stopping IPTS\n"); -+ -+ ipts->status = IPTS_HOST_STATUS_STOPPED; -+} -+ -+void ipts_receiver_callback(struct mei_cl_device *cldev) -+{ -+ struct ipts_response msg; -+ struct ipts_context *ipts = mei_cldev_get_drvdata(cldev); -+ -+ if (mei_cldev_recv(ipts->cldev, (u8 *)&msg, sizeof(msg)) <= 0) { -+ dev_err(ipts->dev, "Error while reading MEI message\n"); -+ return; -+ } -+ -+ if (ipts->status == IPTS_HOST_STATUS_STOPPED) -+ return; -+ -+ ipts_receiver_handle_response(ipts, &msg); -+} -diff --git a/drivers/misc/ipts/receiver.h b/drivers/misc/ipts/receiver.h -new file mode 100644 -index 000000000000..d7939ddbaae9 ---- /dev/null -+++ b/drivers/misc/ipts/receiver.h -@@ -0,0 +1,10 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef _IPTS_RECEIVER_H_ -+#define _IPTS_RECEIVER_H_ -+ -+#include -+ -+void ipts_receiver_callback(struct mei_cl_device *cldev); -+ -+#endif /* _IPTS_RECEIVER_H_ */ -diff --git a/drivers/misc/ipts/resources.c b/drivers/misc/ipts/resources.c -new file mode 100644 -index 000000000000..9f2b60bb7a70 ---- /dev/null -+++ b/drivers/misc/ipts/resources.c -@@ -0,0 +1,133 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+ -+#include "context.h" -+ -+void ipts_resources_free(struct ipts_context *ipts) -+{ -+ int i; -+ u32 touch_buffer_size; -+ u32 feedback_buffer_size; -+ struct ipts_buffer_info *buffers; -+ -+ touch_buffer_size = ipts->device_info.data_size; -+ feedback_buffer_size = ipts->device_info.feedback_size; -+ -+ buffers = ipts->data; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ if (!buffers[i].address) -+ continue; -+ -+ dmam_free_coherent(ipts->dev, touch_buffer_size, -+ buffers[i].address, buffers[i].dma_address); -+ -+ buffers[i].address = NULL; -+ buffers[i].dma_address = 0; -+ } -+ -+ buffers = ipts->feedback; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ if (!buffers[i].address) -+ continue; -+ -+ dmam_free_coherent(ipts->dev, feedback_buffer_size, -+ buffers[i].address, buffers[i].dma_address); -+ -+ buffers[i].address = NULL; -+ buffers[i].dma_address = 0; -+ } -+ -+ if (ipts->doorbell.address) { -+ dmam_free_coherent(ipts->dev, sizeof(u32), -+ ipts->doorbell.address, -+ ipts->doorbell.dma_address); -+ -+ ipts->doorbell.address = NULL; -+ ipts->doorbell.dma_address = 0; -+ } -+ -+ if (ipts->workqueue.address) { -+ dmam_free_coherent(ipts->dev, sizeof(u32), -+ ipts->workqueue.address, -+ ipts->workqueue.dma_address); -+ -+ ipts->workqueue.address = NULL; -+ ipts->workqueue.dma_address = 0; -+ } -+ -+ if (ipts->host2me.address) { -+ dmam_free_coherent(ipts->dev, touch_buffer_size, -+ ipts->host2me.address, -+ ipts->host2me.dma_address); -+ -+ ipts->host2me.address = NULL; -+ ipts->host2me.dma_address = 0; -+ } -+} -+ -+int ipts_resources_init(struct ipts_context *ipts) -+{ -+ int i; -+ u32 touch_buffer_size; -+ u32 feedback_buffer_size; -+ struct ipts_buffer_info *buffers; -+ -+ touch_buffer_size = ipts->device_info.data_size; -+ feedback_buffer_size = ipts->device_info.feedback_size; -+ -+ buffers = ipts->data; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ buffers[i].address = dmam_alloc_coherent(ipts->dev, -+ touch_buffer_size, -+ &buffers[i].dma_address, -+ GFP_KERNEL); -+ -+ if (!buffers[i].address) -+ goto release_resources; -+ } -+ -+ buffers = ipts->feedback; -+ for (i = 0; i < IPTS_BUFFERS; i++) { -+ buffers[i].address = dmam_alloc_coherent(ipts->dev, -+ feedback_buffer_size, -+ &buffers[i].dma_address, -+ GFP_KERNEL); -+ -+ if (!buffers[i].address) -+ goto release_resources; -+ } -+ -+ ipts->doorbell.address = dmam_alloc_coherent(ipts->dev, -+ sizeof(u32), -+ &ipts->doorbell.dma_address, -+ GFP_KERNEL); -+ -+ if (!ipts->doorbell.address) -+ goto release_resources; -+ -+ ipts->workqueue.address = dmam_alloc_coherent(ipts->dev, -+ sizeof(u32), -+ &ipts->workqueue.dma_address, -+ GFP_KERNEL); -+ -+ if (!ipts->workqueue.address) -+ goto release_resources; -+ -+ ipts->host2me.address = dmam_alloc_coherent(ipts->dev, -+ touch_buffer_size, -+ &ipts->host2me.dma_address, -+ GFP_KERNEL); -+ -+ if (!ipts->workqueue.address) -+ goto release_resources; -+ -+ return 0; -+ -+release_resources: -+ -+ dev_err(ipts->dev, "Failed to allocate buffers\n"); -+ ipts_resources_free(ipts); -+ -+ return -ENOMEM; -+} -diff --git a/drivers/misc/ipts/resources.h b/drivers/misc/ipts/resources.h -new file mode 100644 -index 000000000000..cf9807b0dbe6 ---- /dev/null -+++ b/drivers/misc/ipts/resources.h -@@ -0,0 +1,11 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef _IPTS_RESOURCES_H_ -+#define _IPTS_RESOURCES_H_ -+ -+#include "context.h" -+ -+int ipts_resources_init(struct ipts_context *ipts); -+void ipts_resources_free(struct ipts_context *ipts); -+ -+#endif /* _IPTS_RESOURCES_H_ */ -diff --git a/drivers/misc/ipts/uapi.c b/drivers/misc/ipts/uapi.c -new file mode 100644 -index 000000000000..f6f7b2cabd83 ---- /dev/null -+++ b/drivers/misc/ipts/uapi.c -@@ -0,0 +1,297 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "context.h" -+#include "control.h" -+#include "protocol.h" -+ -+/* -+ * struct ipts_info - Information about an IPTS device -+ * -+ * @vendor: Vendor ID of the touch sensor -+ * @product: Device ID of the touch sensor -+ * @version: Revision of the touch sensor firmware -+ * @buffer_size: The maximum size of one touch data payload -+ * @max_touch_points: The amount of concurrent touches supported by the sensor -+ */ -+struct ipts_info { -+ __u16 vendor; -+ __u16 product; -+ __u32 version; -+ __u32 buffer_size; -+ __u8 max_touch_points; -+ -+ /* For future expansion */ -+ __u8 reserved[19]; -+}; -+ -+#define IPTS_UAPI_INFO _IOR(0x86, 0x01, struct ipts_info) -+#define IPTS_UAPI_START _IO(0x86, 0x02) -+#define IPTS_UAPI_STOP _IO(0x86, 0x03) -+ -+/* -+ * struct ipts_uapi_client - A userspace client that has opened the device. -+ * -+ * @ipts: The IPTS driver context, to access the doorbell and data buffers. -+ * @offset: How much of the current data buffer has been read by the client. -+ * @active: Whether this client is the active one. Because the data from the -+ * hardware is not buffered, and sending feedback is linked to -+ * reading it from userspace, there can only be one active client. -+ * All other clients can access the device info, but will only -+ * read 0 from the device. -+ */ -+struct ipts_uapi_client { -+ struct ipts_context *ipts; -+ u32 offset; -+ bool active; -+}; -+ -+DECLARE_WAIT_QUEUE_HEAD(ipts_uapi_wq); -+ -+static int ipts_uapi_open(struct inode *inode, struct file *file) -+{ -+ struct ipts_uapi_client *client; -+ -+ struct ipts_uapi *uapi = container_of(file->private_data, -+ struct ipts_uapi, device); -+ struct ipts_context *ipts = container_of(uapi, -+ struct ipts_context, uapi); -+ -+ if (ipts->status != IPTS_HOST_STATUS_STARTED) -+ return -ENODEV; -+ -+ client = kzalloc(sizeof(struct ipts_uapi_client), GFP_KERNEL); -+ if (!client) -+ return -ENOMEM; -+ -+ client->ipts = ipts; -+ -+ file->private_data = client; -+ nonseekable_open(inode, file); -+ -+ return 0; -+} -+ -+static int ipts_uapi_close(struct inode *inode, struct file *file) -+{ -+ struct ipts_uapi_client *client = file->private_data; -+ struct ipts_context *ipts = client->ipts; -+ -+ if (client->active) -+ ipts->uapi.active = false; -+ -+ kfree(client); -+ file->private_data = NULL; -+ -+ return 0; -+} -+ -+static ssize_t ipts_uapi_read(struct file *file, char __user *buffer, -+ size_t count, loff_t *offs) -+{ -+ u32 available; -+ u32 to_read; -+ -+ char *data; -+ u8 buffer_id; -+ -+ int ret; -+ struct ipts_feedback_cmd cmd; -+ -+ struct ipts_uapi_client *client = file->private_data; -+ struct ipts_context *ipts = client->ipts; -+ u32 *doorbell = (u32 *)ipts->doorbell.address; -+ -+ if (ipts->status != IPTS_HOST_STATUS_STARTED) -+ return 0; -+ -+ if (!client->active) -+ return 0; -+ -+ available = ipts->device_info.data_size - client->offset; -+ to_read = available < count ? available : count; -+ -+ if (ipts->uapi.doorbell == *doorbell) -+ return 0; -+ -+ buffer_id = ipts->uapi.doorbell % IPTS_BUFFERS; -+ data = ipts->data[buffer_id].address; -+ -+ if (copy_to_user(buffer, data + client->offset, to_read)) -+ return -EFAULT; -+ -+ client->offset += to_read; -+ if (client->offset < ipts->device_info.data_size) -+ return to_read; -+ -+ client->offset = 0; -+ ipts->uapi.doorbell++; -+ -+ memset(&cmd, 0, sizeof(struct ipts_feedback_cmd)); -+ cmd.buffer = buffer_id; -+ -+ ret = ipts_control_send(ipts, IPTS_CMD(FEEDBACK), -+ &cmd, sizeof(struct ipts_feedback_cmd)); -+ -+ if (ret) -+ return -EFAULT; -+ -+ return to_read; -+} -+ -+static __poll_t ipts_uapi_poll(struct file *file, struct poll_table_struct *pt) -+{ -+ struct ipts_uapi_client *client = file->private_data; -+ struct ipts_context *ipts = client->ipts; -+ u32 *doorbell = (u32 *)ipts->doorbell.address; -+ -+ poll_wait(file, &ipts_uapi_wq, pt); -+ -+ if (ipts->status != IPTS_HOST_STATUS_STARTED) -+ return EPOLLHUP | EPOLLERR; -+ -+ if (ipts->uapi.doorbell != *doorbell) -+ return EPOLLIN | EPOLLRDNORM; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_info(struct ipts_uapi_client *client, -+ unsigned long arg) -+{ -+ int ret; -+ struct ipts_info info; -+ -+ void __user *buffer = (void __user *)arg; -+ struct ipts_context *ipts = client->ipts; -+ -+ info.vendor = ipts->device_info.vendor_id; -+ info.product = ipts->device_info.device_id; -+ info.version = ipts->device_info.fw_rev; -+ info.buffer_size = ipts->device_info.data_size; -+ info.max_touch_points = ipts->device_info.max_touch_points; -+ -+ ret = copy_to_user(buffer, &info, sizeof(info)); -+ if (ret) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_start(struct ipts_uapi_client *client) -+{ -+ struct ipts_context *ipts = client->ipts; -+ -+ if (ipts->uapi.active || client->active) -+ return -EFAULT; -+ -+ ipts->uapi.active = true; -+ client->active = true; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl_stop(struct ipts_uapi_client *client) -+{ -+ struct ipts_context *ipts = client->ipts; -+ -+ if (!ipts->uapi.active || !client->active) -+ return -EFAULT; -+ -+ ipts->uapi.active = false; -+ client->active = false; -+ -+ return 0; -+} -+ -+static long ipts_uapi_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ struct ipts_uapi_client *client = file->private_data; -+ -+ switch (cmd) { -+ case IPTS_UAPI_INFO: -+ return ipts_uapi_ioctl_info(client, arg); -+ case IPTS_UAPI_START: -+ return ipts_uapi_ioctl_start(client); -+ case IPTS_UAPI_STOP: -+ return ipts_uapi_ioctl_stop(client); -+ default: -+ return -EINVAL; -+ } -+} -+ -+int ipts_uapi_doorbell_loop(void *data) -+{ -+ u32 doorbell; -+ time64_t timeout, seconds; -+ struct ipts_context *ipts; -+ -+ timeout = ktime_get_seconds() + 5; -+ ipts = (struct ipts_context *)data; -+ -+ while (!kthread_should_stop()) { -+ if (ipts->status != IPTS_HOST_STATUS_STARTED) { -+ msleep(1000); -+ continue; -+ } -+ -+ seconds = ktime_get_seconds(); -+ -+ /* -+ * IPTS will increment the doorbell after if filled up one of -+ * the data buffers. If the doorbell didn't change, there is -+ * no work for us to do. Otherwise, the value of the doorbell -+ * will stand for the *next* buffer thats going to be filled. -+ */ -+ doorbell = *(u32 *)ipts->doorbell.address; -+ if (doorbell != ipts->uapi.doorbell) { -+ wake_up_interruptible(&ipts_uapi_wq); -+ timeout = seconds + 5; -+ } -+ -+ if (timeout > seconds) -+ usleep_range(5000, 15000); -+ else -+ msleep(200); -+ } -+ -+ return 0; -+} -+ -+static const struct file_operations ipts_uapi_fops = { -+ .owner = THIS_MODULE, -+ .open = ipts_uapi_open, -+ .release = ipts_uapi_close, -+ .read = ipts_uapi_read, -+ .poll = ipts_uapi_poll, -+ .unlocked_ioctl = ipts_uapi_ioctl, -+ .llseek = no_llseek, -+}; -+ -+int ipts_uapi_init(struct ipts_context *ipts) -+{ -+ ipts->uapi.device.name = "ipts"; -+ ipts->uapi.device.minor = MISC_DYNAMIC_MINOR; -+ ipts->uapi.device.fops = &ipts_uapi_fops; -+ -+ ipts->uapi.db_thread = kthread_run(ipts_uapi_doorbell_loop, -+ (void *)ipts, "ipts_uapi_doorbell_loop"); -+ -+ return misc_register(&ipts->uapi.device); -+} -+ -+void ipts_uapi_free(struct ipts_context *ipts) -+{ -+ wake_up_interruptible(&ipts_uapi_wq); -+ misc_deregister(&ipts->uapi.device); -+ kthread_stop(ipts->uapi.db_thread); -+} -diff --git a/drivers/misc/ipts/uapi.h b/drivers/misc/ipts/uapi.h -new file mode 100644 -index 000000000000..7d7eabc74b17 ---- /dev/null -+++ b/drivers/misc/ipts/uapi.h -@@ -0,0 +1,11 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef _IPTS_UAPI_H_ -+#define _IPTS_UAPI_H_ -+ -+#include "context.h" -+ -+int ipts_uapi_init(struct ipts_context *ipts); -+void ipts_uapi_free(struct ipts_context *ipts); -+ -+#endif /* _IPTS_UAPI_H_ */ diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 7becfc768bbc..0824ef27b08b 100644 --- a/drivers/misc/mei/hw-me-regs.h @@ -1408,3 +97,1352 @@ index 2a3f2fd5df50..319158fd4393 100644 -- 2.28.0 +From e6812c7fe4d645c770c87071d6e30cb6b960c169 Mon Sep 17 00:00:00 2001 +From: Dorian Stoll +Date: Thu, 6 Aug 2020 11:20:41 +0200 +Subject: [PATCH] misc: Add support for Intel Precise Touch & Stylus + +Based on https://github.com/linux-surface/intel-precise-touch/commit/0a4a44c2a9b676bd25d1cd916118dcfe3f447849 + +Signed-off-by: Dorian Stoll +Patchset: ipts +--- + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/ipts/Kconfig | 17 ++ + drivers/misc/ipts/Makefile | 12 ++ + drivers/misc/ipts/context.h | 48 +++++ + drivers/misc/ipts/control.c | 73 ++++++++ + drivers/misc/ipts/control.h | 23 +++ + drivers/misc/ipts/mei.c | 128 ++++++++++++++ + drivers/misc/ipts/protocol.h | 319 ++++++++++++++++++++++++++++++++++ + drivers/misc/ipts/receiver.c | 183 +++++++++++++++++++ + drivers/misc/ipts/receiver.h | 17 ++ + drivers/misc/ipts/resources.c | 134 ++++++++++++++ + drivers/misc/ipts/resources.h | 18 ++ + drivers/misc/ipts/uapi.c | 190 ++++++++++++++++++++ + drivers/misc/ipts/uapi.h | 47 +++++ + 15 files changed, 1211 insertions(+) + create mode 100644 drivers/misc/ipts/Kconfig + create mode 100644 drivers/misc/ipts/Makefile + create mode 100644 drivers/misc/ipts/context.h + create mode 100644 drivers/misc/ipts/control.c + create mode 100644 drivers/misc/ipts/control.h + create mode 100644 drivers/misc/ipts/mei.c + create mode 100644 drivers/misc/ipts/protocol.h + create mode 100644 drivers/misc/ipts/receiver.c + create mode 100644 drivers/misc/ipts/receiver.h + create mode 100644 drivers/misc/ipts/resources.c + create mode 100644 drivers/misc/ipts/resources.h + create mode 100644 drivers/misc/ipts/uapi.c + create mode 100644 drivers/misc/ipts/uapi.h + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index e1b1ba5e2b92..be901ffc66fe 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -472,4 +472,5 @@ source "drivers/misc/ocxl/Kconfig" + source "drivers/misc/cardreader/Kconfig" + source "drivers/misc/habanalabs/Kconfig" + source "drivers/misc/uacce/Kconfig" ++source "drivers/misc/ipts/Kconfig" + endmenu +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index c7bd01ac6291..f97938d777e1 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -57,3 +57,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o + obj-$(CONFIG_HABANA_AI) += habanalabs/ + obj-$(CONFIG_UACCE) += uacce/ + obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o ++obj-$(CONFIG_MISC_IPTS) += ipts/ +diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig +new file mode 100644 +index 000000000000..83e2a930c396 +--- /dev/null ++++ b/drivers/misc/ipts/Kconfig +@@ -0,0 +1,17 @@ ++# SPDX-License-Identifier: GPL-2.0-or-later ++ ++config MISC_IPTS ++ tristate "Intel Precise Touch & Stylus" ++ depends on INTEL_MEI ++ help ++ Say Y here if your system has a touchscreen using Intels ++ Precise Touch & Stylus (IPTS) technology. ++ ++ If unsure say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ipts. ++ ++ Building this driver alone will not give you a working touchscreen. ++ It only exposed a userspace API that can be used by a daemon to ++ receive and process data from the touchscreen hardware. +diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile +new file mode 100644 +index 000000000000..8f58b9adbc94 +--- /dev/null ++++ b/drivers/misc/ipts/Makefile +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0-or-later ++# ++# Makefile for the IPTS touchscreen driver ++# ++ ++obj-$(CONFIG_MISC_IPTS) += ipts.o ++ipts-objs := control.o ++ipts-objs += mei.o ++ipts-objs += receiver.o ++ipts-objs += resources.o ++ipts-objs += uapi.o ++ +diff --git a/drivers/misc/ipts/context.h b/drivers/misc/ipts/context.h +new file mode 100644 +index 000000000000..6e8eba3a47e5 +--- /dev/null ++++ b/drivers/misc/ipts/context.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#ifndef _IPTS_CONTEXT_H_ ++#define _IPTS_CONTEXT_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "protocol.h" ++ ++enum ipts_host_status { ++ IPTS_HOST_STATUS_STARTING, ++ IPTS_HOST_STATUS_STARTED, ++ IPTS_HOST_STATUS_STOPPING, ++ IPTS_HOST_STATUS_STOPPED, ++}; ++ ++struct ipts_buffer_info { ++ u8 *address; ++ dma_addr_t dma_address; ++}; ++ ++struct ipts_context { ++ struct mei_cl_device *cldev; ++ struct device *dev; ++ ++ bool restart; ++ enum ipts_host_status status; ++ struct ipts_get_device_info_rsp device_info; ++ ++ struct ipts_buffer_info data[IPTS_BUFFERS]; ++ struct ipts_buffer_info doorbell; ++ ++ struct ipts_buffer_info feedback[IPTS_BUFFERS]; ++ struct ipts_buffer_info workqueue; ++ struct ipts_buffer_info host2me; ++}; ++ ++#endif /* _IPTS_CONTEXT_H_ */ ++ +diff --git a/drivers/misc/ipts/control.c b/drivers/misc/ipts/control.c +new file mode 100644 +index 000000000000..98787d7ea292 +--- /dev/null ++++ b/drivers/misc/ipts/control.c +@@ -0,0 +1,73 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#include ++ ++#include "context.h" ++#include "protocol.h" ++#include "resources.h" ++#include "uapi.h" ++ ++int ipts_control_send(struct ipts_context *ipts, ++ u32 code, void *payload, size_t size) ++{ ++ int ret; ++ struct ipts_command cmd; ++ ++ memset(&cmd, 0, sizeof(struct ipts_command)); ++ cmd.code = code; ++ ++ if (payload && size > 0) ++ memcpy(&cmd.payload, payload, size); ++ ++ ret = mei_cldev_send(ipts->cldev, (u8 *)&cmd, sizeof(cmd.code) + size); ++ if (ret >= 0 || ret == -EINTR) ++ return 0; ++ ++ dev_err(ipts->dev, "Error while sending: 0x%X:%d\n", code, ret); ++ return ret; ++} ++ ++int ipts_control_start(struct ipts_context *ipts) ++{ ++ if (ipts->status != IPTS_HOST_STATUS_STOPPED) ++ return -EBUSY; ++ ++ dev_info(ipts->dev, "Starting IPTS\n"); ++ ipts->status = IPTS_HOST_STATUS_STARTING; ++ ipts->restart = false; ++ ++ ipts_uapi_link(ipts); ++ return ipts_control_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0); ++} ++ ++int ipts_control_stop(struct ipts_context *ipts) ++{ ++ if (ipts->status == IPTS_HOST_STATUS_STOPPING) ++ return -EBUSY; ++ ++ if (ipts->status == IPTS_HOST_STATUS_STOPPED) ++ return -EBUSY; ++ ++ dev_info(ipts->dev, "Stopping IPTS\n"); ++ ipts->status = IPTS_HOST_STATUS_STOPPING; ++ ++ ipts_uapi_unlink(); ++ ipts_resources_free(ipts); ++ return ipts_control_send(ipts, IPTS_CMD_CLEAR_MEM_WINDOW, NULL, 0); ++} ++ ++int ipts_control_restart(struct ipts_context *ipts) ++{ ++ if (ipts->restart) ++ return -EBUSY; ++ ++ ipts->restart = true; ++ return ipts_control_stop(ipts); ++} ++ +diff --git a/drivers/misc/ipts/control.h b/drivers/misc/ipts/control.h +new file mode 100644 +index 000000000000..2b3172c16063 +--- /dev/null ++++ b/drivers/misc/ipts/control.h +@@ -0,0 +1,23 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#ifndef _IPTS_CONTROL_H_ ++#define _IPTS_CONTROL_H_ ++ ++#include ++ ++#include "context.h" ++ ++int ipts_control_send(struct ipts_context *ipts, ++ u32 cmd, void *payload, size_t size); ++int ipts_control_start(struct ipts_context *ipts); ++int ipts_control_restart(struct ipts_context *ipts); ++int ipts_control_stop(struct ipts_context *ipts); ++ ++#endif /* _IPTS_CONTROL_H_ */ ++ +diff --git a/drivers/misc/ipts/mei.c b/drivers/misc/ipts/mei.c +new file mode 100644 +index 000000000000..b74e45c55b62 +--- /dev/null ++++ b/drivers/misc/ipts/mei.c +@@ -0,0 +1,128 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "context.h" ++#include "control.h" ++#include "protocol.h" ++#include "receiver.h" ++#include "uapi.h" ++ ++static int ipts_mei_set_dma_mask(struct mei_cl_device *cldev) ++{ ++ int ret; ++ ++ ret = dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)); ++ if (!ret) ++ return 0; ++ ++ return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32)); ++} ++ ++static int ipts_mei_probe(struct mei_cl_device *cldev, ++ const struct mei_cl_device_id *id) ++{ ++ int ret; ++ struct ipts_context *ipts; ++ ++ if (ipts_mei_set_dma_mask(cldev)) { ++ dev_err(&cldev->dev, "Failed to set DMA mask for IPTS\n"); ++ return -EFAULT; ++ } ++ ++ ret = mei_cldev_enable(cldev); ++ if (ret) { ++ dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret); ++ return ret; ++ } ++ ++ ipts = kzalloc(sizeof(struct ipts_context), GFP_KERNEL); ++ if (!ipts) { ++ mei_cldev_disable(cldev); ++ return -ENOMEM; ++ } ++ ++ ipts->cldev = cldev; ++ ipts->dev = &cldev->dev; ++ ipts->status = IPTS_HOST_STATUS_STOPPED; ++ ++ mei_cldev_set_drvdata(cldev, ipts); ++ mei_cldev_register_rx_cb(cldev, ipts_receiver_callback); ++ ++ return ipts_control_start(ipts); ++} ++ ++static int ipts_mei_remove(struct mei_cl_device *cldev) ++{ ++ int i; ++ struct ipts_context *ipts = mei_cldev_get_drvdata(cldev); ++ ++ ipts_control_stop(ipts); ++ ++ for (i = 0; i < 20; i++) { ++ if (ipts->status == IPTS_HOST_STATUS_STOPPED) ++ break; ++ ++ msleep(25); ++ } ++ ++ mei_cldev_disable(cldev); ++ kfree(ipts); ++ ++ return 0; ++} ++ ++static struct mei_cl_device_id ipts_mei_device_id_table[] = { ++ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(mei, ipts_mei_device_id_table); ++ ++static struct mei_cl_driver ipts_mei_driver = { ++ .id_table = ipts_mei_device_id_table, ++ .name = "ipts", ++ .probe = ipts_mei_probe, ++ .remove = ipts_mei_remove, ++}; ++ ++static int __init ipts_mei_init(void) ++{ ++ int ret; ++ ++ ret = ipts_uapi_init(); ++ if (ret) ++ return ret; ++ ++ ret = mei_cldev_driver_register(&ipts_mei_driver); ++ if (ret) { ++ ipts_uapi_free(); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void __exit ipts_mei_exit(void) ++{ ++ mei_cldev_driver_unregister(&ipts_mei_driver); ++ ipts_uapi_free(); ++} ++ ++MODULE_DESCRIPTION("IPTS touchscreen driver"); ++MODULE_AUTHOR("Dorian Stoll "); ++MODULE_LICENSE("GPL"); ++ ++module_init(ipts_mei_init); ++module_exit(ipts_mei_exit); ++ +diff --git a/drivers/misc/ipts/protocol.h b/drivers/misc/ipts/protocol.h +new file mode 100644 +index 000000000000..2e179cbb9af3 +--- /dev/null ++++ b/drivers/misc/ipts/protocol.h +@@ -0,0 +1,319 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#ifndef _IPTS_PROTOCOL_H_ ++#define _IPTS_PROTOCOL_H_ ++ ++#include ++ ++/* ++ * The MEI client ID for IPTS functionality. ++ */ ++#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ ++ 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) ++ ++/* ++ * Queries the device for vendor specific information. ++ * ++ * The command must not contain any payload. ++ * The response will contain struct ipts_get_device_info_rsp as payload. ++ */ ++#define IPTS_CMD_GET_DEVICE_INFO 0x00000001 ++#define IPTS_RSP_GET_DEVICE_INFO 0x80000001 ++ ++/* ++ * Sets the mode that IPTS will operate in. ++ * ++ * The command must contain struct ipts_set_mode_cmd as payload. ++ * The response will not contain any payload. ++ */ ++#define IPTS_CMD_SET_MODE 0x00000002 ++#define IPTS_RSP_SET_MODE 0x80000002 ++ ++/* ++ * Configures the memory buffers that the ME will use ++ * for passing data to the host. ++ * ++ * The command must contain struct ipts_set_mem_window_cmd as payload. ++ * The response will not contain any payload. ++ */ ++#define IPTS_CMD_SET_MEM_WINDOW 0x00000003 ++#define IPTS_RSP_SET_MEM_WINDOW 0x80000003 ++ ++/* ++ * Signals that the host is ready to receive data to the ME. ++ * ++ * The command must not contain any payload. ++ * The response will not contain any payload. ++ */ ++#define IPTS_CMD_READY_FOR_DATA 0x00000005 ++#define IPTS_RSP_READY_FOR_DATA 0x80000005 ++ ++/* ++ * Signals that a buffer can be refilled to the ME. ++ * ++ * The command must contain struct ipts_feedback_cmd as payload. ++ * The response will not contain any payload. ++ */ ++#define IPTS_CMD_FEEDBACK 0x00000006 ++#define IPTS_RSP_FEEDBACK 0x80000006 ++ ++/* ++ * Resets the data flow from the ME to the hosts and ++ * clears the buffers that were set with SET_MEM_WINDOW. ++ * ++ * The command must not contain any payload. ++ * The response will not contain any payload. ++ */ ++#define IPTS_CMD_CLEAR_MEM_WINDOW 0x00000007 ++#define IPTS_RSP_CLEAR_MEM_WINDOW 0x80000007 ++ ++/* ++ * Singletouch mode is a fallback that does not support ++ * a stylus or more than one touch input. The data is ++ * received as a HID report with report ID 64. ++ */ ++#define IPTS_MODE_SINGLETOUCH 0x0 ++ ++/* ++ * Multitouch mode is the "proper" operation mode for IPTS. It will ++ * return stylus data as well as capacitive heatmap touch data. ++ * This data needs to be processed in userspace before it can be used. ++ */ ++#define IPTS_MODE_MULTITOUCH 0x1 ++ ++/* ++ * Operation completed successfully. ++ */ ++#define IPTS_STATUS_SUCCESS 0x0 ++ ++/* ++ * Command contained a payload with invalid parameters. ++ */ ++#define IPTS_STATUS_INVALID_PARAMS 0x1 ++ ++/* ++ * ME was unable to validate buffer addresses supplied by the host. ++ */ ++#define IPTS_STATUS_ACCESS_DENIED 0x2 ++ ++/* ++ * Command contained a payload with an invalid size. ++ */ ++#define IPTS_STATUS_CMD_SIZE_ERROR 0x3 ++ ++/* ++ * Buffer addresses have not been set, or the ++ * device is not ready for operation yet. ++ */ ++#define IPTS_STATUS_NOT_READY 0x4 ++ ++/* ++ * There is an outstanding command of the same type. The host must ++ * wait for a response before sending another command of the same type. ++ */ ++#define IPTS_STATUS_REQUEST_OUTSTANDING 0x5 ++ ++/* ++ * No sensor could be found. Either no sensor is connected, it has not ++ * been initialized yet, or the system is improperly configured. ++ */ ++#define IPTS_STATUS_NO_SENSOR_FOUND 0x6 ++ ++/* ++ * Not enough free memory for requested operation. ++ */ ++#define IPTS_STATUS_OUT_OF_MEMORY 0x7 ++ ++/* ++ * An unexpected error occured. ++ */ ++#define IPTS_STATUS_INTERNAL_ERROR 0x8 ++ ++/* ++ * The sensor has been disabled / reset and must be reinitialized. ++ */ ++#define IPTS_STATUS_SENSOR_DISABLED 0x9 ++ ++/* ++ * Compatibility revision check between sensor and ME failed. ++ * The host can ignore this error and attempt to continue. ++ */ ++#define IPTS_STATUS_COMPAT_CHECK_FAIL 0xA ++ ++/* ++ * The sensor went through a reset initiated by the ME / the host. ++ */ ++#define IPTS_STATUS_SENSOR_EXPECTED_RESET 0xB ++ ++/* ++ * The sensor went through an unexpected reset. ++ */ ++#define IPTS_STATUS_SENSOR_UNEXPECTED_RESET 0xC ++ ++/* ++ * Requested sensor reset failed to complete. ++ */ ++#define IPTS_STATUS_RESET_FAILED 0xD ++ ++/* ++ * The operation timed out. ++ */ ++#define IPTS_STATUS_TIMEOUT 0xE ++ ++/* ++ * Test mode pattern did not match expected values. ++ */ ++#define IPTS_STATUS_TEST_MODE_FAIL 0xF ++ ++/* ++ * The sensor reported fatal error during reset sequence. ++ * Futher progress is not possible. ++ */ ++#define IPTS_STATUS_SENSOR_FAIL_FATAL 0x10 ++ ++/* ++ * The sensor reported fatal error during reset sequence. ++ * The host can attempt to continue. ++ */ ++#define IPTS_STATUS_SENSOR_FAIL_NONFATAL 0x11 ++ ++/* ++ * The sensor reported invalid capabilities. ++ */ ++#define IPTS_STATUS_INVALID_DEVICE_CAPS 0x12 ++ ++/* ++ * The command cannot be completed until Quiesce IO flow has completed. ++ */ ++#define IPTS_STATUS_QUIESCE_IO_IN_PROGRESS 0x13 ++ ++/* ++ * The amount of buffers that is used for IPTS ++ */ ++#define IPTS_BUFFERS 16 ++ ++#define IPTS_WORKQUEUE_SIZE 8192 ++#define IPTS_WORKQUEUE_ITEM_SIZE 16 ++ ++/** ++ * struct ipts_set_mode_cmd - Payload for the SET_MODE command. ++ * ++ * @mode: The mode that IPTS should operate in. (IPTS_MODE_*) ++ * ++ * This driver only supports multitouch mode. Singletouch mode ++ * requires a different control flow that is not implemented. ++ */ ++struct ipts_set_mode_cmd { ++ u32 mode; ++ u8 reserved[12]; ++} __packed; ++ ++/** ++ * struct ipts_set_mem_window_cmd - Payload for the SET_MEM_WINDOW command. ++ * ++ * @data_buffer_addr_lower: Lower 32 bits of the data buffer addresses. ++ * @data_buffer_addr_upper: Upper 32 bits of the data buffer addresses. ++ * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address. ++ * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address. ++ * @doorbell_addr_lower: Lower 32 bits of the doorbell buffer address. ++ * @doorbell_addr_upper: Upper 32 bits of the doorbell buffer address. ++ * @feedback_buffer_addr_lower: Lower 32 bits of the feedback buffer addresses. ++ * @feedback_buffer_addr_upper: Upper 32 bits of the feedback buffer addresses. ++ * @host2me_addr_lower: Lower 32 bits of the host2me buffer address. ++ * @host2me_addr_upper: Upper 32 bits of the host2me buffer address. ++ * @workqueue_item_size: Constant value. (IPTS_WORKQUEUE_ITEM_SIZE) ++ * @workqueue_size: Constant value. (IPTS_WORKQUEUE_SIZE) ++ * ++ * The data buffers are buffers that get filled with touch data by the ME. ++ * The doorbell buffer is a u32 that gets incremented by the ME once a data ++ * buffer has been filled with new data. ++ * ++ * The other buffers are required for using GuC submission with binary ++ * firmware. Since support for GuC submission has been dropped from i915, ++ * they are not used anymore, but they need to be allocated to ensure proper ++ * operation. ++ */ ++struct ipts_set_mem_window_cmd { ++ u32 data_buffer_addr_lower[IPTS_BUFFERS]; ++ u32 data_buffer_addr_upper[IPTS_BUFFERS]; ++ u32 workqueue_addr_lower; ++ u32 workqueue_addr_upper; ++ u32 doorbell_addr_lower; ++ u32 doorbell_addr_upper; ++ u32 feedback_buffer_addr_lower[IPTS_BUFFERS]; ++ u32 feedback_buffer_addr_upper[IPTS_BUFFERS]; ++ u32 host2me_addr_lower; ++ u32 host2me_addr_upper; ++ u32 host2me_size; ++ u8 reserved1; ++ u8 workqueue_item_size; ++ u16 workqueue_size; ++ u8 reserved[32]; ++} __packed; ++ ++/** ++ * struct ipts_feedback_cmd - Payload for the FEEDBACK command. ++ * ++ * @buffer: The buffer that the ME should refill. ++ */ ++struct ipts_feedback_cmd { ++ u32 buffer; ++ u8 reserved[12]; ++} __packed; ++ ++/** ++ * struct ipts_command - A message sent from the host to the ME. ++ * ++ * @code: The message code describing the command (IPTS_CMD_*) ++ * @payload: Payload for the command, or 0 if no payload is required. ++ */ ++struct ipts_command { ++ u32 code; ++ u8 payload[320]; ++} __packed; ++ ++/** ++ * struct ipts_device_info - Payload for the GET_DEVICE_INFO response. ++ * ++ * @vendor_id: Vendor ID of the touch sensor. ++ * @device_id: Device ID of the touch sensor. ++ * @hw_rev: Hardware revision of the touch sensor. ++ * @fw_rev: Firmware revision of the touch sensor. ++ * @data_size: Required size of one data buffer. ++ * @feedback_size: Required size of one feedback buffer. ++ * @mode: Current operation mode of IPTS (IPTS_MODE_*) ++ * @max_contacts: The amount of concurrent touches supported by the sensor. ++ */ ++struct ipts_get_device_info_rsp { ++ u16 vendor_id; ++ u16 device_id; ++ u32 hw_rev; ++ u32 fw_rev; ++ u32 data_size; ++ u32 feedback_size; ++ u32 mode; ++ u8 max_contacts; ++ u8 reserved[19]; ++} __packed; ++ ++/** ++ * struct ipts_response - A message sent from the ME to the host. ++ * ++ * @code: The message code describing the response (IPTS_RSP_*) ++ * @status: The status code returned by the command. (IPTS_STATUS_*) ++ * @payload: Payload returned by the command. ++ */ ++struct ipts_response { ++ u32 code; ++ u32 status; ++ u8 payload[80]; ++} __packed; ++ ++#endif /* _IPTS_PROTOCOL_H_ */ ++ +diff --git a/drivers/misc/ipts/receiver.c b/drivers/misc/ipts/receiver.c +new file mode 100644 +index 000000000000..3660a1dcfff9 +--- /dev/null ++++ b/drivers/misc/ipts/receiver.c +@@ -0,0 +1,183 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#include ++#include ++ ++#include "context.h" ++#include "control.h" ++#include "protocol.h" ++#include "resources.h" ++ ++static int ipts_receiver_handle_get_device_info(struct ipts_context *ipts, ++ struct ipts_response *rsp) ++{ ++ struct ipts_set_mode_cmd cmd; ++ ++ memcpy(&ipts->device_info, rsp->payload, ++ sizeof(struct ipts_get_device_info_rsp)); ++ ++ memset(&cmd, 0, sizeof(struct ipts_set_mode_cmd)); ++ cmd.mode = IPTS_MODE_MULTITOUCH; ++ ++ return ipts_control_send(ipts, IPTS_CMD_SET_MODE, ++ &cmd, sizeof(struct ipts_set_mode_cmd)); ++} ++ ++static int ipts_receiver_handle_set_mode(struct ipts_context *ipts) ++{ ++ int i, ret; ++ struct ipts_set_mem_window_cmd cmd; ++ ++ ret = ipts_resources_alloc(ipts); ++ if (ret) { ++ dev_err(ipts->dev, "Failed to allocate resources\n"); ++ return ret; ++ } ++ ++ memset(&cmd, 0, sizeof(struct ipts_set_mem_window_cmd)); ++ ++ for (i = 0; i < IPTS_BUFFERS; i++) { ++ cmd.data_buffer_addr_lower[i] = ++ lower_32_bits(ipts->data[i].dma_address); ++ ++ cmd.data_buffer_addr_upper[i] = ++ upper_32_bits(ipts->data[i].dma_address); ++ ++ cmd.feedback_buffer_addr_lower[i] = ++ lower_32_bits(ipts->feedback[i].dma_address); ++ ++ cmd.feedback_buffer_addr_upper[i] = ++ upper_32_bits(ipts->feedback[i].dma_address); ++ } ++ ++ cmd.workqueue_addr_lower = lower_32_bits(ipts->workqueue.dma_address); ++ cmd.workqueue_addr_upper = upper_32_bits(ipts->workqueue.dma_address); ++ ++ cmd.doorbell_addr_lower = lower_32_bits(ipts->doorbell.dma_address); ++ cmd.doorbell_addr_upper = upper_32_bits(ipts->doorbell.dma_address); ++ ++ cmd.host2me_addr_lower = lower_32_bits(ipts->host2me.dma_address); ++ cmd.host2me_addr_upper = upper_32_bits(ipts->host2me.dma_address); ++ ++ cmd.workqueue_size = IPTS_WORKQUEUE_SIZE; ++ cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE; ++ ++ return ipts_control_send(ipts, IPTS_CMD_SET_MEM_WINDOW, ++ &cmd, sizeof(struct ipts_set_mem_window_cmd)); ++} ++ ++static int ipts_receiver_handle_set_mem_window(struct ipts_context *ipts) ++{ ++ dev_info(ipts->dev, "Device %04hX:%04hX ready\n", ++ ipts->device_info.vendor_id, ++ ipts->device_info.device_id); ++ ipts->status = IPTS_HOST_STATUS_STARTED; ++ ++ return ipts_control_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0); ++} ++ ++static int ipts_receiver_handle_clear_mem_window(struct ipts_context *ipts) ++{ ++ if (ipts->restart) ++ return ipts_control_start(ipts); ++ ++ ipts->status = IPTS_HOST_STATUS_STOPPED; ++ return 0; ++} ++ ++static bool ipts_receiver_handle_error(struct ipts_context *ipts, ++ struct ipts_response *rsp) ++{ ++ bool error; ++ ++ switch (rsp->status) { ++ case IPTS_STATUS_SUCCESS: ++ case IPTS_STATUS_COMPAT_CHECK_FAIL: ++ error = false; ++ break; ++ case IPTS_STATUS_INVALID_PARAMS: ++ error = rsp->code != IPTS_RSP_FEEDBACK; ++ break; ++ case IPTS_STATUS_SENSOR_DISABLED: ++ error = ipts->status != IPTS_HOST_STATUS_STOPPING; ++ break; ++ default: ++ error = true; ++ break; ++ } ++ ++ if (!error) ++ return false; ++ ++ dev_err(ipts->dev, "Command 0x%08x failed: %d\n", ++ rsp->code, rsp->status); ++ ++ if (rsp->code == IPTS_STATUS_SENSOR_UNEXPECTED_RESET) { ++ dev_err(ipts->dev, "Sensor was reset\n"); ++ ++ if (ipts_control_restart(ipts)) ++ dev_err(ipts->dev, "Failed to restart IPTS\n"); ++ } ++ ++ return true; ++} ++ ++static void ipts_receiver_handle_response(struct ipts_context *ipts, ++ struct ipts_response *rsp) ++{ ++ int ret; ++ ++ if (ipts_receiver_handle_error(ipts, rsp)) ++ return; ++ ++ switch (rsp->code) { ++ case IPTS_RSP_GET_DEVICE_INFO: ++ ret = ipts_receiver_handle_get_device_info(ipts, rsp); ++ break; ++ case IPTS_RSP_SET_MODE: ++ ret = ipts_receiver_handle_set_mode(ipts); ++ break; ++ case IPTS_RSP_SET_MEM_WINDOW: ++ ret = ipts_receiver_handle_set_mem_window(ipts); ++ break; ++ case IPTS_RSP_CLEAR_MEM_WINDOW: ++ ret = ipts_receiver_handle_clear_mem_window(ipts); ++ break; ++ default: ++ ret = 0; ++ break; ++ } ++ ++ if (!ret) ++ return; ++ ++ dev_err(ipts->dev, "Error while handling response 0x%08x: %d\n", ++ rsp->code, ret); ++ ++ if (ipts_control_stop(ipts)) ++ dev_err(ipts->dev, "Failed to stop IPTS\n"); ++} ++ ++void ipts_receiver_callback(struct mei_cl_device *cldev) ++{ ++ int ret; ++ struct ipts_response rsp; ++ struct ipts_context *ipts; ++ ++ ipts = mei_cldev_get_drvdata(cldev); ++ ++ ret = mei_cldev_recv(cldev, (u8 *)&rsp, sizeof(struct ipts_response)); ++ if (ret <= 0) { ++ dev_err(ipts->dev, "Error while reading response: %d\n", ret); ++ return; ++ } ++ ++ ipts_receiver_handle_response(ipts, &rsp); ++} ++ +diff --git a/drivers/misc/ipts/receiver.h b/drivers/misc/ipts/receiver.h +new file mode 100644 +index 000000000000..c061d57a9320 +--- /dev/null ++++ b/drivers/misc/ipts/receiver.h +@@ -0,0 +1,17 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#ifndef _IPTS_RECEIVER_H_ ++#define _IPTS_RECEIVER_H_ ++ ++#include ++ ++void ipts_receiver_callback(struct mei_cl_device *cldev); ++ ++#endif /* _IPTS_RECEIVER_H_ */ ++ +diff --git a/drivers/misc/ipts/resources.c b/drivers/misc/ipts/resources.c +new file mode 100644 +index 000000000000..bcf4dd8d7e95 +--- /dev/null ++++ b/drivers/misc/ipts/resources.c +@@ -0,0 +1,134 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#include ++ ++#include "context.h" ++ ++void ipts_resources_free(struct ipts_context *ipts) ++{ ++ int i; ++ struct ipts_buffer_info *buffers; ++ ++ u32 data_buffer_size = ipts->device_info.data_size; ++ u32 feedback_buffer_size = ipts->device_info.feedback_size; ++ ++ buffers = ipts->data; ++ for (i = 0; i < IPTS_BUFFERS; i++) { ++ if (!buffers[i].address) ++ continue; ++ ++ dma_free_coherent(ipts->dev, data_buffer_size, ++ buffers[i].address, buffers[i].dma_address); ++ ++ buffers[i].address = NULL; ++ buffers[i].dma_address = 0; ++ } ++ ++ buffers = ipts->feedback; ++ for (i = 0; i < IPTS_BUFFERS; i++) { ++ if (!buffers[i].address) ++ continue; ++ ++ dma_free_coherent(ipts->dev, feedback_buffer_size, ++ buffers[i].address, buffers[i].dma_address); ++ ++ buffers[i].address = NULL; ++ buffers[i].dma_address = 0; ++ } ++ ++ if (ipts->doorbell.address) { ++ dma_free_coherent(ipts->dev, sizeof(u32), ++ ipts->doorbell.address, ++ ipts->doorbell.dma_address); ++ ++ ipts->doorbell.address = NULL; ++ ipts->doorbell.dma_address = 0; ++ } ++ ++ if (ipts->workqueue.address) { ++ dma_free_coherent(ipts->dev, sizeof(u32), ++ ipts->workqueue.address, ++ ipts->workqueue.dma_address); ++ ++ ipts->workqueue.address = NULL; ++ ipts->workqueue.dma_address = 0; ++ } ++ ++ if (ipts->host2me.address) { ++ dma_free_coherent(ipts->dev, feedback_buffer_size, ++ ipts->host2me.address, ++ ipts->host2me.dma_address); ++ ++ ipts->host2me.address = NULL; ++ ipts->host2me.dma_address = 0; ++ } ++} ++ ++int ipts_resources_alloc(struct ipts_context *ipts) ++{ ++ int i; ++ struct ipts_buffer_info *buffers; ++ ++ u32 data_buffer_size = ipts->device_info.data_size; ++ u32 feedback_buffer_size = ipts->device_info.feedback_size; ++ ++ buffers = ipts->data; ++ for (i = 0; i < IPTS_BUFFERS; i++) { ++ buffers[i].address = dma_alloc_coherent(ipts->dev, ++ data_buffer_size, ++ &buffers[i].dma_address, ++ GFP_KERNEL); ++ ++ if (!buffers[i].address) ++ goto release_resources; ++ } ++ ++ buffers = ipts->feedback; ++ for (i = 0; i < IPTS_BUFFERS; i++) { ++ buffers[i].address = dma_alloc_coherent(ipts->dev, ++ feedback_buffer_size, ++ &buffers[i].dma_address, ++ GFP_KERNEL); ++ ++ if (!buffers[i].address) ++ goto release_resources; ++ } ++ ++ ipts->doorbell.address = dma_alloc_coherent(ipts->dev, ++ sizeof(u32), ++ &ipts->doorbell.dma_address, ++ GFP_KERNEL); ++ ++ if (!ipts->doorbell.address) ++ goto release_resources; ++ ++ ipts->workqueue.address = dma_alloc_coherent(ipts->dev, ++ sizeof(u32), ++ &ipts->workqueue.dma_address, ++ GFP_KERNEL); ++ ++ if (!ipts->workqueue.address) ++ goto release_resources; ++ ++ ipts->host2me.address = dma_alloc_coherent(ipts->dev, ++ feedback_buffer_size, ++ &ipts->host2me.dma_address, ++ GFP_KERNEL); ++ ++ if (!ipts->workqueue.address) ++ goto release_resources; ++ ++ return 0; ++ ++release_resources: ++ ++ ipts_resources_free(ipts); ++ return -ENOMEM; ++} ++ +diff --git a/drivers/misc/ipts/resources.h b/drivers/misc/ipts/resources.h +new file mode 100644 +index 000000000000..8f55af7aae0f +--- /dev/null ++++ b/drivers/misc/ipts/resources.h +@@ -0,0 +1,18 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#ifndef _IPTS_RESOURCES_H_ ++#define _IPTS_RESOURCES_H_ ++ ++#include "context.h" ++ ++int ipts_resources_alloc(struct ipts_context *ipts); ++void ipts_resources_free(struct ipts_context *ipts); ++ ++#endif /* _IPTS_RESOURCES_H_ */ ++ +diff --git a/drivers/misc/ipts/uapi.c b/drivers/misc/ipts/uapi.c +new file mode 100644 +index 000000000000..1b59dbc9a1ad +--- /dev/null ++++ b/drivers/misc/ipts/uapi.c +@@ -0,0 +1,190 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "context.h" ++#include "control.h" ++#include "protocol.h" ++#include "uapi.h" ++ ++struct ipts_uapi uapi; ++ ++static ssize_t ipts_uapi_read(struct file *file, char __user *buf, ++ size_t count, loff_t *offset) ++{ ++ int buffer; ++ int maxbytes; ++ struct ipts_context *ipts = uapi.ipts; ++ ++ buffer = MINOR(file->f_path.dentry->d_inode->i_rdev); ++ ++ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED) ++ return -ENODEV; ++ ++ maxbytes = ipts->device_info.data_size - *offset; ++ if (maxbytes <= 0 || count > maxbytes) ++ return -EINVAL; ++ ++ if (copy_to_user(buf, ipts->data[buffer].address + *offset, count)) ++ return -EFAULT; ++ ++ return count; ++} ++ ++static long ipts_uapi_ioctl_get_device_ready(struct ipts_context *ipts, ++ unsigned long arg) ++{ ++ void __user *buffer = (void __user *)arg; ++ u8 ready = ipts->status == IPTS_HOST_STATUS_STARTED; ++ ++ if (copy_to_user(buffer, &ready, sizeof(u8))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static long ipts_uapi_ioctl_get_device_info(struct ipts_context *ipts, ++ unsigned long arg) ++{ ++ struct ipts_device_info info; ++ void __user *buffer = (void __user *)arg; ++ ++ if (ipts->status != IPTS_HOST_STATUS_STARTED) ++ return -ENODEV; ++ ++ info.vendor = ipts->device_info.vendor_id; ++ info.product = ipts->device_info.device_id; ++ info.version = ipts->device_info.fw_rev; ++ info.buffer_size = ipts->device_info.data_size; ++ info.max_contacts = ipts->device_info.max_contacts; ++ ++ if (copy_to_user(buffer, &info, sizeof(struct ipts_device_info))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static long ipts_uapi_ioctl_get_doorbell(struct ipts_context *ipts, ++ unsigned long arg) ++{ ++ void __user *buffer = (void __user *)arg; ++ ++ if (ipts->status != IPTS_HOST_STATUS_STARTED) ++ return -ENODEV; ++ ++ if (copy_to_user(buffer, ipts->doorbell.address, sizeof(u32))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static long ipts_uapi_ioctl_send_feedback(struct ipts_context *ipts, ++ struct file *file) ++{ ++ int ret; ++ struct ipts_feedback_cmd cmd; ++ ++ if (ipts->status != IPTS_HOST_STATUS_STARTED) ++ return -ENODEV; ++ ++ memset(&cmd, 0, sizeof(struct ipts_feedback_cmd)); ++ cmd.buffer = MINOR(file->f_path.dentry->d_inode->i_rdev); ++ ++ ret = ipts_control_send(ipts, IPTS_CMD_FEEDBACK, ++ &cmd, sizeof(struct ipts_feedback_cmd)); ++ ++ if (ret) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static long ipts_uapi_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct ipts_context *ipts = uapi.ipts; ++ ++ if (!ipts) ++ return -ENODEV; ++ ++ switch (cmd) { ++ case IPTS_IOCTL_GET_DEVICE_READY: ++ return ipts_uapi_ioctl_get_device_ready(ipts, arg); ++ case IPTS_IOCTL_GET_DEVICE_INFO: ++ return ipts_uapi_ioctl_get_device_info(ipts, arg); ++ case IPTS_IOCTL_GET_DOORBELL: ++ return ipts_uapi_ioctl_get_doorbell(ipts, arg); ++ case IPTS_IOCTL_SEND_FEEDBACK: ++ return ipts_uapi_ioctl_send_feedback(ipts, file); ++ default: ++ return -ENOTTY; ++ } ++} ++ ++static const struct file_operations ipts_uapi_fops = { ++ .owner = THIS_MODULE, ++ .read = ipts_uapi_read, ++ .unlocked_ioctl = ipts_uapi_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = ipts_uapi_ioctl, ++#endif ++}; ++ ++void ipts_uapi_link(struct ipts_context *ipts) ++{ ++ uapi.ipts = ipts; ++} ++ ++void ipts_uapi_unlink(void) ++{ ++ uapi.ipts = NULL; ++} ++ ++int ipts_uapi_init(void) ++{ ++ int i, major; ++ ++ alloc_chrdev_region(&uapi.dev, 0, IPTS_BUFFERS, "ipts"); ++ uapi.class = class_create(THIS_MODULE, "ipts"); ++ ++ major = MAJOR(uapi.dev); ++ ++ cdev_init(&uapi.cdev, &ipts_uapi_fops); ++ uapi.cdev.owner = THIS_MODULE; ++ cdev_add(&uapi.cdev, MKDEV(major, 0), IPTS_BUFFERS); ++ ++ for (i = 0; i < IPTS_BUFFERS; i++) { ++ device_create(uapi.class, NULL, ++ MKDEV(major, i), NULL, "ipts/%d", i); ++ } ++ ++ return 0; ++} ++ ++void ipts_uapi_free(void) ++{ ++ int i; ++ int major; ++ ++ major = MAJOR(uapi.dev); ++ ++ for (i = 0; i < IPTS_BUFFERS; i++) ++ device_destroy(uapi.class, MKDEV(major, i)); ++ ++ cdev_del(&uapi.cdev); ++ ++ unregister_chrdev_region(MKDEV(major, 0), MINORMASK); ++ class_destroy(uapi.class); ++} ++ +diff --git a/drivers/misc/ipts/uapi.h b/drivers/misc/ipts/uapi.h +new file mode 100644 +index 000000000000..4c667bb6a7f2 +--- /dev/null ++++ b/drivers/misc/ipts/uapi.h +@@ -0,0 +1,47 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2016 Intel Corporation ++ * Copyright (c) 2020 Dorian Stoll ++ * ++ * Linux driver for Intel Precise Touch & Stylus ++ */ ++ ++#ifndef _IPTS_UAPI_H_ ++#define _IPTS_UAPI_H_ ++ ++#include ++ ++#include "context.h" ++ ++struct ipts_uapi { ++ dev_t dev; ++ struct class *class; ++ struct cdev cdev; ++ ++ struct ipts_context *ipts; ++}; ++ ++struct ipts_device_info { ++ __u16 vendor; ++ __u16 product; ++ __u32 version; ++ __u32 buffer_size; ++ __u8 max_contacts; ++ ++ /* For future expansion */ ++ __u8 reserved[19]; ++}; ++ ++#define IPTS_IOCTL_GET_DEVICE_READY _IOR(0x86, 0x01, __u8) ++#define IPTS_IOCTL_GET_DEVICE_INFO _IOR(0x86, 0x02, struct ipts_device_info) ++#define IPTS_IOCTL_GET_DOORBELL _IOR(0x86, 0x03, __u32) ++#define IPTS_IOCTL_SEND_FEEDBACK _IO(0x86, 0x04) ++ ++void ipts_uapi_link(struct ipts_context *ipts); ++void ipts_uapi_unlink(void); ++ ++int ipts_uapi_init(void); ++void ipts_uapi_free(void); ++ ++#endif /* _IPTS_UAPI_H_ */ ++ +-- +2.28.0 + diff --git a/patches/5.8/0006-surface-gpe.patch b/patches/5.8/0004-surface-gpe.patch similarity index 84% rename from patches/5.8/0006-surface-gpe.patch rename to patches/5.8/0004-surface-gpe.patch index 4e786a015..3e4585007 100644 --- a/patches/5.8/0006-surface-gpe.patch +++ b/patches/5.8/0004-surface-gpe.patch @@ -1,8 +1,35 @@ -From 3965e53a7027b6a17201233cabe6ed146d2f53ea Mon Sep 17 00:00:00 2001 +From aa25cec8fd87df94cabc33c37cc0a2778d63f3d4 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 16 Aug 2020 23:39:56 +0200 -Subject: [PATCH 6/7] surface-gpe +Subject: [PATCH] platform/x86: Add Driver to set up lid GPEs on MS Surface + device +Conventionally, wake-up events for a specific device, in our case the +lid device, are managed via the ACPI _PRW field. While this does not +seem strictly necessary based on ACPI spec, the kernel disables GPE +wakeups to avoid non-wakeup interrupts preventing suspend by default and +only enables GPEs associated via the _PRW field with a wake-up capable +device. This behavior has been introduced in commit + + f941d3e41da7f86bdb9dcc1977c2bcc6b89bfe47 + ACPI: EC / PM: Disable non-wakeup GPEs for suspend-to-idle + +and is described in more detail in its commit message. + +Unfortunately, on MS Surface devices, there is no _PRW field present on +the lid device, thus no GPE is associated with it, and therefore the GPE +responsible for sending the status-change notification to the lid gets +disabled during suspend, making it impossible to wake the device via the +lid. + +This patch introduces a pseudo-device and respective driver which, based +on some DMI matching, mark the corresponding GPE of the lid device for +wake and enable it during suspend. The behavior of this driver models +the behavior of the ACPI/PM core for normal wakeup GPEs, properly +declared via the _PRW field. + +Signed-off-by: Maximilian Luz +Patchset: surface-gpe --- drivers/platform/x86/Kconfig | 9 + drivers/platform/x86/Makefile | 1 + @@ -11,12 +38,12 @@ Subject: [PATCH 6/7] surface-gpe create mode 100644 drivers/platform/x86/surface_gpe.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 14db2795ff63..c64267319696 100644 +index a5ad36083b67..01752d1f40f8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -886,6 +886,15 @@ config SURFACE_BOOK1_DGPU_SWITCH - This driver provides a sysfs switch to set the power-state of the - discrete GPU found on the Microsoft Surface Book 1. +@@ -880,6 +880,15 @@ config SURFACE_PRO3_BUTTON + help + This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. +config SURFACE_GPE + tristate "Surface GPE/Lid Driver" @@ -31,13 +58,13 @@ index 14db2795ff63..c64267319696 100644 tristate "MSI Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 5156523b5863..ef0c3fcab319 100644 +index 2b85852a1a87..a64ce216719a 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile -@@ -86,6 +86,7 @@ obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o +@@ -85,6 +85,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o + obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o - obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += sb1_dgpu_sw.o +obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o # MSI diff --git a/patches/5.8/0005-surface-sam-over-hid.patch b/patches/5.8/0005-surface-sam-over-hid.patch index 1f510892d..e1c2f793f 100644 --- a/patches/5.8/0005-surface-sam-over-hid.patch +++ b/patches/5.8/0005-surface-sam-over-hid.patch @@ -1,15 +1,57 @@ -From 2e7656defabd8c62b9c36b912386f83b8b757d55 Mon Sep 17 00:00:00 2001 +From 256ed7b2d0a06dd15968828ac55d355773b19958 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 -Subject: [PATCH 5/7] surface-sam-over-hid +Subject: [PATCH] i2c: acpi: Implement RawBytes read access +Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C +device via a generic serial bus operation region and RawBytes read +access. On the Surface Book 1, this access is required to turn on (and +off) the discrete GPU. + +Multiple things are to note here: + +a) The RawBytes access is device/driver dependent. The ACPI + specification states: + + > Raw accesses assume that the writer has knowledge of the bus that + > the access is made over and the device that is being accessed. The + > protocol may only ensure that the buffer is transmitted to the + > appropriate driver, but the driver must be able to interpret the + > buffer to communicate to a register. + + Thus this implementation may likely not work on other devices + accessing I2C via the RawBytes accessor type. + +b) The MSHW0030 I2C device is an HID-over-I2C device which seems to + serve multiple functions: + + 1. It is the main access point for the legacy-type Surface Aggregator + Module (also referred to as SAM-over-HID, as opposed to the newer + SAM-over-SSH/UART). It has currently not been determined on how + support for the legacy SAM should be implemented. Likely via a + custom HID driver. + + 2. It seems to serve as the HID device for the Integrated Sensor Hub. + This might complicate matters with regards to implementing a + SAM-over-HID driver required by legacy SAM. + +In light of this, the simplest approach has been chosen for now. +However, it may make more sense regarding breakage and compatibility to +either provide functionality for replacing or enhancing the default +operation region handler via some additional API functions, or even to +completely blacklist MSHW0030 from the I2C core and provide a custom +driver for it. + +Replacing/enhancing the default operation region handler would, however, +either require some sort of secondary driver and access point for it, +from which the new API functions would be called and the new handler +(part) would be installed, or hard-coding them via some sort of +quirk-like interface into the I2C core. + +Patchset: surface-sam-over-hid --- - drivers/i2c/i2c-core-acpi.c | 35 +++++++ - drivers/platform/x86/Kconfig | 7 ++ - drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/sb1_dgpu_sw.c | 162 +++++++++++++++++++++++++++++ - 4 files changed, 205 insertions(+) - create mode 100644 drivers/platform/x86/sb1_dgpu_sw.c + drivers/i2c/i2c-core-acpi.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 2ade99b105b9..60b9cb51d5f7 100644 @@ -64,13 +106,38 @@ index 2ade99b105b9..60b9cb51d5f7 100644 default: dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", accessor_type, client->addr); +-- +2.28.0 + +From 03be5ddae354964501f25958d53525b23c9c6243 Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Sun, 6 Sep 2020 04:01:19 +0200 +Subject: [PATCH] platform/x86: Add driver for Surface Book 1 dGPU switch + +Add driver exposing the discrete GPU power-switch of the Microsoft +Surface Book 1 to user-space. + +On the Surface Book 1, the dGPU power is controlled via the Surface +System Aggregator Module (SAM). The specific SAM-over-HID command for +this is exposed via ACPI. This module provides a simple driver exposing +the ACPI call via a sysfs parameter to user-space, so that users can +easily power-on/-off the dGPU. + +Patchset: surface-sam-over-hid +--- + drivers/platform/x86/Kconfig | 7 ++ + drivers/platform/x86/Makefile | 1 + + drivers/platform/x86/sb1_dgpu_sw.c | 162 +++++++++++++++++++++++++++++ + 3 files changed, 170 insertions(+) + create mode 100644 drivers/platform/x86/sb1_dgpu_sw.c + diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 0581a54cf562..14db2795ff63 100644 +index 01752d1f40f8..19a560c44511 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -879,6 +879,13 @@ config SURFACE_PRO3_BUTTON - help - This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. +@@ -889,6 +889,13 @@ config SURFACE_GPE + accordingly. It is required on those devices to allow wake-ups from + suspend by opening the lid. +config SURFACE_BOOK1_DGPU_SWITCH + tristate "Surface Book 1 dGPU Switch Driver" @@ -83,13 +150,13 @@ index 0581a54cf562..14db2795ff63 100644 tristate "MSI Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 2b85852a1a87..5156523b5863 100644 +index a64ce216719a..f46d4a3ce746 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile -@@ -85,6 +85,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o - obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o +@@ -86,6 +86,7 @@ obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o + obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o +obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += sb1_dgpu_sw.o # MSI diff --git a/patches/5.8/0004-surface-sam.patch b/patches/5.8/0006-surface-sam.patch similarity index 93% rename from patches/5.8/0004-surface-sam.patch rename to patches/5.8/0006-surface-sam.patch index cc202b4f9..f73bd9518 100644 --- a/patches/5.8/0004-surface-sam.patch +++ b/patches/5.8/0006-surface-sam.patch @@ -1,14 +1,128 @@ -From f87393d45896951ac36b702275d35d6c81cd5a54 Mon Sep 17 00:00:00 2001 +From 4f5646ac1b6fca049b627b1cff5c4a7403845c62 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Mon, 17 Aug 2020 01:23:20 +0200 -Subject: [PATCH 4/7] surface-sam +Subject: [PATCH] misc: surface_sam: Add file2alias support for Surface SAM + devices +Implement file2alias support for Surface System Aggregator Module (SSAM) +devices. This allows modules to be auto-loaded for specific devices via +their respective module alias. + +Patchset: surface-sam +--- + include/linux/mod_devicetable.h | 17 +++++++++++++++++ + scripts/mod/devicetable-offsets.c | 7 +++++++ + scripts/mod/file2alias.c | 21 +++++++++++++++++++++ + 3 files changed, 45 insertions(+) + +diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h +index e14cbe444afc..a35f82fb36da 100644 +--- a/include/linux/mod_devicetable.h ++++ b/include/linux/mod_devicetable.h +@@ -836,4 +836,21 @@ struct mhi_device_id { + kernel_ulong_t driver_data; + }; + ++/* Surface System Aggregator Module */ ++ ++#define SSAM_MATCH_CHANNEL 0x1 ++#define SSAM_MATCH_INSTANCE 0x2 ++#define SSAM_MATCH_FUNCTION 0x4 ++ ++struct ssam_device_id { ++ __u8 match_flags; ++ ++ __u8 category; ++ __u8 channel; ++ __u8 instance; ++ __u8 function; ++ ++ kernel_ulong_t driver_data; ++}; ++ + #endif /* LINUX_MOD_DEVICETABLE_H */ +diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c +index 010be8ba2116..35b9899b0b0f 100644 +--- a/scripts/mod/devicetable-offsets.c ++++ b/scripts/mod/devicetable-offsets.c +@@ -241,5 +241,12 @@ int main(void) + DEVID(mhi_device_id); + DEVID_FIELD(mhi_device_id, chan); + ++ DEVID(ssam_device_id); ++ DEVID_FIELD(ssam_device_id, match_flags); ++ DEVID_FIELD(ssam_device_id, category); ++ DEVID_FIELD(ssam_device_id, channel); ++ DEVID_FIELD(ssam_device_id, instance); ++ DEVID_FIELD(ssam_device_id, function); ++ + return 0; + } +diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c +index 9599e2a3f1e6..079672e0747a 100644 +--- a/scripts/mod/file2alias.c ++++ b/scripts/mod/file2alias.c +@@ -1364,6 +1364,26 @@ static int do_mhi_entry(const char *filename, void *symval, char *alias) + return 1; + } + ++/* Looks like: ssam:cNtNiNfN ++ * ++ * N is exactly 2 digits, where each is an upper-case hex digit. ++ */ ++static int do_ssam_entry(const char *filename, void *symval, char *alias) ++{ ++ DEF_FIELD(symval, ssam_device_id, match_flags); ++ DEF_FIELD(symval, ssam_device_id, category); ++ DEF_FIELD(symval, ssam_device_id, channel); ++ DEF_FIELD(symval, ssam_device_id, instance); ++ DEF_FIELD(symval, ssam_device_id, function); ++ ++ sprintf(alias, "ssam:c%02X", category); ++ ADD(alias, "t", match_flags & SSAM_MATCH_CHANNEL, channel); ++ ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); ++ ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); ++ ++ return 1; ++} ++ + /* Does namelen bytes of name exactly match the symbol? */ + static bool sym_is(const char *name, unsigned namelen, const char *symbol) + { +@@ -1438,6 +1458,7 @@ static const struct devtable devtable[] = { + {"tee", SIZE_tee_client_device_id, do_tee_entry}, + {"wmi", SIZE_wmi_device_id, do_wmi_entry}, + {"mhi", SIZE_mhi_device_id, do_mhi_entry}, ++ {"ssam", SIZE_ssam_device_id, do_ssam_entry}, + }; + + /* Create MODULE_ALIAS() statements. +-- +2.28.0 + +From 04c0ee68e67efe9a33446ae8f02f108c7de051dc Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Mon, 17 Aug 2020 01:44:30 +0200 +Subject: [PATCH] misc: Add support for Surface System Aggregator Module + +Add support for the Surface System Aggregator Module (SSAM), an embedded +controller that can be found on 5th and later generation Microsoft +Surface devices. The responsibilities of this EC vary from device to +device. It provides battery information on all 5th and later generation +devices, temperature sensor and cooling capability access, functionality +for clipboard detaching on the Surface Books (2 and 3), as well as +HID-over-SSAM input devices, including keyboard on the Surface Laptop 1 +and 2, and keyboard as well as touchpad input on the Surface Laptop 3 +and Surface Book 3. + +Patchset: surface-sam --- Documentation/driver-api/index.rst | 1 + .../surface_aggregator/client-api.rst | 38 + .../driver-api/surface_aggregator/client.rst | 394 +++ .../surface_aggregator/clients/cdev.rst | 85 + - .../surface_aggregator/clients/index.rst | 21 + + .../surface_aggregator/clients/dtx.rst | 712 +++++ + .../surface_aggregator/clients/index.rst | 22 + .../surface_aggregator/clients/san.rst | 44 + .../driver-api/surface_aggregator/index.rst | 21 + .../surface_aggregator/internal-api.rst | 67 + @@ -27,7 +141,7 @@ Subject: [PATCH 4/7] surface-sam .../clients/surface_aggregator_cdev.c | 299 ++ .../clients/surface_aggregator_registry.c | 652 +++++ .../clients/surface_battery.c | 1196 ++++++++ - .../surface_aggregator/clients/surface_dtx.c | 1129 ++++++++ + .../surface_aggregator/clients/surface_dtx.c | 1270 ++++++++ .../surface_aggregator/clients/surface_hid.c | 925 ++++++ .../clients/surface_hotplug.c | 1285 +++++++++ .../clients/surface_perfmode.c | 122 + @@ -42,18 +156,20 @@ Subject: [PATCH 4/7] surface-sam .../surface_aggregator/ssh_request_layer.c | 1254 ++++++++ .../surface_aggregator/ssh_request_layer.h | 142 + drivers/misc/surface_aggregator/trace.h | 625 ++++ - include/linux/mod_devicetable.h | 18 + + include/linux/mod_devicetable.h | 5 +- include/linux/surface_acpi_notify.h | 39 + include/linux/surface_aggregator/controller.h | 815 ++++++ include/linux/surface_aggregator/device.h | 430 +++ include/linux/surface_aggregator/serial_hub.h | 659 +++++ include/uapi/linux/surface_aggregator/cdev.h | 58 + - scripts/mod/devicetable-offsets.c | 8 + - scripts/mod/file2alias.c | 23 + - 46 files changed, 18848 insertions(+) + include/uapi/linux/surface_aggregator/dtx.h | 150 + + scripts/mod/devicetable-offsets.c | 3 +- + scripts/mod/file2alias.c | 10 +- + 48 files changed, 19814 insertions(+), 7 deletions(-) create mode 100644 Documentation/driver-api/surface_aggregator/client-api.rst create mode 100644 Documentation/driver-api/surface_aggregator/client.rst create mode 100644 Documentation/driver-api/surface_aggregator/clients/cdev.rst + create mode 100644 Documentation/driver-api/surface_aggregator/clients/dtx.rst create mode 100644 Documentation/driver-api/surface_aggregator/clients/index.rst create mode 100644 Documentation/driver-api/surface_aggregator/clients/san.rst create mode 100644 Documentation/driver-api/surface_aggregator/index.rst @@ -91,6 +207,7 @@ Subject: [PATCH 4/7] surface-sam create mode 100644 include/linux/surface_aggregator/device.h create mode 100644 include/linux/surface_aggregator/serial_hub.h create mode 100644 include/uapi/linux/surface_aggregator/cdev.h + create mode 100644 include/uapi/linux/surface_aggregator/dtx.h diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 6567187e7687..e36363f0972b 100644 @@ -639,12 +756,730 @@ index 000000000000..63b5afcb89b5 + +.. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h + :functions: ssam_cdev_request +diff --git a/Documentation/driver-api/surface_aggregator/clients/dtx.rst b/Documentation/driver-api/surface_aggregator/clients/dtx.rst +new file mode 100644 +index 000000000000..e974c2b04e9f +--- /dev/null ++++ b/Documentation/driver-api/surface_aggregator/clients/dtx.rst +@@ -0,0 +1,712 @@ ++.. SPDX-License-Identifier: GPL-2.0+ ++ ++.. |__u16| replace:: :c:type:`__u16 <__u16>` ++.. |sdtx_event| replace:: :c:type:`struct sdtx_event ` ++.. |sdtx_event_code| replace:: :c:type:`enum sdtx_event_code ` ++.. |sdtx_base_info| replace:: :c:type:`struct sdtx_base_info ` ++.. |sdtx_device_mode| replace:: :c:type:`struct sdtx_device_mode ` ++ ++====================================================== ++User-Space DTX (Clipboard Detachment System) Interface ++====================================================== ++ ++The ``surface_dtx`` driver is responsible for proper clipboard detachment ++and re-attachment handling. To this end, it provides the ``/dev/surface/dtx`` ++device file, through which it can interface with a user-space daemon. This ++daemon is then ultimately responsible for determining and taking necessary ++actions, such as unmounting devices attached to the base, ++unloading/reloading the graphics-driver, user-notifications, etc. ++ ++There are two basic communication principles used in this driver: Commands ++(in other parts of the documentation also referred to as requests) and ++events. Commands are sent to the EC and may have a different implications in ++different contexts. Events are sent by the EC upon some internal state ++change. Commands are always driver-initiated, whereas events are always ++initiated by the EC. ++ ++.. contents:: ++ ++Nomenclature ++============ ++ ++* **Clipboard:** ++ The detachable upper part of the Surface Book, housing the screen and CPU. ++ ++* **Base:** ++ The lower part of the Surface Book from which the clipboard can be ++ detached, optionally (model dependent) housing the discrete GPU (dGPU). ++ ++* **Latch:** ++ The mechanism keeping the clipboard attached to the base in normal ++ operation and allowing it to be detached when requested. ++ ++* **Silently ignored commands:** ++ The command is accepted by the EC as a valid command and acknowledged ++ (following the standard communication protocol), but the EC does not act ++ upon it, i.e. ignores it.e upper part of the ++ ++ ++Detachment Process ++================== ++ ++Warning: This part of the documentation is based on reverse engineering and ++testing and thus may contain errors or be incomplete. ++ ++Latch States ++------------ ++ ++The latch mechanism has two major states: *open* and *closed*. In the ++*closed* state (default), the clipboard is secured to the base, whereas in ++the *open* state, the clipboard can be removed by a user. ++ ++The latch can additionally be locked and, correspondingly, unlocked, which ++can influence the detachment procedure. Specifically, this locking mechanism ++is intended to prevent the the dGPU, positioned in the base of the device, ++from being hot-unplugged while in use. More details can be found in the ++documentation for the detachment procedure below. By default, the latch is ++unlocked. ++ ++Detachment Procedure ++-------------------- ++ ++Note that the detachment process is governed fully by the EC. The ++``surface_dtx`` driver only relays events from the EC to user-space and ++commands from user-space to the EC, i.e. it does not influence this process. ++ ++The detachment process is started with the user pressing the *detach* button ++on the base of the device or executing the ``SDTX_IOCTL_LATCH_REQUEST`` IOCTL. ++Following that: ++ ++1. The EC turns on the indicator led on the detach-button, sends a ++ *detach-request* event (``SDTX_EVENT_REQUEST``), and awaits further ++ instructions/commands. In case the latch is unlocked, the led will flash ++ green. If the latch has been locked, the led will be solid red ++ ++2. The event is, via the ``surface_dtx`` driver, relayed to user-space, where ++ an appropriate user-space daemon can handle it and send instructions back ++ to the EC via IOCTLs provided by this driver. ++ ++3. The EC waits for instructions from user-space and acts according to them. ++ If the EC does not receive any instructions in a given period, it will ++ time out and continue as follows: ++ ++ - If the latch is unlocked, the EC will open the latch and the clipboard ++ can be detached from the base. This is the exact behavior as without ++ this driver or any user-space daemon. See the ``SDTX_IOCTL_LATCH_CONFIRM`` ++ description below for more details on the follow-up behavior of the EC. ++ ++ - If the latch is locked, the EC will *not* open the latch, meaning the ++ clipboard cannot be detached from the base. Furthermore, the EC sends ++ an cancel event (``SDTX_EVENT_CANCEL``) detailing this with the cancel ++ reason ``SDTX_DETACH_TIMEDOUT`` (see :ref:`events` for details). ++ ++Valid responses by a user-space daemon to a detachment request event are: ++ ++- Execute ``SDTX_IOCTL_LATCH_REQUEST``. This will immediately abort the ++ detachment process. Furthermore, the EC will send a detach-request event, ++ similar to the user pressing the detach-button to cancel said process (see ++ below). ++ ++- Execute ``SDTX_IOCTL_LATCH_CONFIRM``. This will cause the EC to open the ++ latch, after which the user can separate clipboard and base. ++ ++ As this changes the latch state, a *latch-status* event ++ (``SDTX_EVENT_LATCH_STATUS``) will be sent once the latch has been opened ++ successfully. If the EC fails to open the latch, e.g. due to hardware ++ error or low battery, a latch-cancel event (``SDTX_EVENT_CANCEL``) will be ++ sent with the cancel reason indicating the specific failure. ++ ++ If the latch is currently locked, the latch will automatically be ++ unlocked before it is opened. ++ ++- Execute ``SDTX_IOCTL_LATCH_HEARTBEAT``. This will reset the internal timeout. ++ No other actions will be performed, i.e. the detachment process will neither ++ be completed nor canceled, and the EC will still be waiting for further ++ responses. ++ ++- Execute ``SDTX_IOCTL_LATCH_CANCEL``. This will abort the detachment process, ++ similar to ``SDTX_IOCTL_LATCH_REQUEST``, described above, or the button ++ press, described below. A *generic request* event (``SDTX_EVENT_REQUEST``) ++ is send in response to this. In contrast to those, however, this command ++ does not trigger a new detachment process if none is currently in ++ progress. ++ ++- Do nothing. The detachment process eventually times out as described in ++ point 3. ++ ++See :ref:`ioctls` for more details on these responses. ++ ++It is important to note that, if the user presses the detach button at any ++point when a detachment operation is in progress (i.e. after the the EC has ++sent the initial *detach-request* event (``SDTX_EVENT_REQUEST``) and before ++it received the corresponding response concluding the process), the ++detachment process is canceled on the EC-level and an identical event is ++being sent. Thus a *detach-request* event, by itself, does not signal the ++start of the detachment process. ++ ++The detachment process may further be canceled by the EC due to hardware ++failures or a low clipboard battery. This is done via a cancel event ++(``SDTX_EVENT_CANCEL``) with the corresponding cancel reason. ++ ++ ++User-Space Interface Documentation ++================================== ++ ++Error Codes and Status Values ++----------------------------- ++ ++Error and status codes are divided into different categories, which can be ++used to determine if the status code is an error, and, if it is, the ++severity and type of that error. The current categories are: ++ ++.. flat-table:: Overview of Status/Error Categories. ++ :widths: 2 1 3 ++ :header-rows: 1 ++ ++ * - Name ++ - Value ++ - Short Description ++ ++ * - ``STATUS`` ++ - ``0x0000`` ++ - Non-error status codes. ++ ++ * - ``RUNTIME_ERROR`` ++ - ``0x1000`` ++ - Non-critical runtime errors. ++ ++ * - ``HARDWARE_ERROR`` ++ - ``0x2000`` ++ - Critical hardware failures. ++ ++ * - ``UNKNOWN`` ++ - ``0xF000`` ++ - Unknown error codes. ++ ++Other categories are reserved for future use. The ``SDTX_CATEGORY()`` macro ++can be used to determine the category of any status value. The ++``SDTX_SUCCESS()`` macro can be used to check if the status value is a ++success value (``SDTX_CATEGORY_STATUS``) or if it indicates a failure. ++ ++Unknown status or error codes sent by the EC are assigned to the ``UNKNOWN`` ++category by the driver and may be implemented via their own code in the ++future. ++ ++Currently used error codes are: ++ ++.. flat-table:: Overview of Error Codes. ++ :widths: 2 1 1 3 ++ :header-rows: 1 ++ ++ * - Name ++ - Category ++ - Value ++ - Short Description ++ ++ * - ``SDTX_DETACH_NOT_FEASIBLE`` ++ - ``RUNTIME`` ++ - ``0x1001`` ++ - Detachment not feasible due to low clipboard battery. ++ ++ * - ``SDTX_DETACH_TIMEDOUT`` ++ - ``RUNTIME`` ++ - ``0x1002`` ++ - Detachment process timed out while the latch was locked. ++ ++ * - ``SDTX_ERR_FAILED_TO_OPEN`` ++ - ``HARDWARE`` ++ - ``0x2001`` ++ - Failed to open latch. ++ ++ * - ``SDTX_ERR_FAILED_TO_REMAIN_OPEN`` ++ - ``HARDWARE`` ++ - ``0x2002`` ++ - Failed to keep latch open. ++ ++ * - ``SDTX_ERR_FAILED_TO_CLOSE`` ++ - ``HARDWARE`` ++ - ``0x2003`` ++ - Failed to close latch. ++ ++Other error codes are reserved for future use. Non-error status codes may ++overlap and are generally only unique within their use-case: ++ ++.. flat-table:: Latch Status Codes. ++ :widths: 2 1 1 3 ++ :header-rows: 1 ++ ++ * - Name ++ - Category ++ - Value ++ - Short Description ++ ++ * - ``SDTX_LATCH_CLOSED`` ++ - ``STATUS`` ++ - ``0x0000`` ++ - Latch is closed/has been closed. ++ ++ * - ``SDTX_LATCH_OPENED`` ++ - ``STATUS`` ++ - ``0x0001`` ++ - Latch is open/has been opened. ++ ++.. flat-table:: Base State Codes. ++ :widths: 2 1 1 3 ++ :header-rows: 1 ++ ++ * - Name ++ - Category ++ - Value ++ - Short Description ++ ++ * - ``SDTX_BASE_DETACHED`` ++ - ``STATUS`` ++ - ``0x0000`` ++ - Base has been detached/is not present. ++ ++ * - ``SDTX_BASE_ATTACHED`` ++ - ``STATUS`` ++ - ``0x0001`` ++ - Base has been attached/is present. ++ ++Again, other codes are reserved for future use. ++ ++.. _events: ++ ++Events ++------ ++ ++Events can be received by reading from the device file. They are disabled by ++default and have to be enabled by executing ``SDTX_IOCTL_EVENTS_ENABLE`` ++first. All events follow the layout prescribed by |sdtx_event|. Specific ++event types can be identified by their event code, described in ++|sdtx_event_code|. Note that other event codes are reserved for future use, ++thus an event parser must be able to handle any unknown/unsupported event ++types gracefully, by relying on the payload length given in the event header. ++ ++Currently provided event types are: ++ ++.. flat-table:: Overview of DTX events. ++ :widths: 2 1 1 3 ++ :header-rows: 1 ++ ++ * - Name ++ - Code ++ - Payload ++ - Short Description ++ ++ * - ``SDTX_EVENT_REQUEST`` ++ - ``1`` ++ - ``0`` bytes ++ - Detachment process initiated/aborted. ++ ++ * - ``SDTX_EVENT_CANCEL`` ++ - ``2`` ++ - ``2`` bytes ++ - EC canceled detachment process. ++ ++ * - ``SDTX_EVENT_BASE_CONNECTION`` ++ - ``3`` ++ - ``4`` bytes ++ - Base connection state changed. ++ ++ * - ``SDTX_EVENT_LATCH_STATUS`` ++ - ``4`` ++ - ``2`` bytes ++ - Latch status changed. ++ ++ * - ``SDTX_EVENT_DEVICE_MODE`` ++ - ``5`` ++ - ``2`` bytes ++ - Device mode changed. ++ ++Individual events in more detail: ++ ++``SDTX_EVENT_REQUEST`` ++^^^^^^^^^^^^^^^^^^^^^^ ++ ++Sent when a detachment process is started or, if in progress, aborted by the ++user, either via a detach button press or a detach request ++(``SDTX_IOCTL_LATCH_REQUEST``) being sent from user-space. ++ ++Does not have any payload. ++ ++``SDTX_EVENT_CANCEL`` ++^^^^^^^^^^^^^^^^^^^^^ ++ ++Sent when a detachment process is canceled by the EC due to unfulfilled ++preconditions (e.g. clipboard battery too low to detach) or hardware ++failure. The reason for cancellation is given in the event payload detailed ++below and can be one of ++ ++* ``SDTX_DETACH_TIMEDOUT``: Detachment timed out while the latch was locked. ++ The latch has neither been opened nor unlocked. ++ ++* ``SDTX_DETACH_NOT_FEASIBLE``: Detachment not feasible due to low clipboard ++ battery. ++ ++* ``SDTX_ERR_FAILED_TO_OPEN``: Could not open the latch (hardware failure). ++ ++* ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``: Could not keep the latch open (hardware ++ failure). ++ ++* ``SDTX_ERR_FAILED_TO_CLOSE``: Could not close the latch (hardware failure). ++ ++Other error codes in this context are reserved for future use. ++ ++These codes can be classified via the ``SDTX_CATEGORY()`` macro to discern ++between critical hardware errors (``SDTX_CATEGORY_HARDWARE_ERROR``) or ++runtime errors (``SDTX_CATEGORY_RUNTIME_ERROR``), the latter of which may ++happen during normal operation if certain preconditions for detachment are ++not given. ++ ++.. flat-table:: Detachment Cancel Event Payload ++ :widths: 1 1 4 ++ :header-rows: 1 ++ ++ * - Field ++ - Type ++ - Description ++ ++ * - ``reason`` ++ - |__u16| ++ - Reason for cancellation. ++ ++``SDTX_EVENT_BASE_CONNECTION`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Sent when the base connection state has changed, i.e. when the base has been ++attached, detached, or detachment has become infeasible due to low clipboard ++battery. The new state and, if a base is connected, ID of the base is ++provided as payload of type |sdtx_base_info| with its layout presented ++below: ++ ++.. flat-table:: Base-Connection-Change Event Payload ++ :widths: 1 1 4 ++ :header-rows: 1 ++ ++ * - Field ++ - Type ++ - Description ++ ++ * - ``state`` ++ - |__u16| ++ - Base connection state. ++ ++ * - ``base_id`` ++ - |__u16| ++ - Type of base connected (zero if none). ++ ++Possible values for ``state`` are: ++ ++* ``SDTX_BASE_DETACHED``, ++* ``SDTX_BASE_ATTACHED``, and ++* ``SDTX_DETACH_NOT_FEASIBLE``. ++ ++Other values are reserved for future use. ++ ++``SDTX_EVENT_LATCH_STATUS`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Sent when the latch status has changed, i.e. when the latch has been opened, ++closed, or an error occurred. The current status is provided as payload: ++ ++.. flat-table:: Latch-Status-Change Event Payload ++ :widths: 1 1 4 ++ :header-rows: 1 ++ ++ * - Field ++ - Type ++ - Description ++ ++ * - ``status`` ++ - |__u16| ++ - Latch status. ++ ++Possible values for ``status`` are: ++ ++* ``SDTX_LATCH_CLOSED``, ++* ``SDTX_LATCH_OPENED``, ++* ``SDTX_ERR_FAILED_TO_OPEN``, ++* ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``, and ++* ``SDTX_ERR_FAILED_TO_CLOSE``. ++ ++Other values are reserved for future use. ++ ++``SDTX_EVENT_DEVICE_MODE`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Sent when the device mode has changed. The new device mode is provided as ++payload: ++ ++.. flat-table:: Device-Mode-Change Event Payload ++ :widths: 1 1 4 ++ :header-rows: 1 ++ ++ * - Field ++ - Type ++ - Description ++ ++ * - ``mode`` ++ - |__u16| ++ - Device operation mode. ++ ++Possible values for ``mode`` are: ++ ++* ``SDTX_DEVICE_MODE_TABLET``, ++* ``SDTX_DEVICE_MODE_LAPTOP``, and ++* ``SDTX_DEVICE_MODE_STUDIO``. ++ ++Other values are reserved for future use. ++ ++.. _ioctls: ++ ++IOCTLs ++------ ++ ++The following IOCTLs are provided: ++ ++.. flat-table:: Overview of DTX IOCTLs ++ :widths: 1 1 1 1 4 ++ :header-rows: 1 ++ ++ * - Type ++ - Number ++ - Direction ++ - Name ++ - Description ++ ++ * - ``0xA5`` ++ - ``0x21`` ++ - ``-`` ++ - ``EVENTS_ENABLE`` ++ - Enable events for the current file descriptor. ++ ++ * - ``0xA5`` ++ - ``0x22`` ++ - ``-`` ++ - ``EVENTS_DISABLE`` ++ - Disable events for the current file descriptor. ++ ++ * - ``0xA5`` ++ - ``0x23`` ++ - ``-`` ++ - ``LATCH_LOCK`` ++ - Lock the latch. ++ ++ * - ``0xA5`` ++ - ``0x24`` ++ - ``-`` ++ - ``LATCH_UNLOCK`` ++ - Unlock the latch. ++ ++ * - ``0xA5`` ++ - ``0x25`` ++ - ``-`` ++ - ``LATCH_REQUEST`` ++ - Request clipboard detachment. ++ ++ * - ``0xA5`` ++ - ``0x26`` ++ - ``-`` ++ - ``LATCH_CONFIRM`` ++ - Confirm clipboard detachment request. ++ ++ * - ``0xA5`` ++ - ``0x27`` ++ - ``-`` ++ - ``LATCH_HEARTBEAT`` ++ - Send heartbeat signal to EC. ++ ++ * - ``0xA5`` ++ - ``0x28`` ++ - ``-`` ++ - ``LATCH_CANCEL`` ++ - Cancel detachment process. ++ ++ * - ``0xA5`` ++ - ``0x29`` ++ - ``R`` ++ - ``GET_BASE_INFO`` ++ - Get current base/connection information. ++ ++ * - ``0xA5`` ++ - ``0x2A`` ++ - ``R`` ++ - ``GET_DEVICE_MODE`` ++ - Get current device operation mode. ++ ++ * - ``0xA5`` ++ - ``0x2B`` ++ - ``R`` ++ - ``GET_LATCH_STATUS`` ++ - Get current device latch status. ++ ++``SDTX_IOCTL_EVENTS_ENABLE`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x22)``. ++ ++Enable events for the current file descriptor. Events can be obtained by ++reading from the device, if enabled. Events are disabled by default. ++ ++``SDTX_IOCTL_EVENTS_DISABLE`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x22)``. ++ ++Disable events for the current file descriptor. Events can be obtained by ++reading from the device, if enabled. Events are disabled by default. ++ ++``SDTX_IOCTL_LATCH_LOCK`` ++^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x23)``. ++ ++Locks the latch, causing the detachment procedure to abort without opening ++the latch on timeout. The latch is unlocked by default. This command will be ++silently ignored if the latch is already locked. ++ ++``SDTX_IOCTL_LATCH_UNLOCK`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x24)``. ++ ++Unlocks the latch, causing the detachment procedure to open the latch on ++timeout. The latch is unlocked by default. This command will not open the ++latch when sent during an ongoing detachment process. It will be silently ++ignored if the latch is already unlocked. ++ ++``SDTX_IOCTL_LATCH_REQUEST`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x25)``. ++ ++Generic latch request. Behavior depends on the context: If no ++detachment-process is active, detachment is requested. Otherwise the ++currently active detachment-process will be aborted. ++ ++If a detachment process is canceled by this operation, a generic detachment ++request event (``SDTX_EVENT_REQUEST``) will be sent. ++ ++This essentially behaves the same as a detachment button press. ++ ++``SDTX_IOCTL_LATCH_CONFIRM`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x26)``. ++ ++Acknowledges and confirms a latch request. If sent during an ongoing ++detachment process, this command causes the latch to be opened immediately. ++The latch will also be opened if it has been locked. In this case, the latch ++lock is reset to the unlocked state. ++ ++This command will be silently ignored if there is currently no detachment ++procedure in progress. ++ ++``SDTX_IOCTL_LATCH_HEARTBEAT`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x27)``. ++ ++Sends a heartbeat, essentially resetting the detachment timeout. This ++command can be used to keep the detachment process alive while work required ++for the detachment to succeed is still in progress. ++ ++This command will be silently ignored if there is currently no detachment ++procedure in progress. ++ ++``SDTX_IOCTL_LATCH_CANCEL`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IO(0xA5, 0x28)``. ++ ++Cancels detachment in progress (if any). If a detachment process is canceled ++by this operation, a generic detachment request event ++(``SDTX_EVENT_REQUEST``) will be sent. ++ ++This command will be silently ignored if there is currently no detachment ++procedure in progress. ++ ++``SDTX_IOCTL_GET_BASE_INFO`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IOR(0xA5, 0x29, struct sdtx_base_info)``. ++ ++Get the current base connection state (i.e. attached/detached) and the type ++of the base connected to the clipboard. This is command essentially provides ++a way to query the information provided by the base connection change event ++(``SDTX_EVENT_BASE_CONNECTION``). ++ ++Possible values for ``struct sdtx_base_info.state`` are: ++ ++* ``SDTX_BASE_DETACHED``, ++* ``SDTX_BASE_ATTACHED``, and ++* ``SDTX_DETACH_NOT_FEASIBLE``. ++ ++Other values are reserved for future use. ++ ++``SDTX_IOCTL_GET_DEVICE_MODE`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IOR(0xA5, 0x2A, __u16)``. ++ ++Returns the device operation mode, indicating if and how the base is ++attached to the clipboard. This is command essentially provides a way to ++query the information provided by the device mode change event ++(``SDTX_EVENT_DEVICE_MODE``). ++ ++Returned values are: ++ ++* ``SDTX_DEVICE_MODE_LAPTOP`` ++* ``SDTX_DEVICE_MODE_TABLET`` ++* ``SDTX_DEVICE_MODE_STUDIO`` ++ ++See |sdtx_device_mode| for details. Other values are reserved for future ++use. ++ ++ ++``SDTX_IOCTL_GET_LATCH_STATUS`` ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++Defined as ``_IOR(0xA5, 0x2B, __u16)``. ++ ++Get the current latch status or (presumably) the last error encountered when ++trying to open/close the latch. This is command essentially provides a way ++to query the information provided by the latch status change event ++(``SDTX_EVENT_LATCH_STATUS``). ++ ++Returned values are: ++ ++* ``SDTX_LATCH_CLOSED``, ++* ``SDTX_LATCH_OPENED``, ++* ``SDTX_ERR_FAILED_TO_OPEN``, ++* ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``, and ++* ``SDTX_ERR_FAILED_TO_CLOSE``. ++ ++Other values are reserved for future use. ++ ++A Note on Base IDs ++------------------ ++ ++Base types/IDs provided via ``SDTX_EVENT_BASE_CONNECTION`` or ++``SDTX_IOCTL_GET_BASE_INFO`` are directly forwarded from from the EC in the ++lower byte of the combined |__u16| value, with the driver storing the EC ++type from which this ID comes in the high byte (without this, base IDs over ++different types of ECs may be overlapping). ++ ++The ``SDTX_DEVICE_TYPE()`` macro can be used to determine the EC device ++type. This can be one of ++ ++* ``SDTX_DEVICE_TYPE_HID``, for Surface Aggregator Module over HID, and ++ ++* ``SDTX_DEVICE_TYPE_SSH``, for Surface Aggregator Module over Surface Serial ++ Hub. ++ ++Note that currently only the ``SSH`` type EC is supported, however ``HID`` ++type is reserved for future use. ++ ++Structures and Enums ++-------------------- ++ ++.. kernel-doc:: include/uapi/linux/surface_aggregator/dtx.h diff --git a/Documentation/driver-api/surface_aggregator/clients/index.rst b/Documentation/driver-api/surface_aggregator/clients/index.rst new file mode 100644 -index 000000000000..3ccabce23271 +index 000000000000..98ea9946b8a2 --- /dev/null +++ b/Documentation/driver-api/surface_aggregator/clients/index.rst -@@ -0,0 +1,21 @@ +@@ -0,0 +1,22 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +=========================== @@ -658,6 +1493,7 @@ index 000000000000..3ccabce23271 + :maxdepth: 1 + + cdev ++ dtx + san + +.. only:: subproject and html @@ -5114,10 +5950,10 @@ index 000000000000..21ee212a945a +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_dtx.c b/drivers/misc/surface_aggregator/clients/surface_dtx.c new file mode 100644 -index 000000000000..3b4cfe639509 +index 000000000000..1ac1208edd13 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_dtx.c -@@ -0,0 +1,1129 @@ +@@ -0,0 +1,1270 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface Book (gen. 2 and later) detachment system (DTX) driver. @@ -5149,98 +5985,7 @@ index 000000000000..3b4cfe639509 + +#include +#include -+ -+ -+/* -- Public interface. ----------------------------------------------------- */ -+ -+/* Status/error categories */ -+#define SDTX_CATEGORY_STATUS 0x0000 -+#define SDTX_CATEGORY_RUNTIME_ERROR 0x1000 -+#define SDTX_CATEGORY_HARDWARE_ERROR 0x2000 -+#define SDTX_CATEGORY_UNKNOWN 0xf000 -+ -+#define SDTX_CATEGORY_MASK 0xf000 -+#define SDTX_CATEGORY(value) ((value) & SDTX_CATEGORY_MASK) -+ -+#define SDTX_STATUS(code) ((code) | SDTX_CATEGORY_STATUS) -+#define SDTX_ERR_RT(code) ((code) | SDTX_CATEGORY_RUNTIME_ERROR) -+#define SDTX_ERR_HW(code) ((code) | SDTX_CATEGORY_HARDWARE_ERROR) -+#define SDTX_UNKNOWN(code) ((code) | SDTX_CATEGORY_UNKNOWN) -+ -+#define SDTX_SUCCESS(value) (SDTX_CATEGORY(value) == SDTX_CATEGORY_STATUS) -+ -+/* Latch status values */ -+#define SDTX_LATCH_CLOSED SDTX_STATUS(0x00) -+#define SDTX_LATCH_OPENED SDTX_STATUS(0x01) -+ -+/* Base state values */ -+#define SDTX_BASE_DETACHED SDTX_STATUS(0x00) -+#define SDTX_BASE_ATTACHED SDTX_STATUS(0x01) -+ -+/* Runtime errors (non-critical) */ -+#define SDTX_DETACH_NOT_FEASIBLE SDTX_ERR_RT(0x01) -+#define SDTX_DETACH_TIMEDOUT SDTX_ERR_RT(0x02) -+ -+/* Hardware errors (critical) */ -+#define SDTX_ERR_FAILED_TO_OPEN SDTX_ERR_HW(0x01) -+#define SDTX_ERR_FAILED_TO_REMAIN_OPEN SDTX_ERR_HW(0x02) -+#define SDTX_ERR_FAILED_TO_CLOSE SDTX_ERR_HW(0x03) -+ -+ -+/* Base types */ -+#define SDTX_DEVICE_TYPE_HID 0x0100 -+#define SDTX_DEVICE_TYPE_SSH 0x0200 -+ -+#define SDTX_DEVICE_TYPE_MASK 0x0f00 -+#define SDTX_DEVICE_TYPE(value) ((value) & SDTX_DEVICE_TYPE_MASK) -+ -+#define SDTX_BASE_TYPE_HID(id) ((id) | SDTX_DEVICE_TYPE_HID) -+#define SDTX_BASE_TYPE_SSH(id) ((id) | SDTX_DEVICE_TYPE_SSH) -+ -+ -+/* Device mode */ -+enum sdtx_device_mode { -+ SDTX_DEVICE_MODE_TABLET = 0x00, -+ SDTX_DEVICE_MODE_LAPTOP = 0x01, -+ SDTX_DEVICE_MODE_STUDIO = 0x02, -+}; -+ -+ -+/* Event provided by reading from the device */ -+struct sdtx_event { -+ __u16 length; -+ __u16 code; -+ __u8 data[]; -+} __packed; -+ -+enum sdtx_event_code { -+ SDTX_EVENT_REQUEST = 1, -+ SDTX_EVENT_CANCEL = 2, -+ SDTX_EVENT_BASE_CONNECTION = 3, -+ SDTX_EVENT_LATCH_STATUS = 4, -+ SDTX_EVENT_DEVICE_MODE = 5, -+}; -+ -+ -+/* IOCTL interface */ -+struct sdtx_base_info { -+ __u16 state; -+ __u16 base_id; -+} __packed; -+ -+#define SDTX_IOCTL_EVENTS_ENABLE _IO(0xa5, 0x21) -+#define SDTX_IOCTL_EVENTS_DISABLE _IO(0xa5, 0x22) -+ -+#define SDTX_IOCTL_LATCH_LOCK _IO(0xa5, 0x23) -+#define SDTX_IOCTL_LATCH_UNLOCK _IO(0xa5, 0x24) -+#define SDTX_IOCTL_LATCH_REQUEST _IO(0xa5, 0x25) -+#define SDTX_IOCTL_LATCH_CONFIRM _IO(0xa5, 0x26) -+#define SDTX_IOCTL_LATCH_HEARTBEAT _IO(0xa5, 0x27) -+#define SDTX_IOCTL_LATCH_CANCEL _IO(0xa5, 0x28) -+ -+#define SDTX_IOCTL_GET_BASE_INFO _IOR(0xa5, 0x29, struct sdtx_base_info) -+#define SDTX_IOCTL_GET_DEVICE_MODE _IOR(0xa5, 0x2a, u16) -+#define SDTX_IOCTL_GET_LATCH_STATUS _IOR(0xa5, 0x2b, u16) ++#include + + +/* -- SSAM interface. ------------------------------------------------------- */ @@ -5252,35 +5997,34 @@ index 000000000000..3b4cfe639509 + SAM_EVENT_CID_DTX_LATCH_STATUS = 0x11, +}; + -+enum dtx_base_state { -+ SDTX_BASE_STATE_DETACH_SUCCESS = 0x00, -+ SDTX_BASE_STATE_ATTACHED = 0x01, -+ SDTX_BASE_STATE_NOT_FEASIBLE = 0x02, ++enum ssam_bas_base_state { ++ SSAM_BAS_BASE_STATE_DETACH_SUCCESS = 0x00, ++ SSAM_BAS_BASE_STATE_ATTACHED = 0x01, ++ SSAM_BAS_BASE_STATE_NOT_FEASIBLE = 0x02, +}; + -+enum dtx_latch_status { -+ SDTX_LATCH_STATUS_CLOSED = 0x00, -+ SDTX_LATCH_STATUS_OPENED = 0x01, -+ SDTX_LATCH_STATUS_FAILED_TO_OPEN = 0x02, -+ SDTX_LATCH_STATUS_FAILED_TO_REMAIN_OPEN = 0x03, -+ SDTX_LATCH_STATUS_FAILED_TO_CLOSE = 0x04, ++enum ssam_bas_latch_status { ++ SSAM_BAS_LATCH_STATUS_CLOSED = 0x00, ++ SSAM_BAS_LATCH_STATUS_OPENED = 0x01, ++ SSAM_BAS_LATCH_STATUS_FAILED_TO_OPEN = 0x02, ++ SSAM_BAS_LATCH_STATUS_FAILED_TO_REMAIN_OPEN = 0x03, ++ SSAM_BAS_LATCH_STATUS_FAILED_TO_CLOSE = 0x04, +}; + -+enum dtx_cancel_reason { -+ SDTX_CANCEL_REASON_NOT_FEASIBLE = 0x00, // low battery -+ SDTX_CANCEL_REASON_TIMEOUT = 0x02, -+ SDTX_CANCEL_REASON_FAILED_TO_OPEN = 0x03, -+ SDTX_CANCEL_REASON_FAILED_TO_REMAIN_OPEN = 0x04, -+ SDTX_CANCEL_REASON_FAILED_TO_CLOSE = 0x05, ++enum ssam_bas_cancel_reason { ++ SSAM_BAS_CANCEL_REASON_NOT_FEASIBLE = 0x00, // low battery ++ SSAM_BAS_CANCEL_REASON_TIMEOUT = 0x02, ++ SSAM_BAS_CANCEL_REASON_FAILED_TO_OPEN = 0x03, ++ SSAM_BAS_CANCEL_REASON_FAILED_TO_REMAIN_OPEN = 0x04, ++ SSAM_BAS_CANCEL_REASON_FAILED_TO_CLOSE = 0x05, +}; + -+ -+struct ssam_dtx_base_info { ++struct ssam_bas_base_info { + u8 state; + u8 base_id; +} __packed; + -+static_assert(sizeof(struct ssam_dtx_base_info) == 2); ++static_assert(sizeof(struct ssam_bas_base_info) == 2); + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { + .target_category = SSAM_SSH_TC_BAS, @@ -5324,7 +6068,7 @@ index 000000000000..3b4cfe639509 + .instance_id = 0x00, +}); + -+static SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_base, struct ssam_dtx_base_info, { ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_base, struct ssam_bas_base_info, { + .target_category = SSAM_SSH_TC_BAS, + .target_id = 0x01, + .command_id = 0x0c, @@ -5349,7 +6093,10 @@ index 000000000000..3b4cfe639509 +/* -- Main structures. ------------------------------------------------------ */ + +enum sdtx_device_state { -+ SDTX_DEVICE_SHUTDOWN = BIT(0), ++ SDTX_DEVICE_SHUTDOWN_BIT = BIT(0), ++ SDTX_DEVICE_DIRTY_BASE_BIT = BIT(1), ++ SDTX_DEVICE_DIRTY_MODE_BIT = BIT(2), ++ SDTX_DEVICE_DIRTY_LATCH_BIT = BIT(3), +}; + +struct sdtx_device { @@ -5358,35 +6105,39 @@ index 000000000000..3b4cfe639509 + + struct device *dev; + struct ssam_controller *ctrl; -+ unsigned long state; ++ unsigned long flags; + + struct miscdevice mdev; + wait_queue_head_t waitq; ++ struct mutex write_lock; + struct rw_semaphore client_lock; + struct list_head client_list; + ++ struct delayed_work state_work; ++ struct { ++ struct ssam_bas_base_info base; ++ u8 device_mode; ++ u8 latch_status; ++ } state; ++ + struct delayed_work mode_work; + struct input_dev *mode_switch; + + struct ssam_event_notifier notif; -+ -+ struct mutex mutex; -+ bool active; +}; + +enum sdtx_client_state { -+ SDTX_CLIENT_EVENTS_ENABLED = BIT(0), ++ SDTX_CLIENT_EVENTS_ENABLED_BIT = BIT(0), +}; + +struct sdtx_client { + struct sdtx_device *ddev; + struct list_head node; -+ unsigned long state; ++ unsigned long flags; + + struct fasync_struct *fasync; + + struct mutex read_lock; -+ spinlock_t write_lock; + DECLARE_KFIFO(buffer, u8, 512); +}; + @@ -5415,13 +6166,13 @@ index 000000000000..3b4cfe639509 +static u16 sdtx_translate_base_state(struct sdtx_device *ddev, u8 state) +{ + switch (state) { -+ case SDTX_BASE_STATE_ATTACHED: ++ case SSAM_BAS_BASE_STATE_ATTACHED: + return SDTX_BASE_ATTACHED; + -+ case SDTX_BASE_STATE_DETACH_SUCCESS: ++ case SSAM_BAS_BASE_STATE_DETACH_SUCCESS: + return SDTX_BASE_DETACHED; + -+ case SDTX_BASE_STATE_NOT_FEASIBLE: ++ case SSAM_BAS_BASE_STATE_NOT_FEASIBLE: + return SDTX_DETACH_NOT_FEASIBLE; + + default: @@ -5433,19 +6184,19 @@ index 000000000000..3b4cfe639509 +static u16 sdtx_translate_latch_status(struct sdtx_device *ddev, u8 status) +{ + switch (status) { -+ case SDTX_LATCH_STATUS_CLOSED: ++ case SSAM_BAS_LATCH_STATUS_CLOSED: + return SDTX_LATCH_CLOSED; + -+ case SDTX_LATCH_STATUS_OPENED: ++ case SSAM_BAS_LATCH_STATUS_OPENED: + return SDTX_LATCH_OPENED; + -+ case SDTX_LATCH_STATUS_FAILED_TO_OPEN: ++ case SSAM_BAS_LATCH_STATUS_FAILED_TO_OPEN: + return SDTX_ERR_FAILED_TO_OPEN; + -+ case SDTX_LATCH_STATUS_FAILED_TO_REMAIN_OPEN: ++ case SSAM_BAS_LATCH_STATUS_FAILED_TO_REMAIN_OPEN: + return SDTX_ERR_FAILED_TO_REMAIN_OPEN; + -+ case SDTX_LATCH_STATUS_FAILED_TO_CLOSE: ++ case SSAM_BAS_LATCH_STATUS_FAILED_TO_CLOSE: + return SDTX_ERR_FAILED_TO_CLOSE; + + default: @@ -5457,19 +6208,19 @@ index 000000000000..3b4cfe639509 +static u16 sdtx_translate_cancel_reason(struct sdtx_device *ddev, u8 reason) +{ + switch (reason) { -+ case SDTX_CANCEL_REASON_NOT_FEASIBLE: ++ case SSAM_BAS_CANCEL_REASON_NOT_FEASIBLE: + return SDTX_DETACH_NOT_FEASIBLE; + -+ case SDTX_CANCEL_REASON_TIMEOUT: ++ case SSAM_BAS_CANCEL_REASON_TIMEOUT: + return SDTX_DETACH_TIMEDOUT; + -+ case SDTX_CANCEL_REASON_FAILED_TO_OPEN: ++ case SSAM_BAS_CANCEL_REASON_FAILED_TO_OPEN: + return SDTX_ERR_FAILED_TO_OPEN; + -+ case SDTX_CANCEL_REASON_FAILED_TO_REMAIN_OPEN: ++ case SSAM_BAS_CANCEL_REASON_FAILED_TO_REMAIN_OPEN: + return SDTX_ERR_FAILED_TO_REMAIN_OPEN; + -+ case SDTX_CANCEL_REASON_FAILED_TO_CLOSE: ++ case SSAM_BAS_CANCEL_REASON_FAILED_TO_CLOSE: + return SDTX_ERR_FAILED_TO_CLOSE; + + default: @@ -5484,7 +6235,7 @@ index 000000000000..3b4cfe639509 +static int sdtx_ioctl_get_base_info(struct sdtx_device *ddev, + struct sdtx_base_info __user *buf) +{ -+ struct ssam_dtx_base_info raw; ++ struct ssam_bas_base_info raw; + struct sdtx_base_info info; + int status; + @@ -5532,11 +6283,11 @@ index 000000000000..3b4cfe639509 + + switch (cmd) { + case SDTX_IOCTL_EVENTS_ENABLE: -+ set_bit(SDTX_CLIENT_EVENTS_ENABLED, &client->state); ++ set_bit(SDTX_CLIENT_EVENTS_ENABLED_BIT, &client->flags); + return 0; + + case SDTX_IOCTL_EVENTS_DISABLE: -+ clear_bit(SDTX_CLIENT_EVENTS_ENABLED, &client->state); ++ clear_bit(SDTX_CLIENT_EVENTS_ENABLED_BIT, &client->flags); + return 0; + + case SDTX_IOCTL_LATCH_LOCK: @@ -5581,7 +6332,7 @@ index 000000000000..3b4cfe639509 + if (down_read_killable(&client->ddev->lock)) + return -ERESTARTSYS; + -+ if (test_bit(SDTX_DEVICE_SHUTDOWN, &client->ddev->state)) { ++ if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &client->ddev->flags)) { + up_read(&client->ddev->lock); + return -ENODEV; + } @@ -5612,7 +6363,6 @@ index 000000000000..3b4cfe639509 + INIT_LIST_HEAD(&client->node); + + mutex_init(&client->read_lock); -+ spin_lock_init(&client->write_lock); + INIT_KFIFO(client->buffer); + + file->private_data = client; @@ -5621,7 +6371,7 @@ index 000000000000..3b4cfe639509 + down_write(&ddev->client_lock); + + // do not add a new client if the device has been shut down -+ if (test_bit(SDTX_DEVICE_SHUTDOWN, &ddev->state)) { ++ if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags)) { + up_write(&ddev->client_lock); + sdtx_device_put(client->ddev); + kfree(client); @@ -5662,7 +6412,7 @@ index 000000000000..3b4cfe639509 + return -ERESTARTSYS; + + // make sure we're not shut down -+ if (test_bit(SDTX_DEVICE_SHUTDOWN, &ddev->state)) { ++ if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags)) { + up_read(&ddev->lock); + return -ENODEV; + } @@ -5677,8 +6427,8 @@ index 000000000000..3b4cfe639509 + + status = wait_event_interruptible(ddev->waitq, + !kfifo_is_empty(&client->buffer) -+ || test_bit(SDTX_DEVICE_SHUTDOWN, -+ &ddev->state)); ++ || test_bit(SDTX_DEVICE_SHUTDOWN_BIT, ++ &ddev->flags)); + if (status < 0) + return status; + @@ -5686,7 +6436,7 @@ index 000000000000..3b4cfe639509 + return -ERESTARTSYS; + + // need to check that we're not shut down again -+ if (test_bit(SDTX_DEVICE_SHUTDOWN, &ddev->state)) { ++ if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags)) { + up_read(&ddev->lock); + return -ENODEV; + } @@ -5725,7 +6475,7 @@ index 000000000000..3b4cfe639509 + if (down_read_killable(&client->ddev->lock)) + return -ERESTARTSYS; + -+ if (test_bit(SDTX_DEVICE_SHUTDOWN, &client->ddev->state)) { ++ if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &client->ddev->flags)) { + up_read(&client->ddev->lock); + return EPOLLHUP | EPOLLERR; + } @@ -5775,12 +6525,12 @@ index 000000000000..3b4cfe639509 +#define SDTX_DEVICE_MODE_DELAY_CONNECT msecs_to_jiffies(100) +#define SDTX_DEVICE_MODE_DELAY_RECHECK msecs_to_jiffies(100) + -+static void sdtx_device_mode_update(struct sdtx_device *ddev, unsigned long delay); ++static void sdtx_update_device_mode(struct sdtx_device *ddev, unsigned long delay); + + +struct sdtx_status_event { + struct sdtx_event e; -+ u16 v; ++ __u16 v; +} __packed; + +struct sdtx_base_info_event { @@ -5794,6 +6544,7 @@ index 000000000000..3b4cfe639509 + struct sdtx_base_info_event base; +}; + ++/* Must be executed with ddev->write_lock held. */ +static void sdtx_push_event(struct sdtx_device *ddev, struct sdtx_event *evt) +{ + const size_t len = sizeof(struct sdtx_event) + evt->length; @@ -5801,18 +6552,13 @@ index 000000000000..3b4cfe639509 + + down_read(&ddev->client_lock); + list_for_each_entry(client, &ddev->client_list, node) { -+ if (!test_bit(SDTX_CLIENT_EVENTS_ENABLED, &client->state)) ++ if (!test_bit(SDTX_CLIENT_EVENTS_ENABLED_BIT, &client->flags)) + continue; + -+ spin_lock(&client->write_lock); -+ -+ if (likely(kfifo_avail(&client->buffer) >= len)) { ++ if (likely(kfifo_avail(&client->buffer) >= len)) + kfifo_in(&client->buffer, (const u8 *)evt, len); -+ spin_unlock(&client->write_lock); -+ } else { -+ spin_unlock(&client->write_lock); ++ else + dev_warn(ddev->dev, "event buffer overrun\n"); -+ } + + kill_fasync(&client->fasync, SIGIO, POLL_IN); + } @@ -5856,11 +6602,23 @@ index 000000000000..3b4cfe639509 + return 0; + } + ++ mutex_lock(&ddev->write_lock); ++ + // translate event + switch (in->command_id) { + case SAM_EVENT_CID_DTX_CONNECTION: -+ event.base.e.code = SDTX_EVENT_BASE_CONNECTION; ++ clear_bit(SDTX_DEVICE_DIRTY_BASE_BIT, &ddev->flags); ++ ++ // if state has not changed: do not send new event ++ if (ddev->state.base.state == in->data[0] ++ && ddev->state.base.base_id == in->data[1]) ++ goto out; ++ ++ ddev->state.base.state = in->data[0]; ++ ddev->state.base.base_id = in->data[1]; ++ + event.base.e.length = sizeof(struct sdtx_base_info); ++ event.base.e.code = SDTX_EVENT_BASE_CONNECTION; + event.base.v.state = sdtx_translate_base_state(ddev, in->data[0]); + event.base.v.base_id = SDTX_BASE_TYPE_SSH(in->data[1]); + break; @@ -5871,14 +6629,22 @@ index 000000000000..3b4cfe639509 + break; + + case SAM_EVENT_CID_DTX_CANCEL: -+ event.status.e.code = SDTX_EVENT_CANCEL; + event.status.e.length = sizeof(u16); ++ event.status.e.code = SDTX_EVENT_CANCEL; + event.status.v = sdtx_translate_cancel_reason(ddev, in->data[0]); + break; + + case SAM_EVENT_CID_DTX_LATCH_STATUS: -+ event.status.e.code = SDTX_EVENT_LATCH_STATUS; ++ clear_bit(SDTX_DEVICE_DIRTY_LATCH_BIT, &ddev->flags); ++ ++ // if state has not changed: do not send new event ++ if (ddev->state.latch_status == in->data[0]) ++ goto out; ++ ++ ddev->state.latch_status = in->data[0]; ++ + event.status.e.length = sizeof(u16); ++ event.status.e.code = SDTX_EVENT_LATCH_STATUS; + event.status.v = sdtx_translate_latch_status(ddev, in->data[0]); + break; + } @@ -5890,27 +6656,31 @@ index 000000000000..3b4cfe639509 + unsigned long delay; + + delay = in->data[0] ? SDTX_DEVICE_MODE_DELAY_CONNECT : 0; -+ sdtx_device_mode_update(ddev, delay); ++ sdtx_update_device_mode(ddev, delay); + } + ++out: ++ mutex_unlock(&ddev->write_lock); + return SSAM_NOTIF_HANDLED; +} + + -+/* -- Tablet-mode switch. --------------------------------------------------- */ ++/* -- State update functions. ----------------------------------------------- */ + -+static void sdtx_device_mode_update(struct sdtx_device *ddev, unsigned long delay) ++static bool sdtx_device_mode_invalid(u8 mode, u8 base_state) +{ -+ schedule_delayed_work(&ddev->mode_work, delay); ++ return ((base_state == SSAM_BAS_BASE_STATE_ATTACHED) ++ && (mode == SDTX_DEVICE_MODE_TABLET)) ++ || ((base_state == SSAM_BAS_BASE_STATE_DETACH_SUCCESS) ++ && (mode != SDTX_DEVICE_MODE_TABLET)); +} + +static void sdtx_device_mode_workfn(struct work_struct *work) +{ + struct sdtx_device *ddev; + struct sdtx_status_event event; -+ struct ssam_dtx_base_info base; ++ struct ssam_bas_base_info base; + int status, tablet; -+ bool invalid; + u8 mode; + + ddev = container_of(work, struct sdtx_device, mode_work.work); @@ -5935,19 +6705,89 @@ index 000000000000..3b4cfe639509 + * makes sense for the given base state and try again later if it + * doesn't. + */ -+ invalid = ((base.state == SDTX_BASE_STATE_ATTACHED) -+ && (mode == SDTX_DEVICE_MODE_TABLET)) -+ || ((base.state == SDTX_BASE_STATE_DETACH_SUCCESS) -+ && (mode != SDTX_DEVICE_MODE_TABLET)); -+ -+ if (invalid) { ++ if (sdtx_device_mode_invalid(mode, base.state)) { + dev_dbg(ddev->dev, "device mode is invalid, trying again\n"); -+ sdtx_device_mode_update(ddev, SDTX_DEVICE_MODE_DELAY_RECHECK); ++ sdtx_update_device_mode(ddev, SDTX_DEVICE_MODE_DELAY_RECHECK); + return; + } + -+ event.e.code = SDTX_EVENT_DEVICE_MODE; ++ mutex_lock(&ddev->write_lock); ++ clear_bit(SDTX_DEVICE_DIRTY_MODE_BIT, &ddev->flags); ++ ++ // avoid sending duplicate device-mode events ++ if (ddev->state.device_mode == mode) { ++ mutex_unlock(&ddev->write_lock); ++ return; ++ } ++ ++ ddev->state.device_mode = mode; ++ + event.e.length = sizeof(u16); ++ event.e.code = SDTX_EVENT_DEVICE_MODE; ++ event.v = mode; ++ ++ sdtx_push_event(ddev, &event.e); ++ ++ // send SW_TABLET_MODE event ++ tablet = mode != SDTX_DEVICE_MODE_LAPTOP; ++ input_report_switch(ddev->mode_switch, SW_TABLET_MODE, tablet); ++ input_sync(ddev->mode_switch); ++ ++ mutex_unlock(&ddev->write_lock); ++} ++ ++static void sdtx_update_device_mode(struct sdtx_device *ddev, unsigned long delay) ++{ ++ schedule_delayed_work(&ddev->mode_work, delay); ++} ++ ++ ++static void __sdtx_device_state_update_base(struct sdtx_device *ddev, ++ struct ssam_bas_base_info info) ++{ ++ struct sdtx_base_info_event event; ++ ++ // prevent duplicate events ++ if (ddev->state.base.state == info.state ++ && ddev->state.base.base_id == info.base_id) ++ return; ++ ++ ddev->state.base = info; ++ ++ event.e.length = sizeof(struct sdtx_base_info); ++ event.e.code = SDTX_EVENT_BASE_CONNECTION; ++ event.v.state = sdtx_translate_base_state(ddev, info.state); ++ event.v.base_id = SDTX_BASE_TYPE_SSH(info.base_id); ++ ++ sdtx_push_event(ddev, &event.e); ++} ++ ++static void __sdtx_device_state_update_mode(struct sdtx_device *ddev, u8 mode) ++{ ++ struct sdtx_status_event event; ++ int tablet; ++ ++ /* ++ * Note: This function must be called after updating the base state ++ * via __sdtx_device_state_update_base(), as we rely on the updated ++ * base state value in the validity check below. ++ */ ++ ++ if (sdtx_device_mode_invalid(mode, ddev->state.base.state)) { ++ dev_dbg(ddev->dev, "device mode is invalid, trying again\n"); ++ sdtx_update_device_mode(ddev, SDTX_DEVICE_MODE_DELAY_RECHECK); ++ return; ++ } ++ ++ // prevent duplicate events ++ if (ddev->state.device_mode == mode) ++ return; ++ ++ ddev->state.device_mode = mode; ++ ++ // send event ++ event.e.length = sizeof(u16); ++ event.e.code = SDTX_EVENT_DEVICE_MODE; + event.v = mode; + + sdtx_push_event(ddev, &event.e); @@ -5958,14 +6798,97 @@ index 000000000000..3b4cfe639509 + input_sync(ddev->mode_switch); +} + ++static void __sdtx_device_state_update_latch(struct sdtx_device *ddev, u8 status) ++{ ++ struct sdtx_status_event event; ++ ++ // prevent duplicate events ++ if (ddev->state.latch_status == status) ++ return; ++ ++ ddev->state.latch_status = status; ++ ++ event.e.length = sizeof(struct sdtx_base_info); ++ event.e.code = SDTX_EVENT_BASE_CONNECTION; ++ event.v = sdtx_translate_latch_status(ddev, status); ++ ++ sdtx_push_event(ddev, &event.e); ++} ++ ++static void sdtx_device_state_workfn(struct work_struct *work) ++{ ++ struct sdtx_device *ddev; ++ struct ssam_bas_base_info base; ++ u8 mode, latch; ++ int status; ++ ++ ddev = container_of(work, struct sdtx_device, state_work.work); ++ ++ // mark everyting as dirty ++ set_bit(SDTX_DEVICE_DIRTY_BASE_BIT, &ddev->flags); ++ set_bit(SDTX_DEVICE_DIRTY_MODE_BIT, &ddev->flags); ++ set_bit(SDTX_DEVICE_DIRTY_LATCH_BIT, &ddev->flags); ++ ++ /* ++ * Ensure that the state gets marked as dirty before continuing to ++ * query it. Necessary to ensure that clear_bit() calls in ++ * sdtx_notifier() and sdtx_device_mode_workfn() actually clear these ++ * bits if an event is received while updating the state here. ++ */ ++ smp_mb__after_atomic(); ++ ++ status = ssam_bas_get_base(ddev->ctrl, &base); ++ if (status) { ++ dev_err(ddev->dev, "failed to get base state: %d\n", status); ++ return; ++ } ++ ++ status = ssam_bas_get_device_mode(ddev->ctrl, &mode); ++ if (status) { ++ dev_err(ddev->dev, "failed to get device mode: %d\n", status); ++ return; ++ } ++ ++ status = ssam_bas_get_latch_status(ddev->ctrl, &latch); ++ if (status) { ++ dev_err(ddev->dev, "failed to get latch status: %d\n", status); ++ return; ++ } ++ ++ mutex_lock(&ddev->write_lock); ++ ++ /* ++ * If the respective dirty-bit has been cleared, an event has been ++ * received, updating this state. The queried state may thus be out of ++ * date. At this point, we can safely assume that the state provided ++ * by the event is either up to date, or we're about to receive ++ * another event updating it. ++ */ ++ ++ if (test_and_clear_bit(SDTX_DEVICE_DIRTY_BASE_BIT, &ddev->flags)) ++ __sdtx_device_state_update_base(ddev, base); ++ ++ if (test_and_clear_bit(SDTX_DEVICE_DIRTY_MODE_BIT, &ddev->flags)) ++ __sdtx_device_state_update_mode(ddev, mode); ++ ++ if (test_and_clear_bit(SDTX_DEVICE_DIRTY_LATCH_BIT, &ddev->flags)) ++ __sdtx_device_state_update_latch(ddev, latch); ++ ++ mutex_unlock(&ddev->write_lock); ++} ++ ++static void sdtx_update_device_state(struct sdtx_device *ddev, unsigned long delay) ++{ ++ schedule_delayed_work(&ddev->state_work, delay); ++} ++ + +/* -- Common device initialization. ----------------------------------------- */ + +static int sdtx_device_init(struct sdtx_device *ddev, struct device *dev, + struct ssam_controller *ctrl) +{ -+ int status; -+ u8 mode; ++ int status, tablet_mode; + + // basic initialization + kref_init(&ddev->kref); @@ -5986,17 +6909,35 @@ index 000000000000..3b4cfe639509 + ddev->notif.event.flags = SSAM_EVENT_SEQUENCED; + + init_waitqueue_head(&ddev->waitq); ++ mutex_init(&ddev->write_lock); + init_rwsem(&ddev->client_lock); + INIT_LIST_HEAD(&ddev->client_list); + + INIT_DELAYED_WORK(&ddev->mode_work, sdtx_device_mode_workfn); ++ INIT_DELAYED_WORK(&ddev->state_work, sdtx_device_state_workfn); + -+ // get current device mode -+ status = ssam_bas_get_device_mode(ddev->ctrl, &mode); ++ /* ++ * Get current device state. We want to guarantee that events are only ++ * sent when state actually changes. Thus we cannot use special ++ * "uninitialized" values, as that would cause problems when manually ++ * querying the state in surface_dtx_pm_complete(). I.e. we would not ++ * be able to detect state changes there if no change event has been ++ * received between driver initialization and first device suspension. ++ * ++ * Note that we also need to do this before registring the event ++ * notifier, as that may access the state values. ++ */ ++ status = ssam_bas_get_base(ddev->ctrl, &ddev->state.base); + if (status) + return status; + -+ mode = (mode != SDTX_DEVICE_MODE_LAPTOP); ++ status = ssam_bas_get_device_mode(ddev->ctrl, &ddev->state.device_mode); ++ if (status) ++ return status; ++ ++ status = ssam_bas_get_latch_status(ddev->ctrl, &ddev->state.latch_status); ++ if (status) ++ return status; + + // set up tablet mode switch + ddev->mode_switch = input_allocate_device(); @@ -6008,8 +6949,9 @@ index 000000000000..3b4cfe639509 + ddev->mode_switch->id.bustype = BUS_HOST; + ddev->mode_switch->dev.parent = ddev->dev; + ++ tablet_mode = (ddev->state.device_mode != SDTX_DEVICE_MODE_LAPTOP); + input_set_capability(ddev->mode_switch, EV_SW, SW_TABLET_MODE); -+ input_report_switch(ddev->mode_switch, SW_TABLET_MODE, mode); ++ input_report_switch(ddev->mode_switch, SW_TABLET_MODE, tablet_mode); + + status = input_register_device(ddev->mode_switch); + if (status) { @@ -6028,13 +6970,10 @@ index 000000000000..3b4cfe639509 + goto err_mdev; + + /* -+ * Update device mode in case it has changed between getting the -+ * initial mode and registring the event notifier. Note that this is -+ * safe with regards to concurrent connection change events as the -+ * update work will actually check for consistency with base info. ++ * Update device state in case it has changed between getting the ++ * initial mode and registring the event notifier. + */ -+ sdtx_device_mode_update(ddev, 0); -+ ++ sdtx_update_device_state(ddev, 0); + return 0; + +err_notif: @@ -6081,7 +7020,7 @@ index 000000000000..3b4cfe639509 + * Mark device as shut-down. Prevent new clients from being added and + * new operations from being executed. + */ -+ set_bit(SDTX_DEVICE_SHUTDOWN, &ddev->state); ++ set_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags); + + // wake up async clients + down_write(&ddev->client_lock); @@ -6114,6 +7053,43 @@ index 000000000000..3b4cfe639509 +} + + ++/* -- PM ops. --------------------------------------------------------------- */ ++ ++#ifdef CONFIG_PM_SLEEP ++ ++static void surface_dtx_pm_complete(struct device *dev) ++{ ++ struct sdtx_device *ddev = dev_get_drvdata(dev); ++ ++ /* ++ * Normally, the EC will store events while suspended (i.e. in ++ * display-off state) and release them when resumed (i.e. transitioned ++ * to display-on state). During hibernation, however, the EC will be ++ * shut down and does not store events. Furthermore, events might be ++ * dropped during prolonged suspension (it is scurrently unknown how ++ * big this event buffer is and how it behaves on overruns). ++ * ++ * To prevent any problems, we update the device state here. We do ++ * this delayed to ensure that any events sent by the EC directly ++ * after resuming will be handled first. The delay below has been ++ * chosen (experimentally), so that there should be ample time for ++ * these events to be handled, before we check and, if necessary, ++ * update the state. ++ */ ++ sdtx_update_device_state(ddev, msecs_to_jiffies(1000)); ++} ++ ++static const struct dev_pm_ops surface_dtx_pm_ops = { ++ .complete = surface_dtx_pm_complete, ++}; ++ ++#else /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops surface_dtx_pm_ops = {}; ++ ++#endif /* CONFIG_PM_SLEEP */ ++ ++ +/* -- Platform driver. ------------------------------------------------------ */ + +static int surface_dtx_platform_probe(struct platform_device *pdev) @@ -6153,6 +7129,7 @@ index 000000000000..3b4cfe639509 + .driver = { + .name = "surface_dtx_pltf", + .acpi_match_table = surface_dtx_acpi_match, ++ .pm = &surface_dtx_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; @@ -17130,32 +18107,28 @@ index 000000000000..232bf1142aae + +#include diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h -index e14cbe444afc..9932a610bda0 100644 +index a35f82fb36da..9932a610bda0 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h -@@ -836,4 +836,22 @@ struct mhi_device_id { - kernel_ulong_t driver_data; - }; +@@ -838,15 +838,16 @@ struct mhi_device_id { -+/* Surface System Aggregator Module */ -+ + /* Surface System Aggregator Module */ + +-#define SSAM_MATCH_CHANNEL 0x1 +#define SSAM_MATCH_TARGET 0x1 -+#define SSAM_MATCH_INSTANCE 0x2 -+#define SSAM_MATCH_FUNCTION 0x4 -+ -+struct ssam_device_id { -+ __u8 match_flags; -+ + #define SSAM_MATCH_INSTANCE 0x2 + #define SSAM_MATCH_FUNCTION 0x4 + + struct ssam_device_id { + __u8 match_flags; + + __u8 domain; -+ __u8 category; + __u8 category; +- __u8 channel; + __u8 target; -+ __u8 instance; -+ __u8 function; -+ -+ kernel_ulong_t driver_data; -+}; -+ - #endif /* LINUX_MOD_DEVICETABLE_H */ + __u8 instance; + __u8 function; + diff --git a/include/linux/surface_acpi_notify.h b/include/linux/surface_acpi_notify.h new file mode 100644 index 000000000000..8e3e86c7d78c @@ -19125,7 +20098,7 @@ index 000000000000..376313f402b2 +#endif /* _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H */ diff --git a/include/uapi/linux/surface_aggregator/cdev.h b/include/uapi/linux/surface_aggregator/cdev.h new file mode 100644 -index 000000000000..e85351141a2d +index 000000000000..1a8bc0249f8e --- /dev/null +++ b/include/uapi/linux/surface_aggregator/cdev.h @@ -0,0 +1,58 @@ @@ -19143,8 +20116,8 @@ index 000000000000..e85351141a2d +#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H +#define _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H + -+#include +#include ++#include + +/** + * struct ssam_cdev_request - Controller request IOCTL argument. @@ -19187,65 +20160,208 @@ index 000000000000..e85351141a2d +#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request) + +#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */ +diff --git a/include/uapi/linux/surface_aggregator/dtx.h b/include/uapi/linux/surface_aggregator/dtx.h +new file mode 100644 +index 000000000000..d88cabfb8dd7 +--- /dev/null ++++ b/include/uapi/linux/surface_aggregator/dtx.h +@@ -0,0 +1,150 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* ++ * Surface DTX (clipboard detachment system driver) user-space interface. ++ * ++ * Definitions, structs, and IOCTLs for the /dev/surface/dtx misc device. This ++ * device allows user-space to control the clipboard detachment process on ++ * Surface Book series devices. ++ * ++ * Copyright (C) 2020 Maximilian Luz ++ */ ++ ++#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_DTX_H ++#define _UAPI_LINUX_SURFACE_AGGREGATOR_DTX_H ++ ++#include ++#include ++ ++ ++/* Status/error categories */ ++#define SDTX_CATEGORY_STATUS 0x0000 ++#define SDTX_CATEGORY_RUNTIME_ERROR 0x1000 ++#define SDTX_CATEGORY_HARDWARE_ERROR 0x2000 ++#define SDTX_CATEGORY_UNKNOWN 0xf000 ++ ++#define SDTX_CATEGORY_MASK 0xf000 ++#define SDTX_CATEGORY(value) ((value) & SDTX_CATEGORY_MASK) ++ ++#define SDTX_STATUS(code) ((code) | SDTX_CATEGORY_STATUS) ++#define SDTX_ERR_RT(code) ((code) | SDTX_CATEGORY_RUNTIME_ERROR) ++#define SDTX_ERR_HW(code) ((code) | SDTX_CATEGORY_HARDWARE_ERROR) ++#define SDTX_UNKNOWN(code) ((code) | SDTX_CATEGORY_UNKNOWN) ++ ++#define SDTX_SUCCESS(value) (SDTX_CATEGORY(value) == SDTX_CATEGORY_STATUS) ++ ++/* Latch status values */ ++#define SDTX_LATCH_CLOSED SDTX_STATUS(0x00) ++#define SDTX_LATCH_OPENED SDTX_STATUS(0x01) ++ ++/* Base state values */ ++#define SDTX_BASE_DETACHED SDTX_STATUS(0x00) ++#define SDTX_BASE_ATTACHED SDTX_STATUS(0x01) ++ ++/* Runtime errors (non-critical) */ ++#define SDTX_DETACH_NOT_FEASIBLE SDTX_ERR_RT(0x01) ++#define SDTX_DETACH_TIMEDOUT SDTX_ERR_RT(0x02) ++ ++/* Hardware errors (critical) */ ++#define SDTX_ERR_FAILED_TO_OPEN SDTX_ERR_HW(0x01) ++#define SDTX_ERR_FAILED_TO_REMAIN_OPEN SDTX_ERR_HW(0x02) ++#define SDTX_ERR_FAILED_TO_CLOSE SDTX_ERR_HW(0x03) ++ ++ ++/* Base types */ ++#define SDTX_DEVICE_TYPE_HID 0x0100 ++#define SDTX_DEVICE_TYPE_SSH 0x0200 ++ ++#define SDTX_DEVICE_TYPE_MASK 0x0f00 ++#define SDTX_DEVICE_TYPE(value) ((value) & SDTX_DEVICE_TYPE_MASK) ++ ++#define SDTX_BASE_TYPE_HID(id) ((id) | SDTX_DEVICE_TYPE_HID) ++#define SDTX_BASE_TYPE_SSH(id) ((id) | SDTX_DEVICE_TYPE_SSH) ++ ++ ++/** ++ * enum sdtx_device_mode - Mode describing how (and if) the clipboard is ++ * attached to the base of the device. ++ * @SDTX_DEVICE_MODE_TABLET: The clipboard is detached from the base and the ++ * device operates as tablet. ++ * @SDTX_DEVICE_MODE_LAPTOP: The clipboard is attached normally to the base ++ * and the device operates as laptop. ++ * @SDTX_DEVICE_MODE_STUDIO: The clipboard is attached to the base in reverse. ++ * The device operates as tablet with keyboard and ++ * touchpad deactivated, however, the base battery ++ * and, if present in the specific device model, dGPU ++ * are available to the system. ++ */ ++enum sdtx_device_mode { ++ SDTX_DEVICE_MODE_TABLET = 0x00, ++ SDTX_DEVICE_MODE_LAPTOP = 0x01, ++ SDTX_DEVICE_MODE_STUDIO = 0x02, ++}; ++ ++/** ++ * struct sdtx_event - Event provided by reading from the DTX device file. ++ * @length: Length of the event payload, in bytes. ++ * @code: Event code, detailing what type of event this is. ++ * @data: Payload of the event, containing @length bytes. ++ * ++ * See &enum sdtx_event_code for currently valid event codes. ++ */ ++struct sdtx_event { ++ __u16 length; ++ __u16 code; ++ __u8 data[]; ++} __packed; ++ ++/** ++ * enum sdtx_event_code - Code describing the type of an event. ++ * @SDTX_EVENT_REQUEST: Detachment request event type. ++ * @SDTX_EVENT_CANCEL: Cancel detachment process event type. ++ * @SDTX_EVENT_BASE_CONNECTION: Base/clipboard connection change event type. ++ * @SDTX_EVENT_LATCH_STATUS: Latch status change event type. ++ * @SDTX_EVENT_DEVICE_MODE: Device mode change event type. ++ * ++ * Used in @struct sdtx_event to describe the type of the event. Further event ++ * codes are reserved for future use. Any event parser should be able to ++ * gracefully handle unknown events, i.e. by simply skipping them. ++ * ++ * Consult the DTX user-space interface documentation for details regarding ++ * the individual event types. ++ */ ++enum sdtx_event_code { ++ SDTX_EVENT_REQUEST = 1, ++ SDTX_EVENT_CANCEL = 2, ++ SDTX_EVENT_BASE_CONNECTION = 3, ++ SDTX_EVENT_LATCH_STATUS = 4, ++ SDTX_EVENT_DEVICE_MODE = 5, ++}; ++ ++/** ++ * struct sdtx_base_info - Describes if and what type of base is connected. ++ * @state: The state of the connection. Valid values are %SDTX_BASE_DETACHED, ++ * %SDTX_BASE_ATTACHED, and %SDTX_DETACH_NOT_FEASIBLE (in case a base ++ * is attached but low clipboard battery prevents detachment). Other ++ * values are currently reserved. ++ * @base_id: The type of base connected. Zero if no base is connected. ++ */ ++struct sdtx_base_info { ++ __u16 state; ++ __u16 base_id; ++} __packed; ++ ++ ++/* IOCTLs */ ++#define SDTX_IOCTL_EVENTS_ENABLE _IO(0xa5, 0x21) ++#define SDTX_IOCTL_EVENTS_DISABLE _IO(0xa5, 0x22) ++ ++#define SDTX_IOCTL_LATCH_LOCK _IO(0xa5, 0x23) ++#define SDTX_IOCTL_LATCH_UNLOCK _IO(0xa5, 0x24) ++ ++#define SDTX_IOCTL_LATCH_REQUEST _IO(0xa5, 0x25) ++#define SDTX_IOCTL_LATCH_CONFIRM _IO(0xa5, 0x26) ++#define SDTX_IOCTL_LATCH_HEARTBEAT _IO(0xa5, 0x27) ++#define SDTX_IOCTL_LATCH_CANCEL _IO(0xa5, 0x28) ++ ++#define SDTX_IOCTL_GET_BASE_INFO _IOR(0xa5, 0x29, struct sdtx_base_info) ++#define SDTX_IOCTL_GET_DEVICE_MODE _IOR(0xa5, 0x2a, __u16) ++#define SDTX_IOCTL_GET_LATCH_STATUS _IOR(0xa5, 0x2b, __u16) ++ ++#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_DTX_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c -index 010be8ba2116..6b761c5b3ddd 100644 +index 35b9899b0b0f..6b761c5b3ddd 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c -@@ -241,5 +241,13 @@ int main(void) - DEVID(mhi_device_id); - DEVID_FIELD(mhi_device_id, chan); +@@ -243,8 +243,9 @@ int main(void) -+ DEVID(ssam_device_id); -+ DEVID_FIELD(ssam_device_id, match_flags); + DEVID(ssam_device_id); + DEVID_FIELD(ssam_device_id, match_flags); + DEVID_FIELD(ssam_device_id, domain); -+ DEVID_FIELD(ssam_device_id, category); + DEVID_FIELD(ssam_device_id, category); +- DEVID_FIELD(ssam_device_id, channel); + DEVID_FIELD(ssam_device_id, target); -+ DEVID_FIELD(ssam_device_id, instance); -+ DEVID_FIELD(ssam_device_id, function); -+ - return 0; - } + DEVID_FIELD(ssam_device_id, instance); + DEVID_FIELD(ssam_device_id, function); + diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c -index 9599e2a3f1e6..23d9d368fde9 100644 +index 079672e0747a..23d9d368fde9 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c -@@ -1364,6 +1364,28 @@ static int do_mhi_entry(const char *filename, void *symval, char *alias) +@@ -1364,20 +1364,22 @@ static int do_mhi_entry(const char *filename, void *symval, char *alias) return 1; } +-/* Looks like: ssam:cNtNiNfN +/* + * Looks like: ssam:dNcNtNiNfN -+ * -+ * N is exactly 2 digits, where each is an upper-case hex digit. -+ */ -+static int do_ssam_entry(const char *filename, void *symval, char *alias) -+{ -+ DEF_FIELD(symval, ssam_device_id, match_flags); + * + * N is exactly 2 digits, where each is an upper-case hex digit. + */ + static int do_ssam_entry(const char *filename, void *symval, char *alias) + { + DEF_FIELD(symval, ssam_device_id, match_flags); + DEF_FIELD(symval, ssam_device_id, domain); -+ DEF_FIELD(symval, ssam_device_id, category); + DEF_FIELD(symval, ssam_device_id, category); +- DEF_FIELD(symval, ssam_device_id, channel); + DEF_FIELD(symval, ssam_device_id, target); -+ DEF_FIELD(symval, ssam_device_id, instance); -+ DEF_FIELD(symval, ssam_device_id, function); -+ + DEF_FIELD(symval, ssam_device_id, instance); + DEF_FIELD(symval, ssam_device_id, function); + +- sprintf(alias, "ssam:c%02X", category); +- ADD(alias, "t", match_flags & SSAM_MATCH_CHANNEL, channel); + sprintf(alias, "ssam:d%02Xc%02X", domain, category); + ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target); -+ ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); -+ ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); -+ -+ return 1; -+} -+ - /* Does namelen bytes of name exactly match the symbol? */ - static bool sym_is(const char *name, unsigned namelen, const char *symbol) - { -@@ -1438,6 +1460,7 @@ static const struct devtable devtable[] = { - {"tee", SIZE_tee_client_device_id, do_tee_entry}, - {"wmi", SIZE_wmi_device_id, do_wmi_entry}, - {"mhi", SIZE_mhi_device_id, do_mhi_entry}, -+ {"ssam", SIZE_ssam_device_id, do_ssam_entry}, - }; + ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); + ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); - /* Create MODULE_ALIAS() statements. -- 2.28.0 diff --git a/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch b/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch index e144fb4b5..07664c55f 100644 --- a/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch +++ b/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch @@ -1,8 +1,8 @@ -From eacbf7fd1be0f691fdd5562413a5eea98a7210e5 Mon Sep 17 00:00:00 2001 +From ccc695a9170dc8469d8637568129d4ed8780448c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 14 Oct 2020 16:41:58 +0200 -Subject: [PATCH 7/7] i2c: core: Restore acpi_walk_dep_device_list() getting - called after registering the ACPI i2c devs +Subject: [PATCH] i2c: core: Restore acpi_walk_dep_device_list() getting called + after registering the ACPI i2c devs Commit 21653a4181ff ("i2c: core: Call i2c_acpi_install_space_handler() before i2c_acpi_register_devices()")'s intention was to only move the diff --git a/pkg/arch/kernel/0004-surface-gpe.patch b/pkg/arch/kernel/0004-surface-gpe.patch new file mode 120000 index 000000000..bd66f3e39 --- /dev/null +++ b/pkg/arch/kernel/0004-surface-gpe.patch @@ -0,0 +1 @@ +../../../patches/5.8/0004-surface-gpe.patch \ No newline at end of file diff --git a/pkg/arch/kernel/0004-surface-sam.patch b/pkg/arch/kernel/0004-surface-sam.patch deleted file mode 120000 index 071d5be0f..000000000 --- a/pkg/arch/kernel/0004-surface-sam.patch +++ /dev/null @@ -1 +0,0 @@ -../../../patches/5.8/0004-surface-sam.patch \ No newline at end of file diff --git a/pkg/arch/kernel/0006-surface-gpe.patch b/pkg/arch/kernel/0006-surface-gpe.patch deleted file mode 120000 index 225f4f87e..000000000 --- a/pkg/arch/kernel/0006-surface-gpe.patch +++ /dev/null @@ -1 +0,0 @@ -../../../patches/5.8/0006-surface-gpe.patch \ No newline at end of file diff --git a/pkg/arch/kernel/0006-surface-sam.patch b/pkg/arch/kernel/0006-surface-sam.patch new file mode 120000 index 000000000..2db1c5729 --- /dev/null +++ b/pkg/arch/kernel/0006-surface-sam.patch @@ -0,0 +1 @@ +../../../patches/5.8/0006-surface-sam.patch \ No newline at end of file diff --git a/pkg/arch/kernel/PKGBUILD b/pkg/arch/kernel/PKGBUILD index 6e35e4c3e..07eac189c 100644 --- a/pkg/arch/kernel/PKGBUILD +++ b/pkg/arch/kernel/PKGBUILD @@ -28,9 +28,9 @@ source=( 0001-surface3-oemb.patch 0002-wifi.patch 0003-ipts.patch - 0004-surface-sam.patch + 0004-surface-gpe.patch 0005-surface-sam-over-hid.patch - 0006-surface-gpe.patch + 0006-surface-sam.patch 0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch ) validpgpkeys=( @@ -42,13 +42,13 @@ sha256sums=('1251a7e13c23ad35c38f8e58227fc0183e7087a81ee030766e2db8cbe2453392' '181330a9cf4517abbbe29b93165bc859ad8ca14a43582f4e1d69aae2b5ecc2c9' '7f52b09f0bf62d9b2ba162a424ce99caa50cb671f2b78fca1c1dc81e33e9b57f' '8cd2b019aac6d3807a5cdcbbbe0aad81e63193ff3e8dffd7a79d4a1421b858f6' - '7dc41454b2646c743bf6592bcb0706242469380f8e634b9555a7322d7e015800' - '3005384427716297fe4e7ed77feba56976188f0b411c2ba7da73aef817c41ce0' - 'e4245ee8ab19d46d126ce92e0b784944ae7a9c618d5cf5ae11109a946bd75961' - '5521a6d90e50a7a188f69e5f7e769bcb03f1994bf5d99a170e4dc0cf416d2352' - 'afbfc31f6139f37753926c0ba1e1670190144d65dae13b2f4a66182cadd36235' - '018c3972a248547a83b3650e3f3b11d83078ea928fdbb3d1b965b3dea0e12973' - 'ea2c799e490f3c478a821e00db9b4cd7183d703712f91a8b677de30d6639eb4c') + 'a17c433dfa32ab3c967b217b19ff80aca2cfc07b9179a915b2f5695f07b25b5e' + '055f026f07a61cb02071e3cd3f5a93f8b1daf4688c4eebf1d24d7a012e9a0932' + '23c107c34d4d71f10d1ee37c228a8492d737c563493f7b71a01cdbcf1a8a7244' + '63c90132ef46562733a672cf7fc538c2fa9605605f9ed0bc471d48936147bcaf' + 'f179f9a2385efee788715ad8acddd68b8cfeee0d79433e22d14341f417dd56da' + '750cc0491366373ceb0f24ab7ede555e4691855cee3c6e89a357169f56bf5981' + '7126649ec8af8127c28507b70cbf3f2ec125e496e98e3ea2a47e65f7234a2134') export KBUILD_BUILD_HOST=archlinux