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