diff --git a/configs/surface-5.8.config b/configs/surface-5.8.config index 7ddd53c52..58c48f845 100644 --- a/configs/surface-5.8.config +++ b/configs/surface-5.8.config @@ -5,7 +5,7 @@ CONFIG_GPIO_SYSFS=y # required for SURFACE_HOTPLUG CONFIG_SURFACE_AGGREGATOR=m CONFIG_SURFACE_AGGREGATOR_ERROR_INJECTION=n CONFIG_SURFACE_AGGREGATOR_BUS=y -CONFIG_SURFACE_AGGREGATOR_DEBUGFS=m +CONFIG_SURFACE_AGGREGATOR_CDEV=m CONFIG_SURFACE_AGGREGATOR_REGISTRY=m CONFIG_SURFACE_ACPI_NOTIFY=m CONFIG_SURFACE_BATTERY=m diff --git a/patches/5.8/0001-surface3-oemb.patch b/patches/5.8/0001-surface3-oemb.patch index ce1c5ffec..6b0a7592d 100644 --- a/patches/5.8/0001-surface3-oemb.patch +++ b/patches/5.8/0001-surface3-oemb.patch @@ -1,4 +1,4 @@ -From 625c331320f55988c07ca78eeed5a41cfc3dee90 Mon Sep 17 00:00:00 2001 +From a04d5c9616f422239b00675f1929f3392ba5b224 Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Tue, 18 Sep 2018 11:01:37 +0800 Subject: [PATCH 1/6] surface3-oemb diff --git a/patches/5.8/0002-wifi.patch b/patches/5.8/0002-wifi.patch index b18d4c0e1..640ec3e3f 100644 --- a/patches/5.8/0002-wifi.patch +++ b/patches/5.8/0002-wifi.patch @@ -1,16 +1,35 @@ -From 7010639e23288d951d63e63284f0cfc3ac35211a Mon Sep 17 00:00:00 2001 +From 9b82f30679795c4c1ca55d7095cdd67e6e06fcde 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/6] wifi --- - .../net/wireless/marvell/mwifiex/cfg80211.c | 26 ++++++ - drivers/net/wireless/marvell/mwifiex/fw.h | 2 +- - drivers/net/wireless/marvell/mwifiex/pcie.c | 84 +++++++++++-------- - .../net/wireless/marvell/mwifiex/sta_cmd.c | 31 ++----- - .../wireless/marvell/mwifiex/sta_cmdresp.c | 4 +- - 5 files changed, 87 insertions(+), 60 deletions(-) + drivers/net/wireless/marvell/mwifiex/Makefile | 1 + + .../net/wireless/marvell/mwifiex/cfg80211.c | 26 ++ + drivers/net/wireless/marvell/mwifiex/fw.h | 2 +- + 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 +- + .../wireless/marvell/mwifiex/sta_cmdresp.c | 4 +- + 10 files changed, 377 insertions(+), 35 deletions(-) + 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/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 4e4f59c17ded..528eedfbf41c 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -68,127 +87,136 @@ index 8047e307892e..d9f8bdbc817b 100644 } __packed; struct mwifiex_wapi_param { +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..3bdad5e80ecb 100644 +index 87b4ccca4b9a..696fa48c1ef5 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -146,38 +146,45 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) +@@ -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. -+ * -+ * XXX: ignoring all the above comment and just removes the card to -+ * fix S0ix and "AP scanning (sometimes) not working after suspend". -+ * Required code is extracted from mwifiex_pcie_remove(). +- * 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) { -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct pcie_service_card *card = pci_get_drvdata(pdev); - struct mwifiex_adapter *adapter; -- struct pcie_service_card *card = dev_get_drvdata(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); - -+ struct mwifiex_private *priv; -+ const struct mwifiex_pcie_card_reg *reg; -+ u32 fw_status; -+ int ret; - - /* Might still be loading firmware */ - wait_for_completion(&card->fw_done); - adapter = card->adapter; -- if (!adapter) { -- dev_err(dev, "adapter is not valid\n"); -+ if (!adapter || !adapter->priv_num) + if (!adapter) { + dev_err(dev, "adapter is not valid\n"); return 0; -- } + } - mwifiex_enable_wake(adapter); -+ reg = card->pcie.reg; -+ if (reg) -+ ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status); -+ else -+ fw_status = -1; - +- - /* Enable the Host Sleep */ - if (!mwifiex_enable_hs(adapter)) { -- mwifiex_dbg(adapter, ERROR, -- "cmd: failed to suspend\n"); ++ /* 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; -- } -+ if (fw_status == FIRMWARE_READY_PCIE && !adapter->mfg_mode) { -+ mwifiex_deauthenticate_all(adapter); + return -EFAULT; + } - flush_workqueue(adapter->workqueue); -+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); -+ -+ mwifiex_disable_auto_ds(priv); - -- /* Indicate device suspended */ -- set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); +- + /* Indicate device suspended */ + set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); - clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); -+ mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); -+ } -+ -+ mwifiex_remove_card(adapter); return 0; } -@@ -189,31 +196,35 @@ static int mwifiex_pcie_suspend(struct device *dev) +@@ -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. -+ * -+ * XXX: ignoring all the above comment and probes the card that was -+ * removed on suspend. Required code is extracted from mwifiex_pcie_probe(). +- * 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); -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct pcie_service_card *card = pci_get_drvdata(pdev); + struct mwifiex_adapter *adapter; + struct pcie_service_card *card = dev_get_drvdata(dev); + int ret; -+ pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", -+ pdev->vendor, pdev->device, pdev->revision); -- if (!card->adapter) { -- dev_err(dev, "adapter structure is not valid\n"); -- return 0; -- } -+ init_completion(&card->fw_done); + if (!card->adapter) { +@@ -211,9 +206,11 @@ static int mwifiex_pcie_resume(struct device *dev) -- adapter = card->adapter; -+ card->dev = pdev; + clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); -- if (!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { -- mwifiex_dbg(adapter, WARN, -- "Device already resumed\n"); -- return 0; -+ /* device tree node parsing and platform specific configuration */ -+ if (pdev->dev.of_node) { -+ ret = mwifiex_pcie_probe_of(&pdev->dev); -+ if (ret) -+ return ret; - } - -- 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); -+ if (mwifiex_add_card(card, &card->fw_done, &pcie_ops, -+ MWIFIEX_PCIE, &pdev->dev)) { -+ pr_err("%s failed\n", __func__); -+ return -1; -+ } ++ 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 +240,13 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, +@@ -229,8 +226,13 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pcie_service_card *card; @@ -202,27 +230,396 @@ index 87b4ccca4b9a..3bdad5e80ecb 100644 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); + 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) + dev_err(&pdev->dev, "reinit failed: %d\n", ret); + else + mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); ++ ++ card->pci_reset_ongoing = false; + } + + 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) + int ret; + u32 fw_status; + +- cancel_work_sync(&card->work); ++ /* Perform the cancel_work_sync() only when we're not resetting ++ * the card. It's because that function never returns if we're ++ * in reset path. If we're here when resetting the card, it means ++ * that we failed to reset the card (reset failure path). ++ */ ++ if (!card->pci_reset_ongoing) { ++ mwifiex_dbg(adapter, MSG, "performing cancel_work_sync()...\n"); ++ cancel_work_sync(&card->work); ++ mwifiex_dbg(adapter, MSG, "cancel_work_sync() done\n"); ++ } else { ++ mwifiex_dbg(adapter, MSG, ++ "skipped cancel_work_sync() because we're in card reset failure path\n"); ++ } + + 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 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.h ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.h +@@ -391,6 +391,9 @@ struct pcie_service_card { + struct mwifiex_msix_context share_irq_ctx; + struct work_struct work; + 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 +--- /dev/null ++++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +@@ -0,0 +1,255 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * File for PCIe quirks. ++ */ ++ ++/* The low-level PCI operations will be performed in this file. Therefore, ++ * let's use dev_*() instead of mwifiex_dbg() here to avoid troubles (e.g. ++ * to avoid using mwifiex_adapter struct before init or wifi is powered ++ * 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[] = { ++ { ++ .ident = "Surface Pro 4", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Pro 5", ++ .matches = { ++ /* match for SKU here due to generic product name "Surface Pro" */ ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Pro 5 (LTE)", ++ .matches = { ++ /* match for SKU here due to generic product name "Surface Pro" */ ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Pro 6", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Book 1", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Book 2", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Laptop 1", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface Laptop 2", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), ++ }, ++ .driver_data = (void *)QUIRK_FW_RST_D3COLD, ++ }, ++ { ++ .ident = "Surface 3", ++ .matches = { ++ 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, ++ }, ++ { ++ .ident = "Surface Pro 3", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 3"), ++ }, ++ .driver_data = 0, ++ }, ++ {} ++}; ++ ++void mwifiex_initialize_quirks(struct pcie_service_card *card) ++{ ++ struct pci_dev *pdev = card->dev; ++ const struct dmi_system_id *dmi_id; ++ ++ dmi_id = dmi_first_match(mwifiex_quirk_table); ++ if (dmi_id) ++ card->quirks = (uintptr_t)dmi_id->driver_data; ++ ++ 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) ++{ ++ dev_info(&pdev->dev, "putting into D3cold...\n"); ++ ++ pci_save_state(pdev); ++ if (pci_is_enabled(pdev)) ++ pci_disable_device(pdev); ++ pci_set_power_state(pdev, PCI_D3cold); ++} ++ ++static int mwifiex_pcie_set_power_d0(struct pci_dev *pdev) ++{ ++ int ret; ++ ++ dev_info(&pdev->dev, "putting into D0...\n"); ++ ++ pci_set_power_state(pdev, PCI_D0); ++ ret = pci_enable_device(pdev); ++ if (ret) { ++ dev_err(&pdev->dev, "pci_enable_device failed\n"); ++ return ret; ++ } ++ pci_restore_state(pdev); ++ ++ return 0; ++} ++ ++int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev) ++{ ++ struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); ++ int ret; ++ ++ /* Power-cycle (put into D3cold then D0) */ ++ dev_info(&pdev->dev, "Using reset_d3cold quirk to perform FW reset\n"); ++ ++ /* We need to perform power-cycle also for bridge of wifi because ++ * on some devices (e.g. Surface Book 1), the OS for some reasons ++ * can't know the real power state of the bridge. ++ * When tried to power-cycle only wifi, the reset failed with the ++ * following dmesg log: ++ * "Cannot transition to power state D0 for parent in D3hot". ++ */ ++ mwifiex_pcie_set_power_d3cold(pdev); ++ mwifiex_pcie_set_power_d3cold(parent_pdev); ++ ++ ret = mwifiex_pcie_set_power_d0(parent_pdev); ++ if (ret) ++ return ret; ++ ret = mwifiex_pcie_set_power_d0(pdev); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++int mwifiex_pcie_reset_wsid_quirk(struct pci_dev *pdev) ++{ ++ acpi_handle handle; ++ union acpi_object *obj; ++ acpi_status status; ++ ++ dev_info(&pdev->dev, "Using reset_wsid quirk to perform FW reset\n"); ++ ++ status = acpi_get_handle(NULL, ACPI_WSID_PATH, &handle); ++ if (ACPI_FAILURE(status)) { ++ dev_err(&pdev->dev, "No ACPI handle for path %s\n", ++ ACPI_WSID_PATH); ++ return -ENODEV; ++ } ++ ++ if (!acpi_has_method(handle, "_DSM")) { ++ dev_err(&pdev->dev, "_DSM method not found\n"); ++ return -ENODEV; ++ } ++ ++ if (!acpi_check_dsm(handle, &wsid_dsm_guid, ++ WSID_REV, WSID_FUNC_WIFI_PWR_OFF)) { ++ dev_err(&pdev->dev, ++ "_DSM method doesn't support wifi power off func\n"); ++ return -ENODEV; ++ } ++ ++ if (!acpi_check_dsm(handle, &wsid_dsm_guid, ++ WSID_REV, WSID_FUNC_WIFI_PWR_ON)) { ++ dev_err(&pdev->dev, ++ "_DSM method doesn't support wifi power on func\n"); ++ return -ENODEV; ++ } ++ ++ /* card will be removed immediately after this call on Surface 3 */ ++ dev_info(&pdev->dev, "turning wifi off...\n"); ++ obj = acpi_evaluate_dsm(handle, &wsid_dsm_guid, ++ WSID_REV, WSID_FUNC_WIFI_PWR_OFF, ++ NULL); ++ if (!obj) { ++ dev_err(&pdev->dev, ++ "device _DSM execution failed for turning wifi off\n"); ++ return -EIO; ++ } ++ ACPI_FREE(obj); ++ ++ /* card will be re-probed immediately after this call on Surface 3 */ ++ dev_info(&pdev->dev, "turning wifi on...\n"); ++ obj = acpi_evaluate_dsm(handle, &wsid_dsm_guid, ++ WSID_REV, WSID_FUNC_WIFI_PWR_ON, ++ NULL); ++ if (!obj) { ++ dev_err(&pdev->dev, ++ "device _DSM execution failed for turning wifi on\n"); ++ return -EIO; ++ } ++ ACPI_FREE(obj); ++ ++ 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 ++++ 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) ++/* 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); ++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..256c8c38deee 100644 +index 8bd355d7974e..484ba60b51ab 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -@@ -2247,7 +2247,6 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, - * - Function init (for first interface only) - * - Read MAC address (for first interface only) - * - Reconfigure Tx buffer size (for first interface only) -- * - Enable auto deep sleep (for first interface only) - * - Get Tx rate - * - Get Tx power - * - Set IBSS coalescing status -@@ -2260,7 +2259,6 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; -- struct mwifiex_ds_auto_ds auto_ds; - enum state_11d_t state_11d; - struct mwifiex_ds_11n_tx_cfg tx_cfg; - u8 sdio_sp_rx_aggr_enable; -@@ -2332,16 +2330,10 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) +@@ -2332,16 +2332,10 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) if (ret) return -1; @@ -243,28 +640,6 @@ index 8bd355d7974e..256c8c38deee 100644 if (drcs) { adapter->drcs_enabled = true; -@@ -2388,17 +2380,10 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) - if (ret) - return -1; - -- if (!disable_auto_ds && first_sta && -- priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { -- /* Enable auto deep sleep */ -- auto_ds.auto_ds = DEEP_SLEEP_ON; -- auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; -- ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, -- EN_AUTO_PS, BITMAP_AUTO_DS, -- &auto_ds, true); -- if (ret) -- return -1; -- } -+ /* Not enabling auto deep sleep (auto_ds) by default. Enabling -+ * this reportedly causes "suspend/resume fails when not connected -+ * to an Access Point." Therefore, the relevant code was removed -+ * from here. */ - - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Send cmd to FW to enable/disable 11D function */ diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index 962d8bfe6f10..119ccacd1fcc 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c diff --git a/patches/5.8/0003-ipts.patch b/patches/5.8/0003-ipts.patch index faa091f27..93588f333 100644 --- a/patches/5.8/0003-ipts.patch +++ b/patches/5.8/0003-ipts.patch @@ -1,4 +1,4 @@ -From eae15ff3bcf50d098fbda757e4dbd28e35798820 Mon Sep 17 00:00:00 2001 +From 5ad0753795c3808b796a78bd31efbe7c6edb031e Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 Subject: [PATCH 3/6] ipts diff --git a/patches/5.8/0004-surface-sam.patch b/patches/5.8/0004-surface-sam.patch index 9453c7262..bb0351c96 100644 --- a/patches/5.8/0004-surface-sam.patch +++ b/patches/5.8/0004-surface-sam.patch @@ -1,4 +1,4 @@ -From e49d12efb76b008886ac0a47e9b816671e950c82 Mon Sep 17 00:00:00 2001 +From b8af319992ad0b751fb116b9eeb4182c07b54b92 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Mon, 17 Aug 2020 01:23:20 +0200 Subject: [PATCH 4/6] surface-sam @@ -17,40 +17,41 @@ Subject: [PATCH 4/6] surface-sam .../driver-api/surface_aggregator/ssh.rst | 343 +++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + - drivers/misc/surface_aggregator/Kconfig | 65 + - drivers/misc/surface_aggregator/Makefile | 17 + - drivers/misc/surface_aggregator/bus.c | 419 +++ - drivers/misc/surface_aggregator/bus.h | 22 + - .../misc/surface_aggregator/clients/Kconfig | 154 + - .../misc/surface_aggregator/clients/Makefile | 11 + - .../clients/surface_acpi_notify.c | 847 ++++++ - .../clients/surface_aggregator_debugfs.c | 280 ++ - .../clients/surface_aggregator_registry.c | 645 +++++ - .../clients/surface_battery.c | 1096 +++++++ - .../surface_aggregator/clients/surface_dtx.c | 589 ++++ - .../surface_aggregator/clients/surface_hid.c | 493 ++++ - .../clients/surface_hotplug.c | 1282 +++++++++ - .../clients/surface_keyboard.c | 331 +++ - .../clients/surface_perfmode.c | 194 ++ - drivers/misc/surface_aggregator/controller.c | 2509 +++++++++++++++++ - drivers/misc/surface_aggregator/controller.h | 283 ++ - drivers/misc/surface_aggregator/core.c | 773 +++++ - drivers/misc/surface_aggregator/ssh_msgb.h | 196 ++ - .../surface_aggregator/ssh_packet_layer.c | 2002 +++++++++++++ - .../surface_aggregator/ssh_packet_layer.h | 170 ++ - drivers/misc/surface_aggregator/ssh_parser.c | 224 ++ - drivers/misc/surface_aggregator/ssh_parser.h | 152 + - .../surface_aggregator/ssh_request_layer.c | 1249 ++++++++ - .../surface_aggregator/ssh_request_layer.h | 137 + - drivers/misc/surface_aggregator/trace.h | 621 ++++ + drivers/misc/surface_aggregator/Kconfig | 67 + + drivers/misc/surface_aggregator/Makefile | 18 + + drivers/misc/surface_aggregator/bus.c | 424 +++ + drivers/misc/surface_aggregator/bus.h | 27 + + .../misc/surface_aggregator/clients/Kconfig | 155 + + .../misc/surface_aggregator/clients/Makefile | 12 + + .../clients/surface_acpi_notify.c | 884 ++++++ + .../clients/surface_aggregator_cdev.c | 228 ++ + .../clients/surface_aggregator_registry.c | 646 +++++ + .../clients/surface_battery.c | 1195 ++++++++ + .../surface_aggregator/clients/surface_dtx.c | 591 ++++ + .../surface_aggregator/clients/surface_hid.c | 495 ++++ + .../clients/surface_hotplug.c | 1285 +++++++++ + .../clients/surface_keyboard.c | 333 +++ + .../clients/surface_perfmode.c | 122 + + drivers/misc/surface_aggregator/controller.c | 2554 +++++++++++++++++ + drivers/misc/surface_aggregator/controller.h | 288 ++ + drivers/misc/surface_aggregator/core.c | 831 ++++++ + drivers/misc/surface_aggregator/ssh_msgb.h | 201 ++ + .../surface_aggregator/ssh_packet_layer.c | 2009 +++++++++++++ + .../surface_aggregator/ssh_packet_layer.h | 175 ++ + drivers/misc/surface_aggregator/ssh_parser.c | 229 ++ + drivers/misc/surface_aggregator/ssh_parser.h | 157 + + .../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/surface_acpi_notify.h | 37 + - include/linux/surface_aggregator/controller.h | 812 ++++++ - include/linux/surface_aggregator/device.h | 408 +++ - include/linux/surface_aggregator/serial_hub.h | 657 +++++ + include/linux/surface_acpi_notify.h | 39 + + include/linux/surface_aggregator/controller.h | 814 ++++++ + 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, 17911 insertions(+) + 47 files changed, 18183 insertions(+) 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/dbgdev.rst @@ -68,7 +69,7 @@ Subject: [PATCH 4/6] surface-sam create mode 100644 drivers/misc/surface_aggregator/clients/Kconfig create mode 100644 drivers/misc/surface_aggregator/clients/Makefile create mode 100644 drivers/misc/surface_aggregator/clients/surface_acpi_notify.c - create mode 100644 drivers/misc/surface_aggregator/clients/surface_aggregator_debugfs.c + create mode 100644 drivers/misc/surface_aggregator/clients/surface_aggregator_cdev.c create mode 100644 drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c create mode 100644 drivers/misc/surface_aggregator/clients/surface_battery.c create mode 100644 drivers/misc/surface_aggregator/clients/surface_dtx.c @@ -91,6 +92,7 @@ Subject: [PATCH 4/6] surface-sam create mode 100644 include/linux/surface_aggregator/controller.h 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 diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 6567187e7687..e36363f0972b 100644 @@ -1370,10 +1372,13 @@ index f97938d777e1..b0caee1dd3b7 100644 +obj-$(CONFIG_SURFACE_AGGREGATOR) += surface_aggregator/ diff --git a/drivers/misc/surface_aggregator/Kconfig b/drivers/misc/surface_aggregator/Kconfig new file mode 100644 -index 000000000000..e0a9bb37d178 +index 000000000000..6d33e3259a3d --- /dev/null +++ b/drivers/misc/surface_aggregator/Kconfig -@@ -0,0 +1,65 @@ +@@ -0,0 +1,67 @@ ++# SPDX-License-Identifier: GPL-2.0-or-later ++# Copyright (C) 2019-2020 Maximilian Luz ++ +menuconfig SURFACE_AGGREGATOR + tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers" + depends on SERIAL_DEV_BUS @@ -1427,7 +1432,6 @@ index 000000000000..e0a9bb37d178 + bool "Surface System Aggregator Module Error Injection Capabilities" + depends on SURFACE_AGGREGATOR + depends on FUNCTION_ERROR_INJECTION -+ default n + help + Provides error-injection capabilities for the Surface System + Aggregator Module subsystem and Surface Serial Hub driver. @@ -1441,11 +1445,12 @@ index 000000000000..e0a9bb37d178 +source "drivers/misc/surface_aggregator/clients/Kconfig" diff --git a/drivers/misc/surface_aggregator/Makefile b/drivers/misc/surface_aggregator/Makefile new file mode 100644 -index 000000000000..acf42597e6bb +index 000000000000..c48e58657116 --- /dev/null +++ b/drivers/misc/surface_aggregator/Makefile -@@ -0,0 +1,17 @@ +@@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later ++# Copyright (C) 2019-2020 Maximilian Luz + +# For include/trace/define_trace.h to include trace.h +CFLAGS_core.o = -I$(src) @@ -1464,11 +1469,16 @@ index 000000000000..acf42597e6bb +endif diff --git a/drivers/misc/surface_aggregator/bus.c b/drivers/misc/surface_aggregator/bus.c new file mode 100644 -index 000000000000..5581dca2eee2 +index 000000000000..efd72ce7a672 --- /dev/null +++ b/drivers/misc/surface_aggregator/bus.c -@@ -0,0 +1,419 @@ +@@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Surface System Aggregator Module bus and device integration. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#include +#include @@ -1609,7 +1619,7 @@ index 000000000000..5581dca2eee2 + */ + ssam_controller_statelock(sdev->ctrl); + -+ if (READ_ONCE(sdev->ctrl->state) != SSAM_CONTROLLER_STARTED) { ++ if (sdev->ctrl->state != SSAM_CONTROLLER_STARTED) { + ssam_controller_stateunlock(sdev->ctrl); + return -ENXIO; + } @@ -1889,11 +1899,16 @@ index 000000000000..5581dca2eee2 +} diff --git a/drivers/misc/surface_aggregator/bus.h b/drivers/misc/surface_aggregator/bus.h new file mode 100644 -index 000000000000..82c697b1e45f +index 000000000000..798471a9b369 --- /dev/null +++ b/drivers/misc/surface_aggregator/bus.h -@@ -0,0 +1,22 @@ +@@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Surface System Aggregator Module bus and device integration. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#ifndef _SURFACE_AGGREGATOR_BUS_H +#define _SURFACE_AGGREGATOR_BUS_H @@ -1917,25 +1932,26 @@ index 000000000000..82c697b1e45f +#endif /* _SURFACE_AGGREGATOR_BUS_H */ diff --git a/drivers/misc/surface_aggregator/clients/Kconfig b/drivers/misc/surface_aggregator/clients/Kconfig new file mode 100644 -index 000000000000..1642a43a61a9 +index 000000000000..82c6e7d48e17 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/Kconfig -@@ -0,0 +1,154 @@ -+config SURFACE_AGGREGATOR_DEBUGFS -+ tristate "Surface System Aggregator Module DebugFS interface" -+ depends on SURFACE_AGGREGATOR -+ depends on DEBUG_FS -+ default n -+ help -+ Provides a DebugFS interface to the Surface System Aggregator Module -+ (SSAM) controller. +@@ -0,0 +1,155 @@ ++# SPDX-License-Identifier: GPL-2.0-or-later ++# Copyright (C) 2019-2020 Maximilian Luz + -+ This option provides a module (called surface_aggregator_debugfs), -+ that, when loaded, will add a client device (and its respective -+ driver) to the SSAM controller. Said client device manages a DebugFS -+ interface (/sys/kernel/debug/surface_aggregator/controller), which can -+ be used by user-space tools to directly communicate with the SSAM EC -+ by sending requests and receiving the correspondign responses. ++config SURFACE_AGGREGATOR_CDEV ++ tristate "Surface System Aggregator Module User-Space Interface" ++ depends on SURFACE_AGGREGATOR ++ help ++ Provides a misc-device interface to the Surface System Aggregator ++ Module (SSAM) controller. ++ ++ This option provides a module (called surface_aggregator_cdev), that, ++ when loaded, will add a client device (and its respective driver) to ++ the SSAM controller. Said client device manages a misc-device ++ interface (/dev/surface/aggregator), which can be used by user-space ++ tools to directly communicate with the SSAM EC by sending requests and ++ receiving the correspondign responses. + + The provided interface is intended for debugging and development only, + and should not be used otherwise. @@ -2077,13 +2093,14 @@ index 000000000000..1642a43a61a9 + controlling said mode via the corresponding client device. diff --git a/drivers/misc/surface_aggregator/clients/Makefile b/drivers/misc/surface_aggregator/clients/Makefile new file mode 100644 -index 000000000000..a859fecae8d5 +index 000000000000..c7b1c20f865c --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/Makefile -@@ -0,0 +1,11 @@ +@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later ++# Copyright (C) 2019-2020 Maximilian Luz + -+obj-$(CONFIG_SURFACE_AGGREGATOR_DEBUGFS) += surface_aggregator_debugfs.o ++obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o +obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o +obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o +obj-$(CONFIG_SURFACE_BATTERY) += surface_battery.o @@ -2094,10 +2111,10 @@ index 000000000000..a859fecae8d5 +obj-$(CONFIG_SURFACE_PERFMODE) += surface_perfmode.o diff --git a/drivers/misc/surface_aggregator/clients/surface_acpi_notify.c b/drivers/misc/surface_aggregator/clients/surface_acpi_notify.c new file mode 100644 -index 000000000000..10a623d1afda +index 000000000000..1859db6b7323 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_acpi_notify.c -@@ -0,0 +1,847 @@ +@@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the Surface ACPI Notify (SAN) interface/shim. @@ -2107,6 +2124,8 @@ index 000000000000..10a623d1afda + * events back to ACPI notifications. Allows handling of discrete GPU + * notifications sent from ACPI via the SAN interface by providing them to any + * registered external driver. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -2769,10 +2788,6 @@ index 000000000000..10a623d1afda + +/* -- Driver setup. --------------------------------------------------------- */ + -+struct san_acpi_consumer { -+ const char *path; -+}; -+ +static int san_events_register(struct platform_device *pdev) +{ + struct san_data *d = platform_get_drvdata(pdev); @@ -2813,38 +2828,92 @@ index 000000000000..10a623d1afda + ssam_notifier_unregister(d->ctrl, &d->nf_tmp); +} + -+static int san_consumers_link(struct platform_device *pdev, -+ const struct san_acpi_consumer *cons) ++#define san_consumer_printk(level, dev, handle, fmt, ...) \ ++do { \ ++ char *path = ""; \ ++ struct acpi_buffer buffer = { \ ++ .length = ACPI_ALLOCATE_BUFFER, \ ++ .pointer = NULL, \ ++ }; \ ++ \ ++ if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) \ ++ path = buffer.pointer; \ ++ \ ++ dev_##level(dev, "[%s]: " fmt, path, ##__VA_ARGS__); \ ++ kfree(buffer.pointer); \ ++} while (0) ++ ++#define san_consumer_dbg(dev, handle, fmt, ...) \ ++ san_consumer_printk(dbg, dev, handle, fmt, ##__VA_ARGS__) ++ ++#define san_consumer_warn(dev, handle, fmt, ...) \ ++ san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__) ++ ++static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle) +{ -+ const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER; -+ const struct san_acpi_consumer *c; ++ struct acpi_handle_list dep_devices; ++ acpi_handle supplier = ACPI_HANDLE(&pdev->dev); ++ acpi_status status; ++ int i; + -+ for (c = cons; c && c->path; ++c) { -+ struct acpi_device *adev; -+ acpi_handle handle; -+ acpi_status status; -+ int ret; ++ if (!acpi_has_method(handle, "_DEP")) ++ return false; + -+ status = acpi_get_handle(NULL, (acpi_string)c->path, &handle); -+ if (status == AE_NOT_FOUND) -+ continue; -+ else if (ACPI_FAILURE(status)) -+ return -ENXIO; -+ -+ ret = acpi_bus_get_device(handle, &adev); -+ if (ret) -+ return ret; -+ -+ if (!device_link_add(&adev->dev, &pdev->dev, flags)) -+ return -EFAULT; ++ status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); ++ if (ACPI_FAILURE(status)) { ++ san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n"); ++ return false; + } + -+ return 0; ++ for (i = 0; i < dep_devices.count; i++) { ++ if (dep_devices.handles[i] == supplier) ++ return true; ++ } ++ ++ return false; ++} ++ ++static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl, ++ void *context, void **rv) ++{ ++ const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER; ++ struct platform_device *pdev = context; ++ struct acpi_device *adev; ++ struct device_link *link; ++ ++ if (!is_san_consumer(pdev, handle)) ++ return AE_OK; ++ ++ // ignore ACPI devices that are not present ++ if (acpi_bus_get_device(handle, &adev) != 0) ++ return AE_OK; ++ ++ san_consumer_dbg(&pdev->dev, handle, "creating device link\n"); ++ ++ // try to set up device links, ignore but log errors ++ link = device_link_add(&adev->dev, &pdev->dev, flags); ++ if (!link) { ++ san_consumer_warn(&pdev->dev, handle, ++ "failed to create device link\n"); ++ return AE_OK; ++ } ++ ++ return AE_OK; ++} ++ ++static int san_consumer_links_setup(struct platform_device *pdev) ++{ ++ acpi_status status; ++ ++ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ++ ACPI_UINT32_MAX, san_consumer_setup, NULL, ++ pdev, NULL); ++ ++ return status ? -EFAULT : 0; +} + +static int san_probe(struct platform_device *pdev) +{ -+ const struct san_acpi_consumer *cons; + acpi_handle san = ACPI_HANDLE(&pdev->dev); + struct ssam_controller *ctrl; + struct san_data *data; @@ -2855,12 +2924,9 @@ index 000000000000..10a623d1afda + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + -+ cons = acpi_device_get_match_data(&pdev->dev); -+ if (cons) { -+ status = san_consumers_link(pdev, cons); -+ if (status) -+ return status; -+ } ++ status = san_consumer_links_setup(pdev); ++ if (status) ++ return status; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) @@ -2913,20 +2979,8 @@ index 000000000000..10a623d1afda + return 0; +} + -+/* -+ * ACPI devices that make use of the SAM EC via the SAN interface. Link them -+ * to the SAN device to try and enforce correct suspend/resume orderding. -+ */ -+static const struct san_acpi_consumer san_mshw0091_consumers[] = { -+ { "\\_SB.SRTC" }, -+ { "\\ADP1" }, -+ { "\\_SB.BAT1" }, -+ { "\\_SB.BAT2" }, -+ { }, -+}; -+ +static const struct acpi_device_id san_match[] = { -+ { "MSHW0091", (unsigned long) san_mshw0091_consumers }, ++ { "MSHW0091" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, san_match); @@ -2945,97 +2999,65 @@ index 000000000000..10a623d1afda +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module"); +MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/surface_aggregator/clients/surface_aggregator_debugfs.c b/drivers/misc/surface_aggregator/clients/surface_aggregator_debugfs.c +diff --git a/drivers/misc/surface_aggregator/clients/surface_aggregator_cdev.c b/drivers/misc/surface_aggregator/clients/surface_aggregator_cdev.c new file mode 100644 -index 000000000000..9bbfe724a653 +index 000000000000..287aff6129f6 --- /dev/null -+++ b/drivers/misc/surface_aggregator/clients/surface_aggregator_debugfs.c -@@ -0,0 +1,280 @@ ++++ b/drivers/misc/surface_aggregator/clients/surface_aggregator_cdev.c +@@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* -+ * DebugFS interface for Surface System Aggregator Module (SSAM) controller -+ * access from user-space. Intended for debugging and development. ++ * Provides user-space access to the SSAM EC via the /dev/surface/aggregator ++ * misc device. Intended for debugging and development. ++ * ++ * Copyright (C) 2020 Maximilian Luz + */ + -+#include ++#include +#include ++#include +#include +#include +#include +#include + ++#include +#include + -+#define SSAM_DBG_DEVICE_NAME "surface_aggregator_dbg" -+#define SSAM_DBG_IF_VERSION 0x010000 ++#define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev" + -+/** -+ * struct ssam_debug_request - Controller request IOCTL argument. -+ * @target_category: Target category of the SAM request. -+ * @target_id: Target ID of the SAM request. -+ * @command_id: Command ID of the SAM request. -+ * @instance_id: Instance ID of the SAM request. -+ * @flags: SAM Request flags. -+ * @status: Request status (output). -+ * @payload: Request payload (input data). -+ * @payload.data: Pointer to request payload data. -+ * @payload.length: Length of request payload data (in bytes). -+ * @response: Request response (output data). -+ * @response.data: Pointer to response buffer. -+ * @response.length: On input: Capacity of response buffer (in bytes). -+ * On output: Length of request response (number of bytes -+ * in the buffer that are actually used). -+ */ -+struct ssam_dbg_request { -+ __u8 target_category; -+ __u8 target_id; -+ __u8 command_id; -+ __u8 instance_id; -+ __u16 flags; -+ __s16 status; -+ -+ struct { -+ const __u8 __user *data; -+ __u16 length; -+ __u8 __pad[6]; -+ } payload; -+ -+ struct { -+ __u8 __user *data; -+ __u16 length; -+ __u8 __pad[6]; -+ } response; -+}; -+ -+#define SSAM_DBG_IOCTL_GETVERSION _IOR(0xA5, 0, __u32) -+#define SSAM_DBG_IOCTL_REQUEST _IOWR(0xA5, 1, struct ssam_dbg_request) -+ -+struct ssam_dbg_data { ++struct ssam_cdev { + struct ssam_controller *ctrl; -+ struct dentry *dentry_dir; -+ struct dentry *dentry_dev; ++ struct miscdevice mdev; +}; + -+static int ssam_dbg_device_open(struct inode *inode, struct file *filp) ++static int ssam_cdev_device_open(struct inode *inode, struct file *filp) +{ -+ filp->private_data = inode->i_private; ++ struct miscdevice *mdev = filp->private_data; ++ ++ filp->private_data = container_of(mdev, struct ssam_cdev, mdev); + return nonseekable_open(inode, filp); +} + -+static long ssam_dbg_if_request(struct file *file, unsigned long arg) ++static long ssam_cdev_request(struct file *file, unsigned long arg) +{ -+ struct ssam_dbg_data *data = file->private_data; -+ struct ssam_dbg_request __user *r; -+ struct ssam_dbg_request rqst; ++ struct ssam_cdev *cdev = file->private_data; ++ struct ssam_cdev_request __user *r; ++ struct ssam_cdev_request rqst; + struct ssam_request spec; + struct ssam_response rsp; ++ const void __user *plddata; ++ void __user *rspdata; + int status = 0, ret = 0, tmp; + -+ r = (struct ssam_dbg_request __user *)arg; ++ r = (struct ssam_cdev_request __user *)arg; + ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r)); + if (ret) + goto out; + ++ plddata = u64_to_user_ptr(rqst.payload.data); ++ rspdata = u64_to_user_ptr(rqst.response.data); ++ + // setup basic request fields + spec.target_category = rqst.target_category; + spec.target_id = rqst.target_id; @@ -3051,7 +3073,7 @@ index 000000000000..9bbfe724a653 + + // get request payload from user-space + if (spec.length) { -+ if (!rqst.payload.data) { ++ if (!plddata) { + ret = -EINVAL; + goto out; + } @@ -3063,8 +3085,7 @@ index 000000000000..9bbfe724a653 + goto out; + } + -+ if (copy_from_user((void *)spec.payload, rqst.payload.data, -+ spec.length)) { ++ if (copy_from_user((void *)spec.payload, plddata, spec.length)) { + ret = -EFAULT; + goto out; + } @@ -3072,7 +3093,7 @@ index 000000000000..9bbfe724a653 + + // allocate response buffer + if (rsp.capacity) { -+ if (!rqst.response.data) { ++ if (!rspdata) { + ret = -EINVAL; + goto out; + } @@ -3086,26 +3107,22 @@ index 000000000000..9bbfe724a653 + } + + // perform request -+ status = ssam_request_sync(data->ctrl, &spec, &rsp); ++ status = ssam_request_sync(cdev->ctrl, &spec, &rsp); + if (status) + goto out; + + // copy response to user-space -+ if (rsp.length) { -+ if (copy_to_user(rqst.response.data, rsp.pointer, rsp.length)) { -+ ret = -EFAULT; -+ goto out; -+ } -+ } ++ if (rsp.length && copy_to_user(rspdata, rsp.pointer, rsp.length)) ++ ret = -EFAULT; + +out: + // always try to set response-length and status + tmp = put_user(rsp.length, &r->response.length); -+ if (!ret) ++ if (tmp) + ret = tmp; + + tmp = put_user(status, &r->status); -+ if (!ret) ++ if (tmp) + ret = tmp; + + // cleanup @@ -3115,93 +3132,67 @@ index 000000000000..9bbfe724a653 + return ret; +} + -+static long ssam_dbg_if_getversion(struct file *file, unsigned long arg) -+{ -+ put_user(SSAM_DBG_IF_VERSION, (u32 __user *)arg); -+ return 0; -+} -+ -+static long ssam_dbg_device_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) ++static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) +{ + switch (cmd) { -+ case SSAM_DBG_IOCTL_GETVERSION: -+ return ssam_dbg_if_getversion(file, arg); -+ -+ case SSAM_DBG_IOCTL_REQUEST: -+ return ssam_dbg_if_request(file, arg); ++ case SSAM_CDEV_REQUEST: ++ return ssam_cdev_request(file, arg); + + default: -+ return -ENOIOCTLCMD; ++ return -ENOTTY; + } +} + -+const struct file_operations ssam_dbg_device_fops = { ++static const struct file_operations ssam_controller_fops = { + .owner = THIS_MODULE, -+ .open = ssam_dbg_device_open, -+ .unlocked_ioctl = ssam_dbg_device_ioctl, -+ .compat_ioctl = ssam_dbg_device_ioctl, ++ .open = ssam_cdev_device_open, ++ .unlocked_ioctl = ssam_cdev_device_ioctl, ++ .compat_ioctl = ssam_cdev_device_ioctl, + .llseek = noop_llseek, +}; + +static int ssam_dbg_device_probe(struct platform_device *pdev) +{ -+ struct ssam_dbg_data *data; + struct ssam_controller *ctrl; ++ struct ssam_cdev *cdev; + int status; + + status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + -+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); -+ if (!data) ++ cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL); ++ if (!cdev) + return -ENOMEM; + -+ data->ctrl = ctrl; ++ cdev->ctrl = ctrl; ++ cdev->mdev.parent = &pdev->dev; ++ cdev->mdev.minor = MISC_DYNAMIC_MINOR; ++ cdev->mdev.name = "surface_aggregator"; ++ cdev->mdev.nodename = "surface/aggregator"; ++ cdev->mdev.fops = &ssam_controller_fops; + -+ data->dentry_dir = debugfs_create_dir("surface_aggregator", NULL); -+ if (IS_ERR(data->dentry_dir)) -+ return PTR_ERR(data->dentry_dir); -+ -+ data->dentry_dev = debugfs_create_file("controller", 0600, -+ data->dentry_dir, data, -+ &ssam_dbg_device_fops); -+ if (IS_ERR(data->dentry_dev)) { -+ debugfs_remove(data->dentry_dir); -+ return PTR_ERR(data->dentry_dev); -+ } -+ -+ platform_set_drvdata(pdev, data); -+ return 0; ++ platform_set_drvdata(pdev, cdev); ++ return misc_register(&cdev->mdev); +} + +static int ssam_dbg_device_remove(struct platform_device *pdev) +{ -+ struct ssam_dbg_data *data = platform_get_drvdata(pdev); -+ -+ debugfs_remove(data->dentry_dev); -+ debugfs_remove(data->dentry_dir); ++ struct ssam_cdev *cdev = platform_get_drvdata(pdev); + ++ misc_deregister(&cdev->mdev); + return 0; +} + -+static void ssam_dbg_device_release(struct device *dev) -+{ -+ // nothing to do -+} ++static struct platform_device *ssam_cdev_device; + -+static struct platform_device ssam_dbg_device = { -+ .name = SSAM_DBG_DEVICE_NAME, -+ .id = PLATFORM_DEVID_NONE, -+ .dev.release = ssam_dbg_device_release, -+}; -+ -+static struct platform_driver ssam_dbg_driver = { ++static struct platform_driver ssam_cdev_driver = { + .probe = ssam_dbg_device_probe, + .remove = ssam_dbg_device_remove, + .driver = { -+ .name = SSAM_DBG_DEVICE_NAME, ++ .name = SSAM_CDEV_DEVICE_NAME, ++ .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + @@ -3209,40 +3200,53 @@ index 000000000000..9bbfe724a653 +{ + int status; + -+ status = platform_device_register(&ssam_dbg_device); -+ if (status) -+ return status; ++ ssam_cdev_device = platform_device_alloc(SSAM_CDEV_DEVICE_NAME, ++ PLATFORM_DEVID_NONE); ++ if (!ssam_cdev_device) ++ return -ENOMEM; + -+ status = platform_driver_register(&ssam_dbg_driver); ++ status = platform_device_add(ssam_cdev_device); + if (status) -+ platform_device_unregister(&ssam_dbg_device); ++ goto err_device; + ++ status = platform_driver_register(&ssam_cdev_driver); ++ if (status) ++ goto err_driver; ++ ++ return 0; ++ ++err_driver: ++ platform_device_del(ssam_cdev_device); ++err_device: ++ platform_device_put(ssam_cdev_device); + return status; +} +module_init(ssam_debug_init); + +static void __exit ssam_debug_exit(void) +{ -+ platform_driver_unregister(&ssam_dbg_driver); -+ platform_device_unregister(&ssam_dbg_device); ++ platform_driver_unregister(&ssam_cdev_driver); ++ platform_device_unregister(ssam_cdev_device); +} +module_exit(ssam_debug_exit); + +MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("DebugFS interface for Surface System Aggregator Module"); ++MODULE_DESCRIPTION("User-space interface for Surface System Aggregator Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c b/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c new file mode 100644 -index 000000000000..b1eb3ff6964b +index 000000000000..fc4b2b4e76fd --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c -@@ -0,0 +1,645 @@ +@@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface System Aggregator Module (SSAM) client device registry. + * + * Registry for non-platform/non-ACPI SSAM client devices, i.e. devices that + * cannot be auto-detected. Provides device-hubs for these devices. ++ * ++ * Copyright (C) 2020 Maximilian Luz + */ + +#include @@ -3682,7 +3686,6 @@ index 000000000000..b1eb3ff6964b + hub->sdev = sdev; + hub->state = SSAM_BASE_HUB_UNINITIALIZED; + -+ // TODO: still need to verify registry + hub->notif.base.priority = 1000; // this notifier should run first + hub->notif.base.fn = ssam_base_hub_notif; + hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; @@ -3807,7 +3810,7 @@ index 000000000000..b1eb3ff6964b + + root = software_node_fwnode(&ssam_node_root); + if (!root) -+ return -EFAULT; ++ return -ENOENT; + + set_secondary_fwnode(&pdev->dev, root); + @@ -3884,16 +3887,18 @@ index 000000000000..b1eb3ff6964b +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_battery.c b/drivers/misc/surface_aggregator/clients/surface_battery.c new file mode 100644 -index 000000000000..b5cddd3cc35a +index 000000000000..4c78b054774b --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_battery.c -@@ -0,0 +1,1096 @@ +@@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface battery and AC device driver. + * + * Provides support for battery and AC devices connected via the Surface + * System Aggregator Module. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -3907,38 +3912,46 @@ index 000000000000..b5cddd3cc35a + +#include + ++#define SPWR_RETRY 3 ++#define spwr_retry(fn, args...) ssam_retry(fn, SPWR_RETRY, args) + -+// TODO: check BIX/BST for unknown/unsupported 0xffffffff entries -+// TODO: DPTF (/SAN notifications)? -+// TODO: other properties? ++#define SPWR_AC_BAT_UPDATE_DELAY msecs_to_jiffies(5000) + + ++/* -- Module parameters. ---------------------------------------------------- */ ++ +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); +MODULE_PARM_DESC(cache_time, "battery state chaching time in milliseconds [default: 1000]"); + -+#define SPWR_AC_BAT_UPDATE_DELAY msecs_to_jiffies(5000) + ++/* -- SAM Interface. -------------------------------------------------------- */ + -+/* -+ * SAM Interface. -+ */ ++enum sam_event_cid_bat { ++ SAM_EVENT_CID_BAT_BIX = 0x15, ++ SAM_EVENT_CID_BAT_BST = 0x16, ++ SAM_EVENT_CID_BAT_ADP = 0x17, ++ SAM_EVENT_CID_BAT_PROT = 0x18, ++ SAM_EVENT_CID_BAT_DPTF = 0x53, ++}; + -+#define SAM_EVENT_PWR_CID_BIX 0x15 -+#define SAM_EVENT_PWR_CID_BST 0x16 -+#define SAM_EVENT_PWR_CID_ADAPTER 0x17 ++enum sam_battery_sta { ++ SAM_BATTERY_STA_OK = 0x0f, ++ SAM_BATTERY_STA_PRESENT = 0x10, ++}; + -+#define SAM_BATTERY_STA_OK 0x0f -+#define SAM_BATTERY_STA_PRESENT 0x10 ++enum sam_battery_state { ++ SAM_BATTERY_STATE_DISCHARGING = BIT(0), ++ SAM_BATTERY_STATE_CHARGING = BIT(1), ++ SAM_BATTERY_STATE_CRITICAL = BIT(2), ++}; + -+#define SAM_BATTERY_STATE_DISCHARGING 0x01 -+#define SAM_BATTERY_STATE_CHARGING 0x02 -+#define SAM_BATTERY_STATE_CRITICAL 0x04 ++enum sam_battery_power_unit { ++ SAM_BATTERY_POWER_UNIT_mW = 0, ++ SAM_BATTERY_POWER_UNIT_mA = 1, ++}; + -+#define SAM_BATTERY_POWER_UNIT_MA 1 -+ -+ -+/* Equivalent to data returned in ACPI _BIX method */ ++/* Equivalent to data returned in ACPI _BIX method, revision 0 */ +struct spwr_bix { + u8 revision; + __le32 power_unit; @@ -3956,12 +3969,16 @@ index 000000000000..b5cddd3cc35a + __le32 min_avg_interval; + __le32 bat_cap_granularity_1; + __le32 bat_cap_granularity_2; -+ u8 model[21]; -+ u8 serial[11]; -+ u8 type[5]; -+ u8 oem_info[21]; ++ u8 model[21]; ++ u8 serial[11]; ++ u8 type[5]; ++ u8 oem_info[21]; +} __packed; + ++static_assert(sizeof(struct spwr_bix) == 119); ++ ++#define SPWR_BIX_REVISION 0 ++ +/* Equivalent to data returned in ACPI _BST method */ +struct spwr_bst { + __le32 state; @@ -3970,13 +3987,9 @@ index 000000000000..b5cddd3cc35a + __le32 present_voltage; +} __packed; + -+/* DPTF event payload */ -+struct spwr_event_dptf { -+ __le32 pmax; -+ __le32 _1; /* currently unknown */ -+ __le32 _2; /* currently unknown */ -+} __packed; ++static_assert(sizeof(struct spwr_bst) == 16); + ++#define SPWR_BATTERY_VALUE_UNKNOWN 0xffffffff + +/* Get battery status (_STA) */ +static SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, { @@ -4008,6 +4021,11 @@ index 000000000000..b5cddd3cc35a + .command_id = 0x0d, +}); + ++/* ++ * The following requests are currently unused. They are nevertheless included ++ * for documentation of the SAM interface. ++ */ ++ +/* Get maximum platform power for battery (DPTF PMAX) */ +__always_unused +static SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_pmax, __le32, { @@ -4037,9 +4055,7 @@ index 000000000000..b5cddd3cc35a +}); + + -+/* -+ * Common Power-Subsystem Interface. -+ */ ++/* -- Common Power-Subsystem Interface. ------------------------------------- */ + +struct spwr_psy_properties { + const char *name; @@ -4120,31 +4136,32 @@ index 000000000000..b5cddd3cc35a + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + -+ -+static int spwr_battery_register(struct spwr_battery_device *bat, -+ struct ssam_device *sdev, -+ struct ssam_event_registry registry); -+ -+static void spwr_battery_unregister(struct spwr_battery_device *bat); -+ -+ +static bool spwr_battery_present(struct spwr_battery_device *bat) +{ + return le32_to_cpu(bat->sta) & SAM_BATTERY_STA_PRESENT; +} + -+ +static int spwr_battery_load_sta(struct spwr_battery_device *bat) +{ -+ return ssam_bat_get_sta(bat->sdev, &bat->sta); ++ return spwr_retry(ssam_bat_get_sta, bat->sdev, &bat->sta); +} + +static int spwr_battery_load_bix(struct spwr_battery_device *bat) +{ ++ int status; ++ + if (!spwr_battery_present(bat)) + return 0; + -+ return ssam_bat_get_bix(bat->sdev, &bat->bix); ++ status = spwr_retry(ssam_bat_get_bix, bat->sdev, &bat->bix); ++ ++ // enforce NULL terminated strings in case anything goes wrong... ++ bat->bix.model[ARRAY_SIZE(bat->bix.model) - 1] = 0; ++ bat->bix.serial[ARRAY_SIZE(bat->bix.serial) - 1] = 0; ++ bat->bix.type[ARRAY_SIZE(bat->bix.type) - 1] = 0; ++ bat->bix.oem_info[ARRAY_SIZE(bat->bix.oem_info) - 1] = 0; ++ ++ return status; +} + +static int spwr_battery_load_bst(struct spwr_battery_device *bat) @@ -4152,17 +4169,16 @@ index 000000000000..b5cddd3cc35a + if (!spwr_battery_present(bat)) + return 0; + -+ return ssam_bat_get_bst(bat->sdev, &bat->bst); ++ return spwr_retry(ssam_bat_get_bst, bat->sdev, &bat->bst); +} + -+ +static int spwr_battery_set_alarm_unlocked(struct spwr_battery_device *bat, + u32 value) +{ -+ __le32 alarm = cpu_to_le32(value); ++ __le32 value_le = cpu_to_le32(value); + + bat->alarm = value; -+ return ssam_bat_set_btp(bat->sdev, &alarm); ++ return spwr_retry(ssam_bat_set_btp, bat->sdev, &value_le); +} + +static int spwr_battery_set_alarm(struct spwr_battery_device *bat, u32 value) @@ -4183,7 +4199,6 @@ index 000000000000..b5cddd3cc35a + int status; + + cache_deadline = bat->timestamp + msecs_to_jiffies(cache_time); -+ + if (cached && bat->timestamp && time_is_after_jiffies(cache_deadline)) + return 0; + @@ -4226,27 +4241,21 @@ index 000000000000..b5cddd3cc35a + if (status) + return status; + ++ if (bat->bix.revision != SPWR_BIX_REVISION) { ++ dev_warn(&bat->sdev->dev, "unsupported battery revision: %u\n", ++ bat->bix.revision); ++ } ++ + bat->timestamp = jiffies; + return 0; +} + -+static int spwr_battery_update_bix(struct spwr_battery_device *bat) -+{ -+ int status; -+ -+ mutex_lock(&bat->lock); -+ status = spwr_battery_update_bix_unlocked(bat); -+ mutex_unlock(&bat->lock); -+ -+ return status; -+} -+ +static int spwr_ac_update_unlocked(struct spwr_ac_device *ac) +{ + int status; + u32 old = ac->state; + -+ status = ssam_bat_get_psrc(ac->sdev, &ac->state); ++ status = spwr_retry(ssam_bat_get_psrc, ac->sdev, &ac->state); + if (status < 0) + return status; + @@ -4264,48 +4273,68 @@ index 000000000000..b5cddd3cc35a + return status; +} + -+ -+static int spwr_battery_recheck(struct spwr_battery_device *bat) ++static u32 sprw_battery_get_full_cap_safe(struct spwr_battery_device *bat) +{ -+ bool present = spwr_battery_present(bat); -+ u32 unit = get_unaligned_le32(&bat->bix.power_unit); ++ u32 full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ ++ if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN) ++ full_cap = get_unaligned_le32(&bat->bix.design_cap); ++ ++ return full_cap; ++} ++ ++static bool spwr_battery_is_full(struct spwr_battery_device *bat) ++{ ++ u32 state = get_unaligned_le32(&bat->bst.state); ++ u32 full_cap = sprw_battery_get_full_cap_safe(bat); ++ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); ++ ++ return full_cap != SPWR_BATTERY_VALUE_UNKNOWN && full_cap != 0 ++ && remaining_cap != SPWR_BATTERY_VALUE_UNKNOWN ++ && remaining_cap >= full_cap ++ && state == 0; ++} ++ ++static int spwr_battery_recheck_full(struct spwr_battery_device *bat) ++{ ++ bool present; ++ u32 unit; + int status; + -+ status = spwr_battery_update_bix(bat); ++ mutex_lock(&bat->lock); ++ unit = get_unaligned_le32(&bat->bix.power_unit); ++ present = spwr_battery_present(bat); ++ ++ status = spwr_battery_update_bix_unlocked(bat); + if (status) -+ return status; ++ goto out; + + // if battery has been attached, (re-)initialize alarm + if (!present && spwr_battery_present(bat)) { + u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn); -+ status = spwr_battery_set_alarm(bat, cap_warn); ++ ++ status = spwr_battery_set_alarm_unlocked(bat, cap_warn); + if (status) -+ return status; ++ goto out; + } + -+ // if the unit has changed, re-add the battery -+ if (unit != get_unaligned_le32(&bat->bix.power_unit)) { -+ spwr_battery_unregister(bat); -+ status = spwr_battery_register(bat, bat->sdev, -+ bat->notif.event.reg); -+ } ++ /* ++ * Warn if the unit has changed. This is something we genuinely don't ++ * expect to happen, so make this a big warning. If it does, we'll ++ * need to add support for it. ++ */ ++ WARN_ON(unit != get_unaligned_le32(&bat->bix.power_unit)); + -+ return status; -+} ++out: ++ mutex_unlock(&bat->lock); + -+ -+static int spwr_notify_bix(struct spwr_battery_device *bat) -+{ -+ int status; -+ -+ status = spwr_battery_recheck(bat); + if (!status) + power_supply_changed(bat->psy); + + return status; +} + -+static int spwr_notify_bst(struct spwr_battery_device *bat) ++static int spwr_battery_recheck_status(struct spwr_battery_device *bat) +{ + int status; + @@ -4316,11 +4345,17 @@ index 000000000000..b5cddd3cc35a + return status; +} + -+static int spwr_notify_adapter_bat(struct spwr_battery_device *bat) ++static int spwr_battery_recheck_adapter(struct spwr_battery_device *bat) +{ -+ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ u32 full_cap = sprw_battery_get_full_cap_safe(bat); + u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); + ++ if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN) ++ return 0; ++ ++ if (remaining_cap == SPWR_BATTERY_VALUE_UNKNOWN) ++ return 0; ++ + /* + * Handle battery update quirk: + * When the battery is fully charged and the adapter is plugged in or @@ -4329,13 +4364,13 @@ index 000000000000..b5cddd3cc35a + * the state is updated on the battery. Schedule an update to solve this. + */ + -+ if (remaining_cap >= last_full_cap) ++ if (remaining_cap >= full_cap) + schedule_delayed_work(&bat->update_work, SPWR_AC_BAT_UPDATE_DELAY); + + return 0; +} + -+static int spwr_notify_adapter_ac(struct spwr_ac_device *ac) ++static int spwr_ac_recheck(struct spwr_ac_device *ac) +{ + int status; + @@ -4358,8 +4393,8 @@ index 000000000000..b5cddd3cc35a + event->command_id, event->instance_id, event->target_id); + + // handled here, needs to be handled for all targets/instances -+ if (event->command_id == SAM_EVENT_PWR_CID_ADAPTER) { -+ status = spwr_notify_adapter_bat(bat); ++ if (event->command_id == SAM_EVENT_CID_BAT_ADP) { ++ status = spwr_battery_recheck_adapter(bat); + return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; + } + @@ -4370,12 +4405,27 @@ index 000000000000..b5cddd3cc35a + return 0; + + switch (event->command_id) { -+ case SAM_EVENT_PWR_CID_BIX: -+ status = spwr_notify_bix(bat); ++ case SAM_EVENT_CID_BAT_BIX: ++ status = spwr_battery_recheck_full(bat); + break; + -+ case SAM_EVENT_PWR_CID_BST: -+ status = spwr_notify_bst(bat); ++ case SAM_EVENT_CID_BAT_BST: ++ status = spwr_battery_recheck_status(bat); ++ break; ++ ++ case SAM_EVENT_CID_BAT_PROT: ++ /* ++ * TODO: Implement support for battery protection status change ++ * event. ++ */ ++ status = 0; ++ break; ++ ++ case SAM_EVENT_CID_BAT_DPTF: ++ /* ++ * TODO: Implement support for DPTF event. ++ */ ++ status = 0; + break; + + default: @@ -4406,8 +4456,8 @@ index 000000000000..b5cddd3cc35a + */ + + switch (event->command_id) { -+ case SAM_EVENT_PWR_CID_ADAPTER: -+ status = spwr_notify_adapter_ac(ac); ++ case SAM_EVENT_CID_BAT_ADP: ++ status = spwr_ac_recheck(ac); + return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; + + default: @@ -4433,12 +4483,9 @@ index 000000000000..b5cddd3cc35a + } +} + -+ +static int spwr_battery_prop_status(struct spwr_battery_device *bat) +{ + u32 state = get_unaligned_le32(&bat->bst.state); -+ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); -+ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); + u32 present_rate = get_unaligned_le32(&bat->bst.present_rate); + + if (state & SAM_BATTERY_STATE_DISCHARGING) @@ -4447,7 +4494,7 @@ index 000000000000..b5cddd3cc35a + if (state & SAM_BATTERY_STATE_CHARGING) + return POWER_SUPPLY_STATUS_CHARGING; + -+ if (last_full_cap == remaining_cap) ++ if (spwr_battery_is_full(bat)) + return POWER_SUPPLY_STATUS_FULL; + + if (present_rate == 0) @@ -4478,25 +4525,27 @@ index 000000000000..b5cddd3cc35a + +static int spwr_battery_prop_capacity(struct spwr_battery_device *bat) +{ -+ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ u32 full_cap = sprw_battery_get_full_cap_safe(bat); + u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); + -+ if (remaining_cap && last_full_cap) -+ return remaining_cap * 100 / last_full_cap; -+ else -+ return 0; ++ if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN) ++ return -ENODEV; ++ ++ if (remaining_cap == SPWR_BATTERY_VALUE_UNKNOWN) ++ return -ENODEV; ++ ++ return remaining_cap * 100 / full_cap; +} + +static int spwr_battery_prop_capacity_level(struct spwr_battery_device *bat) +{ + u32 state = get_unaligned_le32(&bat->bst.state); -+ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); + u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); + + if (state & SAM_BATTERY_STATE_CRITICAL) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + -+ if (remaining_cap >= last_full_cap) ++ if (spwr_battery_is_full(bat)) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + + if (remaining_cap <= bat->alarm) @@ -4520,7 +4569,7 @@ index 000000000000..b5cddd3cc35a + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: -+ val->intval = le32_to_cpu(ac->state) == 1; ++ val->intval = !!le32_to_cpu(ac->state); + break; + + default: @@ -4538,6 +4587,7 @@ index 000000000000..b5cddd3cc35a + union power_supply_propval *val) +{ + struct spwr_battery_device *bat = power_supply_get_drvdata(psy); ++ u32 value; + int status; + + mutex_lock(&bat->lock); @@ -4566,39 +4616,63 @@ index 000000000000..b5cddd3cc35a + break; + + case POWER_SUPPLY_PROP_CYCLE_COUNT: -+ val->intval = get_unaligned_le32(&bat->bix.cycle_count); ++ value = get_unaligned_le32(&bat->bix.cycle_count); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: -+ val->intval = get_unaligned_le32(&bat->bix.design_voltage) -+ * 1000; ++ value = get_unaligned_le32(&bat->bix.design_voltage); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value * 1000; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = get_unaligned_le32(&bat->bst.present_voltage) -+ * 1000; ++ value = get_unaligned_le32(&bat->bst.present_voltage); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value * 1000; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_POWER_NOW: -+ val->intval = get_unaligned_le32(&bat->bst.present_rate) * 1000; ++ value = get_unaligned_le32(&bat->bst.present_rate); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value * 1000; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: -+ val->intval = get_unaligned_le32(&bat->bix.design_cap) * 1000; ++ value = get_unaligned_le32(&bat->bix.design_cap); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value * 1000; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: -+ val->intval = get_unaligned_le32(&bat->bix.last_full_charge_cap) -+ * 1000; ++ value = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value * 1000; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: -+ val->intval = get_unaligned_le32(&bat->bst.remaining_cap) -+ * 1000; ++ value = get_unaligned_le32(&bat->bst.remaining_cap); ++ if (value != SPWR_BATTERY_VALUE_UNKNOWN) ++ val->intval = value * 1000; ++ else ++ status = -ENODEV; + break; + + case POWER_SUPPLY_PROP_CAPACITY: @@ -4623,7 +4697,7 @@ index 000000000000..b5cddd3cc35a + + default: + status = -EINVAL; -+ goto out; ++ break; + } + +out: @@ -4672,43 +4746,15 @@ index 000000000000..b5cddd3cc35a +}; + + -+static void spwr_ac_set_name(struct spwr_ac_device *ac, const char *name) -+{ -+ strncpy(ac->name, name, ARRAY_SIZE(ac->name) - 1); -+} -+ -+static int spwr_ac_register(struct spwr_ac_device *ac, ++static void spwr_ac_init(struct spwr_ac_device *ac, + struct ssam_device *sdev, -+ struct ssam_event_registry registry) ++ struct ssam_event_registry registry, ++ const char *name) +{ -+ struct power_supply_config psy_cfg = {}; -+ __le32 sta; -+ int status; -+ -+ // make sure the device is there and functioning properly -+ status = ssam_bat_get_sta(sdev, &sta); -+ if (status) -+ return status; -+ -+ if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK) -+ return -ENODEV; -+ -+ psy_cfg.drv_data = ac; ++ mutex_init(&ac->lock); ++ strncpy(ac->name, name, ARRAY_SIZE(ac->name) - 1); + + ac->sdev = sdev; -+ mutex_init(&ac->lock); -+ -+ ac->psy_desc.name = ac->name; -+ ac->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; -+ ac->psy_desc.properties = spwr_ac_props; -+ ac->psy_desc.num_properties = ARRAY_SIZE(spwr_ac_props); -+ ac->psy_desc.get_property = spwr_ac_get_property; -+ -+ ac->psy = power_supply_register(&ac->sdev->dev, &ac->psy_desc, &psy_cfg); -+ if (IS_ERR(ac->psy)) { -+ status = PTR_ERR(ac->psy); -+ goto err_psy; -+ } + + ac->notif.base.priority = 1; + ac->notif.base.fn = spwr_notify_ac; @@ -4718,16 +4764,41 @@ index 000000000000..b5cddd3cc35a + ac->notif.event.mask = SSAM_EVENT_MASK_NONE; + ac->notif.event.flags = SSAM_EVENT_SEQUENCED; + -+ status = ssam_notifier_register(sdev->ctrl, &ac->notif); -+ if (status) -+ goto err_notif; ++ ac->psy_desc.name = ac->name; ++ ac->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; ++ ac->psy_desc.properties = spwr_ac_props; ++ ac->psy_desc.num_properties = ARRAY_SIZE(spwr_ac_props); ++ ac->psy_desc.get_property = spwr_ac_get_property; ++} + -+ return 0; -+ -+err_notif: -+ power_supply_unregister(ac->psy); -+err_psy: ++static void spwr_ac_destroy(struct spwr_ac_device *ac) ++{ + mutex_destroy(&ac->lock); ++} ++ ++static int spwr_ac_register(struct spwr_ac_device *ac) ++{ ++ struct power_supply_config psy_cfg = {}; ++ __le32 sta; ++ int status; ++ ++ // make sure the device is there and functioning properly ++ status = spwr_retry(ssam_bat_get_sta, ac->sdev, &sta); ++ if (status) ++ return status; ++ ++ if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK) ++ return -ENODEV; ++ ++ psy_cfg.drv_data = ac; ++ ac->psy = power_supply_register(&ac->sdev->dev, &ac->psy_desc, &psy_cfg); ++ if (IS_ERR(ac->psy)) ++ return PTR_ERR(ac->psy); ++ ++ status = ssam_notifier_register(ac->sdev->ctrl, &ac->notif); ++ if (status) ++ power_supply_unregister(ac->psy); ++ + return status; +} + @@ -4735,28 +4806,47 @@ index 000000000000..b5cddd3cc35a +{ + ssam_notifier_unregister(ac->sdev->ctrl, &ac->notif); + power_supply_unregister(ac->psy); -+ mutex_destroy(&ac->lock); + return 0; +} + -+static void spwr_battery_set_name(struct spwr_battery_device *bat, -+ const char *name) ++static void spwr_battery_init(struct spwr_battery_device *bat, ++ struct ssam_device *sdev, ++ struct ssam_event_registry registry, ++ const char *name) +{ ++ mutex_init(&bat->lock); + strncpy(bat->name, name, ARRAY_SIZE(bat->name) - 1); ++ ++ bat->sdev = sdev; ++ ++ bat->notif.base.priority = 1; ++ bat->notif.base.fn = spwr_notify_bat; ++ bat->notif.event.reg = registry; ++ bat->notif.event.id.target_category = sdev->uid.category; ++ bat->notif.event.id.instance = 0; ++ bat->notif.event.mask = SSAM_EVENT_MASK_NONE; ++ bat->notif.event.flags = SSAM_EVENT_SEQUENCED; ++ ++ bat->psy_desc.name = bat->name; ++ bat->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; ++ bat->psy_desc.get_property = spwr_battery_get_property; ++ ++ INIT_DELAYED_WORK(&bat->update_work, spwr_battery_update_bst_workfn); +} + -+static int spwr_battery_register(struct spwr_battery_device *bat, -+ struct ssam_device *sdev, -+ struct ssam_event_registry registry) ++static void spwr_battery_destroy(struct spwr_battery_device *bat) ++{ ++ mutex_destroy(&bat->lock); ++} ++ ++static int spwr_battery_register(struct spwr_battery_device *bat) +{ + struct power_supply_config psy_cfg = {}; + __le32 sta; + int status; + -+ bat->sdev = sdev; -+ + // make sure the device is there and functioning properly -+ status = ssam_bat_get_sta(sdev, &sta); ++ status = spwr_retry(ssam_bat_get_sta, bat->sdev, &sta); + if (status) + return status; + @@ -4774,39 +4864,29 @@ index 000000000000..b5cddd3cc35a + return status; + } + -+ bat->psy_desc.name = bat->name; -+ bat->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; -+ -+ if (get_unaligned_le32(&bat->bix.power_unit) == SAM_BATTERY_POWER_UNIT_MA) { -+ bat->psy_desc.properties = spwr_battery_props_chg; -+ bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_chg); -+ } else { ++ switch (get_unaligned_le32(&bat->bix.power_unit)) { ++ case SAM_BATTERY_POWER_UNIT_mW: + bat->psy_desc.properties = spwr_battery_props_eng; + bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_eng); ++ break; ++ ++ case SAM_BATTERY_POWER_UNIT_mA: ++ bat->psy_desc.properties = spwr_battery_props_chg; ++ bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_chg); ++ break; ++ ++ default: ++ dev_err(&bat->sdev->dev, "unsupported battery power unit: %u\n", ++ get_unaligned_le32(&bat->bix.power_unit)); ++ return -ENOTSUPP; + } + -+ bat->psy_desc.get_property = spwr_battery_get_property; -+ -+ mutex_init(&bat->lock); + psy_cfg.drv_data = bat; -+ -+ INIT_DELAYED_WORK(&bat->update_work, spwr_battery_update_bst_workfn); -+ + bat->psy = power_supply_register(&bat->sdev->dev, &bat->psy_desc, &psy_cfg); -+ if (IS_ERR(bat->psy)) { -+ status = PTR_ERR(bat->psy); -+ goto err_psy; -+ } ++ if (IS_ERR(bat->psy)) ++ return PTR_ERR(bat->psy); + -+ bat->notif.base.priority = 1; -+ bat->notif.base.fn = spwr_notify_bat; -+ bat->notif.event.reg = registry; -+ bat->notif.event.id.target_category = sdev->uid.category; -+ bat->notif.event.id.instance = 0; -+ bat->notif.event.mask = SSAM_EVENT_MASK_NONE; -+ bat->notif.event.flags = SSAM_EVENT_SEQUENCED; -+ -+ status = ssam_notifier_register(sdev->ctrl, &bat->notif); ++ status = ssam_notifier_register(bat->sdev->ctrl, &bat->notif); + if (status) + goto err_notif; + @@ -4817,11 +4897,9 @@ index 000000000000..b5cddd3cc35a + return 0; + +err_file: -+ ssam_notifier_unregister(sdev->ctrl, &bat->notif); ++ ssam_notifier_unregister(bat->sdev->ctrl, &bat->notif); +err_notif: + power_supply_unregister(bat->psy); -+err_psy: -+ mutex_destroy(&bat->lock); + return status; +} + @@ -4835,26 +4913,38 @@ index 000000000000..b5cddd3cc35a +} + + -+/* -+ * Battery Driver. -+ */ ++/* -- Power Management. ----------------------------------------------------- */ + -+static int surface_sam_sid_battery_resume(struct device *dev) ++#ifdef CONFIG_PM_SLEEP ++ ++static int surface_battery_resume(struct device *dev) +{ -+ struct spwr_battery_device *bat; -+ -+ // TODO: run this on workqueue -+ -+ bat = dev_get_drvdata(dev); -+ return spwr_battery_recheck(bat); ++ return spwr_battery_recheck_full(dev_get_drvdata(dev)); +} -+SIMPLE_DEV_PM_OPS(surface_sam_sid_battery_pm, -+ NULL, surface_sam_sid_battery_resume); + -+static int surface_sam_sid_battery_probe(struct ssam_device *sdev) ++static int surface_ac_resume(struct device *dev) ++{ ++ return spwr_ac_recheck(dev_get_drvdata(dev)); ++} ++ ++#else /* CONFIG_PM_SLEEP */ ++ ++#define surface_battery_resume NULL ++#define surface_ac_resume NULL ++ ++#endif /* CONFIG_PM_SLEEP */ ++ ++SIMPLE_DEV_PM_OPS(surface_battery_pm_ops, NULL, surface_battery_resume); ++SIMPLE_DEV_PM_OPS(surface_ac_pm_ops, NULL, surface_ac_resume); ++ ++ ++/* -- Battery Driver. ------------------------------------------------------- */ ++ ++static int surface_battery_probe(struct ssam_device *sdev) +{ + const struct spwr_psy_properties *p; + struct spwr_battery_device *bat; ++ int status; + + p = ssam_device_get_match_data(sdev); + if (!p) @@ -4864,15 +4954,22 @@ index 000000000000..b5cddd3cc35a + if (!bat) + return -ENOMEM; + -+ spwr_battery_set_name(bat, p->name); ++ spwr_battery_init(bat, sdev, p->registry, p->name); + ssam_device_set_drvdata(sdev, bat); + -+ return spwr_battery_register(bat, sdev, p->registry); ++ status = spwr_battery_register(bat); ++ if (status) ++ spwr_battery_destroy(bat); ++ ++ return status; +} + -+static void surface_sam_sid_battery_remove(struct ssam_device *sdev) ++static void surface_battery_remove(struct ssam_device *sdev) +{ -+ spwr_battery_unregister(ssam_device_get_drvdata(sdev)); ++ struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev); ++ ++ spwr_battery_unregister(bat); ++ spwr_battery_destroy(bat); +} + +static const struct spwr_psy_properties spwr_psy_props_bat1 = { @@ -4885,35 +4982,32 @@ index 000000000000..b5cddd3cc35a + .registry = SSAM_EVENT_REGISTRY_KIP, +}; + -+static const struct ssam_device_id surface_sam_sid_battery_match[] = { ++static const struct ssam_device_id surface_battery_match[] = { + { SSAM_SDEV(BAT, 0x01, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat1 }, + { SSAM_SDEV(BAT, 0x02, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat2_sb3 }, + { }, +}; -+MODULE_DEVICE_TABLE(ssam, surface_sam_sid_battery_match); ++MODULE_DEVICE_TABLE(ssam, surface_battery_match); + -+static struct ssam_device_driver surface_sam_sid_battery = { -+ .probe = surface_sam_sid_battery_probe, -+ .remove = surface_sam_sid_battery_remove, -+ .match_table = surface_sam_sid_battery_match, ++static struct ssam_device_driver surface_battery_driver = { ++ .probe = surface_battery_probe, ++ .remove = surface_battery_remove, ++ .match_table = surface_battery_match, + .driver = { + .name = "surface_battery", -+ .pm = &surface_sam_sid_battery_pm, ++ .pm = &surface_battery_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + + -+/* -+ * AC Driver. -+ */ ++/* -- AC Driver. ------------------------------------------------------------ */ + -+// TODO: check/update on resume, call power_supply_changed? -+ -+static int surface_sam_sid_ac_probe(struct ssam_device *sdev) ++static int surface_ac_probe(struct ssam_device *sdev) +{ + const struct spwr_psy_properties *p; + struct spwr_ac_device *ac; ++ int status; + + p = ssam_device_get_match_data(sdev); + if (!p) @@ -4923,15 +5017,22 @@ index 000000000000..b5cddd3cc35a + if (!ac) + return -ENOMEM; + -+ spwr_ac_set_name(ac, p->name); ++ spwr_ac_init(ac, sdev, p->registry, p->name); + ssam_device_set_drvdata(sdev, ac); + -+ return spwr_ac_register(ac, sdev, p->registry); ++ status = spwr_ac_register(ac); ++ if (status) ++ spwr_ac_destroy(ac); ++ ++ return status; +} + -+static void surface_sam_sid_ac_remove(struct ssam_device *sdev) ++static void surface_ac_remove(struct ssam_device *sdev) +{ -+ spwr_ac_unregister(ssam_device_get_drvdata(sdev)); ++ struct spwr_ac_device *ac = ssam_device_get_drvdata(sdev); ++ ++ spwr_ac_unregister(ac); ++ spwr_ac_destroy(ac); +} + +static const struct spwr_psy_properties spwr_psy_props_adp1 = { @@ -4939,57 +5040,58 @@ index 000000000000..b5cddd3cc35a + .registry = SSAM_EVENT_REGISTRY_SAM, +}; + -+static const struct ssam_device_id surface_sam_sid_ac_match[] = { ++static const struct ssam_device_id surface_ac_match[] = { + { SSAM_SDEV(BAT, 0x01, 0x01, 0x01), (unsigned long)&spwr_psy_props_adp1 }, + { }, +}; -+MODULE_DEVICE_TABLE(ssam, surface_sam_sid_ac_match); ++MODULE_DEVICE_TABLE(ssam, surface_ac_match); + -+static struct ssam_device_driver surface_sam_sid_ac = { -+ .probe = surface_sam_sid_ac_probe, -+ .remove = surface_sam_sid_ac_remove, -+ .match_table = surface_sam_sid_ac_match, ++static struct ssam_device_driver surface_ac_driver = { ++ .probe = surface_ac_probe, ++ .remove = surface_ac_remove, ++ .match_table = surface_ac_match, + .driver = { + .name = "surface_ac", ++ .pm = &surface_ac_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + + -+static int __init surface_sam_sid_power_init(void) ++/* -- Module Setup. --------------------------------------------------------- */ ++ ++static int __init surface_battery_init(void) +{ + int status; + -+ status = ssam_device_driver_register(&surface_sam_sid_battery); ++ status = ssam_device_driver_register(&surface_battery_driver); + if (status) + return status; + -+ status = ssam_device_driver_register(&surface_sam_sid_ac); -+ if (status) { -+ ssam_device_driver_unregister(&surface_sam_sid_battery); -+ return status; -+ } ++ status = ssam_device_driver_register(&surface_ac_driver); ++ if (status) ++ ssam_device_driver_unregister(&surface_battery_driver); + -+ return 0; ++ return status; +} -+module_init(surface_sam_sid_power_init); ++module_init(surface_battery_init); + -+static void __exit surface_sam_sid_power_exit(void) ++static void __exit surface_battery_exit(void) +{ -+ ssam_device_driver_unregister(&surface_sam_sid_battery); -+ ssam_device_driver_unregister(&surface_sam_sid_ac); ++ ssam_device_driver_unregister(&surface_battery_driver); ++ ssam_device_driver_unregister(&surface_ac_driver); +} -+module_exit(surface_sam_sid_power_exit); ++module_exit(surface_battery_exit); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Battery/AC driver for Surface System Aggregator Module"); +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..51b21e194ee3 +index 000000000000..ee815ad0fdad --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_dtx.c -@@ -0,0 +1,589 @@ +@@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Book (gen. 2 and later) detachment system (DTX) driver. @@ -4999,6 +5101,8 @@ index 000000000000..51b21e194ee3 + * (containing the keyboard and optionally a discrete GPU). Allows to + * acknowledge (to speed things up), abort (e.g. in case the dGPU is stil in + * use), or request detachment via user-space. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -5581,16 +5685,18 @@ index 000000000000..51b21e194ee3 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_hid.c b/drivers/misc/surface_aggregator/clients/surface_hid.c new file mode 100644 -index 000000000000..50528a7c3680 +index 000000000000..c29cb39543c5 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_hid.c -@@ -0,0 +1,493 @@ +@@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface System Aggregator Module (SSAM) HID device driver. + * + * Provides support for HID input devices connected via the Surface System + * Aggregator Module. ++ * ++ * Copyright (C) 2019-2020 Blaž Hrastnik + */ + +#include @@ -6080,10 +6186,10 @@ index 000000000000..50528a7c3680 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_hotplug.c b/drivers/misc/surface_aggregator/clients/surface_hotplug.c new file mode 100644 -index 000000000000..33acbbc5de94 +index 000000000000..165c5b452bee --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_hotplug.c -@@ -0,0 +1,1282 @@ +@@ -0,0 +1,1285 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Book (gen. 2 and later) discrete GPU (dGPU) hot-plug system driver. @@ -6091,6 +6197,8 @@ index 000000000000..33acbbc5de94 + * Supports explicit setting of the dGPU power-state on the Surface Books via + * a user-space interface. Properly handles dGPU hot-plugging by detaching the + * base of the device. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -7359,6 +7467,7 @@ index 000000000000..33acbbc5de94 + .name = "surface_dgpu_hotplug", + .acpi_match_table = shps_acpi_match, + .pm = &shps_pm_ops, ++ .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_platform_driver(surface_sam_hps); @@ -7368,16 +7477,18 @@ index 000000000000..33acbbc5de94 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_keyboard.c b/drivers/misc/surface_aggregator/clients/surface_keyboard.c new file mode 100644 -index 000000000000..580c4255d068 +index 000000000000..94b1e0728bb2 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_keyboard.c -@@ -0,0 +1,331 @@ +@@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface System Aggregator Module (SSAM) legacy HID input device driver. + * + * Provides support for the legacy HID keyboard device found on the Surface + * Laptop 1 and 2. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -7705,10 +7816,10 @@ index 000000000000..580c4255d068 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_perfmode.c b/drivers/misc/surface_aggregator/clients/surface_perfmode.c new file mode 100644 -index 000000000000..ed93596adcf6 +index 000000000000..580ac680a183 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_perfmode.c -@@ -0,0 +1,194 @@ +@@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface performance-mode driver. @@ -7716,6 +7827,8 @@ index 000000000000..ed93596adcf6 + * Proides a user-space interface for the performance mode control provided by + * the Surface System Aggregator Module (SSAM), influencing cooling behavior + * of the device and potentially managing power limits. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -7726,31 +7839,16 @@ index 000000000000..ed93596adcf6 + +#include + -+ -+#define SID_PARAM_PERM 0644 -+ +enum sam_perf_mode { -+ SAM_PERF_MODE_NORMAL = 1, -+ SAM_PERF_MODE_BATTERY = 2, -+ SAM_PERF_MODE_PERF1 = 3, -+ SAM_PERF_MODE_PERF2 = 4, ++ SAM_PERF_MODE_NORMAL = 1, ++ SAM_PERF_MODE_BATTERY = 2, ++ SAM_PERF_MODE_PERF1 = 3, ++ SAM_PERF_MODE_PERF2 = 4, + -+ __SAM_PERF_MODE__START = 1, -+ __SAM_PERF_MODE__END = 4, ++ __SAM_PERF_MODE__MIN = 1, ++ __SAM_PERF_MODE__MAX = 4, +}; + -+enum sid_param_perf_mode { -+ SID_PARAM_PERF_MODE_AS_IS = 0, -+ SID_PARAM_PERF_MODE_NORMAL = SAM_PERF_MODE_NORMAL, -+ SID_PARAM_PERF_MODE_BATTERY = SAM_PERF_MODE_BATTERY, -+ SID_PARAM_PERF_MODE_PERF1 = SAM_PERF_MODE_PERF1, -+ SID_PARAM_PERF_MODE_PERF2 = SAM_PERF_MODE_PERF2, -+ -+ __SID_PARAM_PERF_MODE__START = 0, -+ __SID_PARAM_PERF_MODE__END = 4, -+}; -+ -+ +struct ssam_perf_info { + __le32 mode; + __le16 unknown1; @@ -7771,44 +7869,14 @@ index 000000000000..ed93596adcf6 +{ + __le32 mode_le = cpu_to_le32(mode); + -+ if (mode < __SAM_PERF_MODE__START || mode > __SAM_PERF_MODE__END) ++ if (mode < __SAM_PERF_MODE__MIN || mode > __SAM_PERF_MODE__MAX) + return -EINVAL; + + return __ssam_tmp_perf_mode_set(sdev, &mode_le); +} + -+ -+static int param_perf_mode_set(const char *val, const struct kernel_param *kp) -+{ -+ int perf_mode; -+ int status; -+ -+ status = kstrtoint(val, 0, &perf_mode); -+ if (status) -+ return status; -+ -+ if (perf_mode < __SID_PARAM_PERF_MODE__START || perf_mode > __SID_PARAM_PERF_MODE__END) -+ return -EINVAL; -+ -+ return param_set_int(val, kp); -+} -+ -+static const struct kernel_param_ops param_perf_mode_ops = { -+ .set = param_perf_mode_set, -+ .get = param_get_int, -+}; -+ -+static int param_perf_mode_init = SID_PARAM_PERF_MODE_AS_IS; -+static int param_perf_mode_exit = SID_PARAM_PERF_MODE_AS_IS; -+ -+module_param_cb(perf_mode_init, ¶m_perf_mode_ops, ¶m_perf_mode_init, SID_PARAM_PERM); -+module_param_cb(perf_mode_exit, ¶m_perf_mode_ops, ¶m_perf_mode_exit, SID_PARAM_PERM); -+ -+MODULE_PARM_DESC(perf_mode_init, "Performance-mode to be set on module initialization"); -+MODULE_PARM_DESC(perf_mode_exit, "Performance-mode to be set on module exit"); -+ -+ -+static ssize_t perf_mode_show(struct device *dev, struct device_attribute *attr, char *data) ++static ssize_t perf_mode_show(struct device *dev, struct device_attribute *attr, ++ char *data) +{ + struct ssam_device *sdev = to_ssam_device(dev); + struct ssam_perf_info info; @@ -7816,7 +7884,8 @@ index 000000000000..ed93596adcf6 + + status = ssam_tmp_perf_mode_get(sdev, &info); + if (status) { -+ dev_err(dev, "failed to get current performance mode: %d\n", status); ++ dev_err(dev, "failed to get current performance mode: %d\n", ++ status); + return -EIO; + } + @@ -7831,58 +7900,28 @@ index 000000000000..ed93596adcf6 + int status; + + status = kstrtoint(data, 0, &perf_mode); -+ if (status) ++ if (status < 0) + return status; + + status = ssam_tmp_perf_mode_set(sdev, perf_mode); -+ if (status) ++ if (status < 0) + return status; + -+ // TODO: Should we notify ACPI here? -+ // -+ // There is a _DSM call described as -+ // WSID._DSM: Notify DPTF on Slider State change -+ // which calls -+ // ODV3 = ToInteger (Arg3) -+ // Notify(IETM, 0x88) -+ // IETM is an INT3400 Intel Dynamic Power Performance Management -+ // device, part of the DPTF framework. From the corresponding -+ // kernel driver, it looks like event 0x88 is being ignored. Also -+ // it is currently unknown what the consequecnes of setting ODV3 -+ // are. -+ + return count; +} + +static const DEVICE_ATTR_RW(perf_mode); + -+ +static int surface_sam_sid_perfmode_probe(struct ssam_device *sdev) +{ -+ int status; -+ -+ // set initial perf_mode -+ if (param_perf_mode_init != SID_PARAM_PERF_MODE_AS_IS) { -+ status = ssam_tmp_perf_mode_set(sdev, param_perf_mode_init); -+ if (status) -+ return status; -+ } -+ -+ // register perf_mode attribute -+ status = sysfs_create_file(&sdev->dev.kobj, &dev_attr_perf_mode.attr); -+ if (status) -+ ssam_tmp_perf_mode_set(sdev, param_perf_mode_exit); -+ -+ return status; ++ return sysfs_create_file(&sdev->dev.kobj, &dev_attr_perf_mode.attr); +} + +static void surface_sam_sid_perfmode_remove(struct ssam_device *sdev) +{ + sysfs_remove_file(&sdev->dev.kobj, &dev_attr_perf_mode.attr); -+ ssam_tmp_perf_mode_set(sdev, param_perf_mode_exit); +} + -+ +static const struct ssam_device_id ssam_perfmode_match[] = { + { SSAM_SDEV(TMP, 0x01, 0x00, 0x01) }, + { }, @@ -7905,11 +7944,16 @@ index 000000000000..ed93596adcf6 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/controller.c b/drivers/misc/surface_aggregator/controller.c new file mode 100644 -index 000000000000..7d6656027f4b +index 000000000000..9125e51e7c8d --- /dev/null +++ b/drivers/misc/surface_aggregator/controller.c -@@ -0,0 +1,2509 @@ +@@ -0,0 +1,2554 @@ +// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Main SSAM/SSH controller structure and functionality. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#include +#include @@ -8447,8 +8491,9 @@ index 000000000000..7d6656027f4b + * SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN - Maximum payload length for a cached + * &struct ssam_event_item. + * -+ * This length has been chosen to be accomodate standard touchpad and keyboard -+ * input events. Events with larger payloads will be allocated separately. ++ * This length has been chosen to be accommodate standard touchpad and ++ * keyboard input events. Events with larger payloads will be allocated ++ * separately. + */ +#define SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN 32 + @@ -8605,7 +8650,7 @@ index 000000000000..7d6656027f4b + u16 tidx = ssh_tid_to_index(tid); + + if (!ssh_rqid_is_event(rqid)) { -+ dev_err(cplt->dev, "event: unsupported rquest ID: 0x%04x\n", rqid); ++ dev_err(cplt->dev, "event: unsupported request ID: 0x%04x\n", rqid); + return NULL; + } + @@ -8797,7 +8842,8 @@ index 000000000000..7d6656027f4b + */ +struct ssam_controller *ssam_controller_get(struct ssam_controller *c) +{ -+ kref_get(&c->kref); ++ if (c) ++ kref_get(&c->kref); + return c; +} +EXPORT_SYMBOL_GPL(ssam_controller_get); @@ -8808,7 +8854,8 @@ index 000000000000..7d6656027f4b + */ +void ssam_controller_put(struct ssam_controller *c) +{ -+ kref_put(&c->kref, __ssam_controller_release); ++ if (c) ++ kref_put(&c->kref, __ssam_controller_release); +} +EXPORT_SYMBOL_GPL(ssam_controller_put); + @@ -9084,7 +9131,11 @@ index 000000000000..7d6656027f4b + return status; + } + -+ // update state ++ /* ++ * Set state via write_once even though we expect to be in an ++ * exclusive context, due to smoke-testing in ++ * ssam_request_sync_submit(). ++ */ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_INITIALIZED); + return 0; +} @@ -9097,18 +9148,26 @@ index 000000000000..7d6656027f4b + * Note: When this function is called, the controller shouldbe properly hooked + * up to the serdev core via &struct serdev_device_ops. Please refert to + * ssam_controller_init() for more details on controller initialization. ++ * ++ * This function must be called from an exclusive context with regards to the ++ * state, if necessary, by locking the controller via ssam_controller_lock(). + */ +int ssam_controller_start(struct ssam_controller *ctrl) +{ + int status; + -+ if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_INITIALIZED) ++ if (ctrl->state != SSAM_CONTROLLER_INITIALIZED) + return -EINVAL; + + status = ssh_rtl_start(&ctrl->rtl); + if (status) + return status; + ++ /* ++ * Set state via write_once even though we expect to be locked/in an ++ * exclusive context, due to smoke-testing in ++ * ssam_request_sync_submit(). ++ */ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED); + return 0; +} @@ -9131,10 +9190,13 @@ index 000000000000..7d6656027f4b + * Note that events may still be pending after this call, but due to the + * notifiers being unregistered, the will be dropped when the controller is + * subsequently being destroyed via ssam_controller_destroy(). ++ * ++ * This function must be called from an exclusive context with regards to the ++ * state, if necessary, by locking the controller via ssam_controller_lock(). + */ +void ssam_controller_shutdown(struct ssam_controller *ctrl) +{ -+ enum ssam_controller_state s = READ_ONCE(ctrl->state); ++ enum ssam_controller_state s = ctrl->state; + int status; + + if (s == SSAM_CONTROLLER_UNINITIALIZED || s == SSAM_CONTROLLER_STOPPED) @@ -9166,6 +9228,11 @@ index 000000000000..7d6656027f4b + // cancel rem. requests, ensure no new ones can be queued, stop threads + ssh_rtl_shutdown(&ctrl->rtl); + ++ /* ++ * Set state via write_once even though we expect to be locked/in an ++ * exclusive context, due to smoke-testing in ++ * ssam_request_sync_submit(). ++ */ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STOPPED); + ctrl->rtl.ptl.serdev = NULL; +} @@ -9176,11 +9243,18 @@ index 000000000000..7d6656027f4b + * + * Ensures that all resources associated with the controller get freed. This + * function should only be called after the controller has been stopped via -+ * ssam_controller_shutdown(). ++ * ssam_controller_shutdown(). In general, this function should not be called ++ * directly. The only valid place to call this function direclty is during ++ * initialization, before the controller has been fully initialized and passed ++ * to other processes. This function is called automatically when the ++ * reference count of the controller reaches zero. ++ * ++ * Must be called from an exclusive context with regards to the controller ++ * state. + */ +void ssam_controller_destroy(struct ssam_controller *ctrl) +{ -+ if (READ_ONCE(ctrl->state) == SSAM_CONTROLLER_UNINITIALIZED) ++ if (ctrl->state == SSAM_CONTROLLER_UNINITIALIZED) + return; + + WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED); @@ -9197,6 +9271,11 @@ index 000000000000..7d6656027f4b + ssam_cplt_destroy(&ctrl->cplt); + ssh_rtl_destroy(&ctrl->rtl); + ++ /* ++ * Set state via write_once even though we expect to be locked/in an ++ * exclusive context, due to smoke-testing in ++ * ssam_request_sync_submit(). ++ */ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_UNINITIALIZED); +} + @@ -9217,12 +9296,17 @@ index 000000000000..7d6656027f4b +{ + ssam_controller_lock(ctrl); + -+ if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_STARTED) { ++ if (ctrl->state != SSAM_CONTROLLER_STARTED) { + ssam_controller_unlock(ctrl); + return -EINVAL; + } + + ssam_dbg(ctrl, "pm: suspending controller\n"); ++ ++ /* ++ * Set state via write_once even though we're locked, due to ++ * smoke-testing in ssam_request_sync_submit(). ++ */ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_SUSPENDED); + + ssam_controller_unlock(ctrl); @@ -9244,12 +9328,17 @@ index 000000000000..7d6656027f4b +{ + ssam_controller_lock(ctrl); + -+ if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_SUSPENDED) { ++ if (ctrl->state != SSAM_CONTROLLER_SUSPENDED) { + ssam_controller_unlock(ctrl); + return -EINVAL; + } + + ssam_dbg(ctrl, "pm: resuming controller\n"); ++ ++ /* ++ * Set state via write_once even though we're locked, due to ++ * smoke-testing in ssam_request_sync_submit(). ++ */ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED); + + ssam_controller_unlock(ctrl); @@ -9767,28 +9856,23 @@ index 000000000000..7d6656027f4b +/* -- Wrappers for internal SAM requests. ----------------------------------- */ + +/** -+ * ssam_log_firmware_version() - Log SAM/EC firmware version to kernel log. -+ * @ctrl: The controller. ++ * ssam_get_firmware_version() - Get the SAM/EC firmware version. ++ * @ctrl: The controller. ++ * @version: Where to store the version number. + * + * Return: Returns zero on success or the status of the executed SAM request + * if that request failed. + */ -+int ssam_log_firmware_version(struct ssam_controller *ctrl) ++int ssam_get_firmware_version(struct ssam_controller *ctrl, u32 *version) +{ + __le32 __version; -+ u32 version, a, b, c; + int status; + + status = ssam_ssh_get_firmware_version(ctrl, &__version); + if (status) + return status; + -+ version = le32_to_cpu(__version); -+ a = (version >> 24) & 0xff; -+ b = ((version >> 8) & 0xffff); -+ c = version & 0xff; -+ -+ ssam_info(ctrl, "SAM controller version: %u.%u.%u\n", a, b, c); ++ *version = le32_to_cpu(__version); + return 0; +} + @@ -10126,7 +10210,7 @@ index 000000000000..7d6656027f4b + * + * This function is intended to disable all events prior to hibenration entry. + * See ssam_notifier_restore_registered() to restore/re-enable all events -+ * disabled with this fucntion. ++ * disabled with this function. + * + * Note that this function will not disable events for notifiers registered + * after calling this function. It should thus be made sure that no new @@ -10319,8 +10403,8 @@ index 000000000000..7d6656027f4b + * command to SAM (or alternatively the display-on notification). As + * proper handling for this interrupt is not implemented yet, leaving + * the IRQ at TRIGGER_HIGH would cause an IRQ storm (as the callback -+ * never gets sent and thus the line line never gets reset). To avoid -+ * this, mark the IRQ as TRIGGER_RISING for now, only creating a single ++ * never gets sent and thus the line never gets reset). To avoid this, ++ * mark the IRQ as TRIGGER_RISING for now, only creating a single + * interrupt, and let the SAM resume callback during the controller + * resume process clear it. + */ @@ -10420,11 +10504,16 @@ index 000000000000..7d6656027f4b +} diff --git a/drivers/misc/surface_aggregator/controller.h b/drivers/misc/surface_aggregator/controller.h new file mode 100644 -index 000000000000..b45b5b23cbd6 +index 000000000000..b855fd54a163 --- /dev/null +++ b/drivers/misc/surface_aggregator/controller.h -@@ -0,0 +1,283 @@ +@@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Main SSAM/SSH controller structure and functionality. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#ifndef _SURFACE_AGGREGATOR_CONTROLLER_H +#define _SURFACE_AGGREGATOR_CONTROLLER_H @@ -10669,7 +10758,7 @@ index 000000000000..b45b5b23cbd6 + +/** + * ssam_controller_write_wakeup() - Notify the controller that the underlying -+ * device has space avaliable for data to be written. ++ * device has space available for data to be written. + * @ctrl: The controller. + */ +static inline void ssam_controller_write_wakeup(struct ssam_controller *ctrl) @@ -10694,7 +10783,7 @@ index 000000000000..b45b5b23cbd6 +void ssam_controller_lock(struct ssam_controller *c); +void ssam_controller_unlock(struct ssam_controller *c); + -+int ssam_log_firmware_version(struct ssam_controller *ctrl); ++int ssam_get_firmware_version(struct ssam_controller *ctrl, u32 *version); +int ssam_ctrl_notif_display_off(struct ssam_controller *ctrl); +int ssam_ctrl_notif_display_on(struct ssam_controller *ctrl); +int ssam_ctrl_notif_d0_exit(struct ssam_controller *ctrl); @@ -10709,10 +10798,10 @@ index 000000000000..b45b5b23cbd6 +#endif /* _SURFACE_AGGREGATOR_CONTROLLER_H */ diff --git a/drivers/misc/surface_aggregator/core.c b/drivers/misc/surface_aggregator/core.c new file mode 100644 -index 000000000000..957bdcc4e3d3 +index 000000000000..b9992f0c3ff1 --- /dev/null +++ b/drivers/misc/surface_aggregator/core.c -@@ -0,0 +1,773 @@ +@@ -0,0 +1,831 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System @@ -10721,6 +10810,8 @@ index 000000000000..957bdcc4e3d3 + * Provides access to a SAM-over-SSH connected EC via a controller device. + * Handles communication via requests as well as enabling, disabling, and + * relaying of events. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include @@ -10732,6 +10823,7 @@ index 000000000000..957bdcc4e3d3 +#include +#include +#include ++#include + +#include + @@ -10844,7 +10936,7 @@ index 000000000000..957bdcc4e3d3 + + ssam_controller_statelock(c); + -+ if (READ_ONCE(c->state) != SSAM_CONTROLLER_STARTED) { ++ if (c->state != SSAM_CONTROLLER_STARTED) { + ssam_controller_stateunlock(c); + return -ENXIO; + } @@ -10961,6 +11053,55 @@ index 000000000000..957bdcc4e3d3 +}; + + ++/* -- SysFS and misc. ------------------------------------------------------- */ ++ ++static int ssam_log_firmware_version(struct ssam_controller *ctrl) ++{ ++ u32 version, a, b, c; ++ int status; ++ ++ status = ssam_get_firmware_version(ctrl, &version); ++ if (status) ++ return status; ++ ++ a = (version >> 24) & 0xff; ++ b = ((version >> 8) & 0xffff); ++ c = version & 0xff; ++ ++ ssam_info(ctrl, "SAM firmware version: %u.%u.%u\n", a, b, c); ++ return 0; ++} ++ ++static ssize_t firmware_version_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ssam_controller *ctrl = dev_get_drvdata(dev); ++ u32 version, a, b, c; ++ int status; ++ ++ status = ssam_get_firmware_version(ctrl, &version); ++ if (status < 0) ++ return status; ++ ++ a = (version >> 24) & 0xff; ++ b = ((version >> 8) & 0xffff); ++ c = version & 0xff; ++ ++ return snprintf(buf, PAGE_SIZE - 1, "%u.%u.%u\n", a, b, c); ++} ++static DEVICE_ATTR_RO(firmware_version); ++ ++static struct attribute *ssam_sam_attrs[] = { ++ &dev_attr_firmware_version.attr, ++ NULL, ++}; ++ ++static const struct attribute_group ssam_sam_group = { ++ .name = "sam", ++ .attrs = ssam_sam_attrs, ++}; ++ ++ +/* -- ACPI based device setup. ---------------------------------------------- */ + +static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc, @@ -11058,6 +11199,8 @@ index 000000000000..957bdcc4e3d3 + ssam_err(c, "pm: D0-exit notification failed: %d\n", status); +} + ++#ifdef CONFIG_PM_SLEEP ++ +static int ssam_serial_hub_pm_prepare(struct device *dev) +{ + struct ssam_controller *c = dev_get_drvdata(dev); @@ -11264,6 +11407,12 @@ index 000000000000..957bdcc4e3d3 + .restore = ssam_serial_hub_pm_restore, +}; + ++#else /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops ssam_serial_hub_pm_ops = { }; ++ ++#endif /* CONFIG_PM_SLEEP */ ++ + +/* -- Device/driver setup. -------------------------------------------------- */ + @@ -11331,15 +11480,19 @@ index 000000000000..957bdcc4e3d3 + if (status) + goto err_initrq; + ++ status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group); ++ if (status) ++ goto err_initrq; ++ + // setup IRQ + status = ssam_irq_setup(ctrl); + if (status) -+ goto err_initrq; ++ goto err_irq; + + // finally, set main controller reference + status = ssam_try_set_controller(ctrl); + if (WARN_ON(status)) // currently, we're the only provider -+ goto err_initrq; ++ goto err_mainref; + + /* + * TODO: The EC can wake up the system via the associated GPIO interrupt @@ -11356,6 +11509,10 @@ index 000000000000..957bdcc4e3d3 + + return 0; + ++err_mainref: ++ ssam_irq_free(ctrl); ++err_irq: ++ sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group); +err_initrq: + ssam_controller_shutdown(ctrl); +err_devinit: @@ -11376,6 +11533,7 @@ index 000000000000..957bdcc4e3d3 + ssam_clear_controller(); + + ssam_irq_free(ctrl); ++ sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group); + ssam_controller_lock(ctrl); + + // remove all client devices @@ -11461,6 +11619,7 @@ index 000000000000..957bdcc4e3d3 +err_bus: + return status; +} ++module_init(ssam_core_init); + +static void __exit ssam_core_exit(void) +{ @@ -11469,18 +11628,6 @@ index 000000000000..957bdcc4e3d3 + ssh_ctrl_packet_cache_destroy(); + ssam_bus_unregister(); +} -+ -+/* -+ * Ensure that the driver is loaded late due to some issues with the UART -+ * communication. Specifically, we want to ensure that DMA is ready and being -+ * used. Not using DMA can result in spurious communication failures, -+ * especially during boot, which among other things will result in wrong -+ * battery information (via ACPI _BIX) being displayed. Using a late init_call -+ * instead of the normal module_init gives the DMA subsystem time to -+ * initialize and via that results in a more stable communication, avoiding -+ * such failures. -+ */ -+late_initcall(ssam_core_init); +module_exit(ssam_core_exit); + +MODULE_AUTHOR("Maximilian Luz "); @@ -11488,11 +11635,16 @@ index 000000000000..957bdcc4e3d3 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/ssh_msgb.h b/drivers/misc/surface_aggregator/ssh_msgb.h new file mode 100644 -index 000000000000..046e5e44a100 +index 000000000000..1735a01f1f95 --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_msgb.h -@@ -0,0 +1,196 @@ +@@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * SSH message builder functions. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H +#define _SURFACE_AGGREGATOR_SSH_MSGB_H @@ -11690,11 +11842,16 @@ index 000000000000..046e5e44a100 +#endif /* _SURFACE_AGGREGATOR_SSH_MSGB_H */ diff --git a/drivers/misc/surface_aggregator/ssh_packet_layer.c b/drivers/misc/surface_aggregator/ssh_packet_layer.c new file mode 100644 -index 000000000000..3cc1e9e24a07 +index 000000000000..54f16ef040ef --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_packet_layer.c -@@ -0,0 +1,2002 @@ +@@ -0,0 +1,2009 @@ +// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * SSH packet transport layer. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#include +#include @@ -12207,7 +12364,8 @@ index 000000000000..3cc1e9e24a07 + */ +struct ssh_packet *ssh_packet_get(struct ssh_packet *packet) +{ -+ kref_get(&packet->refcnt); ++ if (packet) ++ kref_get(&packet->refcnt); + return packet; +} +EXPORT_SYMBOL_GPL(ssh_packet_get); @@ -12224,7 +12382,8 @@ index 000000000000..3cc1e9e24a07 + */ +void ssh_packet_put(struct ssh_packet *packet) +{ -+ kref_put(&packet->refcnt, __ssh_ptl_packet_release); ++ if (packet) ++ kref_put(&packet->refcnt, __ssh_ptl_packet_release); +} +EXPORT_SYMBOL_GPL(ssh_packet_put); + @@ -12453,7 +12612,7 @@ index 000000000000..3cc1e9e24a07 + + head = __ssh_ptl_queue_find_entrypoint(packet); + -+ list_add_tail(&ssh_packet_get(packet)->queue_node, &ptl->queue.head); ++ list_add_tail(&ssh_packet_get(packet)->queue_node, head); + return 0; +} + @@ -12545,7 +12704,7 @@ index 000000000000..3cc1e9e24a07 +static void ssh_ptl_remove_and_complete(struct ssh_packet *p, int status) +{ + /* -+ * A call to this function should in general be preceeded by ++ * A call to this function should in general be preceded by + * set_bit(SSH_PACKET_SF_LOCKED_BIT, &p->flags) to avoid re-adding the + * packet to the structures it's going to be removed from. + * @@ -12947,7 +13106,7 @@ index 000000000000..3cc1e9e24a07 + +/** + * ssh_ptl_submit() - Submit a packet to the transport layer. -+ * @ptl: The packet transport layer to to submit to. ++ * @ptl: The packet transport layer to submit the packet to. + * @p: The packet to submit. + * + * Submits a new packet to the transport layer, queuing it to be sent. This @@ -13036,7 +13195,7 @@ index 000000000000..3cc1e9e24a07 + * Note: We deliberately do not remove/attempt to cancel and complete + * packets that are out of tires in this function. The packet will be + * eventually canceled and completed by the timeout. Removing the packet -+ * here could lead to overly eager cancelation if the packet has not ++ * here could lead to overly eager cancellation if the packet has not + * been re-transmitted yet but the tries-counter already updated (i.e + * ssh_ptl_tx_next() removed the packet from the queue and updated the + * counter, but re-transmission for the last try has not actually @@ -13570,7 +13729,7 @@ index 000000000000..3cc1e9e24a07 + * + * Note 3: There may be overlap between complete_p and complete_q. + * This is handled via test_and_set_bit() on the "completed" flag -+ * (also handles cancelation). ++ * (also handles cancellation). + */ + + // mark queued packets as locked and move them to complete_q @@ -13698,11 +13857,16 @@ index 000000000000..3cc1e9e24a07 +} diff --git a/drivers/misc/surface_aggregator/ssh_packet_layer.h b/drivers/misc/surface_aggregator/ssh_packet_layer.h new file mode 100644 -index 000000000000..413388d256ff +index 000000000000..39500b05d1f8 --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_packet_layer.h -@@ -0,0 +1,170 @@ +@@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * SSH packet transport layer. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H +#define _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H @@ -13769,7 +13933,7 @@ index 000000000000..413388d256ff + * @rx.blocked.seqs: Array of blocked sequence IDs. + * @rx.blocked.offset: Offset indicating where a new ID should be inserted. + * @rtx_timeout: Retransmission timeout subsystem. -+ * @rtx_timeout.timeout: Timout inverval for retransmission. ++ * @rtx_timeout.timeout: Timeout inverval for retransmission. + * @rtx_timeout.expires: Time specifying when the reaper work is next scheduled. + * @rtx_timeout.reaper: Work performing timeout checks and subsequent actions. + * @ops: Packet layer operations. @@ -13874,11 +14038,16 @@ index 000000000000..413388d256ff +#endif /* _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H */ diff --git a/drivers/misc/surface_aggregator/ssh_parser.c b/drivers/misc/surface_aggregator/ssh_parser.c new file mode 100644 -index 000000000000..7d9f7de74100 +index 000000000000..fb96ce326d63 --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_parser.c -@@ -0,0 +1,224 @@ +@@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * SSH message parser. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#include +#include @@ -14007,7 +14176,7 @@ index 000000000000..7d9f7de74100 + return -ENOMSG; + } + -+ // check for minumum packet length ++ // check for minimum packet length + if (unlikely(source->len < SSH_MESSAGE_LENGTH(0))) { + dev_dbg(dev, "rx: parser: not enough data for frame\n"); + return 0; @@ -14104,11 +14273,16 @@ index 000000000000..7d9f7de74100 +} diff --git a/drivers/misc/surface_aggregator/ssh_parser.h b/drivers/misc/surface_aggregator/ssh_parser.h new file mode 100644 -index 000000000000..a766223b18e7 +index 000000000000..ba6eaa1ea2bd --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_parser.h -@@ -0,0 +1,152 @@ +@@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * SSH message parser. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H +#define _SURFACE_AGGREGATOR_SSH_PARSER_H @@ -14178,7 +14352,7 @@ index 000000000000..a766223b18e7 + * @buf: The buffer to free. + * + * Frees a SSH parser buffer by freeing the memory backing it and then -+ * reseting its pointer to %NULL and length and capacity to zero. Intended to ++ * resetting its pointer to %NULL and length and capacity to zero. Intended to + * free a buffer previously allocated with sshp_buf_alloc(). + */ +static inline void sshp_buf_free(struct sshp_buf *buf) @@ -14213,7 +14387,7 @@ index 000000000000..a766223b18e7 + * limited either by the remaining space in the buffer or by the number of + * bytes available in the fifo. + * -+ * Return: Returns the number of bytes transfered. ++ * Return: Returns the number of bytes transferred. + */ +static inline size_t sshp_buf_read_from_fifo(struct sshp_buf *buf, + struct kfifo *fifo) @@ -14262,11 +14436,16 @@ index 000000000000..a766223b18e7 +#endif /* _SURFACE_AGGREGATOR_SSH_PARSER_h */ diff --git a/drivers/misc/surface_aggregator/ssh_request_layer.c b/drivers/misc/surface_aggregator/ssh_request_layer.c new file mode 100644 -index 000000000000..5494217afa88 +index 000000000000..d536f40635ae --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_request_layer.c -@@ -0,0 +1,1249 @@ +@@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * SSH request transport layer. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#include +#include @@ -14582,7 +14761,7 @@ index 000000000000..5494217afa88 + /* + * Try to be nice and not block/live-lock the workqueue: Run a maximum + * of 10 tries, then re-submit if necessary. This should not be -+ * neccesary for normal execution, but guarantee it anyway. ++ * necessary for normal execution, but guarantee it anyway. + */ + for (i = 0; i < 10; i++) { + status = ssh_rtl_tx_try_process_one(rtl); @@ -14894,8 +15073,8 @@ index 000000000000..5494217afa88 + /* + * Note: 1) Requests cannot be re-submitted. 2) If a request is queued, + * it cannot be "transmitting"/"pending" yet. Thus, if we successfully -+ * remove the the request here, we have removed all its occurences in -+ * the system. ++ * remove the request here, we have removed all its occurences in the ++ * system. + */ + + remove = test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state); @@ -15517,11 +15696,16 @@ index 000000000000..5494217afa88 +} diff --git a/drivers/misc/surface_aggregator/ssh_request_layer.h b/drivers/misc/surface_aggregator/ssh_request_layer.h new file mode 100644 -index 000000000000..cfcfeb0c2c56 +index 000000000000..0265f38dfa5f --- /dev/null +++ b/drivers/misc/surface_aggregator/ssh_request_layer.h -@@ -0,0 +1,137 @@ +@@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * SSH request transport layer. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz ++ */ + +#ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H +#define _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H @@ -15575,7 +15759,7 @@ index 000000000000..cfcfeb0c2c56 + * @tx: Transmitter subsystem. + * @tx.work: Transmitter work item. + * @rtx_timeout: Retransmission timeout subsystem. -+ * @rtx_timeout.timeout: Timout inverval for retransmission. ++ * @rtx_timeout.timeout: Timeout inverval for retransmission. + * @rtx_timeout.expires: Time specifying when the reaper work is next scheduled. + * @rtx_timeout.reaper: Work performing timeout checks and subsequent actions. + * @ops: Request layer operations. @@ -15660,11 +15844,16 @@ index 000000000000..cfcfeb0c2c56 +#endif /* _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H */ diff --git a/drivers/misc/surface_aggregator/trace.h b/drivers/misc/surface_aggregator/trace.h new file mode 100644 -index 000000000000..e99e35a2d939 +index 000000000000..86da9631fdf2 --- /dev/null +++ b/drivers/misc/surface_aggregator/trace.h -@@ -0,0 +1,621 @@ +@@ -0,0 +1,625 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Trace points for SSAM/SSH. ++ * ++ * Copyright (C) 2020 Maximilian Luz ++ */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM surface_aggregator @@ -16004,18 +16193,18 @@ index 000000000000..e99e35a2d939 + TP_ARGS(packet), + + TP_STRUCT__entry( ++ __field(unsigned long, state) + __array(char, uid, SSAM_PTR_UID_LEN) + __field(u8, priority) + __field(u16, length) -+ __field(unsigned long, state) + __field(u16, seq) + ), + + TP_fast_assign( ++ __entry->state = READ_ONCE(packet->state); + ssam_trace_ptr_uid(packet, __entry->uid); + __entry->priority = READ_ONCE(packet->priority); + __entry->length = packet->data.len; -+ __entry->state = READ_ONCE(packet->state); + __entry->seq = ssam_trace_get_packet_seq(packet); + ), + @@ -16042,21 +16231,21 @@ index 000000000000..e99e35a2d939 + TP_ARGS(packet, status), + + TP_STRUCT__entry( ++ __field(unsigned long, state) ++ __field(int, status) + __array(char, uid, SSAM_PTR_UID_LEN) + __field(u8, priority) + __field(u16, length) -+ __field(unsigned long, state) + __field(u16, seq) -+ __field(int, status) + ), + + TP_fast_assign( ++ __entry->state = READ_ONCE(packet->state); ++ __entry->status = status; + ssam_trace_ptr_uid(packet, __entry->uid); + __entry->priority = READ_ONCE(packet->priority); + __entry->length = packet->data.len; -+ __entry->state = READ_ONCE(packet->state); + __entry->seq = ssam_trace_get_packet_seq(packet); -+ __entry->status = status; + ), + + TP_printk("uid=%s, seq=%s, ty=%s, pri=0x%02x, len=%u, sta=%s, status=%d", @@ -16083,9 +16272,9 @@ index 000000000000..e99e35a2d939 + TP_ARGS(request), + + TP_STRUCT__entry( -+ __array(char, uid, SSAM_PTR_UID_LEN) + __field(unsigned long, state) + __field(u32, rqid) ++ __array(char, uid, SSAM_PTR_UID_LEN) + __field(u8, tc) + __field(u16, cid) + __field(u16, iid) @@ -16095,9 +16284,9 @@ index 000000000000..e99e35a2d939 + const struct ssh_packet *p = &request->packet; + + // use packet for UID so we can match requests to packets -+ ssam_trace_ptr_uid(p, __entry->uid); + __entry->state = READ_ONCE(request->state); + __entry->rqid = ssam_trace_get_request_id(p); ++ ssam_trace_ptr_uid(p, __entry->uid); + __entry->tc = ssam_trace_get_request_tc(p); + __entry->cid = ssam_trace_get_command_field_u8(p, cid); + __entry->iid = ssam_trace_get_command_field_u8(p, iid); @@ -16127,26 +16316,26 @@ index 000000000000..e99e35a2d939 + TP_ARGS(request, status), + + TP_STRUCT__entry( -+ __array(char, uid, SSAM_PTR_UID_LEN) + __field(unsigned long, state) + __field(u32, rqid) ++ __field(int, status) ++ __array(char, uid, SSAM_PTR_UID_LEN) + __field(u8, tc) + __field(u16, cid) + __field(u16, iid) -+ __field(int, status) + ), + + TP_fast_assign( + const struct ssh_packet *p = &request->packet; + + // use packet for UID so we can match requests to packets -+ ssam_trace_ptr_uid(p, __entry->uid); + __entry->state = READ_ONCE(request->state); + __entry->rqid = ssam_trace_get_request_id(p); ++ __entry->status = status; ++ ssam_trace_ptr_uid(p, __entry->uid); + __entry->tc = ssam_trace_get_request_tc(p); + __entry->cid = ssam_trace_get_command_field_u8(p, cid); + __entry->iid = ssam_trace_get_command_field_u8(p, iid); -+ __entry->status = status; + ), + + TP_printk("uid=%s, rqid=%s, ty=%s, sta=%s, tc=%s, cid=%s, iid=%s, status=%d", @@ -16174,13 +16363,13 @@ index 000000000000..e99e35a2d939 + TP_ARGS(ptr, len), + + TP_STRUCT__entry( -+ __array(char, uid, SSAM_PTR_UID_LEN) + __field(size_t, len) ++ __array(char, uid, SSAM_PTR_UID_LEN) + ), + + TP_fast_assign( -+ ssam_trace_ptr_uid(ptr, __entry->uid); + __entry->len = len; ++ ssam_trace_ptr_uid(ptr, __entry->uid); + ), + + TP_printk("uid=%s, len=%zu", __entry->uid, __entry->len) @@ -16200,7 +16389,6 @@ index 000000000000..e99e35a2d939 + + TP_STRUCT__entry( + __array(char, uid, SSAM_PTR_UID_LEN) -+ __field(size_t, len) + ), + + TP_fast_assign( @@ -16223,13 +16411,13 @@ index 000000000000..e99e35a2d939 + TP_ARGS(property, value), + + TP_STRUCT__entry( -+ __string(property, property) + __field(unsigned int, value) ++ __string(property, property) + ), + + TP_fast_assign( -+ __assign_str(property, property); + __entry->value = value; ++ __assign_str(property, property); + ), + + TP_printk("%s=%u", __get_str(property), __entry->value) @@ -16314,16 +16502,18 @@ index e14cbe444afc..9932a610bda0 100644 #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/surface_acpi_notify.h b/include/linux/surface_acpi_notify.h new file mode 100644 -index 000000000000..ee5e04f2eb48 +index 000000000000..d45e2420eaa0 --- /dev/null +++ b/include/linux/surface_acpi_notify.h -@@ -0,0 +1,37 @@ +@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Interface for Surface ACPI Notify (SAN) driver. + * + * Provides access to discrete GPU notifications sent from ACPI via the SAN + * driver, which are not handled by this driver directly. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#ifndef _LINUX_SURFACE_ACPI_NOTIFY_H @@ -16357,10 +16547,10 @@ index 000000000000..ee5e04f2eb48 +#endif /* _LINUX_SURFACE_ACPI_NOTIFY_H */ diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h new file mode 100644 -index 000000000000..2fac2cbf8816 +index 000000000000..f4f93c2072f0 --- /dev/null +++ b/include/linux/surface_aggregator/controller.h -@@ -0,0 +1,812 @@ +@@ -0,0 +1,814 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Surface System Aggregator Module (SSAM) controller interface. @@ -16368,6 +16558,8 @@ index 000000000000..2fac2cbf8816 + * Main communication interface for the SSAM EC. Provides a controller + * managing access and communication to and from the SSAM EC, as well as main + * communication structures and definitions. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H @@ -17175,10 +17367,10 @@ index 000000000000..2fac2cbf8816 +#endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */ diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h new file mode 100644 -index 000000000000..9c5f691a957e +index 000000000000..80b6d5a0fa97 --- /dev/null +++ b/include/linux/surface_aggregator/device.h -@@ -0,0 +1,408 @@ +@@ -0,0 +1,430 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Surface System Aggregator Module (SSAM) bus and client-device subsystem. @@ -17187,6 +17379,8 @@ index 000000000000..9c5f691a957e + * devices, and respective drivers building on top of the SSAM controller. + * Provides support for non-platform/non-ACPI SSAM clients via dedicated + * subsystem. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H @@ -17240,6 +17434,11 @@ index 000000000000..9c5f691a957e + +/* + * Special values for device matching. ++ * ++ * These values are intended to be used with SSAM_DEVICE(), SSAM_VDEV(), and ++ * SSAM_SDEV() exclusively. Specifically, they are used to initialize the ++ * match_flags member of the device ID structure. Do not use them directly ++ * with struct ssam_device_id or struct ssam_device_uid. + */ +#define SSAM_ANY_TID 0xffff +#define SSAM_ANY_IID 0xffff @@ -17260,6 +17459,11 @@ index 000000000000..9c5f691a957e + * matching should ignore target ID, instance ID, and/or sub-function, + * respectively. This macro initializes the ``match_flags`` field based on the + * given parameters. ++ * ++ * Note: The parameters @d and @cat must be valid &u8 values, the parameters ++ * @tid, @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID, ++ * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not ++ * allowed. + */ +#define SSAM_DEVICE(d, cat, tid, iid, fun) \ + .match_flags = (((tid) != SSAM_ANY_TID) ? SSAM_MATCH_TARGET : 0) \ @@ -17285,6 +17489,11 @@ index 000000000000..9c5f691a957e + * %SSAM_ANY_FUN can be used to specify that matching should ignore target ID, + * instance ID, and/or sub-function, respectively. This macro initializes the + * ``match_flags`` field based on the given parameters. ++ * ++ * Note: The parameter @cat must be a valid &u8 value, the parameters @tid, ++ * @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID, ++ * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not ++ * allowed. + */ +#define SSAM_VDEV(cat, tid, iid, fun) \ + SSAM_DEVICE(SSAM_DOMAIN_VIRTUAL, SSAM_VIRTUAL_TC_##cat, tid, iid, fun) @@ -17304,6 +17513,11 @@ index 000000000000..9c5f691a957e + * used to specify that matching should ignore target ID, instance ID, and/or + * sub-function, respectively. This macro initializes the ``match_flags`` + * field based on the given parameters. ++ * ++ * Note: The parameter @cat must be a valid &u8 value, the parameters @tid, ++ * @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID, ++ * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not ++ * allowed. + */ +#define SSAM_SDEV(cat, tid, iid, fun) \ + SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun) @@ -17419,8 +17633,7 @@ index 000000000000..9c5f691a957e + */ +static inline struct ssam_device *ssam_device_get(struct ssam_device *sdev) +{ -+ get_device(&sdev->dev); -+ return sdev; ++ return sdev ? to_ssam_device(get_device(&sdev->dev)) : NULL; +} + +/** @@ -17435,7 +17648,8 @@ index 000000000000..9c5f691a957e + */ +static inline void ssam_device_put(struct ssam_device *sdev) +{ -+ put_device(&sdev->dev); ++ if (sdev) ++ put_device(&sdev->dev); +} + +/** @@ -17589,10 +17803,10 @@ index 000000000000..9c5f691a957e +#endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */ diff --git a/include/linux/surface_aggregator/serial_hub.h b/include/linux/surface_aggregator/serial_hub.h new file mode 100644 -index 000000000000..01abd2b78172 +index 000000000000..de4dbb3cda58 --- /dev/null +++ b/include/linux/surface_aggregator/serial_hub.h -@@ -0,0 +1,657 @@ +@@ -0,0 +1,659 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Surface Serial Hub (SSH) protocol and communication interface. @@ -17600,6 +17814,8 @@ index 000000000000..01abd2b78172 + * Lower-level communication layers and SSH protocol definitions for the + * Surface System Aggregator Module (SSAM). Provides the interface for basic + * packet- and request-based communication with the SSAM EC via SSH. ++ * ++ * Copyright (C) 2019-2020 Maximilian Luz + */ + +#ifndef _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H @@ -17726,7 +17942,7 @@ index 000000000000..01abd2b78172 +#define SSH_MSG_LEN_CTRL SSH_MSG_LEN_BASE + +/** -+ * SSH_MESSAGE_LENGTH() - Comute lenght of SSH message. ++ * SSH_MESSAGE_LENGTH() - Comute length of SSH message. + * @payload_size: Length of the payload inside the SSH frame. + * + * Return: Returns the length of a SSH message with payload of specified size. @@ -18213,8 +18429,7 @@ index 000000000000..01abd2b78172 + */ +static inline struct ssh_request *ssh_request_get(struct ssh_request *r) +{ -+ ssh_packet_get(&r->packet); -+ return r; ++ return r ? to_ssh_request(ssh_packet_get(&r->packet)) : NULL; +} + +/** @@ -18231,7 +18446,8 @@ index 000000000000..01abd2b78172 + */ +static inline void ssh_request_put(struct ssh_request *r) +{ -+ ssh_packet_put(&r->packet); ++ if (r) ++ ssh_packet_put(&r->packet); +} + +/** @@ -18250,6 +18466,70 @@ index 000000000000..01abd2b78172 +} + +#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..43819986f911 +--- /dev/null ++++ b/include/uapi/linux/surface_aggregator/cdev.h +@@ -0,0 +1,58 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Surface System Aggregator Module (SSAM) user-space EC interface. ++ * ++ * Definitions, structs, and IOCTLs for the /dev/surface/aggregator misc ++ * device. This device provides direct user-space access to the SSAM EC. ++ * Intended for debugging and development. ++ * ++ * Copyright (C) 2020 Maximilian Luz ++ */ ++ ++#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H ++#define _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H ++ ++#include ++#include ++ ++/** ++ * struct ssam_cdev_request - Controller request IOCTL argument. ++ * @target_category: Target category of the SAM request. ++ * @target_id: Target ID of the SAM request. ++ * @command_id: Command ID of the SAM request. ++ * @instance_id: Instance ID of the SAM request. ++ * @flags: SAM Request flags. ++ * @status: Request status (output). ++ * @payload: Request payload (input data). ++ * @payload.data: Pointer to request payload data. ++ * @payload.length: Length of request payload data (in bytes). ++ * @response: Request response (output data). ++ * @response.data: Pointer to response buffer. ++ * @response.length: On input: Capacity of response buffer (in bytes). ++ * On output: Length of request response (number of bytes ++ * in the buffer that are actually used). ++ */ ++struct ssam_cdev_request { ++ __u8 target_category; ++ __u8 target_id; ++ __u8 command_id; ++ __u8 instance_id; ++ __u16 flags; ++ __s16 status; ++ ++ struct { ++ __u64 data; ++ __u16 length; ++ __u8 __pad[6]; ++ } payload; ++ ++ struct { ++ __u64 data; ++ __u16 length; ++ __u8 __pad[6]; ++ } response; ++} __attribute__((__packed__)); ++ ++#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request) ++ ++#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 010be8ba2116..6b761c5b3ddd 100644 --- a/scripts/mod/devicetable-offsets.c @@ -18269,7 +18549,7 @@ index 010be8ba2116..6b761c5b3ddd 100644 return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c -index 9599e2a3f1e6..6c308e18f16b 100644 +index 9599e2a3f1e6..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) @@ -18277,7 +18557,7 @@ index 9599e2a3f1e6..6c308e18f16b 100644 } +/* -+ * Looks like: ssam:cNtNiNfN ++ * Looks like: ssam:dNcNtNiNfN + * + * N is exactly 2 digits, where each is an upper-case hex digit. + */ diff --git a/patches/5.8/0005-surface-sam-over-hid.patch b/patches/5.8/0005-surface-sam-over-hid.patch index 4fd45cfa8..0500827ba 100644 --- a/patches/5.8/0005-surface-sam-over-hid.patch +++ b/patches/5.8/0005-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From c60bb64dbfa33c12dd39f441405f95b60dab533e Mon Sep 17 00:00:00 2001 +From eeca95aae2ca982584ff3fea350e0db1b46db63d Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH 5/6] surface-sam-over-hid diff --git a/patches/5.8/0006-surface-gpe.patch b/patches/5.8/0006-surface-gpe.patch index 9e1b42e74..7e3e04e31 100644 --- a/patches/5.8/0006-surface-gpe.patch +++ b/patches/5.8/0006-surface-gpe.patch @@ -1,4 +1,4 @@ -From 5f1628a5cbab46c365dded5808524286cb8d686f Mon Sep 17 00:00:00 2001 +From d21aa94d36a6b3361b03bc3d154525cf30cf9bad Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 16 Aug 2020 23:39:56 +0200 Subject: [PATCH 6/6] surface-gpe diff --git a/pkg/arch/kernel/PKGBUILD b/pkg/arch/kernel/PKGBUILD index 8ec2f9e4a..1561ebdc4 100644 --- a/pkg/arch/kernel/PKGBUILD +++ b/pkg/arch/kernel/PKGBUILD @@ -39,14 +39,14 @@ validpgpkeys=( ) sha256sums=('aa509b88cd57f909d5aaaf6d337d9553b828bfc58eea79a1e660f5fe09bcb4c5' '181330a9cf4517abbbe29b93165bc859ad8ca14a43582f4e1d69aae2b5ecc2c9' - '2639d6f278da82a70746d002d358197145a1916c45f495c39bb6e2ef168a676a' - '8dbaa21d2c03621b0c5d96c4fbcc7a839bea5a34a5f2279a409c3b404756e753' - 'e23c77883c3dca61c5ebc6689310bbb5b28f22255354bfb02a53a43e18354643' - 'b7c4f87ca8b0b14ec5bc55cf6ed1eee9c0a567090448964cdb5b580625e386dc' - 'd37b50ea734343fb48284583ea75363d39bc747e1f8fe5a10361d09bd791a805' - '6359d231c78e3976cde00aecb7dfbb7b04ee5952eb7ad8f8de09eafd5c4d72d1' - 'e0ec4e54262829a771b84de688750cace6be5f3d1141ba15a0812b03c6fd13b9' - 'afe54fbf214b55ccd6cd6ee80bd194cdc1f44eb52f0967d2d8f46c6786ef1e3f') + 'ba10bbf15bdc395691d14be9ce62d9f68d02eb9f59530743b6db34987953c9a8' + '8cd2b019aac6d3807a5cdcbbbe0aad81e63193ff3e8dffd7a79d4a1421b858f6' + 'cc6a195f5145d6d99f7f4758bc31009f4227247f2af43de9e9f72af5b0cdd7a7' + '53fcd223d74941ed10a8f7ee0a8c3fa01bd8a2515ff5f629a680c3fbcf7cd1e1' + '7e20487e8aa4814a6442823802931e6e2d4f784cd903f922f7d78c4241519e58' + '2f4aeec03ca00933e893a56e1e9236e2dd208886f9957c0f6b9bbec2d80785d8' + 'b7437281fce14010fbbeafdfbcc06c0210507ff56d7df1e689bba8b79bd09ccd' + 'ae0d4045afcd32e5d7a61d03738c8f47b679676291e144fe353fb1e8e1187dff') export KBUILD_BUILD_HOST=archlinux