From b931b147da670fd9507391d41e56d34953ae687b Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Dec 2019 00:54:14 +0100 Subject: [PATCH] Update patches from kernel repo - surface-acpi: - integrate dGPU hot-plug system - fix MODULE_ALIAS definitions for auto-loading SID battery and ac drivers - disable DMA for surface3-spi touchscreen driver - new wifi suspend patch (only 5.3/5.4) - ipts: - do not automatically switch sensor modes (only 5.3) --- patches/4.19/0001-surface-acpi.patch | 1348 ++++++++++++++++- patches/4.19/0002-suspend.patch | 6 +- patches/4.19/0003-buttons.patch | 4 +- patches/4.19/0004-cameras.patch | 8 +- patches/4.19/0005-ipts.patch | 4 +- patches/4.19/0006-hid.patch | 4 +- patches/4.19/0007-sdcard-reader.patch | 6 +- patches/4.19/0008-wifi.patch | 6 +- patches/4.19/0009-surface3-power.patch | 4 +- patches/4.19/0010-mwlwifi.patch | 4 +- patches/4.19/0011-surface-lte.patch | 4 +- ...ioremap_uc.patch => 0012-ioremap_uc.patch} | 23 +- patches/4.19/0012-surfacebook2-dgpu.patch | 359 ----- patches/4.19/0013-surface3-spi-dma.patch | 63 + patches/5.3/0001-surface-acpi.patch | 1348 ++++++++++++++++- patches/5.3/0002-buttons.patch | 4 +- .../5.3/{0004-hid.patch => 0003-hid.patch} | 6 +- patches/5.3/0003-surfacebook2-dgpu.patch | 359 ----- ...-power.patch => 0004-surface3-power.patch} | 14 +- ...rface-lte.patch => 0005-surface-lte.patch} | 6 +- patches/5.3/0006-wifi.patch | 171 +++ ...gacy-i915.patch => 0007-legacy-i915.patch} | 6 +- patches/5.3/0007-wifi.patch | 267 ---- .../5.3/{0009-ipts.patch => 0008-ipts.patch} | 44 +- ...ioremap_uc.patch => 0009-ioremap_uc.patch} | 23 +- patches/5.3/0010-surface3-spi-dma.patch | 63 + patches/5.4/0001-ioremap_uc.patch | 21 +- patches/5.4/0002-hid.patch | 4 +- patches/5.4/0003-surface-acpi.patch | 1348 ++++++++++++++++- ...-power.patch => 0004-surface3-power.patch} | 14 +- patches/5.4/0004-surfacebook2-dgpu.patch | 359 ----- ...rface-lte.patch => 0005-surface-lte.patch} | 6 +- patches/5.4/0006-wifi.patch | 168 ++ patches/5.4/0007-surface3-spi-dma.patch | 63 + patches/5.4/0007-wifi.patch | 267 ---- 35 files changed, 4537 insertions(+), 1867 deletions(-) rename patches/4.19/{0013-ioremap_uc.patch => 0012-ioremap_uc.patch} (81%) delete mode 100644 patches/4.19/0012-surfacebook2-dgpu.patch create mode 100644 patches/4.19/0013-surface3-spi-dma.patch rename patches/5.3/{0004-hid.patch => 0003-hid.patch} (89%) delete mode 100644 patches/5.3/0003-surfacebook2-dgpu.patch rename patches/5.3/{0005-surface3-power.patch => 0004-surface3-power.patch} (98%) rename patches/5.3/{0006-surface-lte.patch => 0005-surface-lte.patch} (88%) create mode 100644 patches/5.3/0006-wifi.patch rename patches/5.3/{0008-legacy-i915.patch => 0007-legacy-i915.patch} (99%) delete mode 100644 patches/5.3/0007-wifi.patch rename patches/5.3/{0009-ipts.patch => 0008-ipts.patch} (99%) rename patches/5.3/{0010-ioremap_uc.patch => 0009-ioremap_uc.patch} (81%) create mode 100644 patches/5.3/0010-surface3-spi-dma.patch rename patches/5.4/{0005-surface3-power.patch => 0004-surface3-power.patch} (98%) delete mode 100644 patches/5.4/0004-surfacebook2-dgpu.patch rename patches/5.4/{0006-surface-lte.patch => 0005-surface-lte.patch} (88%) create mode 100644 patches/5.4/0006-wifi.patch create mode 100644 patches/5.4/0007-surface3-spi-dma.patch delete mode 100644 patches/5.4/0007-wifi.patch diff --git a/patches/4.19/0001-surface-acpi.patch b/patches/4.19/0001-surface-acpi.patch index f83c408a2..33ab9ea62 100644 --- a/patches/4.19/0001-surface-acpi.patch +++ b/patches/4.19/0001-surface-acpi.patch @@ -1,4 +1,4 @@ -From 6f856737d284a9242f98f646408af7d4c04b2319 Mon Sep 17 00:00:00 2001 +From 0003680bafe3efd813a0802fcf7add69873a3b4a Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:15:40 +0200 Subject: [PATCH 01/13] surface-acpi @@ -8,24 +8,28 @@ Subject: [PATCH 01/13] surface-acpi drivers/acpi/acpica/exfield.c | 26 +- drivers/platform/x86/Kconfig | 2 + drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface_sam/Kconfig | 155 ++ - drivers/platform/x86/surface_sam/Makefile | 9 + + drivers/platform/x86/surface_sam/Kconfig | 166 ++ + drivers/platform/x86/surface_sam/Makefile | 10 + .../x86/surface_sam/surface_sam_dtx.c | 623 ++++++ - .../x86/surface_sam/surface_sam_san.c | 791 ++++++++ + .../x86/surface_sam/surface_sam_hps.c | 1110 +++++++++++ + .../x86/surface_sam/surface_sam_san.c | 901 +++++++++ + .../x86/surface_sam/surface_sam_san.h | 29 + .../x86/surface_sam/surface_sam_sid.c | 117 ++ .../x86/surface_sam/surface_sam_sid_gpelid.c | 219 ++ .../surface_sam/surface_sam_sid_perfmode.c | 225 +++ - .../x86/surface_sam/surface_sam_sid_power.c | 1258 ++++++++++++ + .../x86/surface_sam/surface_sam_sid_power.c | 1259 ++++++++++++ .../x86/surface_sam/surface_sam_sid_vhf.c | 440 ++++ - .../x86/surface_sam/surface_sam_ssh.c | 1779 +++++++++++++++++ + .../x86/surface_sam/surface_sam_ssh.c | 1773 +++++++++++++++++ .../x86/surface_sam/surface_sam_ssh.h | 97 + .../x86/surface_sam/surface_sam_vhf.c | 276 +++ drivers/tty/serdev/core.c | 111 +- - 17 files changed, 6102 insertions(+), 29 deletions(-) + 19 files changed, 7358 insertions(+), 29 deletions(-) create mode 100644 drivers/platform/x86/surface_sam/Kconfig create mode 100644 drivers/platform/x86/surface_sam/Makefile create mode 100644 drivers/platform/x86/surface_sam/surface_sam_dtx.c + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_hps.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.c + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.h create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid_gpelid.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid_perfmode.c @@ -125,10 +129,10 @@ index dc29af4d8e2f..ddc2fbfaf110 100644 +obj-$(CONFIG_SURFACE_SAM) += surface_sam/ diff --git a/drivers/platform/x86/surface_sam/Kconfig b/drivers/platform/x86/surface_sam/Kconfig new file mode 100644 -index 000000000000..256a7c3f1fa3 +index 000000000000..4eff58a121cb --- /dev/null +++ b/drivers/platform/x86/surface_sam/Kconfig -@@ -0,0 +1,155 @@ +@@ -0,0 +1,166 @@ +menuconfig SURFACE_SAM + depends on ACPI + tristate "Microsoft Surface/System Aggregator Module and Platform Drivers" @@ -222,6 +226,17 @@ index 000000000000..256a7c3f1fa3 + + If you are not sure, say M here. + ++config SURFACE_SAM_HPS ++ tristate "Surface dGPU Hot-Plug System (dGPU-HPS) Driver" ++ depends on SURFACE_SAM_SSH ++ depends on SURFACE_SAM_SAN ++ default m ++ ---help--- ++ Driver to properly handle hot-plugging and explicit power-on/power-off ++ of the discrete GPU (dGPU) on the Surface Book 2. ++ ++ If you are not sure, say M here. ++ +config SURFACE_SAM_SID + tristate "Surface Platform Integration Driver" + depends on SURFACE_SAM_SSH @@ -286,13 +301,14 @@ index 000000000000..256a7c3f1fa3 + If you are not sure, say M here. diff --git a/drivers/platform/x86/surface_sam/Makefile b/drivers/platform/x86/surface_sam/Makefile new file mode 100644 -index 000000000000..97ef66ff273d +index 000000000000..188975ccde5c --- /dev/null +++ b/drivers/platform/x86/surface_sam/Makefile -@@ -0,0 +1,9 @@ +@@ -0,0 +1,10 @@ +obj-$(CONFIG_SURFACE_SAM_SSH) += surface_sam_ssh.o +obj-$(CONFIG_SURFACE_SAM_SAN) += surface_sam_san.o +obj-$(CONFIG_SURFACE_SAM_DTX) += surface_sam_dtx.o ++obj-$(CONFIG_SURFACE_SAM_HPS) += surface_sam_hps.o +obj-$(CONFIG_SURFACE_SAM_VHF) += surface_sam_vhf.o +obj-$(CONFIG_SURFACE_SAM_SID) += surface_sam_sid.o +obj-$(CONFIG_SURFACE_SAM_SID_GPELID) += surface_sam_sid_gpelid.o @@ -928,12 +944,1128 @@ index 000000000000..4b924de6ab09 +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface Detachment System (DTX) Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surface_sam/surface_sam_hps.c b/drivers/platform/x86/surface_sam/surface_sam_hps.c +new file mode 100644 +index 000000000000..3b123bd3dcfe +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_hps.c +@@ -0,0 +1,1110 @@ ++/* ++ * Surface dGPU hot-plug system driver. ++ * Supports explicit setting of the dGPU power-state on the Surface Book 2 and ++ * properly handles hot-plugging by detaching the base. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "surface_sam_ssh.h" ++#include "surface_sam_san.h" ++ ++ ++// TODO: vgaswitcheroo integration ++ ++ ++static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix); ++ ++ ++#define SHPS_DSM_REVISION 1 ++#define SHPS_DSM_GPU_ADDRS 0x02 ++#define SHPS_DSM_GPU_POWER 0x05 ++static const guid_t SHPS_DSM_UUID = ++ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, ++ 0x32, 0x0e, 0x10, 0x36, 0x0a); ++ ++ ++#define SAM_DGPU_TC 0x13 ++#define SAM_DGPU_CID_POWERON 0x02 ++ ++#define SAM_DTX_TC 0x11 ++#define SAM_DTX_CID_LATCH_LOCK 0x06 ++#define SAM_DTX_CID_LATCH_UNLOCK 0x07 ++ ++#define SHPS_DSM_GPU_ADDRS_RP "RP5_PCIE" ++#define SHPS_DSM_GPU_ADDRS_DGPU "DGPU_PCIE" ++ ++ ++static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; ++static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; ++ ++static const struct acpi_gpio_mapping shps_acpi_gpios[] = { ++ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, ++ { "base_presence-gpio", &gpio_base_presence, 1 }, ++ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, ++ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, ++ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, ++ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, ++ { }, ++}; ++ ++ ++enum shps_dgpu_power { ++ SHPS_DGPU_POWER_OFF = 0, ++ SHPS_DGPU_POWER_ON = 1, ++ SHPS_DGPU_POWER_UNKNOWN = 2, ++}; ++ ++static const char* shps_dgpu_power_str(enum shps_dgpu_power power) { ++ if (power == SHPS_DGPU_POWER_OFF) ++ return "off"; ++ else if (power == SHPS_DGPU_POWER_ON) ++ return "on"; ++ else if (power == SHPS_DGPU_POWER_UNKNOWN) ++ return "unknown"; ++ else ++ return ""; ++} ++ ++ ++struct shps_driver_data { ++ struct mutex lock; ++ struct pci_dev *dgpu_root_port; ++ struct pci_saved_state *dgpu_root_port_state; ++ struct gpio_desc *gpio_dgpu_power; ++ struct gpio_desc *gpio_dgpu_presence; ++ struct gpio_desc *gpio_base_presence; ++ unsigned int irq_dgpu_presence; ++ unsigned int irq_base_presence; ++ unsigned long state; ++}; ++ ++#define SHPS_STATE_BIT_PWRTGT 0 /* desired power state: 1 for on, 0 for off */ ++#define SHPS_STATE_BIT_RPPWRON_SYNC 1 /* synchronous/requested power-up in progress */ ++#define SHPS_STATE_BIT_WAKE_ENABLED 2 /* wakeup via base-presence GPIO enabled */ ++ ++ ++#define SHPS_DGPU_PARAM_PERM (S_IRUGO | S_IWUSR) ++ ++enum shps_dgpu_power_mp { ++ SHPS_DGPU_MP_POWER_OFF = SHPS_DGPU_POWER_OFF, ++ SHPS_DGPU_MP_POWER_ON = SHPS_DGPU_POWER_ON, ++ SHPS_DGPU_MP_POWER_ASIS = -1, ++ ++ __SHPS_DGPU_MP_POWER_START = -1, ++ __SHPS_DGPU_MP_POWER_END = 1, ++}; ++ ++static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) ++{ ++ int power = SHPS_DGPU_MP_POWER_OFF; ++ int status; ++ ++ status = kstrtoint(val, 0, &power); ++ if (status) { ++ return status; ++ } ++ ++ if (power < __SHPS_DGPU_MP_POWER_START || power > __SHPS_DGPU_MP_POWER_END) { ++ return -EINVAL; ++ } ++ ++ return param_set_int(val, kp); ++} ++ ++static const struct kernel_param_ops param_dgpu_power_ops = { ++ .set = param_dgpu_power_set, ++ .get = param_get_int, ++}; ++ ++static int param_dgpu_power_init = SHPS_DGPU_MP_POWER_OFF; ++static int param_dgpu_power_exit = SHPS_DGPU_MP_POWER_ON; ++static int param_dgpu_power_susp = SHPS_DGPU_MP_POWER_ASIS; ++static bool param_dtx_latch = true; ++ ++module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SHPS_DGPU_PARAM_PERM); ++module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SHPS_DGPU_PARAM_PERM); ++module_param_cb(dgpu_power_susp, ¶m_dgpu_power_ops, ¶m_dgpu_power_susp, SHPS_DGPU_PARAM_PERM); ++module_param_named(dtx_latch, param_dtx_latch, bool, SHPS_DGPU_PARAM_PERM); ++ ++MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is, default: off)"); ++MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: on)"); ++MODULE_PARM_DESC(dgpu_power_susp, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: as-is)"); ++MODULE_PARM_DESC(dtx_latch, "lock/unlock DTX base latch in accordance to power-state (Y/n)"); ++ ++ ++static int dtx_cmd_simple(u8 cid) ++{ ++ struct surface_sam_ssh_rqst rqst = { ++ .tc = SAM_DTX_TC, ++ .cid = cid, ++ .iid = 0, ++ .pri = SURFACE_SAM_PRIORITY_NORMAL, ++ .snc = 0, ++ .cdl = 0, ++ .pld = NULL, ++ }; ++ ++ return surface_sam_ssh_rqst(&rqst, NULL); ++} ++ ++inline static int shps_dtx_latch_lock(void) ++{ ++ return dtx_cmd_simple(SAM_DTX_CID_LATCH_LOCK); ++} ++ ++inline static int shps_dtx_latch_unlock(void) ++{ ++ return dtx_cmd_simple(SAM_DTX_CID_LATCH_UNLOCK); ++} ++ ++ ++static int shps_dgpu_dsm_get_pci_addr(struct platform_device *pdev, const char* entry) ++{ ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ union acpi_object *result; ++ union acpi_object *e0; ++ union acpi_object *e1; ++ union acpi_object *e2; ++ u64 device_addr = 0; ++ u8 bus, dev, fun; ++ int i; ++ ++ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, ++ SHPS_DSM_GPU_ADDRS, NULL, ACPI_TYPE_PACKAGE); ++ ++ if (IS_ERR_OR_NULL(result)) ++ return result ? PTR_ERR(result) : -EIO; ++ ++ // three entries per device: name, address, ++ for (i = 0; i + 2 < result->package.count; i += 3) { ++ e0 = &result->package.elements[i]; ++ e1 = &result->package.elements[i + 1]; ++ e2 = &result->package.elements[i + 2]; ++ ++ if (e0->type != ACPI_TYPE_STRING) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (e1->type != ACPI_TYPE_INTEGER) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (e2->type != ACPI_TYPE_INTEGER) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (strncmp(e0->string.pointer, entry, 64) == 0) ++ device_addr = e1->integer.value; ++ } ++ ++ ACPI_FREE(result); ++ if (device_addr == 0) ++ return -ENODEV; ++ ++ // convert address ++ bus = (device_addr & 0x0FF00000) >> 20; ++ dev = (device_addr & 0x000F8000) >> 15; ++ fun = (device_addr & 0x00007000) >> 12; ++ ++ return bus << 8 | PCI_DEVFN(dev, fun); ++} ++ ++static struct pci_dev *shps_dgpu_dsm_get_pci_dev(struct platform_device *pdev, const char* entry) ++{ ++ struct pci_dev *dev; ++ int addr; ++ ++ addr = shps_dgpu_dsm_get_pci_addr(pdev, entry); ++ if (addr < 0) ++ return ERR_PTR(addr); ++ ++ dev = pci_get_domain_bus_and_slot(0, (addr & 0xFF00) >> 8, addr & 0xFF); ++ return dev ? dev : ERR_PTR(-ENODEV); ++} ++ ++ ++static int shps_dgpu_dsm_get_power_unlocked(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct gpio_desc *gpio = drvdata->gpio_dgpu_power; ++ int status; ++ ++ status = gpiod_get_value_cansleep(gpio); ++ if (status < 0) ++ return status; ++ ++ return status == 0 ? SHPS_DGPU_POWER_OFF : SHPS_DGPU_POWER_ON; ++} ++ ++static int shps_dgpu_dsm_get_power(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_dsm_get_power_unlocked(pdev); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++static int __shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ union acpi_object *result; ++ union acpi_object param; ++ ++ dev_info(&pdev->dev, "setting dGPU direct power to \'%s\'\n", shps_dgpu_power_str(power)); ++ ++ param.type = ACPI_TYPE_INTEGER; ++ param.integer.value = power == SHPS_DGPU_POWER_ON; ++ ++ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, ++ SHPS_DSM_GPU_POWER, ¶m, ACPI_TYPE_BUFFER); ++ ++ if (IS_ERR_OR_NULL(result)) ++ return result ? PTR_ERR(result) : -EIO; ++ ++ // check for the expected result ++ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ ACPI_FREE(result); ++ return 0; ++} ++ ++static int shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) ++ return -EINVAL; ++ ++ status = shps_dgpu_dsm_get_power_unlocked(pdev); ++ if (status < 0) ++ return status; ++ if (status == power) ++ return 0; ++ ++ return __shps_dgpu_dsm_set_power_unlocked(pdev, power); ++} ++ ++static int shps_dgpu_dsm_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_dsm_set_power_unlocked(pdev, power); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++ ++static bool shps_rp_link_up(struct pci_dev *rp) ++{ ++ u16 lnksta = 0, sltsta = 0; ++ ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); ++ ++ return (lnksta & PCI_EXP_LNKSTA_DLLLA) || (sltsta & PCI_EXP_SLTSTA_PDS); ++} ++ ++ ++static int shps_dgpu_rp_get_power_unlocked(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ ++ if (rp->current_state == PCI_D3hot || rp->current_state == PCI_D3cold) ++ return SHPS_DGPU_POWER_OFF; ++ else if (rp->current_state == PCI_UNKNOWN || rp->current_state == PCI_POWER_ERROR) ++ return SHPS_DGPU_POWER_UNKNOWN; ++ else ++ return SHPS_DGPU_POWER_ON; ++} ++ ++static int shps_dgpu_rp_get_power(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++static int __shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ int status, i; ++ ++ dev_info(&pdev->dev, "setting dGPU power state to \'%s\'\n", shps_dgpu_power_str(power)); ++ ++ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.1"); ++ if (power == SHPS_DGPU_POWER_ON) { ++ set_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); ++ pci_set_power_state(rp, PCI_D0); ++ ++ if (drvdata->dgpu_root_port_state) ++ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); ++ ++ pci_restore_state(rp); ++ ++ if (!pci_is_enabled(rp)) ++ pci_enable_device(rp); ++ ++ pci_set_master(rp); ++ clear_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); ++ ++ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } else { ++ if (!drvdata->dgpu_root_port_state) { ++ pci_save_state(rp); ++ drvdata->dgpu_root_port_state = pci_store_saved_state(rp); ++ } ++ ++ /* ++ * To properly update the hot-plug system we need to "remove" the dGPU ++ * before disabling it and sending it to D3cold. Following this, we ++ * need to wait for the link and slot status to actually change. ++ */ ++ status = shps_dgpu_dsm_set_power_unlocked(pdev, SHPS_DGPU_POWER_OFF); ++ if (status) ++ return status; ++ ++ for (i = 0; i < 20 && shps_rp_link_up(rp); i++) ++ msleep(50); ++ ++ if (shps_rp_link_up(rp)) ++ dev_err(&pdev->dev, "dGPU removal via DSM timed out\n"); ++ ++ pci_clear_master(rp); ++ ++ if (pci_is_enabled(rp)) ++ pci_disable_device(rp); ++ ++ pci_set_power_state(rp, PCI_D3cold); ++ ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.2"); ++ ++ return 0; ++} ++ ++static int shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) ++ return -EINVAL; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ return status; ++ if (status == power) ++ return 0; ++ ++ return __shps_dgpu_rp_set_power_unlocked(pdev, power); ++} ++ ++static int shps_dgpu_rp_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_rp_set_power_unlocked(pdev, power); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++ ++static int shps_dgpu_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (!param_dtx_latch) ++ return shps_dgpu_rp_set_power(pdev, power); ++ ++ if (power == SHPS_DGPU_POWER_ON) { ++ status = shps_dtx_latch_lock(); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_rp_set_power(pdev, power); ++ if (status) ++ shps_dtx_latch_unlock(); ++ ++ return status; ++ } else { ++ status = shps_dgpu_rp_set_power(pdev, power); ++ if (status) ++ return status; ++ ++ return shps_dtx_latch_unlock(); ++ } ++} ++ ++ ++static int shps_dgpu_is_present(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ return gpiod_get_value_cansleep(drvdata->gpio_dgpu_presence); ++} ++ ++ ++static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int power = shps_dgpu_rp_get_power(pdev); ++ ++ if (power < 0) ++ return power; ++ ++ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); ++} ++ ++static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, ++ const char *data, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ enum shps_dgpu_power power; ++ bool b = false; ++ int status; ++ ++ status = kstrtobool(data, &b); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status <= 0) ++ return status < 0 ? status : -EPERM; ++ ++ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; ++ status = shps_dgpu_set_power(pdev, power); ++ ++ return status < 0 ? status : count; ++} ++ ++static ssize_t dgpu_power_dsm_show(struct device *dev, struct device_attribute *attr, char *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int power = shps_dgpu_dsm_get_power(pdev); ++ ++ if (power < 0) ++ return power; ++ ++ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); ++} ++ ++static ssize_t dgpu_power_dsm_store(struct device *dev, struct device_attribute *attr, ++ const char *data, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ enum shps_dgpu_power power; ++ bool b = false; ++ int status; ++ ++ status = kstrtobool(data, &b); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status <= 0) ++ return status < 0 ? status : -EPERM; ++ ++ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; ++ status = shps_dgpu_dsm_set_power(pdev, power); ++ ++ return status < 0 ? status : count; ++} ++ ++static DEVICE_ATTR_RW(dgpu_power); ++static DEVICE_ATTR_RW(dgpu_power_dsm); ++ ++static struct attribute *shps_power_attrs[] = { ++ &dev_attr_dgpu_power.attr, ++ &dev_attr_dgpu_power_dsm.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(shps_power); ++ ++ ++static void dbg_dump_power_states(struct platform_device *pdev, const char *prefix) ++{ ++ enum shps_dgpu_power power_dsm; ++ enum shps_dgpu_power power_rp; ++ int status; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ dev_err(&pdev->dev, "%s: failed to get root-port power state: %d\n", prefix, status); ++ power_rp = status; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ dev_err(&pdev->dev, "%s: failed to get direct power state: %d\n", prefix, status); ++ power_dsm = status; ++ ++ dev_dbg(&pdev->dev, "%s: root-port power state: %d\n", prefix, power_rp); ++ dev_dbg(&pdev->dev, "%s: direct power state: %d\n", prefix, power_dsm); ++} ++ ++static void dbg_dump_pciesta(struct platform_device *pdev, const char *prefix) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ u16 lnksta, lnksta2, sltsta, sltsta2; ++ ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA2, &lnksta2); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA2, &sltsta2); ++ ++ dev_dbg(&pdev->dev, "%s: LNKSTA: 0x%04x", prefix, lnksta); ++ dev_dbg(&pdev->dev, "%s: LNKSTA2: 0x%04x", prefix, lnksta2); ++ dev_dbg(&pdev->dev, "%s: SLTSTA: 0x%04x", prefix, sltsta); ++ dev_dbg(&pdev->dev, "%s: SLTSTA2: 0x%04x", prefix, sltsta2); ++} ++ ++static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ ++ dev_dbg(&pdev->dev, "%s: RP power: %d", prefix, rp->current_state); ++ dev_dbg(&pdev->dev, "%s: RP state saved: %d", prefix, rp->state_saved); ++ dev_dbg(&pdev->dev, "%s: RP state stored: %d", prefix, !!drvdata->dgpu_root_port_state); ++ dev_dbg(&pdev->dev, "%s: RP enabled: %d", prefix, atomic_read(&rp->enable_cnt)); ++ dev_dbg(&pdev->dev, "%s: RP mastered: %d", prefix, rp->is_busmaster); ++} ++ ++ ++static int shps_pm_prepare(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ bool pwrtgt; ++ int status = 0; ++ ++ dbg_dump_power_states(pdev, "shps_pm_prepare"); ++ ++ if (param_dgpu_power_susp != SHPS_DGPU_MP_POWER_ASIS) { ++ pwrtgt = test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ ++ status = shps_dgpu_set_power(pdev, param_dgpu_power_susp); ++ if (status) { ++ dev_err(&pdev->dev, "failed to power %s dGPU: %d\n", ++ param_dgpu_power_susp == SHPS_DGPU_MP_POWER_OFF ? "off" : "on", ++ status); ++ return status; ++ } ++ ++ if (pwrtgt) ++ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ else ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ ++ return 0; ++} ++ ++static void shps_pm_complete(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ dbg_dump_power_states(pdev, "shps_pm_complete"); ++ dbg_dump_pciesta(pdev, "shps_pm_complete"); ++ dbg_dump_drvsta(pdev, "shps_pm_complete.1"); ++ ++ // update power target, dGPU may have been detached while suspended ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) { ++ dev_err(&pdev->dev, "failed to get dGPU presence: %d\n", status); ++ return; ++ } else if (status == 0) { ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ ++ /* ++ * During resume, the PCIe core will power on the root-port, which in turn ++ * will power on the dGPU. Most of the state synchronization is already ++ * handled via the SAN RQSG handler, so it is in a fully consistent ++ * on-state here. If requested, turn it off here. ++ * ++ * As there seem to be some synchronization issues turning off the dGPU ++ * directly after the power-on SAN RQSG notification during the resume ++ * process, let's do this here. ++ * ++ * TODO/FIXME: ++ * This does not combat unhandled power-ons when the device is not fully ++ * resumed, i.e. re-suspended before shps_pm_complete is called. Those ++ * should normally not be an issue, but the dGPU does get hot even though ++ * it is suspended, so ideally we want to keep it off. ++ */ ++ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { ++ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); ++ if (status) ++ dev_err(&pdev->dev, "failed to power-off dGPU: %d\n", status); ++ } ++ ++ dbg_dump_drvsta(pdev, "shps_pm_complete.2"); ++} ++ ++static int shps_pm_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ if (device_may_wakeup(dev)) { ++ status = enable_irq_wake(drvdata->irq_base_presence); ++ if (status) ++ return status; ++ ++ set_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state); ++ } ++ ++ return 0; ++} ++ ++static int shps_pm_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status = 0; ++ ++ if (test_and_clear_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state)) { ++ status = disable_irq_wake(drvdata->irq_base_presence); ++ } ++ ++ return status; ++} ++ ++static void shps_shutdown(struct platform_device *pdev) ++{ ++ int status; ++ ++ /* ++ * Turn on dGPU before shutting down. This allows the core drivers to ++ * properly shut down the device. If we don't do this, the pcieport driver ++ * will complain that the device has already been disabled. ++ */ ++ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_ON); ++ if (status) ++ dev_err(&pdev->dev, "failed to turn on dGPU: %d\n", status); ++} ++ ++static int shps_dgpu_detached(struct platform_device *pdev) ++{ ++ dbg_dump_power_states(pdev, "shps_dgpu_detached"); ++ return shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); ++} ++ ++static int shps_dgpu_attached(struct platform_device *pdev) ++{ ++ dbg_dump_power_states(pdev, "shps_dgpu_attached"); ++ return 0; ++} ++ ++static int shps_dgpu_powered_on(struct platform_device *pdev) ++{ ++ /* ++ * This function gets called directly after a power-state transition of ++ * the dGPU root port out of D3cold state, indicating a power-on of the ++ * dGPU. Specifically, this function is called from the RQSG handler of ++ * SAN, invoked by the ACPI _ON method of the dGPU root port. This means ++ * that this function is run inside `pci_set_power_state(rp, ...)` ++ * syncrhonously and thus returns before the `pci_set_power_state` call ++ * does. ++ * ++ * `pci_set_power_state` may either be called by us or when the PCI ++ * subsystem decides to power up the root port (e.g. during resume). Thus ++ * we should use this function to ensure that the dGPU and root port ++ * states are consistent when an unexpected power-up is encountered. ++ */ ++ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ int status; ++ ++ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.1"); ++ ++ // if we caused the root port to power-on, return ++ if (test_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state)) ++ return 0; ++ ++ // if dGPU is not present, force power-target to off and return ++ status = shps_dgpu_is_present(pdev); ++ if (status == 0) ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ if (status <= 0) ++ return status; ++ ++ mutex_lock(&drvdata->lock); ++ ++ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.1"); ++ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.1"); ++ if (drvdata->dgpu_root_port_state) ++ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); ++ pci_restore_state(rp); ++ if (!pci_is_enabled(rp)) ++ pci_enable_device(rp); ++ pci_set_master(rp); ++ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.2"); ++ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.2"); ++ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.2"); ++ ++ mutex_unlock(&drvdata->lock); ++ ++ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { ++ dev_warn(&pdev->dev, "unexpected dGPU power-on detected"); ++ // TODO: schedule state re-check and update ++ } ++ ++ return 0; ++} ++ ++ ++static int shps_dgpu_handle_rqsg(struct surface_sam_san_rqsg *rqsg, void *data) ++{ ++ struct platform_device *pdev = data; ++ ++ if (rqsg->tc == SAM_DGPU_TC && rqsg->cid == SAM_DGPU_CID_POWERON) ++ return shps_dgpu_powered_on(pdev); ++ ++ dev_warn(&pdev->dev, "unimplemented dGPU request: RQSG(0x%02x, 0x%02x, 0x%02x)", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ return 0; ++} ++ ++static irqreturn_t shps_dgpu_presence_irq(int irq, void *data) ++{ ++ struct platform_device *pdev = data; ++ bool dgpu_present; ++ int status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) { ++ dev_err(&pdev->dev, "failed to check physical dGPU presence: %d\n", status); ++ return IRQ_HANDLED; ++ } ++ ++ dgpu_present = status != 0; ++ dev_info(&pdev->dev, "dGPU physically %s\n", dgpu_present ? "attached" : "detached"); ++ ++ if (dgpu_present) ++ status = shps_dgpu_attached(pdev); ++ else ++ status = shps_dgpu_detached(pdev); ++ ++ if (status) ++ dev_err(&pdev->dev, "error handling dGPU interrupt: %d\n", status); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t shps_base_presence_irq(int irq, void *data) ++{ ++ return IRQ_HANDLED; // nothing to do, just wake ++} ++ ++ ++static int shps_gpios_setup(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct gpio_desc *gpio_dgpu_power; ++ struct gpio_desc *gpio_dgpu_presence; ++ struct gpio_desc *gpio_base_presence; ++ int status; ++ ++ // get GPIOs ++ gpio_dgpu_power = devm_gpiod_get(&pdev->dev, "dgpu_power", GPIOD_IN); ++ if (IS_ERR(gpio_dgpu_power)) { ++ status = PTR_ERR(gpio_dgpu_power); ++ goto err_out; ++ } ++ ++ gpio_dgpu_presence = devm_gpiod_get(&pdev->dev, "dgpu_presence", GPIOD_IN); ++ if (IS_ERR(gpio_dgpu_presence)) { ++ status = PTR_ERR(gpio_dgpu_presence); ++ goto err_out; ++ } ++ ++ gpio_base_presence = devm_gpiod_get(&pdev->dev, "base_presence", GPIOD_IN); ++ if (IS_ERR(gpio_base_presence)) { ++ status = PTR_ERR(gpio_base_presence); ++ goto err_out; ++ } ++ ++ // export GPIOs ++ status = gpiod_export(gpio_dgpu_power, false); ++ if (status) ++ goto err_out; ++ ++ status = gpiod_export(gpio_dgpu_presence, false); ++ if (status) ++ goto err_export_dgpu_presence; ++ ++ status = gpiod_export(gpio_base_presence, false); ++ if (status) ++ goto err_export_base_presence; ++ ++ // create sysfs links ++ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_power", gpio_dgpu_power); ++ if (status) ++ goto err_link_dgpu_power; ++ ++ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_presence", gpio_dgpu_presence); ++ if (status) ++ goto err_link_dgpu_presence; ++ ++ status = gpiod_export_link(&pdev->dev, "gpio-base_presence", gpio_base_presence); ++ if (status) ++ goto err_link_base_presence; ++ ++ drvdata->gpio_dgpu_power = gpio_dgpu_power; ++ drvdata->gpio_dgpu_presence = gpio_dgpu_presence; ++ drvdata->gpio_base_presence = gpio_base_presence; ++ return 0; ++ ++err_link_base_presence: ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); ++err_link_dgpu_presence: ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); ++err_link_dgpu_power: ++ gpiod_unexport(gpio_base_presence); ++err_export_base_presence: ++ gpiod_unexport(gpio_dgpu_presence); ++err_export_dgpu_presence: ++ gpiod_unexport(gpio_dgpu_power); ++err_out: ++ return status; ++} ++ ++static void shps_gpios_remove(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-base_presence"); ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); ++ gpiod_unexport(drvdata->gpio_base_presence); ++ gpiod_unexport(drvdata->gpio_dgpu_presence); ++ gpiod_unexport(drvdata->gpio_dgpu_power); ++} ++ ++static int shps_gpios_setup_irq(struct platform_device *pdev) ++{ ++ const int irqf_dgpu = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ const int irqf_base = IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ status = gpiod_to_irq(drvdata->gpio_base_presence); ++ if (status < 0) ++ return status; ++ drvdata->irq_base_presence = status; ++ ++ status = gpiod_to_irq(drvdata->gpio_dgpu_presence); ++ if (status < 0) ++ return status; ++ drvdata->irq_dgpu_presence = status; ++ ++ status = request_irq(drvdata->irq_base_presence, ++ shps_base_presence_irq, irqf_base, ++ "shps_base_presence_irq", pdev); ++ if (status) ++ return status; ++ ++ status = request_threaded_irq(drvdata->irq_dgpu_presence, ++ NULL, shps_dgpu_presence_irq, irqf_dgpu, ++ "shps_dgpu_presence_irq", pdev); ++ if (status) { ++ free_irq(drvdata->irq_base_presence, pdev); ++ return status; ++ } ++ ++ return 0; ++} ++ ++static void shps_gpios_remove_irq(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ ++ free_irq(drvdata->irq_base_presence, pdev); ++ free_irq(drvdata->irq_dgpu_presence, pdev); ++} ++ ++static int shps_probe(struct platform_device *pdev) ++{ ++ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); ++ struct shps_driver_data *drvdata; ++ struct device_link *link; ++ int power, status; ++ ++ if (gpiod_count(&pdev->dev, NULL) < 0) ++ return -ENODEV; ++ ++ // link to SSH ++ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ if (status) { ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ } ++ ++ // link to SAN ++ status = surface_sam_san_consumer_register(&pdev->dev, 0); ++ if (status) { ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ } ++ ++ status = acpi_dev_add_driver_gpios(shps_dev, shps_acpi_gpios); ++ if (status) ++ return status; ++ ++ drvdata = kzalloc(sizeof(struct shps_driver_data), GFP_KERNEL); ++ if (!drvdata) { ++ status = -ENOMEM; ++ goto err_drvdata; ++ } ++ mutex_init(&drvdata->lock); ++ platform_set_drvdata(pdev, drvdata); ++ ++ drvdata->dgpu_root_port = shps_dgpu_dsm_get_pci_dev(pdev, SHPS_DSM_GPU_ADDRS_RP); ++ if (IS_ERR(drvdata->dgpu_root_port)) { ++ status = PTR_ERR(drvdata->dgpu_root_port); ++ goto err_rp_lookup; ++ } ++ ++ status = shps_gpios_setup(pdev); ++ if (status) ++ goto err_gpio; ++ ++ status = shps_gpios_setup_irq(pdev); ++ if (status) ++ goto err_gpio_irqs; ++ ++ status = device_add_groups(&pdev->dev, shps_power_groups); ++ if (status) ++ goto err_devattr; ++ ++ link = device_link_add(&pdev->dev, &drvdata->dgpu_root_port->dev, ++ DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER); ++ if (!link) ++ goto err_devlink; ++ ++ surface_sam_san_set_rqsg_handler(shps_dgpu_handle_rqsg, pdev); ++ ++ // if dGPU is not present turn-off root-port, else obey module param ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) ++ goto err_devlink; ++ ++ power = status == 0 ? SHPS_DGPU_POWER_OFF : param_dgpu_power_init; ++ if (power != SHPS_DGPU_MP_POWER_ASIS) { ++ status = shps_dgpu_set_power(pdev, power); ++ if (status) ++ goto err_devlink; ++ } ++ ++ device_init_wakeup(&pdev->dev, true); ++ return 0; ++ ++err_devlink: ++ device_remove_groups(&pdev->dev, shps_power_groups); ++err_devattr: ++ shps_gpios_remove_irq(pdev); ++err_gpio_irqs: ++ shps_gpios_remove(pdev); ++err_gpio: ++ pci_dev_put(drvdata->dgpu_root_port); ++err_rp_lookup: ++ platform_set_drvdata(pdev, NULL); ++ kfree(drvdata); ++err_drvdata: ++ acpi_dev_remove_driver_gpios(shps_dev); ++ return status; ++} ++ ++static int shps_remove(struct platform_device *pdev) ++{ ++ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ if (param_dgpu_power_exit != SHPS_DGPU_MP_POWER_ASIS) { ++ status = shps_dgpu_set_power(pdev, param_dgpu_power_exit); ++ if (status) ++ dev_err(&pdev->dev, "failed to set dGPU power state: %d\n", status); ++ } ++ ++ device_set_wakeup_capable(&pdev->dev, false); ++ surface_sam_san_set_rqsg_handler(NULL, NULL); ++ device_remove_groups(&pdev->dev, shps_power_groups); ++ shps_gpios_remove_irq(pdev); ++ shps_gpios_remove(pdev); ++ pci_dev_put(drvdata->dgpu_root_port); ++ platform_set_drvdata(pdev, NULL); ++ kfree(drvdata); ++ ++ acpi_dev_remove_driver_gpios(shps_dev); ++ return 0; ++} ++ ++ ++static const struct dev_pm_ops shps_pm_ops = { ++ .prepare = shps_pm_prepare, ++ .complete = shps_pm_complete, ++ .suspend = shps_pm_suspend, ++ .resume = shps_pm_resume, ++}; ++ ++static const struct acpi_device_id shps_acpi_match[] = { ++ { "MSHW0153", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, shps_acpi_match); ++ ++struct platform_driver surface_sam_hps = { ++ .probe = shps_probe, ++ .remove = shps_remove, ++ .shutdown = shps_shutdown, ++ .driver = { ++ .name = "surface_dgpu_hps", ++ .acpi_match_table = ACPI_PTR(shps_acpi_match), ++ .pm = &shps_pm_ops, ++ }, ++}; ++module_platform_driver(surface_sam_hps); ++ ++MODULE_AUTHOR("Maximilian Luz "); ++MODULE_DESCRIPTION("Surface Hot-Plug System (HPS) and dGPU power-state Driver for Surface Book 2"); ++MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.c b/drivers/platform/x86/surface_sam/surface_sam_san.c new file mode 100644 -index 000000000000..810165083e0e +index 000000000000..aa0cfc4262be --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_san.c -@@ -0,0 +1,791 @@ +@@ -0,0 +1,901 @@ +/* + * Surface ACPI Notify (SAN) and ACPI integration driver for SAM. + * Translates communication from ACPI to SSH and back. @@ -946,6 +2078,7 @@ index 000000000000..810165083e0e +#include + +#include "surface_sam_ssh.h" ++#include "surface_sam_san.h" + + +#define SAN_RQST_RETRY 5 @@ -971,7 +2104,8 @@ index 000000000000..810165083e0e +#define SAM_EVENT_TEMP_RQID 0x0003 +#define SAM_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT 0x0b + -+#define SAN_RQST_TAG "surface_sam_san_rqst: " ++#define SAN_RQST_TAG "surface_sam_san: rqst: " ++#define SAN_RQSG_TAG "surface_sam_san: rqsg: " + +#define SAN_QUIRK_BASE_STATE_DELAY 1000 + @@ -989,17 +2123,17 @@ index 000000000000..810165083e0e + +struct san_consumer_link { + const struct san_acpi_consumer *properties; -+ struct device_link *link; ++ struct device_link *link; +}; + +struct san_consumers { -+ u32 num; ++ u32 num; + struct san_consumer_link *links; +}; + +struct san_drvdata { + struct san_opreg_context opreg_ctx; -+ struct san_consumers consumers; ++ struct san_consumers consumers; + bool has_power_events; +}; + @@ -1056,6 +2190,79 @@ index 000000000000..810165083e0e +}; + + ++static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data); ++ ++struct sam_san_rqsg_if { ++ struct mutex lock; ++ struct device *san_dev; ++ surface_sam_san_rqsg_handler_fn handler; ++ void *handler_data; ++}; ++ ++static struct sam_san_rqsg_if rqsg_if = { ++ .lock = __MUTEX_INITIALIZER(rqsg_if.lock), ++ .san_dev = NULL, ++ .handler = sam_san_default_rqsg_handler, ++ .handler_data = NULL, ++}; ++ ++int surface_sam_san_consumer_register(struct device *consumer, u32 flags) ++{ ++ const u32 valid = DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE; ++ int status; ++ ++ if ((flags | valid) != valid) ++ return -EINVAL; ++ ++ flags |= DL_FLAG_AUTOREMOVE_CONSUMER; ++ ++ mutex_lock(&rqsg_if.lock); ++ if (rqsg_if.san_dev) ++ status = device_link_add(consumer, rqsg_if.san_dev, flags) ? 0 : -EINVAL; ++ else ++ status = -ENXIO; ++ mutex_unlock(&rqsg_if.lock); ++ return status; ++} ++EXPORT_SYMBOL_GPL(surface_sam_san_consumer_register); ++ ++int surface_sam_san_set_rqsg_handler(surface_sam_san_rqsg_handler_fn fn, void *data) ++{ ++ int status = -EBUSY; ++ ++ mutex_lock(&rqsg_if.lock); ++ ++ if (rqsg_if.handler == sam_san_default_rqsg_handler || !fn) { ++ rqsg_if.handler = fn ? fn : sam_san_default_rqsg_handler; ++ rqsg_if.handler_data = data; ++ status = 0; ++ } ++ ++ mutex_unlock(&rqsg_if.lock); ++ return status; ++} ++EXPORT_SYMBOL_GPL(surface_sam_san_set_rqsg_handler); ++ ++int san_call_rqsg_handler(struct surface_sam_san_rqsg *rqsg) ++{ ++ int status; ++ ++ mutex_lock(&rqsg_if.lock); ++ status = rqsg_if.handler(rqsg, rqsg_if.handler_data); ++ mutex_unlock(&rqsg_if.lock); ++ ++ return status; ++} ++ ++static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data) ++{ ++ pr_warn(SAN_RQSG_TAG "unhandled request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ ++ return 0; ++} ++ ++ +static int san_acpi_notify_power_event(struct device *dev, enum san_pwr_event event) +{ + acpi_handle san = ACPI_HANDLE(dev); @@ -1391,16 +2598,33 @@ index 000000000000..810165083e0e +static acpi_status +san_rqsg(struct san_opreg_context *ctx, struct gsb_buffer *buffer) +{ -+ struct gsb_data_rqsx *rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ struct gsb_data_rqsx *gsb_rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ struct surface_sam_san_rqsg rqsg = {}; ++ int status; + -+ if (!rqsg) { ++ if (!gsb_rqsg) { + return AE_OK; + } + -+ // TODO: RQSG handler ++ rqsg.tc = gsb_rqsg->tc; ++ rqsg.cid = gsb_rqsg->cid; ++ rqsg.iid = gsb_rqsg->iid; ++ rqsg.cdl = gsb_rqsg->cdl; ++ rqsg.pld = &gsb_rqsg->pld[0]; + -+ dev_warn(ctx->dev, "unsupported request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", -+ rqsg->tc, rqsg->cid, rqsg->iid); ++ status = san_call_rqsg_handler(&rqsg); ++ if (!status) { ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x00; ++ buffer->data.out.len = 0x00; ++ } else { ++ dev_err(ctx->dev, SAN_RQSG_TAG "failed with error %d\n", status); ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x01; // indicate _SSH error ++ buffer->data.out.len = 0x00; ++ } + + return AE_OK; +} @@ -1667,9 +2891,23 @@ index 000000000000..810165083e0e + goto err_enable_events; + } + ++ mutex_lock(&rqsg_if.lock); ++ if (!rqsg_if.san_dev) { ++ rqsg_if.san_dev = &pdev->dev; ++ } else { ++ status = -EBUSY; ++ } ++ mutex_unlock(&rqsg_if.lock); ++ ++ if (status) { ++ goto err_install_dev; ++ } ++ + acpi_walk_dep_device_list(san); + return 0; + ++err_install_dev: ++ san_disable_events(pdev); +err_enable_events: + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); +err_install_handler: @@ -1686,6 +2924,10 @@ index 000000000000..810165083e0e + acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node + acpi_status status = AE_OK; + ++ mutex_lock(&rqsg_if.lock); ++ rqsg_if.san_dev = NULL; ++ mutex_unlock(&rqsg_if.lock); ++ + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); + san_disable_events(pdev); + @@ -1725,6 +2967,41 @@ index 000000000000..810165083e0e +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface ACPI Notify Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.h b/drivers/platform/x86/surface_sam/surface_sam_san.h +new file mode 100644 +index 000000000000..1ea8713db367 +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_san.h +@@ -0,0 +1,29 @@ ++/* ++ * Interface for Surface ACPI/Notify (SAN). ++ * ++ * The SAN is the main interface between the Surface Serial Hub (SSH) and the ++ * Surface/System Aggregator Module (SAM). It allows requests to be translated ++ * from ACPI to SSH/SAM. It also interfaces with the discrete GPU hot-plug ++ * driver. ++ */ ++ ++#ifndef _SURFACE_SAM_SAN_H ++#define _SURFACE_SAM_SAN_H ++ ++#include ++ ++ ++struct surface_sam_san_rqsg { ++ u8 tc; // target category ++ u8 cid; // command ID ++ u8 iid; // instance ID ++ u8 cdl; // command data length (lenght of payload) ++ u8 *pld; // pointer to payload of length cdl ++}; ++ ++typedef int (*surface_sam_san_rqsg_handler_fn)(struct surface_sam_san_rqsg *rqsg, void *data); ++ ++int surface_sam_san_consumer_register(struct device *consumer, u32 flags); ++int surface_sam_san_set_rqsg_handler(surface_sam_san_rqsg_handler_fn fn, void *data); ++ ++#endif /* _SURFACE_SAM_SAN_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid.c b/drivers/platform/x86/surface_sam/surface_sam_sid.c new file mode 100644 index 000000000000..f64dcd590494 @@ -2306,10 +3583,10 @@ index 000000000000..880a2567cf1b +MODULE_ALIAS("platform:surface_sam_sid_perfmode"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_power.c b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c new file mode 100644 -index 000000000000..ce4274709d68 +index 000000000000..1f2c88eda394 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c -@@ -0,0 +1,1258 @@ +@@ -0,0 +1,1259 @@ +/* + * Surface SID Battery/AC Driver. + * Provides support for the battery and AC on 7th generation Surface devices. @@ -3567,7 +4844,8 @@ index 000000000000..ce4274709d68 +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface Battery/AC Driver for 7th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:surface_sam_sid_power"); ++MODULE_ALIAS("platform:surface_sam_sid_ac"); ++MODULE_ALIAS("platform:surface_sam_sid_battery"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c new file mode 100644 index 000000000000..dc5be3a14a8c @@ -4016,10 +5294,10 @@ index 000000000000..dc5be3a14a8c +MODULE_ALIAS("platform:surface_sam_sid_vhf"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c new file mode 100644 -index 000000000000..665d956eec01 +index 000000000000..34905cf29a51 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c -@@ -0,0 +1,1779 @@ +@@ -0,0 +1,1773 @@ +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System + * Aggregator Module. @@ -4102,8 +5380,8 @@ index 000000000000..665d956eec01 + +/* + * A note on Request IDs (RQIDs): -+ * 0x0000 is not a valid RQID -+ * 0x0001 is valid, but reserved for Surface Laptop keyboard events ++ * 0x0000 is not a valid RQID ++ * 0x0001 is valid, but reserved for Surface Laptop keyboard events + */ +#define SAM_NUM_EVENT_TYPES ((1 << SURFACE_SAM_SSH_RQID_EVENT_BITS) - 1) + @@ -5356,11 +6634,6 @@ index 000000000000..665d956eec01 + +static irqreturn_t surface_sam_irq_handler(int irq, void *dev_id) +{ -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t surface_sam_irq_handler_th(int irq, void *dev_id) -+{ + struct serdev_device *serdev = dev_id; + + dev_info(&serdev->dev, "wake irq triggered\n"); @@ -5369,7 +6642,7 @@ index 000000000000..665d956eec01 + +static int surface_sam_setup_irq(struct serdev_device *serdev) +{ -+ const int irqf = IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING; + struct gpio_desc *gpiod; + int irq; + int status; @@ -5384,8 +6657,7 @@ index 000000000000..665d956eec01 + if (irq < 0) + return irq; + -+ status = request_threaded_irq(irq, surface_sam_irq_handler, -+ surface_sam_irq_handler_th, ++ status = request_threaded_irq(irq, NULL, surface_sam_irq_handler, + irqf, "surface_sam_wakeup", serdev); + if (status) + return status; @@ -6336,5 +7608,5 @@ index 9db93f500b4e..42d1dae34b21 100644 if (!ctrl->serdev) return -ENODEV; -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0002-suspend.patch b/patches/4.19/0002-suspend.patch index 7499aed1f..431b5db6b 100644 --- a/patches/4.19/0002-suspend.patch +++ b/patches/4.19/0002-suspend.patch @@ -1,4 +1,4 @@ -From a0440209503a70ceba6353b40ab335528c7f7663 Mon Sep 17 00:00:00 2001 +From 55e646f0c2850aaf2732fa3f8d97aa97c0863837 Mon Sep 17 00:00:00 2001 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> Date: Sat, 28 Sep 2019 17:48:21 +0200 Subject: [PATCH 02/13] suspend @@ -14,7 +14,7 @@ Subject: [PATCH 02/13] suspend 7 files changed, 182 insertions(+), 6 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c -index c4ff4f079448..4e050ae8a6d4 100644 +index b2d9bd564960..7c522d2a0f99 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1049,15 +1049,15 @@ static struct nvme_id_ns *nvme_identify_ns(struct nvme_ctrl *ctrl, @@ -364,5 +364,5 @@ index f8576509c7be..a5fa80e72aba 100644 .procname = "sched_child_runs_first", .data = &sysctl_sched_child_runs_first, -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0003-buttons.patch b/patches/4.19/0003-buttons.patch index fd57f5cb7..660c3e6fd 100644 --- a/patches/4.19/0003-buttons.patch +++ b/patches/4.19/0003-buttons.patch @@ -1,4 +1,4 @@ -From f3c4fbbf4bd7222199fff5162a0f790414b7ca29 Mon Sep 17 00:00:00 2001 +From 54e82d57d6201545852600365c240fcf26440226 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Jul 2019 17:51:37 +0200 Subject: [PATCH 03/13] buttons @@ -270,5 +270,5 @@ index 1b491690ce07..96627627060e 100644 if (!button) return -ENOMEM; -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0004-cameras.patch b/patches/4.19/0004-cameras.patch index b26b31803..a51ba015e 100644 --- a/patches/4.19/0004-cameras.patch +++ b/patches/4.19/0004-cameras.patch @@ -1,4 +1,4 @@ -From 215d8d4ae78f1bfaebdc0fb6b56f2a2e3b0b61b3 Mon Sep 17 00:00:00 2001 +From aba5b117008ee57e9d9d65e971ea75cb9b1bed34 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 17:53:54 +0200 Subject: [PATCH 04/13] cameras @@ -21,10 +21,10 @@ Subject: [PATCH 04/13] cameras create mode 100644 drivers/staging/ov5693/ov5693.h diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c -index cf4feff2a48c..db24e360f6ab 100644 +index 063e229ead5e..355777bb8ba8 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c -@@ -2367,6 +2367,46 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { +@@ -2389,6 +2389,46 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { * though they are compliant. */ static const struct usb_device_id uvc_ids[] = { @@ -2749,5 +2749,5 @@ index 000000000000..79aef69666e8 +}; +#endif -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0005-ipts.patch b/patches/4.19/0005-ipts.patch index aa6dd0327..82766da72 100644 --- a/patches/4.19/0005-ipts.patch +++ b/patches/4.19/0005-ipts.patch @@ -1,4 +1,4 @@ -From 678ce41480476ac90e3951e055cfd6448b9d95d6 Mon Sep 17 00:00:00 2001 +From 687d4da6fce826b6d0f02c99420badadb262bcb0 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 17:58:17 +0200 Subject: [PATCH 05/13] ipts @@ -6848,5 +6848,5 @@ index 000000000000..bad44fb4f233 + +#endif // INTEL_IPTS_IF_H -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0006-hid.patch b/patches/4.19/0006-hid.patch index 8c5cb5ee9..f165c41e3 100644 --- a/patches/4.19/0006-hid.patch +++ b/patches/4.19/0006-hid.patch @@ -1,4 +1,4 @@ -From 24943eb655f03a88c39295916dd25bf5a53cc933 Mon Sep 17 00:00:00 2001 +From 927b2d49811547c673eb3fc16126c8afca385757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 6 Nov 2019 19:43:26 +0900 Subject: [PATCH 06/13] hid @@ -23,5 +23,5 @@ index b0c8fae7f903..3a359716fb38 100644 static void hid_scan_collection(struct hid_parser *parser, unsigned type) -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0007-sdcard-reader.patch b/patches/4.19/0007-sdcard-reader.patch index 85480372b..8941f2867 100644 --- a/patches/4.19/0007-sdcard-reader.patch +++ b/patches/4.19/0007-sdcard-reader.patch @@ -1,4 +1,4 @@ -From 9bb4a6a8a1aef64e4fcaa3527108b8e6da35e845 Mon Sep 17 00:00:00 2001 +From 97eabdac0e56338eb68d67eb46d9e589e0621f1f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 17:59:13 +0200 Subject: [PATCH 07/13] sdcard-reader @@ -8,7 +8,7 @@ Subject: [PATCH 07/13] sdcard-reader 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index d5fbd36cf462..606e63691d50 100644 +index b33ec768404b..360477fe8d0c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4171,7 +4171,8 @@ void usb_enable_lpm(struct usb_device *udev) @@ -22,5 +22,5 @@ index d5fbd36cf462..606e63691d50 100644 udev->lpm_disable_count--; -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0008-wifi.patch b/patches/4.19/0008-wifi.patch index 8e3d67dfa..b75763494 100644 --- a/patches/4.19/0008-wifi.patch +++ b/patches/4.19/0008-wifi.patch @@ -1,4 +1,4 @@ -From facdc3529e5ee42e976484f1a5e824867e5bb3df Mon Sep 17 00:00:00 2001 +From eb5625524a9f025ade096508db976f6318387fc4 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 18:00:19 +0200 Subject: [PATCH 08/13] wifi @@ -97,7 +97,7 @@ index 1fb76d2f5d3f..fb32379da99d 100644 struct hw_spec_api_rev { diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c -index 20cee5c397fb..13e49a3ae812 100644 +index e48b47f42554..15bbcbf1420f 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -163,6 +163,7 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) @@ -266,5 +266,5 @@ diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl old mode 100755 new mode 100644 -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0009-surface3-power.patch b/patches/4.19/0009-surface3-power.patch index fee5d5016..0957fa0b8 100644 --- a/patches/4.19/0009-surface3-power.patch +++ b/patches/4.19/0009-surface3-power.patch @@ -1,4 +1,4 @@ -From 310f0563c32936e54eb60e9e2e2ad2280b7f7208 Mon Sep 17 00:00:00 2001 +From e40a7fdd66839e4adfac65ca1ab770d50551d95c Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 18:00:43 +0200 Subject: [PATCH 09/13] surface3-power @@ -651,5 +651,5 @@ index 000000000000..e0af01a60302 +MODULE_DESCRIPTION("mshw0011 driver"); +MODULE_LICENSE("GPL v2"); -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0010-mwlwifi.patch b/patches/4.19/0010-mwlwifi.patch index 2a9449996..1e998c334 100644 --- a/patches/4.19/0010-mwlwifi.patch +++ b/patches/4.19/0010-mwlwifi.patch @@ -1,4 +1,4 @@ -From 64a2a0f032a96a09ee29d63e510f83834b6aa88e Mon Sep 17 00:00:00 2001 +From e256371ddeb11d23ca22a10fc14c00dd7ab8288c Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 18:01:27 +0200 Subject: [PATCH 10/13] mwlwifi @@ -19751,5 +19751,5 @@ index 000000000000..b6fdf70c22fb + +#endif /* _VENDOR_CMD_H_ */ -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0011-surface-lte.patch b/patches/4.19/0011-surface-lte.patch index d019169c9..b62f0c3c3 100644 --- a/patches/4.19/0011-surface-lte.patch +++ b/patches/4.19/0011-surface-lte.patch @@ -1,4 +1,4 @@ -From a9414a3a06df5074fa44c920857104e9eb8f8031 Mon Sep 17 00:00:00 2001 +From e3295d7e95d4ccc587677e9e96664187b10ecec5 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 18:02:03 +0200 Subject: [PATCH 11/13] surface-lte @@ -20,5 +20,5 @@ index 613f91add03d..e1428222dd73 100644 /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0013-ioremap_uc.patch b/patches/4.19/0012-ioremap_uc.patch similarity index 81% rename from patches/4.19/0013-ioremap_uc.patch rename to patches/4.19/0012-ioremap_uc.patch index 54c042d5d..6d71b1139 100644 --- a/patches/4.19/0013-ioremap_uc.patch +++ b/patches/4.19/0012-ioremap_uc.patch @@ -1,15 +1,14 @@ -From 45a30685cf30d7ea4f07d6b3a86aac80feccf32d Mon Sep 17 00:00:00 2001 +From d63ae78ee99fb9c0684a1fb23fed472f0b9ea2b3 Mon Sep 17 00:00:00 2001 From: Tuowen Zhao -Date: Wed, 16 Oct 2019 15:06:27 -0600 -Subject: [PATCH 13/13] ioremap_uc +Date: Wed, 16 Oct 2019 15:06:28 -0600 +Subject: [PATCH 12/13] ioremap_uc --- Documentation/driver-model/devres.txt | 1 + - arch/sparc/include/asm/io_64.h | 1 + drivers/mfd/intel-lpss.c | 2 +- include/linux/io.h | 2 ++ lib/devres.c | 19 +++++++++++++++++++ - 5 files changed, 24 insertions(+), 1 deletion(-) + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 43681ca0837f..7b1ee4190208 100644 @@ -23,18 +22,6 @@ index 43681ca0837f..7b1ee4190208 100644 devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps devm_iounmap() -diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h -index b162c23ae8c2..7a836d21ff0c 100644 ---- a/arch/sparc/include/asm/io_64.h -+++ b/arch/sparc/include/asm/io_64.h -@@ -409,6 +409,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size) - } - - #define ioremap_nocache(X,Y) ioremap((X),(Y)) -+#define ioremap_uc(X,Y) ioremap((X),(Y)) - #define ioremap_wc(X,Y) ioremap((X),(Y)) - #define ioremap_wt(X,Y) ioremap((X),(Y)) - diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index ff3fba16e735..37a5bb09d228 100644 --- a/drivers/mfd/intel-lpss.c @@ -106,5 +93,5 @@ index faccf1a037d0..97c56f9893a4 100644 * devm_ioremap_nocache - Managed ioremap_nocache() * @dev: Generic device to remap IO address for -- -2.24.0 +2.24.1 diff --git a/patches/4.19/0012-surfacebook2-dgpu.patch b/patches/4.19/0012-surfacebook2-dgpu.patch deleted file mode 100644 index d751f49ba..000000000 --- a/patches/4.19/0012-surfacebook2-dgpu.patch +++ /dev/null @@ -1,359 +0,0 @@ -From cc366ca006cf7e176d5edfa17c12d844701f543d Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Sat, 28 Sep 2019 18:02:33 +0200 -Subject: [PATCH 12/13] surfacebook2-dgpu - ---- - drivers/platform/x86/Kconfig | 9 + - drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surfacebook2_dgpu_hps.c | 306 +++++++++++++++++++ - 3 files changed, 316 insertions(+) - create mode 100644 drivers/platform/x86/surfacebook2_dgpu_hps.c - -diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 7cee1015981d..75665b560a6f 100644 ---- a/drivers/platform/x86/Kconfig -+++ b/drivers/platform/x86/Kconfig -@@ -436,6 +436,15 @@ config SURFACE3_WMI - To compile this driver as a module, choose M here: the module will - be called surface3-wmi. - -+config SURFACE_BOOK2_DGPU_HPS -+ tristate "Surface Book 2 dGPU Hot-Plug System Driver" -+ depends on ACPI -+ ---help--- -+ This is an experimetnal driver to control the power-state of the -+ Surface Book 2 dGPU. -+ -+ If you have a Surface Book 2, say Y or M here. -+ - config THINKPAD_ACPI - tristate "ThinkPad ACPI Laptop Extras" - depends on ACPI -diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index cbea9579c1d2..6eb62f822953 100644 ---- a/drivers/platform/x86/Makefile -+++ b/drivers/platform/x86/Makefile -@@ -44,6 +44,7 @@ obj-$(CONFIG_ACPI_WMI) += wmi.o - obj-$(CONFIG_MSI_WMI) += msi-wmi.o - obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o - obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o -+obj-$(CONFIG_SURFACE_BOOK2_DGPU_HPS) += surfacebook2_dgpu_hps.o - obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o - obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o - obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o -diff --git a/drivers/platform/x86/surfacebook2_dgpu_hps.c b/drivers/platform/x86/surfacebook2_dgpu_hps.c -new file mode 100644 -index 000000000000..7639fb0029d8 ---- /dev/null -+++ b/drivers/platform/x86/surfacebook2_dgpu_hps.c -@@ -0,0 +1,306 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+ -+#define SB2_SHPS_DSM_REVISION 1 -+#define SB2_SHPS_DSM_GPU_STATE 0x05 -+ -+static const guid_t SB2_SHPS_DSM_UUID = -+ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, -+ 0x32, 0x0e, 0x10, 0x36, 0x0a); -+ -+#define SB2_PARAM_PERM (S_IRUGO | S_IWUSR) -+ -+ -+static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; -+static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; -+ -+static const struct acpi_gpio_mapping sb2_mshw0153_acpi_gpios[] = { -+ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, -+ { "base_presence-gpio", &gpio_base_presence, 1 }, -+ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, -+ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, -+ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, -+ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, -+ { }, -+}; -+ -+ -+enum sb2_dgpu_power { -+ SB2_DGPU_POWER_OFF = 0, -+ SB2_DGPU_POWER_ON = 1, -+ -+ __SB2_DGPU_POWER__START = 0, -+ __SB2_DGPU_POWER__END = 1, -+}; -+ -+enum sb2_param_dgpu_power { -+ SB2_PARAM_DGPU_POWER_OFF = SB2_DGPU_POWER_OFF, -+ SB2_PARAM_DGPU_POWER_ON = SB2_DGPU_POWER_ON, -+ SB2_PARAM_DGPU_POWER_AS_IS = 2, -+ -+ __SB2_PARAM_DGPU_POWER__START = 0, -+ __SB2_PARAM_DGPU_POWER__END = 2, -+}; -+ -+static const char* sb2_dgpu_power_str(enum sb2_dgpu_power power) { -+ if (power == SB2_DGPU_POWER_OFF) { -+ return "off"; -+ } else if (power == SB2_DGPU_POWER_ON) { -+ return "on"; -+ } else { -+ return ""; -+ } -+} -+ -+ -+struct sb2_shps_driver_data { -+ struct mutex dgpu_power_lock; -+ enum sb2_dgpu_power dgpu_power; -+}; -+ -+ -+static int __sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ acpi_handle handle = ACPI_HANDLE(&pdev->dev); -+ union acpi_object *result; -+ union acpi_object param; -+ -+ param.type = ACPI_TYPE_INTEGER; -+ param.integer.value = power == SB2_DGPU_POWER_ON; -+ -+ result = acpi_evaluate_dsm_typed(handle, &SB2_SHPS_DSM_UUID, SB2_SHPS_DSM_REVISION, -+ SB2_SHPS_DSM_GPU_STATE, ¶m, ACPI_TYPE_BUFFER); -+ -+ if (IS_ERR_OR_NULL(result)) { -+ return result ? PTR_ERR(result) : -EFAULT; -+ } -+ -+ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { -+ return -EIO; -+ } -+ -+ drvdata->dgpu_power = power; -+ -+ printk(KERN_INFO "sb2_shps: dGPU power state set to \'%s\'\n", sb2_dgpu_power_str(power)); -+ -+ ACPI_FREE(result); -+ return 0; -+} -+ -+static int sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status = 0; -+ -+ if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&drvdata->dgpu_power_lock); -+ if (power != drvdata->dgpu_power) { -+ status = __sb2_shps_dgpu_set_power(pdev, power); -+ } -+ mutex_unlock(&drvdata->dgpu_power_lock); -+ -+ return status; -+} -+ -+static int sb2_shps_dgpu_force_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&drvdata->dgpu_power_lock); -+ status = __sb2_shps_dgpu_set_power(pdev, power); -+ mutex_unlock(&drvdata->dgpu_power_lock); -+ -+ return status; -+} -+ -+ -+static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) -+{ -+ int power = SB2_PARAM_DGPU_POWER_OFF; -+ int status; -+ -+ status = kstrtoint(val, 0, &power); -+ if (status) { -+ return status; -+ } -+ -+ if (power < __SB2_PARAM_DGPU_POWER__START || power > __SB2_PARAM_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ return param_set_int(val, kp); -+} -+ -+static const struct kernel_param_ops param_dgpu_power_ops = { -+ .set = param_dgpu_power_set, -+ .get = param_get_int, -+}; -+ -+static int param_dgpu_power_init = SB2_PARAM_DGPU_POWER_OFF; -+static int param_dgpu_power_exit = SB2_PARAM_DGPU_POWER_OFF; -+ -+module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SB2_PARAM_PERM); -+module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SB2_PARAM_PERM); -+ -+MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is)"); -+MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is)"); -+ -+ -+static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ return sprintf(data, "%s\n", sb2_dgpu_power_str(drvdata->dgpu_power)); -+} -+ -+static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, -+ const char *data, size_t count) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ bool power = false; -+ int status; -+ -+ status = kstrtobool(data, &power); -+ if (status) { -+ return status; -+ } -+ -+ if (power) { -+ status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_ON); -+ } else { -+ status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_OFF); -+ } -+ -+ return status < 0 ? status : count; -+} -+ -+const static DEVICE_ATTR_RW(dgpu_power); -+ -+ -+#ifdef CONFIG_PM -+ -+static int sb2_shps_resume(struct device *dev) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ return sb2_shps_dgpu_force_power(pdev, drvdata->dgpu_power); -+} -+ -+static SIMPLE_DEV_PM_OPS(sb2_shps_pm_ops, NULL, sb2_shps_resume); -+ -+#endif -+ -+ -+static int sb2_shps_probe(struct platform_device *pdev) -+{ -+ struct sb2_shps_driver_data *drvdata; -+ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); -+ int status = 0; -+ -+ if (gpiod_count(&pdev->dev, NULL) < 0) { -+ return -ENODEV; -+ } -+ -+ status = acpi_dev_add_driver_gpios(shps_dev, sb2_mshw0153_acpi_gpios); -+ if (status) { -+ return status; -+ } -+ -+ drvdata = kzalloc(sizeof(struct sb2_shps_driver_data), GFP_KERNEL); -+ if (!drvdata) { -+ status = -ENOMEM; -+ goto err_alloc_drvdata; -+ } -+ -+ mutex_init(&drvdata->dgpu_power_lock); -+ drvdata->dgpu_power = SB2_DGPU_POWER_OFF; -+ platform_set_drvdata(pdev, drvdata); -+ -+ if (param_dgpu_power_init != SB2_PARAM_DGPU_POWER_AS_IS) { -+ status = sb2_shps_dgpu_force_power(pdev, param_dgpu_power_init); -+ if (status) { -+ goto err_set_power; -+ } -+ } -+ -+ status = sysfs_create_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr); -+ if (status) { -+ goto err_sysfs; -+ } -+ -+ return 0; -+ -+err_sysfs: -+ sb2_shps_dgpu_force_power(pdev, SB2_DGPU_POWER_OFF); -+err_set_power: -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+err_alloc_drvdata: -+ acpi_dev_remove_driver_gpios(shps_dev); -+ return status; -+} -+ -+static int sb2_shps_remove(struct platform_device *pdev) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); -+ -+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr); -+ -+ if (param_dgpu_power_exit != SB2_PARAM_DGPU_POWER_AS_IS) { -+ sb2_shps_dgpu_set_power(pdev, param_dgpu_power_exit); -+ } -+ acpi_dev_remove_driver_gpios(shps_dev); -+ -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+ -+ return 0; -+} -+ -+ -+static const struct acpi_device_id sb2_shps_acpi_match[] = { -+ { "MSHW0153", 0 }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, sb2_shps_acpi_match); -+ -+static struct platform_driver sb2_shps_driver = { -+ .probe = sb2_shps_probe, -+ .remove = sb2_shps_remove, -+ .driver = { -+ .name = "sb2_shps", -+ .acpi_match_table = ACPI_PTR(sb2_shps_acpi_match), -+#ifdef CONFIG_PM -+ .pm = &sb2_shps_pm_ops, -+#endif -+ }, -+}; -+module_platform_driver(sb2_shps_driver); -+ -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("Surface Book 2 Hot-Plug System Driver"); -+MODULE_LICENSE("GPL v2"); --- -2.24.0 - diff --git a/patches/4.19/0013-surface3-spi-dma.patch b/patches/4.19/0013-surface3-spi-dma.patch new file mode 100644 index 000000000..c0a3ff03a --- /dev/null +++ b/patches/4.19/0013-surface3-spi-dma.patch @@ -0,0 +1,63 @@ +From 981e1123997405f871305892fe26d79ee64abf1d Mon Sep 17 00:00:00 2001 +From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> +Date: Fri, 6 Dec 2019 23:10:30 +0900 +Subject: [PATCH 13/13] surface3-spi dma + +--- + drivers/input/touchscreen/surface3_spi.c | 26 ++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c +index 5db0f1c4ef38..8935ddbc2357 100644 +--- a/drivers/input/touchscreen/surface3_spi.c ++++ b/drivers/input/touchscreen/surface3_spi.c +@@ -29,6 +29,12 @@ + #define SURFACE3_REPORT_TOUCH 0xd2 + #define SURFACE3_REPORT_PEN 0x16 + ++bool use_dma = false; ++module_param(use_dma, bool, 0644); ++MODULE_PARM_DESC(use_dma, ++ "Disable DMA mode if you encounter touch input crash. " ++ "(default: false, disabled to avoid crash)"); ++ + struct surface3_ts_data { + struct spi_device *spi; + struct gpio_desc *gpiod_rst[2]; +@@ -330,6 +336,13 @@ static int surface3_spi_create_pen_input(struct surface3_ts_data *data) + return 0; + } + ++static bool surface3_spi_can_dma(struct spi_controller *ctlr, ++ struct spi_device *spi, ++ struct spi_transfer *tfr) ++{ ++ return use_dma; ++} ++ + static int surface3_spi_probe(struct spi_device *spi) + { + struct surface3_ts_data *data; +@@ -372,6 +385,19 @@ static int surface3_spi_probe(struct spi_device *spi) + if (error) + return error; + ++ /* ++ * Set up DMA ++ * ++ * TODO: Currently, touch input with DMA seems to be broken. ++ * On 4.19 LTS, touch input will crash after suspend. ++ * On recent stable kernel (at least after 5.1), touch input will crash after ++ * the first touch. No problem with PIO on those kernels. ++ * Maybe we need to configure DMA here. ++ * ++ * Link to issue: https://github.com/jakeday/linux-surface/issues/596 ++ */ ++ spi->controller->can_dma = surface3_spi_can_dma; ++ + return 0; + } + +-- +2.24.1 + diff --git a/patches/5.3/0001-surface-acpi.patch b/patches/5.3/0001-surface-acpi.patch index fb6b434ae..ea015dc17 100644 --- a/patches/5.3/0001-surface-acpi.patch +++ b/patches/5.3/0001-surface-acpi.patch @@ -1,4 +1,4 @@ -From 710ca2bf92d353351c1071e5fcf8990955b2f1cf Mon Sep 17 00:00:00 2001 +From fa60a6d5cdd3cac447311bf2185ce31fe13942fd Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:11:08 +0200 Subject: [PATCH 01/10] surface-acpi @@ -8,24 +8,28 @@ Subject: [PATCH 01/10] surface-acpi drivers/acpi/acpica/exfield.c | 12 +- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface_sam/Kconfig | 155 ++ - drivers/platform/x86/surface_sam/Makefile | 9 + + drivers/platform/x86/surface_sam/Kconfig | 166 ++ + drivers/platform/x86/surface_sam/Makefile | 10 + .../x86/surface_sam/surface_sam_dtx.c | 623 ++++++ - .../x86/surface_sam/surface_sam_san.c | 791 ++++++++ + .../x86/surface_sam/surface_sam_hps.c | 1110 +++++++++++ + .../x86/surface_sam/surface_sam_san.c | 901 +++++++++ + .../x86/surface_sam/surface_sam_san.h | 29 + .../x86/surface_sam/surface_sam_sid.c | 117 ++ .../x86/surface_sam/surface_sam_sid_gpelid.c | 219 ++ .../surface_sam/surface_sam_sid_perfmode.c | 225 +++ - .../x86/surface_sam/surface_sam_sid_power.c | 1258 ++++++++++++ + .../x86/surface_sam/surface_sam_sid_power.c | 1259 ++++++++++++ .../x86/surface_sam/surface_sam_sid_vhf.c | 440 ++++ - .../x86/surface_sam/surface_sam_ssh.c | 1779 +++++++++++++++++ + .../x86/surface_sam/surface_sam_ssh.c | 1773 +++++++++++++++++ .../x86/surface_sam/surface_sam_ssh.h | 97 + .../x86/surface_sam/surface_sam_vhf.c | 276 +++ drivers/tty/serdev/core.c | 111 +- - 17 files changed, 6100 insertions(+), 16 deletions(-) + 19 files changed, 7356 insertions(+), 16 deletions(-) create mode 100644 drivers/platform/x86/surface_sam/Kconfig create mode 100644 drivers/platform/x86/surface_sam/Makefile create mode 100644 drivers/platform/x86/surface_sam/surface_sam_dtx.c + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_hps.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.c + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.h create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid_gpelid.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid_perfmode.c @@ -103,10 +107,10 @@ index 415104033060..18f5a4ba7244 100644 +obj-$(CONFIG_SURFACE_SAM) += surface_sam/ diff --git a/drivers/platform/x86/surface_sam/Kconfig b/drivers/platform/x86/surface_sam/Kconfig new file mode 100644 -index 000000000000..256a7c3f1fa3 +index 000000000000..4eff58a121cb --- /dev/null +++ b/drivers/platform/x86/surface_sam/Kconfig -@@ -0,0 +1,155 @@ +@@ -0,0 +1,166 @@ +menuconfig SURFACE_SAM + depends on ACPI + tristate "Microsoft Surface/System Aggregator Module and Platform Drivers" @@ -200,6 +204,17 @@ index 000000000000..256a7c3f1fa3 + + If you are not sure, say M here. + ++config SURFACE_SAM_HPS ++ tristate "Surface dGPU Hot-Plug System (dGPU-HPS) Driver" ++ depends on SURFACE_SAM_SSH ++ depends on SURFACE_SAM_SAN ++ default m ++ ---help--- ++ Driver to properly handle hot-plugging and explicit power-on/power-off ++ of the discrete GPU (dGPU) on the Surface Book 2. ++ ++ If you are not sure, say M here. ++ +config SURFACE_SAM_SID + tristate "Surface Platform Integration Driver" + depends on SURFACE_SAM_SSH @@ -264,13 +279,14 @@ index 000000000000..256a7c3f1fa3 + If you are not sure, say M here. diff --git a/drivers/platform/x86/surface_sam/Makefile b/drivers/platform/x86/surface_sam/Makefile new file mode 100644 -index 000000000000..97ef66ff273d +index 000000000000..188975ccde5c --- /dev/null +++ b/drivers/platform/x86/surface_sam/Makefile -@@ -0,0 +1,9 @@ +@@ -0,0 +1,10 @@ +obj-$(CONFIG_SURFACE_SAM_SSH) += surface_sam_ssh.o +obj-$(CONFIG_SURFACE_SAM_SAN) += surface_sam_san.o +obj-$(CONFIG_SURFACE_SAM_DTX) += surface_sam_dtx.o ++obj-$(CONFIG_SURFACE_SAM_HPS) += surface_sam_hps.o +obj-$(CONFIG_SURFACE_SAM_VHF) += surface_sam_vhf.o +obj-$(CONFIG_SURFACE_SAM_SID) += surface_sam_sid.o +obj-$(CONFIG_SURFACE_SAM_SID_GPELID) += surface_sam_sid_gpelid.o @@ -906,12 +922,1128 @@ index 000000000000..4b924de6ab09 +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface Detachment System (DTX) Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surface_sam/surface_sam_hps.c b/drivers/platform/x86/surface_sam/surface_sam_hps.c +new file mode 100644 +index 000000000000..3b123bd3dcfe +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_hps.c +@@ -0,0 +1,1110 @@ ++/* ++ * Surface dGPU hot-plug system driver. ++ * Supports explicit setting of the dGPU power-state on the Surface Book 2 and ++ * properly handles hot-plugging by detaching the base. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "surface_sam_ssh.h" ++#include "surface_sam_san.h" ++ ++ ++// TODO: vgaswitcheroo integration ++ ++ ++static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix); ++ ++ ++#define SHPS_DSM_REVISION 1 ++#define SHPS_DSM_GPU_ADDRS 0x02 ++#define SHPS_DSM_GPU_POWER 0x05 ++static const guid_t SHPS_DSM_UUID = ++ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, ++ 0x32, 0x0e, 0x10, 0x36, 0x0a); ++ ++ ++#define SAM_DGPU_TC 0x13 ++#define SAM_DGPU_CID_POWERON 0x02 ++ ++#define SAM_DTX_TC 0x11 ++#define SAM_DTX_CID_LATCH_LOCK 0x06 ++#define SAM_DTX_CID_LATCH_UNLOCK 0x07 ++ ++#define SHPS_DSM_GPU_ADDRS_RP "RP5_PCIE" ++#define SHPS_DSM_GPU_ADDRS_DGPU "DGPU_PCIE" ++ ++ ++static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; ++static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; ++ ++static const struct acpi_gpio_mapping shps_acpi_gpios[] = { ++ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, ++ { "base_presence-gpio", &gpio_base_presence, 1 }, ++ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, ++ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, ++ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, ++ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, ++ { }, ++}; ++ ++ ++enum shps_dgpu_power { ++ SHPS_DGPU_POWER_OFF = 0, ++ SHPS_DGPU_POWER_ON = 1, ++ SHPS_DGPU_POWER_UNKNOWN = 2, ++}; ++ ++static const char* shps_dgpu_power_str(enum shps_dgpu_power power) { ++ if (power == SHPS_DGPU_POWER_OFF) ++ return "off"; ++ else if (power == SHPS_DGPU_POWER_ON) ++ return "on"; ++ else if (power == SHPS_DGPU_POWER_UNKNOWN) ++ return "unknown"; ++ else ++ return ""; ++} ++ ++ ++struct shps_driver_data { ++ struct mutex lock; ++ struct pci_dev *dgpu_root_port; ++ struct pci_saved_state *dgpu_root_port_state; ++ struct gpio_desc *gpio_dgpu_power; ++ struct gpio_desc *gpio_dgpu_presence; ++ struct gpio_desc *gpio_base_presence; ++ unsigned int irq_dgpu_presence; ++ unsigned int irq_base_presence; ++ unsigned long state; ++}; ++ ++#define SHPS_STATE_BIT_PWRTGT 0 /* desired power state: 1 for on, 0 for off */ ++#define SHPS_STATE_BIT_RPPWRON_SYNC 1 /* synchronous/requested power-up in progress */ ++#define SHPS_STATE_BIT_WAKE_ENABLED 2 /* wakeup via base-presence GPIO enabled */ ++ ++ ++#define SHPS_DGPU_PARAM_PERM (S_IRUGO | S_IWUSR) ++ ++enum shps_dgpu_power_mp { ++ SHPS_DGPU_MP_POWER_OFF = SHPS_DGPU_POWER_OFF, ++ SHPS_DGPU_MP_POWER_ON = SHPS_DGPU_POWER_ON, ++ SHPS_DGPU_MP_POWER_ASIS = -1, ++ ++ __SHPS_DGPU_MP_POWER_START = -1, ++ __SHPS_DGPU_MP_POWER_END = 1, ++}; ++ ++static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) ++{ ++ int power = SHPS_DGPU_MP_POWER_OFF; ++ int status; ++ ++ status = kstrtoint(val, 0, &power); ++ if (status) { ++ return status; ++ } ++ ++ if (power < __SHPS_DGPU_MP_POWER_START || power > __SHPS_DGPU_MP_POWER_END) { ++ return -EINVAL; ++ } ++ ++ return param_set_int(val, kp); ++} ++ ++static const struct kernel_param_ops param_dgpu_power_ops = { ++ .set = param_dgpu_power_set, ++ .get = param_get_int, ++}; ++ ++static int param_dgpu_power_init = SHPS_DGPU_MP_POWER_OFF; ++static int param_dgpu_power_exit = SHPS_DGPU_MP_POWER_ON; ++static int param_dgpu_power_susp = SHPS_DGPU_MP_POWER_ASIS; ++static bool param_dtx_latch = true; ++ ++module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SHPS_DGPU_PARAM_PERM); ++module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SHPS_DGPU_PARAM_PERM); ++module_param_cb(dgpu_power_susp, ¶m_dgpu_power_ops, ¶m_dgpu_power_susp, SHPS_DGPU_PARAM_PERM); ++module_param_named(dtx_latch, param_dtx_latch, bool, SHPS_DGPU_PARAM_PERM); ++ ++MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is, default: off)"); ++MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: on)"); ++MODULE_PARM_DESC(dgpu_power_susp, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: as-is)"); ++MODULE_PARM_DESC(dtx_latch, "lock/unlock DTX base latch in accordance to power-state (Y/n)"); ++ ++ ++static int dtx_cmd_simple(u8 cid) ++{ ++ struct surface_sam_ssh_rqst rqst = { ++ .tc = SAM_DTX_TC, ++ .cid = cid, ++ .iid = 0, ++ .pri = SURFACE_SAM_PRIORITY_NORMAL, ++ .snc = 0, ++ .cdl = 0, ++ .pld = NULL, ++ }; ++ ++ return surface_sam_ssh_rqst(&rqst, NULL); ++} ++ ++inline static int shps_dtx_latch_lock(void) ++{ ++ return dtx_cmd_simple(SAM_DTX_CID_LATCH_LOCK); ++} ++ ++inline static int shps_dtx_latch_unlock(void) ++{ ++ return dtx_cmd_simple(SAM_DTX_CID_LATCH_UNLOCK); ++} ++ ++ ++static int shps_dgpu_dsm_get_pci_addr(struct platform_device *pdev, const char* entry) ++{ ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ union acpi_object *result; ++ union acpi_object *e0; ++ union acpi_object *e1; ++ union acpi_object *e2; ++ u64 device_addr = 0; ++ u8 bus, dev, fun; ++ int i; ++ ++ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, ++ SHPS_DSM_GPU_ADDRS, NULL, ACPI_TYPE_PACKAGE); ++ ++ if (IS_ERR_OR_NULL(result)) ++ return result ? PTR_ERR(result) : -EIO; ++ ++ // three entries per device: name, address, ++ for (i = 0; i + 2 < result->package.count; i += 3) { ++ e0 = &result->package.elements[i]; ++ e1 = &result->package.elements[i + 1]; ++ e2 = &result->package.elements[i + 2]; ++ ++ if (e0->type != ACPI_TYPE_STRING) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (e1->type != ACPI_TYPE_INTEGER) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (e2->type != ACPI_TYPE_INTEGER) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (strncmp(e0->string.pointer, entry, 64) == 0) ++ device_addr = e1->integer.value; ++ } ++ ++ ACPI_FREE(result); ++ if (device_addr == 0) ++ return -ENODEV; ++ ++ // convert address ++ bus = (device_addr & 0x0FF00000) >> 20; ++ dev = (device_addr & 0x000F8000) >> 15; ++ fun = (device_addr & 0x00007000) >> 12; ++ ++ return bus << 8 | PCI_DEVFN(dev, fun); ++} ++ ++static struct pci_dev *shps_dgpu_dsm_get_pci_dev(struct platform_device *pdev, const char* entry) ++{ ++ struct pci_dev *dev; ++ int addr; ++ ++ addr = shps_dgpu_dsm_get_pci_addr(pdev, entry); ++ if (addr < 0) ++ return ERR_PTR(addr); ++ ++ dev = pci_get_domain_bus_and_slot(0, (addr & 0xFF00) >> 8, addr & 0xFF); ++ return dev ? dev : ERR_PTR(-ENODEV); ++} ++ ++ ++static int shps_dgpu_dsm_get_power_unlocked(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct gpio_desc *gpio = drvdata->gpio_dgpu_power; ++ int status; ++ ++ status = gpiod_get_value_cansleep(gpio); ++ if (status < 0) ++ return status; ++ ++ return status == 0 ? SHPS_DGPU_POWER_OFF : SHPS_DGPU_POWER_ON; ++} ++ ++static int shps_dgpu_dsm_get_power(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_dsm_get_power_unlocked(pdev); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++static int __shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ union acpi_object *result; ++ union acpi_object param; ++ ++ dev_info(&pdev->dev, "setting dGPU direct power to \'%s\'\n", shps_dgpu_power_str(power)); ++ ++ param.type = ACPI_TYPE_INTEGER; ++ param.integer.value = power == SHPS_DGPU_POWER_ON; ++ ++ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, ++ SHPS_DSM_GPU_POWER, ¶m, ACPI_TYPE_BUFFER); ++ ++ if (IS_ERR_OR_NULL(result)) ++ return result ? PTR_ERR(result) : -EIO; ++ ++ // check for the expected result ++ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ ACPI_FREE(result); ++ return 0; ++} ++ ++static int shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) ++ return -EINVAL; ++ ++ status = shps_dgpu_dsm_get_power_unlocked(pdev); ++ if (status < 0) ++ return status; ++ if (status == power) ++ return 0; ++ ++ return __shps_dgpu_dsm_set_power_unlocked(pdev, power); ++} ++ ++static int shps_dgpu_dsm_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_dsm_set_power_unlocked(pdev, power); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++ ++static bool shps_rp_link_up(struct pci_dev *rp) ++{ ++ u16 lnksta = 0, sltsta = 0; ++ ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); ++ ++ return (lnksta & PCI_EXP_LNKSTA_DLLLA) || (sltsta & PCI_EXP_SLTSTA_PDS); ++} ++ ++ ++static int shps_dgpu_rp_get_power_unlocked(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ ++ if (rp->current_state == PCI_D3hot || rp->current_state == PCI_D3cold) ++ return SHPS_DGPU_POWER_OFF; ++ else if (rp->current_state == PCI_UNKNOWN || rp->current_state == PCI_POWER_ERROR) ++ return SHPS_DGPU_POWER_UNKNOWN; ++ else ++ return SHPS_DGPU_POWER_ON; ++} ++ ++static int shps_dgpu_rp_get_power(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++static int __shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ int status, i; ++ ++ dev_info(&pdev->dev, "setting dGPU power state to \'%s\'\n", shps_dgpu_power_str(power)); ++ ++ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.1"); ++ if (power == SHPS_DGPU_POWER_ON) { ++ set_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); ++ pci_set_power_state(rp, PCI_D0); ++ ++ if (drvdata->dgpu_root_port_state) ++ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); ++ ++ pci_restore_state(rp); ++ ++ if (!pci_is_enabled(rp)) ++ pci_enable_device(rp); ++ ++ pci_set_master(rp); ++ clear_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); ++ ++ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } else { ++ if (!drvdata->dgpu_root_port_state) { ++ pci_save_state(rp); ++ drvdata->dgpu_root_port_state = pci_store_saved_state(rp); ++ } ++ ++ /* ++ * To properly update the hot-plug system we need to "remove" the dGPU ++ * before disabling it and sending it to D3cold. Following this, we ++ * need to wait for the link and slot status to actually change. ++ */ ++ status = shps_dgpu_dsm_set_power_unlocked(pdev, SHPS_DGPU_POWER_OFF); ++ if (status) ++ return status; ++ ++ for (i = 0; i < 20 && shps_rp_link_up(rp); i++) ++ msleep(50); ++ ++ if (shps_rp_link_up(rp)) ++ dev_err(&pdev->dev, "dGPU removal via DSM timed out\n"); ++ ++ pci_clear_master(rp); ++ ++ if (pci_is_enabled(rp)) ++ pci_disable_device(rp); ++ ++ pci_set_power_state(rp, PCI_D3cold); ++ ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.2"); ++ ++ return 0; ++} ++ ++static int shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) ++ return -EINVAL; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ return status; ++ if (status == power) ++ return 0; ++ ++ return __shps_dgpu_rp_set_power_unlocked(pdev, power); ++} ++ ++static int shps_dgpu_rp_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_rp_set_power_unlocked(pdev, power); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++ ++static int shps_dgpu_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (!param_dtx_latch) ++ return shps_dgpu_rp_set_power(pdev, power); ++ ++ if (power == SHPS_DGPU_POWER_ON) { ++ status = shps_dtx_latch_lock(); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_rp_set_power(pdev, power); ++ if (status) ++ shps_dtx_latch_unlock(); ++ ++ return status; ++ } else { ++ status = shps_dgpu_rp_set_power(pdev, power); ++ if (status) ++ return status; ++ ++ return shps_dtx_latch_unlock(); ++ } ++} ++ ++ ++static int shps_dgpu_is_present(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ return gpiod_get_value_cansleep(drvdata->gpio_dgpu_presence); ++} ++ ++ ++static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int power = shps_dgpu_rp_get_power(pdev); ++ ++ if (power < 0) ++ return power; ++ ++ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); ++} ++ ++static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, ++ const char *data, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ enum shps_dgpu_power power; ++ bool b = false; ++ int status; ++ ++ status = kstrtobool(data, &b); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status <= 0) ++ return status < 0 ? status : -EPERM; ++ ++ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; ++ status = shps_dgpu_set_power(pdev, power); ++ ++ return status < 0 ? status : count; ++} ++ ++static ssize_t dgpu_power_dsm_show(struct device *dev, struct device_attribute *attr, char *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int power = shps_dgpu_dsm_get_power(pdev); ++ ++ if (power < 0) ++ return power; ++ ++ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); ++} ++ ++static ssize_t dgpu_power_dsm_store(struct device *dev, struct device_attribute *attr, ++ const char *data, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ enum shps_dgpu_power power; ++ bool b = false; ++ int status; ++ ++ status = kstrtobool(data, &b); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status <= 0) ++ return status < 0 ? status : -EPERM; ++ ++ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; ++ status = shps_dgpu_dsm_set_power(pdev, power); ++ ++ return status < 0 ? status : count; ++} ++ ++static DEVICE_ATTR_RW(dgpu_power); ++static DEVICE_ATTR_RW(dgpu_power_dsm); ++ ++static struct attribute *shps_power_attrs[] = { ++ &dev_attr_dgpu_power.attr, ++ &dev_attr_dgpu_power_dsm.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(shps_power); ++ ++ ++static void dbg_dump_power_states(struct platform_device *pdev, const char *prefix) ++{ ++ enum shps_dgpu_power power_dsm; ++ enum shps_dgpu_power power_rp; ++ int status; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ dev_err(&pdev->dev, "%s: failed to get root-port power state: %d\n", prefix, status); ++ power_rp = status; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ dev_err(&pdev->dev, "%s: failed to get direct power state: %d\n", prefix, status); ++ power_dsm = status; ++ ++ dev_dbg(&pdev->dev, "%s: root-port power state: %d\n", prefix, power_rp); ++ dev_dbg(&pdev->dev, "%s: direct power state: %d\n", prefix, power_dsm); ++} ++ ++static void dbg_dump_pciesta(struct platform_device *pdev, const char *prefix) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ u16 lnksta, lnksta2, sltsta, sltsta2; ++ ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA2, &lnksta2); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA2, &sltsta2); ++ ++ dev_dbg(&pdev->dev, "%s: LNKSTA: 0x%04x", prefix, lnksta); ++ dev_dbg(&pdev->dev, "%s: LNKSTA2: 0x%04x", prefix, lnksta2); ++ dev_dbg(&pdev->dev, "%s: SLTSTA: 0x%04x", prefix, sltsta); ++ dev_dbg(&pdev->dev, "%s: SLTSTA2: 0x%04x", prefix, sltsta2); ++} ++ ++static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ ++ dev_dbg(&pdev->dev, "%s: RP power: %d", prefix, rp->current_state); ++ dev_dbg(&pdev->dev, "%s: RP state saved: %d", prefix, rp->state_saved); ++ dev_dbg(&pdev->dev, "%s: RP state stored: %d", prefix, !!drvdata->dgpu_root_port_state); ++ dev_dbg(&pdev->dev, "%s: RP enabled: %d", prefix, atomic_read(&rp->enable_cnt)); ++ dev_dbg(&pdev->dev, "%s: RP mastered: %d", prefix, rp->is_busmaster); ++} ++ ++ ++static int shps_pm_prepare(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ bool pwrtgt; ++ int status = 0; ++ ++ dbg_dump_power_states(pdev, "shps_pm_prepare"); ++ ++ if (param_dgpu_power_susp != SHPS_DGPU_MP_POWER_ASIS) { ++ pwrtgt = test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ ++ status = shps_dgpu_set_power(pdev, param_dgpu_power_susp); ++ if (status) { ++ dev_err(&pdev->dev, "failed to power %s dGPU: %d\n", ++ param_dgpu_power_susp == SHPS_DGPU_MP_POWER_OFF ? "off" : "on", ++ status); ++ return status; ++ } ++ ++ if (pwrtgt) ++ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ else ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ ++ return 0; ++} ++ ++static void shps_pm_complete(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ dbg_dump_power_states(pdev, "shps_pm_complete"); ++ dbg_dump_pciesta(pdev, "shps_pm_complete"); ++ dbg_dump_drvsta(pdev, "shps_pm_complete.1"); ++ ++ // update power target, dGPU may have been detached while suspended ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) { ++ dev_err(&pdev->dev, "failed to get dGPU presence: %d\n", status); ++ return; ++ } else if (status == 0) { ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ ++ /* ++ * During resume, the PCIe core will power on the root-port, which in turn ++ * will power on the dGPU. Most of the state synchronization is already ++ * handled via the SAN RQSG handler, so it is in a fully consistent ++ * on-state here. If requested, turn it off here. ++ * ++ * As there seem to be some synchronization issues turning off the dGPU ++ * directly after the power-on SAN RQSG notification during the resume ++ * process, let's do this here. ++ * ++ * TODO/FIXME: ++ * This does not combat unhandled power-ons when the device is not fully ++ * resumed, i.e. re-suspended before shps_pm_complete is called. Those ++ * should normally not be an issue, but the dGPU does get hot even though ++ * it is suspended, so ideally we want to keep it off. ++ */ ++ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { ++ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); ++ if (status) ++ dev_err(&pdev->dev, "failed to power-off dGPU: %d\n", status); ++ } ++ ++ dbg_dump_drvsta(pdev, "shps_pm_complete.2"); ++} ++ ++static int shps_pm_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ if (device_may_wakeup(dev)) { ++ status = enable_irq_wake(drvdata->irq_base_presence); ++ if (status) ++ return status; ++ ++ set_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state); ++ } ++ ++ return 0; ++} ++ ++static int shps_pm_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status = 0; ++ ++ if (test_and_clear_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state)) { ++ status = disable_irq_wake(drvdata->irq_base_presence); ++ } ++ ++ return status; ++} ++ ++static void shps_shutdown(struct platform_device *pdev) ++{ ++ int status; ++ ++ /* ++ * Turn on dGPU before shutting down. This allows the core drivers to ++ * properly shut down the device. If we don't do this, the pcieport driver ++ * will complain that the device has already been disabled. ++ */ ++ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_ON); ++ if (status) ++ dev_err(&pdev->dev, "failed to turn on dGPU: %d\n", status); ++} ++ ++static int shps_dgpu_detached(struct platform_device *pdev) ++{ ++ dbg_dump_power_states(pdev, "shps_dgpu_detached"); ++ return shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); ++} ++ ++static int shps_dgpu_attached(struct platform_device *pdev) ++{ ++ dbg_dump_power_states(pdev, "shps_dgpu_attached"); ++ return 0; ++} ++ ++static int shps_dgpu_powered_on(struct platform_device *pdev) ++{ ++ /* ++ * This function gets called directly after a power-state transition of ++ * the dGPU root port out of D3cold state, indicating a power-on of the ++ * dGPU. Specifically, this function is called from the RQSG handler of ++ * SAN, invoked by the ACPI _ON method of the dGPU root port. This means ++ * that this function is run inside `pci_set_power_state(rp, ...)` ++ * syncrhonously and thus returns before the `pci_set_power_state` call ++ * does. ++ * ++ * `pci_set_power_state` may either be called by us or when the PCI ++ * subsystem decides to power up the root port (e.g. during resume). Thus ++ * we should use this function to ensure that the dGPU and root port ++ * states are consistent when an unexpected power-up is encountered. ++ */ ++ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ int status; ++ ++ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.1"); ++ ++ // if we caused the root port to power-on, return ++ if (test_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state)) ++ return 0; ++ ++ // if dGPU is not present, force power-target to off and return ++ status = shps_dgpu_is_present(pdev); ++ if (status == 0) ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ if (status <= 0) ++ return status; ++ ++ mutex_lock(&drvdata->lock); ++ ++ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.1"); ++ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.1"); ++ if (drvdata->dgpu_root_port_state) ++ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); ++ pci_restore_state(rp); ++ if (!pci_is_enabled(rp)) ++ pci_enable_device(rp); ++ pci_set_master(rp); ++ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.2"); ++ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.2"); ++ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.2"); ++ ++ mutex_unlock(&drvdata->lock); ++ ++ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { ++ dev_warn(&pdev->dev, "unexpected dGPU power-on detected"); ++ // TODO: schedule state re-check and update ++ } ++ ++ return 0; ++} ++ ++ ++static int shps_dgpu_handle_rqsg(struct surface_sam_san_rqsg *rqsg, void *data) ++{ ++ struct platform_device *pdev = data; ++ ++ if (rqsg->tc == SAM_DGPU_TC && rqsg->cid == SAM_DGPU_CID_POWERON) ++ return shps_dgpu_powered_on(pdev); ++ ++ dev_warn(&pdev->dev, "unimplemented dGPU request: RQSG(0x%02x, 0x%02x, 0x%02x)", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ return 0; ++} ++ ++static irqreturn_t shps_dgpu_presence_irq(int irq, void *data) ++{ ++ struct platform_device *pdev = data; ++ bool dgpu_present; ++ int status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) { ++ dev_err(&pdev->dev, "failed to check physical dGPU presence: %d\n", status); ++ return IRQ_HANDLED; ++ } ++ ++ dgpu_present = status != 0; ++ dev_info(&pdev->dev, "dGPU physically %s\n", dgpu_present ? "attached" : "detached"); ++ ++ if (dgpu_present) ++ status = shps_dgpu_attached(pdev); ++ else ++ status = shps_dgpu_detached(pdev); ++ ++ if (status) ++ dev_err(&pdev->dev, "error handling dGPU interrupt: %d\n", status); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t shps_base_presence_irq(int irq, void *data) ++{ ++ return IRQ_HANDLED; // nothing to do, just wake ++} ++ ++ ++static int shps_gpios_setup(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct gpio_desc *gpio_dgpu_power; ++ struct gpio_desc *gpio_dgpu_presence; ++ struct gpio_desc *gpio_base_presence; ++ int status; ++ ++ // get GPIOs ++ gpio_dgpu_power = devm_gpiod_get(&pdev->dev, "dgpu_power", GPIOD_IN); ++ if (IS_ERR(gpio_dgpu_power)) { ++ status = PTR_ERR(gpio_dgpu_power); ++ goto err_out; ++ } ++ ++ gpio_dgpu_presence = devm_gpiod_get(&pdev->dev, "dgpu_presence", GPIOD_IN); ++ if (IS_ERR(gpio_dgpu_presence)) { ++ status = PTR_ERR(gpio_dgpu_presence); ++ goto err_out; ++ } ++ ++ gpio_base_presence = devm_gpiod_get(&pdev->dev, "base_presence", GPIOD_IN); ++ if (IS_ERR(gpio_base_presence)) { ++ status = PTR_ERR(gpio_base_presence); ++ goto err_out; ++ } ++ ++ // export GPIOs ++ status = gpiod_export(gpio_dgpu_power, false); ++ if (status) ++ goto err_out; ++ ++ status = gpiod_export(gpio_dgpu_presence, false); ++ if (status) ++ goto err_export_dgpu_presence; ++ ++ status = gpiod_export(gpio_base_presence, false); ++ if (status) ++ goto err_export_base_presence; ++ ++ // create sysfs links ++ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_power", gpio_dgpu_power); ++ if (status) ++ goto err_link_dgpu_power; ++ ++ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_presence", gpio_dgpu_presence); ++ if (status) ++ goto err_link_dgpu_presence; ++ ++ status = gpiod_export_link(&pdev->dev, "gpio-base_presence", gpio_base_presence); ++ if (status) ++ goto err_link_base_presence; ++ ++ drvdata->gpio_dgpu_power = gpio_dgpu_power; ++ drvdata->gpio_dgpu_presence = gpio_dgpu_presence; ++ drvdata->gpio_base_presence = gpio_base_presence; ++ return 0; ++ ++err_link_base_presence: ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); ++err_link_dgpu_presence: ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); ++err_link_dgpu_power: ++ gpiod_unexport(gpio_base_presence); ++err_export_base_presence: ++ gpiod_unexport(gpio_dgpu_presence); ++err_export_dgpu_presence: ++ gpiod_unexport(gpio_dgpu_power); ++err_out: ++ return status; ++} ++ ++static void shps_gpios_remove(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-base_presence"); ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); ++ gpiod_unexport(drvdata->gpio_base_presence); ++ gpiod_unexport(drvdata->gpio_dgpu_presence); ++ gpiod_unexport(drvdata->gpio_dgpu_power); ++} ++ ++static int shps_gpios_setup_irq(struct platform_device *pdev) ++{ ++ const int irqf_dgpu = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ const int irqf_base = IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ status = gpiod_to_irq(drvdata->gpio_base_presence); ++ if (status < 0) ++ return status; ++ drvdata->irq_base_presence = status; ++ ++ status = gpiod_to_irq(drvdata->gpio_dgpu_presence); ++ if (status < 0) ++ return status; ++ drvdata->irq_dgpu_presence = status; ++ ++ status = request_irq(drvdata->irq_base_presence, ++ shps_base_presence_irq, irqf_base, ++ "shps_base_presence_irq", pdev); ++ if (status) ++ return status; ++ ++ status = request_threaded_irq(drvdata->irq_dgpu_presence, ++ NULL, shps_dgpu_presence_irq, irqf_dgpu, ++ "shps_dgpu_presence_irq", pdev); ++ if (status) { ++ free_irq(drvdata->irq_base_presence, pdev); ++ return status; ++ } ++ ++ return 0; ++} ++ ++static void shps_gpios_remove_irq(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ ++ free_irq(drvdata->irq_base_presence, pdev); ++ free_irq(drvdata->irq_dgpu_presence, pdev); ++} ++ ++static int shps_probe(struct platform_device *pdev) ++{ ++ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); ++ struct shps_driver_data *drvdata; ++ struct device_link *link; ++ int power, status; ++ ++ if (gpiod_count(&pdev->dev, NULL) < 0) ++ return -ENODEV; ++ ++ // link to SSH ++ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ if (status) { ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ } ++ ++ // link to SAN ++ status = surface_sam_san_consumer_register(&pdev->dev, 0); ++ if (status) { ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ } ++ ++ status = acpi_dev_add_driver_gpios(shps_dev, shps_acpi_gpios); ++ if (status) ++ return status; ++ ++ drvdata = kzalloc(sizeof(struct shps_driver_data), GFP_KERNEL); ++ if (!drvdata) { ++ status = -ENOMEM; ++ goto err_drvdata; ++ } ++ mutex_init(&drvdata->lock); ++ platform_set_drvdata(pdev, drvdata); ++ ++ drvdata->dgpu_root_port = shps_dgpu_dsm_get_pci_dev(pdev, SHPS_DSM_GPU_ADDRS_RP); ++ if (IS_ERR(drvdata->dgpu_root_port)) { ++ status = PTR_ERR(drvdata->dgpu_root_port); ++ goto err_rp_lookup; ++ } ++ ++ status = shps_gpios_setup(pdev); ++ if (status) ++ goto err_gpio; ++ ++ status = shps_gpios_setup_irq(pdev); ++ if (status) ++ goto err_gpio_irqs; ++ ++ status = device_add_groups(&pdev->dev, shps_power_groups); ++ if (status) ++ goto err_devattr; ++ ++ link = device_link_add(&pdev->dev, &drvdata->dgpu_root_port->dev, ++ DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER); ++ if (!link) ++ goto err_devlink; ++ ++ surface_sam_san_set_rqsg_handler(shps_dgpu_handle_rqsg, pdev); ++ ++ // if dGPU is not present turn-off root-port, else obey module param ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) ++ goto err_devlink; ++ ++ power = status == 0 ? SHPS_DGPU_POWER_OFF : param_dgpu_power_init; ++ if (power != SHPS_DGPU_MP_POWER_ASIS) { ++ status = shps_dgpu_set_power(pdev, power); ++ if (status) ++ goto err_devlink; ++ } ++ ++ device_init_wakeup(&pdev->dev, true); ++ return 0; ++ ++err_devlink: ++ device_remove_groups(&pdev->dev, shps_power_groups); ++err_devattr: ++ shps_gpios_remove_irq(pdev); ++err_gpio_irqs: ++ shps_gpios_remove(pdev); ++err_gpio: ++ pci_dev_put(drvdata->dgpu_root_port); ++err_rp_lookup: ++ platform_set_drvdata(pdev, NULL); ++ kfree(drvdata); ++err_drvdata: ++ acpi_dev_remove_driver_gpios(shps_dev); ++ return status; ++} ++ ++static int shps_remove(struct platform_device *pdev) ++{ ++ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ if (param_dgpu_power_exit != SHPS_DGPU_MP_POWER_ASIS) { ++ status = shps_dgpu_set_power(pdev, param_dgpu_power_exit); ++ if (status) ++ dev_err(&pdev->dev, "failed to set dGPU power state: %d\n", status); ++ } ++ ++ device_set_wakeup_capable(&pdev->dev, false); ++ surface_sam_san_set_rqsg_handler(NULL, NULL); ++ device_remove_groups(&pdev->dev, shps_power_groups); ++ shps_gpios_remove_irq(pdev); ++ shps_gpios_remove(pdev); ++ pci_dev_put(drvdata->dgpu_root_port); ++ platform_set_drvdata(pdev, NULL); ++ kfree(drvdata); ++ ++ acpi_dev_remove_driver_gpios(shps_dev); ++ return 0; ++} ++ ++ ++static const struct dev_pm_ops shps_pm_ops = { ++ .prepare = shps_pm_prepare, ++ .complete = shps_pm_complete, ++ .suspend = shps_pm_suspend, ++ .resume = shps_pm_resume, ++}; ++ ++static const struct acpi_device_id shps_acpi_match[] = { ++ { "MSHW0153", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, shps_acpi_match); ++ ++struct platform_driver surface_sam_hps = { ++ .probe = shps_probe, ++ .remove = shps_remove, ++ .shutdown = shps_shutdown, ++ .driver = { ++ .name = "surface_dgpu_hps", ++ .acpi_match_table = ACPI_PTR(shps_acpi_match), ++ .pm = &shps_pm_ops, ++ }, ++}; ++module_platform_driver(surface_sam_hps); ++ ++MODULE_AUTHOR("Maximilian Luz "); ++MODULE_DESCRIPTION("Surface Hot-Plug System (HPS) and dGPU power-state Driver for Surface Book 2"); ++MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.c b/drivers/platform/x86/surface_sam/surface_sam_san.c new file mode 100644 -index 000000000000..810165083e0e +index 000000000000..aa0cfc4262be --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_san.c -@@ -0,0 +1,791 @@ +@@ -0,0 +1,901 @@ +/* + * Surface ACPI Notify (SAN) and ACPI integration driver for SAM. + * Translates communication from ACPI to SSH and back. @@ -924,6 +2056,7 @@ index 000000000000..810165083e0e +#include + +#include "surface_sam_ssh.h" ++#include "surface_sam_san.h" + + +#define SAN_RQST_RETRY 5 @@ -949,7 +2082,8 @@ index 000000000000..810165083e0e +#define SAM_EVENT_TEMP_RQID 0x0003 +#define SAM_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT 0x0b + -+#define SAN_RQST_TAG "surface_sam_san_rqst: " ++#define SAN_RQST_TAG "surface_sam_san: rqst: " ++#define SAN_RQSG_TAG "surface_sam_san: rqsg: " + +#define SAN_QUIRK_BASE_STATE_DELAY 1000 + @@ -967,17 +2101,17 @@ index 000000000000..810165083e0e + +struct san_consumer_link { + const struct san_acpi_consumer *properties; -+ struct device_link *link; ++ struct device_link *link; +}; + +struct san_consumers { -+ u32 num; ++ u32 num; + struct san_consumer_link *links; +}; + +struct san_drvdata { + struct san_opreg_context opreg_ctx; -+ struct san_consumers consumers; ++ struct san_consumers consumers; + bool has_power_events; +}; + @@ -1034,6 +2168,79 @@ index 000000000000..810165083e0e +}; + + ++static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data); ++ ++struct sam_san_rqsg_if { ++ struct mutex lock; ++ struct device *san_dev; ++ surface_sam_san_rqsg_handler_fn handler; ++ void *handler_data; ++}; ++ ++static struct sam_san_rqsg_if rqsg_if = { ++ .lock = __MUTEX_INITIALIZER(rqsg_if.lock), ++ .san_dev = NULL, ++ .handler = sam_san_default_rqsg_handler, ++ .handler_data = NULL, ++}; ++ ++int surface_sam_san_consumer_register(struct device *consumer, u32 flags) ++{ ++ const u32 valid = DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE; ++ int status; ++ ++ if ((flags | valid) != valid) ++ return -EINVAL; ++ ++ flags |= DL_FLAG_AUTOREMOVE_CONSUMER; ++ ++ mutex_lock(&rqsg_if.lock); ++ if (rqsg_if.san_dev) ++ status = device_link_add(consumer, rqsg_if.san_dev, flags) ? 0 : -EINVAL; ++ else ++ status = -ENXIO; ++ mutex_unlock(&rqsg_if.lock); ++ return status; ++} ++EXPORT_SYMBOL_GPL(surface_sam_san_consumer_register); ++ ++int surface_sam_san_set_rqsg_handler(surface_sam_san_rqsg_handler_fn fn, void *data) ++{ ++ int status = -EBUSY; ++ ++ mutex_lock(&rqsg_if.lock); ++ ++ if (rqsg_if.handler == sam_san_default_rqsg_handler || !fn) { ++ rqsg_if.handler = fn ? fn : sam_san_default_rqsg_handler; ++ rqsg_if.handler_data = data; ++ status = 0; ++ } ++ ++ mutex_unlock(&rqsg_if.lock); ++ return status; ++} ++EXPORT_SYMBOL_GPL(surface_sam_san_set_rqsg_handler); ++ ++int san_call_rqsg_handler(struct surface_sam_san_rqsg *rqsg) ++{ ++ int status; ++ ++ mutex_lock(&rqsg_if.lock); ++ status = rqsg_if.handler(rqsg, rqsg_if.handler_data); ++ mutex_unlock(&rqsg_if.lock); ++ ++ return status; ++} ++ ++static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data) ++{ ++ pr_warn(SAN_RQSG_TAG "unhandled request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ ++ return 0; ++} ++ ++ +static int san_acpi_notify_power_event(struct device *dev, enum san_pwr_event event) +{ + acpi_handle san = ACPI_HANDLE(dev); @@ -1369,16 +2576,33 @@ index 000000000000..810165083e0e +static acpi_status +san_rqsg(struct san_opreg_context *ctx, struct gsb_buffer *buffer) +{ -+ struct gsb_data_rqsx *rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ struct gsb_data_rqsx *gsb_rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ struct surface_sam_san_rqsg rqsg = {}; ++ int status; + -+ if (!rqsg) { ++ if (!gsb_rqsg) { + return AE_OK; + } + -+ // TODO: RQSG handler ++ rqsg.tc = gsb_rqsg->tc; ++ rqsg.cid = gsb_rqsg->cid; ++ rqsg.iid = gsb_rqsg->iid; ++ rqsg.cdl = gsb_rqsg->cdl; ++ rqsg.pld = &gsb_rqsg->pld[0]; + -+ dev_warn(ctx->dev, "unsupported request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", -+ rqsg->tc, rqsg->cid, rqsg->iid); ++ status = san_call_rqsg_handler(&rqsg); ++ if (!status) { ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x00; ++ buffer->data.out.len = 0x00; ++ } else { ++ dev_err(ctx->dev, SAN_RQSG_TAG "failed with error %d\n", status); ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x01; // indicate _SSH error ++ buffer->data.out.len = 0x00; ++ } + + return AE_OK; +} @@ -1645,9 +2869,23 @@ index 000000000000..810165083e0e + goto err_enable_events; + } + ++ mutex_lock(&rqsg_if.lock); ++ if (!rqsg_if.san_dev) { ++ rqsg_if.san_dev = &pdev->dev; ++ } else { ++ status = -EBUSY; ++ } ++ mutex_unlock(&rqsg_if.lock); ++ ++ if (status) { ++ goto err_install_dev; ++ } ++ + acpi_walk_dep_device_list(san); + return 0; + ++err_install_dev: ++ san_disable_events(pdev); +err_enable_events: + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); +err_install_handler: @@ -1664,6 +2902,10 @@ index 000000000000..810165083e0e + acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node + acpi_status status = AE_OK; + ++ mutex_lock(&rqsg_if.lock); ++ rqsg_if.san_dev = NULL; ++ mutex_unlock(&rqsg_if.lock); ++ + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); + san_disable_events(pdev); + @@ -1703,6 +2945,41 @@ index 000000000000..810165083e0e +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface ACPI Notify Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.h b/drivers/platform/x86/surface_sam/surface_sam_san.h +new file mode 100644 +index 000000000000..1ea8713db367 +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_san.h +@@ -0,0 +1,29 @@ ++/* ++ * Interface for Surface ACPI/Notify (SAN). ++ * ++ * The SAN is the main interface between the Surface Serial Hub (SSH) and the ++ * Surface/System Aggregator Module (SAM). It allows requests to be translated ++ * from ACPI to SSH/SAM. It also interfaces with the discrete GPU hot-plug ++ * driver. ++ */ ++ ++#ifndef _SURFACE_SAM_SAN_H ++#define _SURFACE_SAM_SAN_H ++ ++#include ++ ++ ++struct surface_sam_san_rqsg { ++ u8 tc; // target category ++ u8 cid; // command ID ++ u8 iid; // instance ID ++ u8 cdl; // command data length (lenght of payload) ++ u8 *pld; // pointer to payload of length cdl ++}; ++ ++typedef int (*surface_sam_san_rqsg_handler_fn)(struct surface_sam_san_rqsg *rqsg, void *data); ++ ++int surface_sam_san_consumer_register(struct device *consumer, u32 flags); ++int surface_sam_san_set_rqsg_handler(surface_sam_san_rqsg_handler_fn fn, void *data); ++ ++#endif /* _SURFACE_SAM_SAN_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid.c b/drivers/platform/x86/surface_sam/surface_sam_sid.c new file mode 100644 index 000000000000..f64dcd590494 @@ -2284,10 +3561,10 @@ index 000000000000..880a2567cf1b +MODULE_ALIAS("platform:surface_sam_sid_perfmode"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_power.c b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c new file mode 100644 -index 000000000000..ce4274709d68 +index 000000000000..1f2c88eda394 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c -@@ -0,0 +1,1258 @@ +@@ -0,0 +1,1259 @@ +/* + * Surface SID Battery/AC Driver. + * Provides support for the battery and AC on 7th generation Surface devices. @@ -3545,7 +4822,8 @@ index 000000000000..ce4274709d68 +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface Battery/AC Driver for 7th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:surface_sam_sid_power"); ++MODULE_ALIAS("platform:surface_sam_sid_ac"); ++MODULE_ALIAS("platform:surface_sam_sid_battery"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c new file mode 100644 index 000000000000..dc5be3a14a8c @@ -3994,10 +5272,10 @@ index 000000000000..dc5be3a14a8c +MODULE_ALIAS("platform:surface_sam_sid_vhf"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c new file mode 100644 -index 000000000000..665d956eec01 +index 000000000000..34905cf29a51 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c -@@ -0,0 +1,1779 @@ +@@ -0,0 +1,1773 @@ +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System + * Aggregator Module. @@ -4080,8 +5358,8 @@ index 000000000000..665d956eec01 + +/* + * A note on Request IDs (RQIDs): -+ * 0x0000 is not a valid RQID -+ * 0x0001 is valid, but reserved for Surface Laptop keyboard events ++ * 0x0000 is not a valid RQID ++ * 0x0001 is valid, but reserved for Surface Laptop keyboard events + */ +#define SAM_NUM_EVENT_TYPES ((1 << SURFACE_SAM_SSH_RQID_EVENT_BITS) - 1) + @@ -5334,11 +6612,6 @@ index 000000000000..665d956eec01 + +static irqreturn_t surface_sam_irq_handler(int irq, void *dev_id) +{ -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t surface_sam_irq_handler_th(int irq, void *dev_id) -+{ + struct serdev_device *serdev = dev_id; + + dev_info(&serdev->dev, "wake irq triggered\n"); @@ -5347,7 +6620,7 @@ index 000000000000..665d956eec01 + +static int surface_sam_setup_irq(struct serdev_device *serdev) +{ -+ const int irqf = IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING; + struct gpio_desc *gpiod; + int irq; + int status; @@ -5362,8 +6635,7 @@ index 000000000000..665d956eec01 + if (irq < 0) + return irq; + -+ status = request_threaded_irq(irq, surface_sam_irq_handler, -+ surface_sam_irq_handler_th, ++ status = request_threaded_irq(irq, NULL, surface_sam_irq_handler, + irqf, "surface_sam_wakeup", serdev); + if (status) + return status; @@ -6314,5 +7586,5 @@ index a0ac16ee6575..226adeec2aed 100644 if (!ctrl->serdev) return -ENODEV; -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0002-buttons.patch b/patches/5.3/0002-buttons.patch index 883eaa941..a35ed5d18 100644 --- a/patches/5.3/0002-buttons.patch +++ b/patches/5.3/0002-buttons.patch @@ -1,4 +1,4 @@ -From d836ba289ea5db5f1cee4dbc6c9d01221a485dfb Mon Sep 17 00:00:00 2001 +From 5e6d16a8fb200eb0325061c7a0f10126d69c2466 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Jul 2019 17:51:37 +0200 Subject: [PATCH 02/10] buttons @@ -270,5 +270,5 @@ index 47c6d000465a..ec515223f654 100644 if (!button) return -ENOMEM; -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0004-hid.patch b/patches/5.3/0003-hid.patch similarity index 89% rename from patches/5.3/0004-hid.patch rename to patches/5.3/0003-hid.patch index afcfad51d..69318d7ab 100644 --- a/patches/5.3/0004-hid.patch +++ b/patches/5.3/0003-hid.patch @@ -1,7 +1,7 @@ -From 4d565b178d767e637429bfe12feeda48cddf1484 Mon Sep 17 00:00:00 2001 +From 36d79941fc26dde7702139c3df51a6f4dc54b64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 6 Nov 2019 19:43:26 +0900 -Subject: [PATCH 04/10] hid +Subject: [PATCH 03/10] hid --- drivers/hid/hid-core.c | 4 ++++ @@ -23,5 +23,5 @@ index 12149c5c39e4..dea1f9139b5c 100644 static void hid_scan_collection(struct hid_parser *parser, unsigned type) -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0003-surfacebook2-dgpu.patch b/patches/5.3/0003-surfacebook2-dgpu.patch deleted file mode 100644 index 0ac138f62..000000000 --- a/patches/5.3/0003-surfacebook2-dgpu.patch +++ /dev/null @@ -1,359 +0,0 @@ -From 84256bec041bd435d4413fc07841a103c7335583 Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 2 Jul 2019 22:17:46 +0200 -Subject: [PATCH 03/10] surfacebook2-dgpu - ---- - drivers/platform/x86/Kconfig | 9 + - drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surfacebook2_dgpu_hps.c | 306 +++++++++++++++++++ - 3 files changed, 316 insertions(+) - create mode 100644 drivers/platform/x86/surfacebook2_dgpu_hps.c - -diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 9dea69a1526a..0c8971f1774e 100644 ---- a/drivers/platform/x86/Kconfig -+++ b/drivers/platform/x86/Kconfig -@@ -481,6 +481,15 @@ config SURFACE3_WMI - To compile this driver as a module, choose M here: the module will - be called surface3-wmi. - -+config SURFACE_BOOK2_DGPU_HPS -+ tristate "Surface Book 2 dGPU Hot-Plug System Driver" -+ depends on ACPI -+ ---help--- -+ This is an experimetnal driver to control the power-state of the -+ Surface Book 2 dGPU. -+ -+ If you have a Surface Book 2, say Y or M here. -+ - config THINKPAD_ACPI - tristate "ThinkPad ACPI Laptop Extras" - depends on ACPI -diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 18f5a4ba7244..725dedf5fbfe 100644 ---- a/drivers/platform/x86/Makefile -+++ b/drivers/platform/x86/Makefile -@@ -48,6 +48,7 @@ obj-$(CONFIG_ACPI_WMI) += wmi.o - obj-$(CONFIG_MSI_WMI) += msi-wmi.o - obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o - obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o -+obj-$(CONFIG_SURFACE_BOOK2_DGPU_HPS) += surfacebook2_dgpu_hps.o - obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o - obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o - obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o -diff --git a/drivers/platform/x86/surfacebook2_dgpu_hps.c b/drivers/platform/x86/surfacebook2_dgpu_hps.c -new file mode 100644 -index 000000000000..7639fb0029d8 ---- /dev/null -+++ b/drivers/platform/x86/surfacebook2_dgpu_hps.c -@@ -0,0 +1,306 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+ -+#define SB2_SHPS_DSM_REVISION 1 -+#define SB2_SHPS_DSM_GPU_STATE 0x05 -+ -+static const guid_t SB2_SHPS_DSM_UUID = -+ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, -+ 0x32, 0x0e, 0x10, 0x36, 0x0a); -+ -+#define SB2_PARAM_PERM (S_IRUGO | S_IWUSR) -+ -+ -+static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; -+static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; -+ -+static const struct acpi_gpio_mapping sb2_mshw0153_acpi_gpios[] = { -+ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, -+ { "base_presence-gpio", &gpio_base_presence, 1 }, -+ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, -+ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, -+ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, -+ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, -+ { }, -+}; -+ -+ -+enum sb2_dgpu_power { -+ SB2_DGPU_POWER_OFF = 0, -+ SB2_DGPU_POWER_ON = 1, -+ -+ __SB2_DGPU_POWER__START = 0, -+ __SB2_DGPU_POWER__END = 1, -+}; -+ -+enum sb2_param_dgpu_power { -+ SB2_PARAM_DGPU_POWER_OFF = SB2_DGPU_POWER_OFF, -+ SB2_PARAM_DGPU_POWER_ON = SB2_DGPU_POWER_ON, -+ SB2_PARAM_DGPU_POWER_AS_IS = 2, -+ -+ __SB2_PARAM_DGPU_POWER__START = 0, -+ __SB2_PARAM_DGPU_POWER__END = 2, -+}; -+ -+static const char* sb2_dgpu_power_str(enum sb2_dgpu_power power) { -+ if (power == SB2_DGPU_POWER_OFF) { -+ return "off"; -+ } else if (power == SB2_DGPU_POWER_ON) { -+ return "on"; -+ } else { -+ return ""; -+ } -+} -+ -+ -+struct sb2_shps_driver_data { -+ struct mutex dgpu_power_lock; -+ enum sb2_dgpu_power dgpu_power; -+}; -+ -+ -+static int __sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ acpi_handle handle = ACPI_HANDLE(&pdev->dev); -+ union acpi_object *result; -+ union acpi_object param; -+ -+ param.type = ACPI_TYPE_INTEGER; -+ param.integer.value = power == SB2_DGPU_POWER_ON; -+ -+ result = acpi_evaluate_dsm_typed(handle, &SB2_SHPS_DSM_UUID, SB2_SHPS_DSM_REVISION, -+ SB2_SHPS_DSM_GPU_STATE, ¶m, ACPI_TYPE_BUFFER); -+ -+ if (IS_ERR_OR_NULL(result)) { -+ return result ? PTR_ERR(result) : -EFAULT; -+ } -+ -+ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { -+ return -EIO; -+ } -+ -+ drvdata->dgpu_power = power; -+ -+ printk(KERN_INFO "sb2_shps: dGPU power state set to \'%s\'\n", sb2_dgpu_power_str(power)); -+ -+ ACPI_FREE(result); -+ return 0; -+} -+ -+static int sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status = 0; -+ -+ if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&drvdata->dgpu_power_lock); -+ if (power != drvdata->dgpu_power) { -+ status = __sb2_shps_dgpu_set_power(pdev, power); -+ } -+ mutex_unlock(&drvdata->dgpu_power_lock); -+ -+ return status; -+} -+ -+static int sb2_shps_dgpu_force_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&drvdata->dgpu_power_lock); -+ status = __sb2_shps_dgpu_set_power(pdev, power); -+ mutex_unlock(&drvdata->dgpu_power_lock); -+ -+ return status; -+} -+ -+ -+static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) -+{ -+ int power = SB2_PARAM_DGPU_POWER_OFF; -+ int status; -+ -+ status = kstrtoint(val, 0, &power); -+ if (status) { -+ return status; -+ } -+ -+ if (power < __SB2_PARAM_DGPU_POWER__START || power > __SB2_PARAM_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ return param_set_int(val, kp); -+} -+ -+static const struct kernel_param_ops param_dgpu_power_ops = { -+ .set = param_dgpu_power_set, -+ .get = param_get_int, -+}; -+ -+static int param_dgpu_power_init = SB2_PARAM_DGPU_POWER_OFF; -+static int param_dgpu_power_exit = SB2_PARAM_DGPU_POWER_OFF; -+ -+module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SB2_PARAM_PERM); -+module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SB2_PARAM_PERM); -+ -+MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is)"); -+MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is)"); -+ -+ -+static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ return sprintf(data, "%s\n", sb2_dgpu_power_str(drvdata->dgpu_power)); -+} -+ -+static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, -+ const char *data, size_t count) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ bool power = false; -+ int status; -+ -+ status = kstrtobool(data, &power); -+ if (status) { -+ return status; -+ } -+ -+ if (power) { -+ status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_ON); -+ } else { -+ status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_OFF); -+ } -+ -+ return status < 0 ? status : count; -+} -+ -+const static DEVICE_ATTR_RW(dgpu_power); -+ -+ -+#ifdef CONFIG_PM -+ -+static int sb2_shps_resume(struct device *dev) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ return sb2_shps_dgpu_force_power(pdev, drvdata->dgpu_power); -+} -+ -+static SIMPLE_DEV_PM_OPS(sb2_shps_pm_ops, NULL, sb2_shps_resume); -+ -+#endif -+ -+ -+static int sb2_shps_probe(struct platform_device *pdev) -+{ -+ struct sb2_shps_driver_data *drvdata; -+ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); -+ int status = 0; -+ -+ if (gpiod_count(&pdev->dev, NULL) < 0) { -+ return -ENODEV; -+ } -+ -+ status = acpi_dev_add_driver_gpios(shps_dev, sb2_mshw0153_acpi_gpios); -+ if (status) { -+ return status; -+ } -+ -+ drvdata = kzalloc(sizeof(struct sb2_shps_driver_data), GFP_KERNEL); -+ if (!drvdata) { -+ status = -ENOMEM; -+ goto err_alloc_drvdata; -+ } -+ -+ mutex_init(&drvdata->dgpu_power_lock); -+ drvdata->dgpu_power = SB2_DGPU_POWER_OFF; -+ platform_set_drvdata(pdev, drvdata); -+ -+ if (param_dgpu_power_init != SB2_PARAM_DGPU_POWER_AS_IS) { -+ status = sb2_shps_dgpu_force_power(pdev, param_dgpu_power_init); -+ if (status) { -+ goto err_set_power; -+ } -+ } -+ -+ status = sysfs_create_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr); -+ if (status) { -+ goto err_sysfs; -+ } -+ -+ return 0; -+ -+err_sysfs: -+ sb2_shps_dgpu_force_power(pdev, SB2_DGPU_POWER_OFF); -+err_set_power: -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+err_alloc_drvdata: -+ acpi_dev_remove_driver_gpios(shps_dev); -+ return status; -+} -+ -+static int sb2_shps_remove(struct platform_device *pdev) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); -+ -+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr); -+ -+ if (param_dgpu_power_exit != SB2_PARAM_DGPU_POWER_AS_IS) { -+ sb2_shps_dgpu_set_power(pdev, param_dgpu_power_exit); -+ } -+ acpi_dev_remove_driver_gpios(shps_dev); -+ -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+ -+ return 0; -+} -+ -+ -+static const struct acpi_device_id sb2_shps_acpi_match[] = { -+ { "MSHW0153", 0 }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, sb2_shps_acpi_match); -+ -+static struct platform_driver sb2_shps_driver = { -+ .probe = sb2_shps_probe, -+ .remove = sb2_shps_remove, -+ .driver = { -+ .name = "sb2_shps", -+ .acpi_match_table = ACPI_PTR(sb2_shps_acpi_match), -+#ifdef CONFIG_PM -+ .pm = &sb2_shps_pm_ops, -+#endif -+ }, -+}; -+module_platform_driver(sb2_shps_driver); -+ -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("Surface Book 2 Hot-Plug System Driver"); -+MODULE_LICENSE("GPL v2"); --- -2.24.0 - diff --git a/patches/5.3/0005-surface3-power.patch b/patches/5.3/0004-surface3-power.patch similarity index 98% rename from patches/5.3/0005-surface3-power.patch rename to patches/5.3/0004-surface3-power.patch index d871c5539..b12af0ea7 100644 --- a/patches/5.3/0005-surface3-power.patch +++ b/patches/5.3/0004-surface3-power.patch @@ -1,7 +1,7 @@ -From 01219c3aed6f2f1887c4abeac02e90db353fd922 Mon Sep 17 00:00:00 2001 +From 40f02c1a770696c46b38c949f418c561711b6819 Mon Sep 17 00:00:00 2001 From: qzed Date: Tue, 17 Sep 2019 17:17:56 +0200 -Subject: [PATCH 05/10] surface3-power +Subject: [PATCH 04/10] surface3-power --- drivers/platform/x86/Kconfig | 7 + @@ -11,10 +11,10 @@ Subject: [PATCH 05/10] surface3-power create mode 100644 drivers/platform/x86/surface3_power.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 0c8971f1774e..c3d68aeec587 100644 +index 9dea69a1526a..275f6498e162 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -1218,6 +1218,13 @@ config SURFACE_3_BUTTON +@@ -1209,6 +1209,13 @@ config SURFACE_3_BUTTON ---help--- This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. @@ -29,10 +29,10 @@ index 0c8971f1774e..c3d68aeec587 100644 tristate "Intel P-Unit IPC Driver" ---help--- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 725dedf5fbfe..705525ff99a7 100644 +index 18f5a4ba7244..19b56f2181eb 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile -@@ -86,6 +86,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o +@@ -85,6 +85,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o @@ -651,5 +651,5 @@ index 000000000000..e0af01a60302 +MODULE_DESCRIPTION("mshw0011 driver"); +MODULE_LICENSE("GPL v2"); -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0006-surface-lte.patch b/patches/5.3/0005-surface-lte.patch similarity index 88% rename from patches/5.3/0006-surface-lte.patch rename to patches/5.3/0005-surface-lte.patch index ebb489711..e50cab583 100644 --- a/patches/5.3/0006-surface-lte.patch +++ b/patches/5.3/0005-surface-lte.patch @@ -1,7 +1,7 @@ -From 0e6c9d5398a6d2254c6d595112b234e222637a8b Mon Sep 17 00:00:00 2001 +From 5aba1417751252a6dcdb18ff3ad311bf7611689c Mon Sep 17 00:00:00 2001 From: qzed Date: Tue, 17 Sep 2019 17:21:43 +0200 -Subject: [PATCH 06/10] surface-lte +Subject: [PATCH 05/10] surface-lte --- drivers/usb/serial/qcserial.c | 1 + @@ -20,5 +20,5 @@ index 613f91add03d..e1428222dd73 100644 /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0006-wifi.patch b/patches/5.3/0006-wifi.patch new file mode 100644 index 000000000..da2908722 --- /dev/null +++ b/patches/5.3/0006-wifi.patch @@ -0,0 +1,171 @@ +From 1d1f574d38351171cb963be29c2eaee4b5317895 Mon Sep 17 00:00:00 2001 +From: sebanc <22224731+sebanc@users.noreply.github.com> +Date: Mon, 4 Nov 2019 09:30:57 +0100 +Subject: [PATCH 06/10] wifi + +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 75 ++++++++++--------- + .../net/wireless/marvell/mwifiex/sta_cmd.c | 15 +--- + 2 files changed, 40 insertions(+), 50 deletions(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index b54f73e3d508..f0925b3d5aaf 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -149,37 +149,39 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) + */ + static int mwifiex_pcie_suspend(struct device *dev) + { +- struct mwifiex_adapter *adapter; +- struct pcie_service_card *card; + struct pci_dev *pdev = to_pci_dev(dev); ++ struct pcie_service_card *card = pci_get_drvdata(pdev); ++ struct mwifiex_adapter *adapter; ++ struct mwifiex_private *priv; ++ const struct mwifiex_pcie_card_reg *reg; ++ u32 fw_status; ++ int ret; + +- card = pci_get_drvdata(pdev); + + /* 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) + 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; ++ ++ if (fw_status == FIRMWARE_READY_PCIE && !adapter->mfg_mode) { ++ mwifiex_deauthenticate_all(adapter); + +- /* Enable the Host Sleep */ +- if (!mwifiex_enable_hs(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; +- } ++ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + +- flush_workqueue(adapter->workqueue); ++ mwifiex_disable_auto_ds(priv); ++ ++ mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); ++ } + +- /* Indicate device suspended */ +- set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); +- clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); ++ mwifiex_remove_card(adapter); + + return 0; + } +@@ -194,30 +196,29 @@ static int mwifiex_pcie_suspend(struct device *dev) + */ + static int mwifiex_pcie_resume(struct device *dev) + { +- struct mwifiex_adapter *adapter; +- struct pcie_service_card *card; + struct pci_dev *pdev = to_pci_dev(dev); ++ struct pcie_service_card *card = pci_get_drvdata(pdev); ++ int ret; + +- card = pci_get_drvdata(pdev); ++ 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); + +- adapter = card->adapter; ++ card->dev = pdev; + +- 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; ++ } + + return 0; + } +@@ -271,6 +272,8 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, + return -1; + } + ++ pdev->bus->self->bridge_d3 = false; ++ + return 0; + } + +diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +index 4ed10cf82f9a..013db4386c39 100644 +--- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c ++++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +@@ -2265,14 +2265,13 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, + 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; + u16 packet_aggr_enable; + int data; ++ int ret; + + if (first_sta) { + if (priv->adapter->iface_type == MWIFIEX_PCIE) { +@@ -2395,18 +2394,6 @@ 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; +- } +- + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Send cmd to FW to enable/disable 11D function */ + state_11d = ENABLE_11D; +-- +2.24.1 + diff --git a/patches/5.3/0008-legacy-i915.patch b/patches/5.3/0007-legacy-i915.patch similarity index 99% rename from patches/5.3/0008-legacy-i915.patch rename to patches/5.3/0007-legacy-i915.patch index 4f3455226..07e2ed22e 100644 --- a/patches/5.3/0008-legacy-i915.patch +++ b/patches/5.3/0007-legacy-i915.patch @@ -1,7 +1,7 @@ -From 53a9de609c3ffeb174f9551c221d4a63d1d15542 Mon Sep 17 00:00:00 2001 +From 42e6a02bc692206b0e6800278e781fc03d988b89 Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Mon, 16 Sep 2019 04:10:51 +0200 -Subject: [PATCH 08/10] legacy-i915 +Subject: [PATCH 07/10] legacy-i915 --- drivers/gpu/drm/Kconfig | 2 +- @@ -247066,5 +247066,5 @@ index 000000000000..8ea1c927dbad + I915_WRITE(MIPI_EOT_DISABLE(port), CLOCKSTOP); +} -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0007-wifi.patch b/patches/5.3/0007-wifi.patch deleted file mode 100644 index a08ea4b9d..000000000 --- a/patches/5.3/0007-wifi.patch +++ /dev/null @@ -1,267 +0,0 @@ -From 2e405d90dc591416b6e1aebaeba84cb09e317f07 Mon Sep 17 00:00:00 2001 -From: qzed -Date: Wed, 18 Sep 2019 03:18:25 +0200 -Subject: [PATCH 07/10] wifi - ---- - drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 3 +-- - drivers/net/wireless/marvell/mwifiex/cfg80211.c | 5 ++++- - drivers/net/wireless/marvell/mwifiex/cmdevt.c | 10 ++++++---- - drivers/net/wireless/marvell/mwifiex/fw.h | 1 + - drivers/net/wireless/marvell/mwifiex/main.c | 17 +++++++++++++---- - drivers/net/wireless/marvell/mwifiex/main.h | 2 ++ - drivers/net/wireless/marvell/mwifiex/pcie.c | 9 +++++++++ - drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 4 ++-- - .../net/wireless/marvell/mwifiex/sta_cmdresp.c | 10 +++++++--- - drivers/net/wireless/marvell/mwifiex/usb.c | 2 ++ - scripts/leaking_addresses.pl | 0 - 11 files changed, 47 insertions(+), 16 deletions(-) - mode change 100755 => 100644 scripts/leaking_addresses.pl - -diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -index 088612438530..4386e657dfdb 100644 ---- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -@@ -198,8 +198,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - - do { - /* Check if AMSDU can accommodate this MSDU */ -- if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) > -- adapter->tx_buf_size) -+ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) - break; - - skb_src = skb_dequeue(&pra_list->skb_head); -diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index d89684168500..1545bae9d6cf 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c -+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -@@ -437,7 +437,10 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, - mwifiex_dbg(priv->adapter, INFO, - "info: ignore timeout value for IEEE Power Save\n"); - -- ps_mode = enabled; -+ //ps_mode = enabled; -+ -+ mwifiex_dbg(priv->adapter, INFO, "overriding ps_mode to false\n"); -+ ps_mode = 0; - - return mwifiex_drv_set_power(priv, &ps_mode); - } -diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c -index e8788c35a453..82d25b3ca914 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c -+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c -@@ -1004,6 +1004,7 @@ mwifiex_cmd_timeout_func(struct timer_list *t) - if (cmd_node->wait_q_enabled) { - adapter->cmd_wait_q.status = -ETIMEDOUT; - mwifiex_cancel_pending_ioctl(adapter); -+ adapter->cmd_sent = false; - } - } - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { -@@ -1011,11 +1012,11 @@ mwifiex_cmd_timeout_func(struct timer_list *t) - return; - } - -- if (adapter->if_ops.device_dump) -- adapter->if_ops.device_dump(adapter); -+ //if (adapter->if_ops.device_dump) -+ // adapter->if_ops.device_dump(adapter); - -- if (adapter->if_ops.card_reset) -- adapter->if_ops.card_reset(adapter); -+ //if (adapter->if_ops.card_reset) -+ // adapter->if_ops.card_reset(adapter); - } - - void -@@ -1578,6 +1579,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - adapter->key_api_minor_ver); - break; - case FW_API_VER_ID: -+ case FW_KEY_API_VER_ID: - adapter->fw_api_ver = - api_rev->major_ver; - mwifiex_dbg(adapter, INFO, -diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h -index 1fb76d2f5d3f..fb32379da99d 100644 ---- a/drivers/net/wireless/marvell/mwifiex/fw.h -+++ b/drivers/net/wireless/marvell/mwifiex/fw.h -@@ -1052,6 +1052,7 @@ struct host_cmd_ds_802_11_ps_mode_enh { - enum API_VER_ID { - KEY_API_VER_ID = 1, - FW_API_VER_ID = 2, -+ FW_KEY_API_VER_ID = 4, - }; - - struct hw_spec_api_rev { -diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c -index a9657ae6d782..ba99d84a31ef 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.c -+++ b/drivers/net/wireless/marvell/mwifiex/main.c -@@ -163,6 +163,7 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->mwifiex_processing) { - adapter->more_task_flag = true; -+ adapter->more_rx_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); -@@ -171,16 +172,18 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) - } - EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); - --static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) -+void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) - { - spin_lock_bh(&adapter->rx_proc_lock); - if (adapter->rx_processing) { -+ adapter->more_rx_task_flag = true; - spin_unlock_bh(&adapter->rx_proc_lock); - } else { - spin_unlock_bh(&adapter->rx_proc_lock); - queue_work(adapter->rx_workqueue, &adapter->rx_work); - } - } -+EXPORT_SYMBOL_GPL(mwifiex_queue_rx_work); - - static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - { -@@ -189,6 +192,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - - spin_lock_bh(&adapter->rx_proc_lock); - if (adapter->rx_processing || adapter->rx_locked) { -+ adapter->more_rx_task_flag = true; - spin_unlock_bh(&adapter->rx_proc_lock); - goto exit_rx_proc; - } else { -@@ -196,6 +200,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - spin_unlock_bh(&adapter->rx_proc_lock); - } - -+rx_process_start: - /* Check for Rx data */ - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - atomic_dec(&adapter->rx_pending); -@@ -217,6 +222,11 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - } - } - spin_lock_bh(&adapter->rx_proc_lock); -+ if (adapter->more_rx_task_flag) { -+ adapter->more_rx_task_flag = false; -+ spin_unlock_bh(&adapter->rx_proc_lock); -+ goto rx_process_start; -+ } - adapter->rx_processing = false; - spin_unlock_bh(&adapter->rx_proc_lock); - -@@ -280,11 +290,10 @@ int mwifiex_main_process(struct mwifiex_adapter *adapter) - mwifiex_process_hs_config(adapter); - if (adapter->if_ops.process_int_status) - adapter->if_ops.process_int_status(adapter); -+ if (adapter->rx_work_enabled && adapter->data_received) -+ mwifiex_queue_rx_work(adapter); - } - -- if (adapter->rx_work_enabled && adapter->data_received) -- mwifiex_queue_rx_work(adapter); -- - /* Need to wake up the card ? */ - if ((adapter->ps_state == PS_STATE_SLEEP) && - (adapter->pm_wakeup_card_req && -diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h -index 095837fba300..5dca5c25c601 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.h -+++ b/drivers/net/wireless/marvell/mwifiex/main.h -@@ -909,6 +909,7 @@ struct mwifiex_adapter { - spinlock_t main_proc_lock; - u32 mwifiex_processing; - u8 more_task_flag; -+ u8 more_rx_task_flag; - u16 tx_buf_size; - u16 curr_tx_buf_size; - /* sdio single port rx aggregation capability */ -@@ -1695,6 +1696,7 @@ void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); - void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); - void mwifiex_fw_dump_event(struct mwifiex_private *priv); - void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); -+void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter); - int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action, - int cmd_type, - struct mwifiex_ds_wakeup_reason *wakeup_reason); -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index b54f73e3d508..32502f443b9a 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -1743,6 +1743,15 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) - } - - rx_len = get_unaligned_le16(skb->data); -+ -+ if (rx_len == 0) { -+ mwifiex_dbg(adapter, ERROR, -+ "0 byte cmdrsp\n"); -+ mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, -+ PCI_DMA_FROMDEVICE); -+ return 0; -+ } -+ - skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); - skb_trim(skb, rx_len); - -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -index 4ed10cf82f9a..485360e8534b 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -@@ -30,8 +30,8 @@ static bool drcs; - module_param(drcs, bool, 0644); - MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); - --static bool disable_auto_ds; --module_param(disable_auto_ds, bool, 0); -+static bool disable_auto_ds = 1; -+module_param(disable_auto_ds, bool, 0644); - MODULE_PARM_DESC(disable_auto_ds, - "deepsleep enabled=0(default), deepsleep disabled=1"); - /* -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -index 20c206da0631..0e58da83417c 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -@@ -47,9 +47,13 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ps_mode_enh *pm; - -- mwifiex_dbg(adapter, ERROR, -- "CMD_RESP: cmd %#x error, result=%#x\n", -- resp->command, resp->result); -+ if (resp->command == 271 && resp->result == 2) { -+ // ignore this command as the firmware does not support it -+ } else { -+ mwifiex_dbg(adapter, ERROR, -+ "CMD_RESP: cmd %#x error, result=%#x\n", -+ resp->command, resp->result); -+ } - - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; -diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c -index c2365eeb7016..0a219ba378dd 100644 ---- a/drivers/net/wireless/marvell/mwifiex/usb.c -+++ b/drivers/net/wireless/marvell/mwifiex/usb.c -@@ -144,6 +144,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); -+ if (adapter->rx_work_enabled) -+ mwifiex_queue_rx_work(adapter); - break; - default: - mwifiex_dbg(adapter, ERROR, -diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl -old mode 100755 -new mode 100644 --- -2.24.0 - diff --git a/patches/5.3/0009-ipts.patch b/patches/5.3/0008-ipts.patch similarity index 99% rename from patches/5.3/0009-ipts.patch rename to patches/5.3/0008-ipts.patch index a038645fe..373ed9a6a 100644 --- a/patches/5.3/0009-ipts.patch +++ b/patches/5.3/0008-ipts.patch @@ -1,7 +1,7 @@ -From 415661df64a887487ef496afaf38ec7a36833d0d Mon Sep 17 00:00:00 2001 +From 49678a8400c73921f11b2a89b844f1baeba40739 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 18 Sep 2019 13:04:18 +0200 -Subject: [PATCH 09/10] ipts +Subject: [PATCH 08/10] ipts --- drivers/gpu/drm/i915_legacy/Makefile | 3 + @@ -35,12 +35,12 @@ Subject: [PATCH 09/10] ipts drivers/misc/ipts/hid.c | 496 ++++++++ drivers/misc/ipts/hid.h | 21 + drivers/misc/ipts/ipts.c | 62 + - drivers/misc/ipts/ipts.h | 176 +++ + drivers/misc/ipts/ipts.h | 172 +++ drivers/misc/ipts/kernel.c | 1047 +++++++++++++++++ drivers/misc/ipts/kernel.h | 17 + drivers/misc/ipts/mei-msgs.h | 894 ++++++++++++++ drivers/misc/ipts/mei.c | 238 ++++ - drivers/misc/ipts/msg-handler.c | 416 +++++++ + drivers/misc/ipts/msg-handler.c | 396 +++++++ drivers/misc/ipts/msg-handler.h | 28 + drivers/misc/ipts/params.c | 46 + drivers/misc/ipts/params.h | 26 + @@ -54,7 +54,7 @@ Subject: [PATCH 09/10] ipts include/linux/ipts-companion.h | 30 + include/linux/ipts-gfx.h | 86 ++ include/linux/ipts.h | 20 + - 50 files changed, 6806 insertions(+), 21 deletions(-) + 50 files changed, 6782 insertions(+), 21 deletions(-) create mode 100644 drivers/gpu/drm/i915_legacy/intel_ipts.c create mode 100644 drivers/gpu/drm/i915_legacy/intel_ipts.h create mode 100644 drivers/misc/ipts/Kconfig @@ -2973,10 +2973,10 @@ index 000000000000..dfafabf8dd94 +} diff --git a/drivers/misc/ipts/ipts.h b/drivers/misc/ipts/ipts.h new file mode 100644 -index 000000000000..a044f839e4d0 +index 000000000000..32eb3ffd68a3 --- /dev/null +++ b/drivers/misc/ipts/ipts.h -@@ -0,0 +1,176 @@ +@@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * @@ -3067,10 +3067,6 @@ index 000000000000..a044f839e4d0 + int gfx_status; + bool display_status; + -+ bool switch_sensor_mode; -+ enum touch_sensor_mode new_sensor_mode; -+ -+ int retry; + bool restart; +}; + @@ -5375,10 +5371,10 @@ index 000000000000..03b5d747a728 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ipts/msg-handler.c b/drivers/misc/ipts/msg-handler.c new file mode 100644 -index 000000000000..dc4b730b2254 +index 000000000000..b2b382ea4675 --- /dev/null +++ b/drivers/misc/ipts/msg-handler.c -@@ -0,0 +1,416 @@ +@@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * @@ -5552,14 +5548,6 @@ index 000000000000..dc4b730b2254 +{ + ipts_dbg(ipts, "ipts restart\n"); + ipts_stop(ipts); -+ ipts->retry++; -+ -+ // Try wth HID mode -+ if (ipts->retry == IPTS_MAX_RETRY && -+ ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) -+ ipts->sensor_mode = TOUCH_SENSOR_MODE_HID; -+ else if (ipts->retry > IPTS_MAX_RETRY) -+ return -EPERM; + + ipts_send_sensor_quiesce_io_cmd(ipts); + ipts->restart = true; @@ -5741,9 +5729,6 @@ index 000000000000..dc4b730b2254 + cmd_status = ipts_handle_cmd(ipts, + TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); + -+ // reset retry since we are getting touh data -+ ipts->retry = 0; -+ + break; + } + case TOUCH_SENSOR_QUIESCE_IO_RSP: { @@ -5762,15 +5747,6 @@ index 000000000000..dc4b730b2254 + break; + } + -+ // support sysfs debug node for switch sensor mode -+ if (ipts->switch_sensor_mode) { -+ ipts_set_state(ipts, IPTS_STA_INIT); -+ ipts->sensor_mode = ipts->new_sensor_mode; -+ ipts->switch_sensor_mode = false; -+ -+ ipts_send_sensor_clear_mem_window_cmd(ipts); -+ } -+ + break; + } + } @@ -7428,5 +7404,5 @@ index 000000000000..bfa8e1375926 + +#endif // IPTS_H -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0010-ioremap_uc.patch b/patches/5.3/0009-ioremap_uc.patch similarity index 81% rename from patches/5.3/0010-ioremap_uc.patch rename to patches/5.3/0009-ioremap_uc.patch index 9376d9924..6bf90bbcd 100644 --- a/patches/5.3/0010-ioremap_uc.patch +++ b/patches/5.3/0009-ioremap_uc.patch @@ -1,15 +1,14 @@ -From 441b6e5c8fbe40ec9858147a521a5bb192160d49 Mon Sep 17 00:00:00 2001 +From 9c0380701b988307a75832e0eb1c083291fddc7c Mon Sep 17 00:00:00 2001 From: Tuowen Zhao -Date: Wed, 16 Oct 2019 15:06:27 -0600 -Subject: [PATCH 10/10] ioremap_uc +Date: Wed, 16 Oct 2019 15:06:28 -0600 +Subject: [PATCH 09/10] ioremap_uc --- .../driver-api/driver-model/devres.rst | 1 + - arch/sparc/include/asm/io_64.h | 1 + drivers/mfd/intel-lpss.c | 2 +- include/linux/io.h | 2 ++ lib/devres.c | 19 +++++++++++++++++++ - 5 files changed, 24 insertions(+), 1 deletion(-) + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index a100bef54952..92628fdc2f11 100644 @@ -23,18 +22,6 @@ index a100bef54952..92628fdc2f11 100644 devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps devm_iounmap() -diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h -index 688911051b44..f4afa301954a 100644 ---- a/arch/sparc/include/asm/io_64.h -+++ b/arch/sparc/include/asm/io_64.h -@@ -407,6 +407,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size) - } - - #define ioremap_nocache(X,Y) ioremap((X),(Y)) -+#define ioremap_uc(X,Y) ioremap((X),(Y)) - #define ioremap_wc(X,Y) ioremap((X),(Y)) - #define ioremap_wt(X,Y) ioremap((X),(Y)) - diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 277f48f1cc1c..06106c9320bb 100644 --- a/drivers/mfd/intel-lpss.c @@ -106,5 +93,5 @@ index 6a0e9bd6524a..17624d35e82d 100644 * devm_ioremap_nocache - Managed ioremap_nocache() * @dev: Generic device to remap IO address for -- -2.24.0 +2.24.1 diff --git a/patches/5.3/0010-surface3-spi-dma.patch b/patches/5.3/0010-surface3-spi-dma.patch new file mode 100644 index 000000000..c237e46ed --- /dev/null +++ b/patches/5.3/0010-surface3-spi-dma.patch @@ -0,0 +1,63 @@ +From 4aded6dfb23129b9bd03f7d37ff655912db5cfb0 Mon Sep 17 00:00:00 2001 +From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> +Date: Fri, 6 Dec 2019 23:10:30 +0900 +Subject: [PATCH 10/10] surface3-spi dma + +--- + drivers/input/touchscreen/surface3_spi.c | 26 ++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c +index ce4828b1415a..63b0b8ddf090 100644 +--- a/drivers/input/touchscreen/surface3_spi.c ++++ b/drivers/input/touchscreen/surface3_spi.c +@@ -25,6 +25,12 @@ + #define SURFACE3_REPORT_TOUCH 0xd2 + #define SURFACE3_REPORT_PEN 0x16 + ++bool use_dma = false; ++module_param(use_dma, bool, 0644); ++MODULE_PARM_DESC(use_dma, ++ "Disable DMA mode if you encounter touch input crash. " ++ "(default: false, disabled to avoid crash)"); ++ + struct surface3_ts_data { + struct spi_device *spi; + struct gpio_desc *gpiod_rst[2]; +@@ -326,6 +332,13 @@ static int surface3_spi_create_pen_input(struct surface3_ts_data *data) + return 0; + } + ++static bool surface3_spi_can_dma(struct spi_controller *ctlr, ++ struct spi_device *spi, ++ struct spi_transfer *tfr) ++{ ++ return use_dma; ++} ++ + static int surface3_spi_probe(struct spi_device *spi) + { + struct surface3_ts_data *data; +@@ -368,6 +381,19 @@ static int surface3_spi_probe(struct spi_device *spi) + if (error) + return error; + ++ /* ++ * Set up DMA ++ * ++ * TODO: Currently, touch input with DMA seems to be broken. ++ * On 4.19 LTS, touch input will crash after suspend. ++ * On recent stable kernel (at least after 5.1), touch input will crash after ++ * the first touch. No problem with PIO on those kernels. ++ * Maybe we need to configure DMA here. ++ * ++ * Link to issue: https://github.com/jakeday/linux-surface/issues/596 ++ */ ++ spi->controller->can_dma = surface3_spi_can_dma; ++ + return 0; + } + +-- +2.24.1 + diff --git a/patches/5.4/0001-ioremap_uc.patch b/patches/5.4/0001-ioremap_uc.patch index 2788ddd48..da74f8839 100644 --- a/patches/5.4/0001-ioremap_uc.patch +++ b/patches/5.4/0001-ioremap_uc.patch @@ -1,15 +1,14 @@ -From e595c41258f70ccc9d980683bd6a447f04153d86 Mon Sep 17 00:00:00 2001 +From 8b8b25e23cc3501b18674b8ed599ffef33bafa6e Mon Sep 17 00:00:00 2001 From: Tuowen Zhao -Date: Wed, 16 Oct 2019 15:06:27 -0600 +Date: Wed, 16 Oct 2019 15:06:28 -0600 Subject: [PATCH 1/7] ioremap_uc --- .../driver-api/driver-model/devres.rst | 1 + - arch/sparc/include/asm/io_64.h | 1 + drivers/mfd/intel-lpss.c | 2 +- include/linux/io.h | 2 ++ lib/devres.c | 19 +++++++++++++++++++ - 5 files changed, 24 insertions(+), 1 deletion(-) + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index a100bef54952..92628fdc2f11 100644 @@ -23,18 +22,6 @@ index a100bef54952..92628fdc2f11 100644 devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps devm_iounmap() -diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h -index 688911051b44..f4afa301954a 100644 ---- a/arch/sparc/include/asm/io_64.h -+++ b/arch/sparc/include/asm/io_64.h -@@ -407,6 +407,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size) - } - - #define ioremap_nocache(X,Y) ioremap((X),(Y)) -+#define ioremap_uc(X,Y) ioremap((X),(Y)) - #define ioremap_wc(X,Y) ioremap((X),(Y)) - #define ioremap_wt(X,Y) ioremap((X),(Y)) - diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index bfe4ff337581..b0f0781a6b9c 100644 --- a/drivers/mfd/intel-lpss.c @@ -106,5 +93,5 @@ index 6a0e9bd6524a..17624d35e82d 100644 * devm_ioremap_nocache - Managed ioremap_nocache() * @dev: Generic device to remap IO address for -- -2.24.0 +2.24.1 diff --git a/patches/5.4/0002-hid.patch b/patches/5.4/0002-hid.patch index 0fafb2cff..3a914eb71 100644 --- a/patches/5.4/0002-hid.patch +++ b/patches/5.4/0002-hid.patch @@ -1,4 +1,4 @@ -From 5b13ed4b0c6dccb70cce67a80cc6c83492b58e28 Mon Sep 17 00:00:00 2001 +From 4e3ea1440ba9d3b72d7aebdf54221d27dee36793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 6 Nov 2019 19:43:26 +0900 Subject: [PATCH 2/7] hid @@ -23,5 +23,5 @@ index 2fa3587d974f..e0b241bd3070 100644 static void hid_scan_collection(struct hid_parser *parser, unsigned type) -- -2.24.0 +2.24.1 diff --git a/patches/5.4/0003-surface-acpi.patch b/patches/5.4/0003-surface-acpi.patch index 6a9efe93e..3b89f964d 100644 --- a/patches/5.4/0003-surface-acpi.patch +++ b/patches/5.4/0003-surface-acpi.patch @@ -1,4 +1,4 @@ -From 125adf6ef31b988d71377e67f4e865dee518ab84 Mon Sep 17 00:00:00 2001 +From e6fa887173e4d0e6477421b9c38072f7f4063ade Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:11:08 +0200 Subject: [PATCH 3/7] surface-acpi @@ -8,24 +8,28 @@ Subject: [PATCH 3/7] surface-acpi drivers/acpi/acpica/exfield.c | 12 +- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface_sam/Kconfig | 155 ++ - drivers/platform/x86/surface_sam/Makefile | 9 + + drivers/platform/x86/surface_sam/Kconfig | 166 ++ + drivers/platform/x86/surface_sam/Makefile | 10 + .../x86/surface_sam/surface_sam_dtx.c | 623 ++++++ - .../x86/surface_sam/surface_sam_san.c | 791 ++++++++ + .../x86/surface_sam/surface_sam_hps.c | 1110 +++++++++++ + .../x86/surface_sam/surface_sam_san.c | 901 +++++++++ + .../x86/surface_sam/surface_sam_san.h | 29 + .../x86/surface_sam/surface_sam_sid.c | 117 ++ .../x86/surface_sam/surface_sam_sid_gpelid.c | 219 ++ .../surface_sam/surface_sam_sid_perfmode.c | 225 +++ - .../x86/surface_sam/surface_sam_sid_power.c | 1258 ++++++++++++ + .../x86/surface_sam/surface_sam_sid_power.c | 1259 ++++++++++++ .../x86/surface_sam/surface_sam_sid_vhf.c | 440 ++++ - .../x86/surface_sam/surface_sam_ssh.c | 1779 +++++++++++++++++ + .../x86/surface_sam/surface_sam_ssh.c | 1773 +++++++++++++++++ .../x86/surface_sam/surface_sam_ssh.h | 97 + .../x86/surface_sam/surface_sam_vhf.c | 276 +++ drivers/tty/serdev/core.c | 111 +- - 17 files changed, 6100 insertions(+), 16 deletions(-) + 19 files changed, 7356 insertions(+), 16 deletions(-) create mode 100644 drivers/platform/x86/surface_sam/Kconfig create mode 100644 drivers/platform/x86/surface_sam/Makefile create mode 100644 drivers/platform/x86/surface_sam/surface_sam_dtx.c + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_hps.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.c + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.h create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid_gpelid.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_sid_perfmode.c @@ -103,10 +107,10 @@ index 415104033060..18f5a4ba7244 100644 +obj-$(CONFIG_SURFACE_SAM) += surface_sam/ diff --git a/drivers/platform/x86/surface_sam/Kconfig b/drivers/platform/x86/surface_sam/Kconfig new file mode 100644 -index 000000000000..256a7c3f1fa3 +index 000000000000..4eff58a121cb --- /dev/null +++ b/drivers/platform/x86/surface_sam/Kconfig -@@ -0,0 +1,155 @@ +@@ -0,0 +1,166 @@ +menuconfig SURFACE_SAM + depends on ACPI + tristate "Microsoft Surface/System Aggregator Module and Platform Drivers" @@ -200,6 +204,17 @@ index 000000000000..256a7c3f1fa3 + + If you are not sure, say M here. + ++config SURFACE_SAM_HPS ++ tristate "Surface dGPU Hot-Plug System (dGPU-HPS) Driver" ++ depends on SURFACE_SAM_SSH ++ depends on SURFACE_SAM_SAN ++ default m ++ ---help--- ++ Driver to properly handle hot-plugging and explicit power-on/power-off ++ of the discrete GPU (dGPU) on the Surface Book 2. ++ ++ If you are not sure, say M here. ++ +config SURFACE_SAM_SID + tristate "Surface Platform Integration Driver" + depends on SURFACE_SAM_SSH @@ -264,13 +279,14 @@ index 000000000000..256a7c3f1fa3 + If you are not sure, say M here. diff --git a/drivers/platform/x86/surface_sam/Makefile b/drivers/platform/x86/surface_sam/Makefile new file mode 100644 -index 000000000000..97ef66ff273d +index 000000000000..188975ccde5c --- /dev/null +++ b/drivers/platform/x86/surface_sam/Makefile -@@ -0,0 +1,9 @@ +@@ -0,0 +1,10 @@ +obj-$(CONFIG_SURFACE_SAM_SSH) += surface_sam_ssh.o +obj-$(CONFIG_SURFACE_SAM_SAN) += surface_sam_san.o +obj-$(CONFIG_SURFACE_SAM_DTX) += surface_sam_dtx.o ++obj-$(CONFIG_SURFACE_SAM_HPS) += surface_sam_hps.o +obj-$(CONFIG_SURFACE_SAM_VHF) += surface_sam_vhf.o +obj-$(CONFIG_SURFACE_SAM_SID) += surface_sam_sid.o +obj-$(CONFIG_SURFACE_SAM_SID_GPELID) += surface_sam_sid_gpelid.o @@ -906,12 +922,1128 @@ index 000000000000..4b924de6ab09 +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface Detachment System (DTX) Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surface_sam/surface_sam_hps.c b/drivers/platform/x86/surface_sam/surface_sam_hps.c +new file mode 100644 +index 000000000000..3b123bd3dcfe +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_hps.c +@@ -0,0 +1,1110 @@ ++/* ++ * Surface dGPU hot-plug system driver. ++ * Supports explicit setting of the dGPU power-state on the Surface Book 2 and ++ * properly handles hot-plugging by detaching the base. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "surface_sam_ssh.h" ++#include "surface_sam_san.h" ++ ++ ++// TODO: vgaswitcheroo integration ++ ++ ++static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix); ++ ++ ++#define SHPS_DSM_REVISION 1 ++#define SHPS_DSM_GPU_ADDRS 0x02 ++#define SHPS_DSM_GPU_POWER 0x05 ++static const guid_t SHPS_DSM_UUID = ++ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, ++ 0x32, 0x0e, 0x10, 0x36, 0x0a); ++ ++ ++#define SAM_DGPU_TC 0x13 ++#define SAM_DGPU_CID_POWERON 0x02 ++ ++#define SAM_DTX_TC 0x11 ++#define SAM_DTX_CID_LATCH_LOCK 0x06 ++#define SAM_DTX_CID_LATCH_UNLOCK 0x07 ++ ++#define SHPS_DSM_GPU_ADDRS_RP "RP5_PCIE" ++#define SHPS_DSM_GPU_ADDRS_DGPU "DGPU_PCIE" ++ ++ ++static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; ++static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; ++static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; ++ ++static const struct acpi_gpio_mapping shps_acpi_gpios[] = { ++ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, ++ { "base_presence-gpio", &gpio_base_presence, 1 }, ++ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, ++ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, ++ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, ++ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, ++ { }, ++}; ++ ++ ++enum shps_dgpu_power { ++ SHPS_DGPU_POWER_OFF = 0, ++ SHPS_DGPU_POWER_ON = 1, ++ SHPS_DGPU_POWER_UNKNOWN = 2, ++}; ++ ++static const char* shps_dgpu_power_str(enum shps_dgpu_power power) { ++ if (power == SHPS_DGPU_POWER_OFF) ++ return "off"; ++ else if (power == SHPS_DGPU_POWER_ON) ++ return "on"; ++ else if (power == SHPS_DGPU_POWER_UNKNOWN) ++ return "unknown"; ++ else ++ return ""; ++} ++ ++ ++struct shps_driver_data { ++ struct mutex lock; ++ struct pci_dev *dgpu_root_port; ++ struct pci_saved_state *dgpu_root_port_state; ++ struct gpio_desc *gpio_dgpu_power; ++ struct gpio_desc *gpio_dgpu_presence; ++ struct gpio_desc *gpio_base_presence; ++ unsigned int irq_dgpu_presence; ++ unsigned int irq_base_presence; ++ unsigned long state; ++}; ++ ++#define SHPS_STATE_BIT_PWRTGT 0 /* desired power state: 1 for on, 0 for off */ ++#define SHPS_STATE_BIT_RPPWRON_SYNC 1 /* synchronous/requested power-up in progress */ ++#define SHPS_STATE_BIT_WAKE_ENABLED 2 /* wakeup via base-presence GPIO enabled */ ++ ++ ++#define SHPS_DGPU_PARAM_PERM (S_IRUGO | S_IWUSR) ++ ++enum shps_dgpu_power_mp { ++ SHPS_DGPU_MP_POWER_OFF = SHPS_DGPU_POWER_OFF, ++ SHPS_DGPU_MP_POWER_ON = SHPS_DGPU_POWER_ON, ++ SHPS_DGPU_MP_POWER_ASIS = -1, ++ ++ __SHPS_DGPU_MP_POWER_START = -1, ++ __SHPS_DGPU_MP_POWER_END = 1, ++}; ++ ++static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) ++{ ++ int power = SHPS_DGPU_MP_POWER_OFF; ++ int status; ++ ++ status = kstrtoint(val, 0, &power); ++ if (status) { ++ return status; ++ } ++ ++ if (power < __SHPS_DGPU_MP_POWER_START || power > __SHPS_DGPU_MP_POWER_END) { ++ return -EINVAL; ++ } ++ ++ return param_set_int(val, kp); ++} ++ ++static const struct kernel_param_ops param_dgpu_power_ops = { ++ .set = param_dgpu_power_set, ++ .get = param_get_int, ++}; ++ ++static int param_dgpu_power_init = SHPS_DGPU_MP_POWER_OFF; ++static int param_dgpu_power_exit = SHPS_DGPU_MP_POWER_ON; ++static int param_dgpu_power_susp = SHPS_DGPU_MP_POWER_ASIS; ++static bool param_dtx_latch = true; ++ ++module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SHPS_DGPU_PARAM_PERM); ++module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SHPS_DGPU_PARAM_PERM); ++module_param_cb(dgpu_power_susp, ¶m_dgpu_power_ops, ¶m_dgpu_power_susp, SHPS_DGPU_PARAM_PERM); ++module_param_named(dtx_latch, param_dtx_latch, bool, SHPS_DGPU_PARAM_PERM); ++ ++MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is, default: off)"); ++MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: on)"); ++MODULE_PARM_DESC(dgpu_power_susp, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: as-is)"); ++MODULE_PARM_DESC(dtx_latch, "lock/unlock DTX base latch in accordance to power-state (Y/n)"); ++ ++ ++static int dtx_cmd_simple(u8 cid) ++{ ++ struct surface_sam_ssh_rqst rqst = { ++ .tc = SAM_DTX_TC, ++ .cid = cid, ++ .iid = 0, ++ .pri = SURFACE_SAM_PRIORITY_NORMAL, ++ .snc = 0, ++ .cdl = 0, ++ .pld = NULL, ++ }; ++ ++ return surface_sam_ssh_rqst(&rqst, NULL); ++} ++ ++inline static int shps_dtx_latch_lock(void) ++{ ++ return dtx_cmd_simple(SAM_DTX_CID_LATCH_LOCK); ++} ++ ++inline static int shps_dtx_latch_unlock(void) ++{ ++ return dtx_cmd_simple(SAM_DTX_CID_LATCH_UNLOCK); ++} ++ ++ ++static int shps_dgpu_dsm_get_pci_addr(struct platform_device *pdev, const char* entry) ++{ ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ union acpi_object *result; ++ union acpi_object *e0; ++ union acpi_object *e1; ++ union acpi_object *e2; ++ u64 device_addr = 0; ++ u8 bus, dev, fun; ++ int i; ++ ++ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, ++ SHPS_DSM_GPU_ADDRS, NULL, ACPI_TYPE_PACKAGE); ++ ++ if (IS_ERR_OR_NULL(result)) ++ return result ? PTR_ERR(result) : -EIO; ++ ++ // three entries per device: name, address, ++ for (i = 0; i + 2 < result->package.count; i += 3) { ++ e0 = &result->package.elements[i]; ++ e1 = &result->package.elements[i + 1]; ++ e2 = &result->package.elements[i + 2]; ++ ++ if (e0->type != ACPI_TYPE_STRING) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (e1->type != ACPI_TYPE_INTEGER) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (e2->type != ACPI_TYPE_INTEGER) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ if (strncmp(e0->string.pointer, entry, 64) == 0) ++ device_addr = e1->integer.value; ++ } ++ ++ ACPI_FREE(result); ++ if (device_addr == 0) ++ return -ENODEV; ++ ++ // convert address ++ bus = (device_addr & 0x0FF00000) >> 20; ++ dev = (device_addr & 0x000F8000) >> 15; ++ fun = (device_addr & 0x00007000) >> 12; ++ ++ return bus << 8 | PCI_DEVFN(dev, fun); ++} ++ ++static struct pci_dev *shps_dgpu_dsm_get_pci_dev(struct platform_device *pdev, const char* entry) ++{ ++ struct pci_dev *dev; ++ int addr; ++ ++ addr = shps_dgpu_dsm_get_pci_addr(pdev, entry); ++ if (addr < 0) ++ return ERR_PTR(addr); ++ ++ dev = pci_get_domain_bus_and_slot(0, (addr & 0xFF00) >> 8, addr & 0xFF); ++ return dev ? dev : ERR_PTR(-ENODEV); ++} ++ ++ ++static int shps_dgpu_dsm_get_power_unlocked(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct gpio_desc *gpio = drvdata->gpio_dgpu_power; ++ int status; ++ ++ status = gpiod_get_value_cansleep(gpio); ++ if (status < 0) ++ return status; ++ ++ return status == 0 ? SHPS_DGPU_POWER_OFF : SHPS_DGPU_POWER_ON; ++} ++ ++static int shps_dgpu_dsm_get_power(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_dsm_get_power_unlocked(pdev); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++static int __shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ union acpi_object *result; ++ union acpi_object param; ++ ++ dev_info(&pdev->dev, "setting dGPU direct power to \'%s\'\n", shps_dgpu_power_str(power)); ++ ++ param.type = ACPI_TYPE_INTEGER; ++ param.integer.value = power == SHPS_DGPU_POWER_ON; ++ ++ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, ++ SHPS_DSM_GPU_POWER, ¶m, ACPI_TYPE_BUFFER); ++ ++ if (IS_ERR_OR_NULL(result)) ++ return result ? PTR_ERR(result) : -EIO; ++ ++ // check for the expected result ++ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { ++ ACPI_FREE(result); ++ return -EIO; ++ } ++ ++ ACPI_FREE(result); ++ return 0; ++} ++ ++static int shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) ++ return -EINVAL; ++ ++ status = shps_dgpu_dsm_get_power_unlocked(pdev); ++ if (status < 0) ++ return status; ++ if (status == power) ++ return 0; ++ ++ return __shps_dgpu_dsm_set_power_unlocked(pdev, power); ++} ++ ++static int shps_dgpu_dsm_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_dsm_set_power_unlocked(pdev, power); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++ ++static bool shps_rp_link_up(struct pci_dev *rp) ++{ ++ u16 lnksta = 0, sltsta = 0; ++ ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); ++ ++ return (lnksta & PCI_EXP_LNKSTA_DLLLA) || (sltsta & PCI_EXP_SLTSTA_PDS); ++} ++ ++ ++static int shps_dgpu_rp_get_power_unlocked(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ ++ if (rp->current_state == PCI_D3hot || rp->current_state == PCI_D3cold) ++ return SHPS_DGPU_POWER_OFF; ++ else if (rp->current_state == PCI_UNKNOWN || rp->current_state == PCI_POWER_ERROR) ++ return SHPS_DGPU_POWER_UNKNOWN; ++ else ++ return SHPS_DGPU_POWER_ON; ++} ++ ++static int shps_dgpu_rp_get_power(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++static int __shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ int status, i; ++ ++ dev_info(&pdev->dev, "setting dGPU power state to \'%s\'\n", shps_dgpu_power_str(power)); ++ ++ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.1"); ++ if (power == SHPS_DGPU_POWER_ON) { ++ set_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); ++ pci_set_power_state(rp, PCI_D0); ++ ++ if (drvdata->dgpu_root_port_state) ++ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); ++ ++ pci_restore_state(rp); ++ ++ if (!pci_is_enabled(rp)) ++ pci_enable_device(rp); ++ ++ pci_set_master(rp); ++ clear_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); ++ ++ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } else { ++ if (!drvdata->dgpu_root_port_state) { ++ pci_save_state(rp); ++ drvdata->dgpu_root_port_state = pci_store_saved_state(rp); ++ } ++ ++ /* ++ * To properly update the hot-plug system we need to "remove" the dGPU ++ * before disabling it and sending it to D3cold. Following this, we ++ * need to wait for the link and slot status to actually change. ++ */ ++ status = shps_dgpu_dsm_set_power_unlocked(pdev, SHPS_DGPU_POWER_OFF); ++ if (status) ++ return status; ++ ++ for (i = 0; i < 20 && shps_rp_link_up(rp); i++) ++ msleep(50); ++ ++ if (shps_rp_link_up(rp)) ++ dev_err(&pdev->dev, "dGPU removal via DSM timed out\n"); ++ ++ pci_clear_master(rp); ++ ++ if (pci_is_enabled(rp)) ++ pci_disable_device(rp); ++ ++ pci_set_power_state(rp, PCI_D3cold); ++ ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.2"); ++ ++ return 0; ++} ++ ++static int shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) ++ return -EINVAL; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ return status; ++ if (status == power) ++ return 0; ++ ++ return __shps_dgpu_rp_set_power_unlocked(pdev, power); ++} ++ ++static int shps_dgpu_rp_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ mutex_lock(&drvdata->lock); ++ status = shps_dgpu_rp_set_power_unlocked(pdev, power); ++ mutex_unlock(&drvdata->lock); ++ ++ return status; ++} ++ ++ ++static int shps_dgpu_set_power(struct platform_device *pdev, enum shps_dgpu_power power) ++{ ++ int status; ++ ++ if (!param_dtx_latch) ++ return shps_dgpu_rp_set_power(pdev, power); ++ ++ if (power == SHPS_DGPU_POWER_ON) { ++ status = shps_dtx_latch_lock(); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_rp_set_power(pdev, power); ++ if (status) ++ shps_dtx_latch_unlock(); ++ ++ return status; ++ } else { ++ status = shps_dgpu_rp_set_power(pdev, power); ++ if (status) ++ return status; ++ ++ return shps_dtx_latch_unlock(); ++ } ++} ++ ++ ++static int shps_dgpu_is_present(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ return gpiod_get_value_cansleep(drvdata->gpio_dgpu_presence); ++} ++ ++ ++static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int power = shps_dgpu_rp_get_power(pdev); ++ ++ if (power < 0) ++ return power; ++ ++ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); ++} ++ ++static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, ++ const char *data, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ enum shps_dgpu_power power; ++ bool b = false; ++ int status; ++ ++ status = kstrtobool(data, &b); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status <= 0) ++ return status < 0 ? status : -EPERM; ++ ++ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; ++ status = shps_dgpu_set_power(pdev, power); ++ ++ return status < 0 ? status : count; ++} ++ ++static ssize_t dgpu_power_dsm_show(struct device *dev, struct device_attribute *attr, char *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ int power = shps_dgpu_dsm_get_power(pdev); ++ ++ if (power < 0) ++ return power; ++ ++ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); ++} ++ ++static ssize_t dgpu_power_dsm_store(struct device *dev, struct device_attribute *attr, ++ const char *data, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ enum shps_dgpu_power power; ++ bool b = false; ++ int status; ++ ++ status = kstrtobool(data, &b); ++ if (status) ++ return status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status <= 0) ++ return status < 0 ? status : -EPERM; ++ ++ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; ++ status = shps_dgpu_dsm_set_power(pdev, power); ++ ++ return status < 0 ? status : count; ++} ++ ++static DEVICE_ATTR_RW(dgpu_power); ++static DEVICE_ATTR_RW(dgpu_power_dsm); ++ ++static struct attribute *shps_power_attrs[] = { ++ &dev_attr_dgpu_power.attr, ++ &dev_attr_dgpu_power_dsm.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(shps_power); ++ ++ ++static void dbg_dump_power_states(struct platform_device *pdev, const char *prefix) ++{ ++ enum shps_dgpu_power power_dsm; ++ enum shps_dgpu_power power_rp; ++ int status; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ dev_err(&pdev->dev, "%s: failed to get root-port power state: %d\n", prefix, status); ++ power_rp = status; ++ ++ status = shps_dgpu_rp_get_power_unlocked(pdev); ++ if (status < 0) ++ dev_err(&pdev->dev, "%s: failed to get direct power state: %d\n", prefix, status); ++ power_dsm = status; ++ ++ dev_dbg(&pdev->dev, "%s: root-port power state: %d\n", prefix, power_rp); ++ dev_dbg(&pdev->dev, "%s: direct power state: %d\n", prefix, power_dsm); ++} ++ ++static void dbg_dump_pciesta(struct platform_device *pdev, const char *prefix) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ u16 lnksta, lnksta2, sltsta, sltsta2; ++ ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); ++ pcie_capability_read_word(rp, PCI_EXP_LNKSTA2, &lnksta2); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); ++ pcie_capability_read_word(rp, PCI_EXP_SLTSTA2, &sltsta2); ++ ++ dev_dbg(&pdev->dev, "%s: LNKSTA: 0x%04x", prefix, lnksta); ++ dev_dbg(&pdev->dev, "%s: LNKSTA2: 0x%04x", prefix, lnksta2); ++ dev_dbg(&pdev->dev, "%s: SLTSTA: 0x%04x", prefix, sltsta); ++ dev_dbg(&pdev->dev, "%s: SLTSTA2: 0x%04x", prefix, sltsta2); ++} ++ ++static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ ++ dev_dbg(&pdev->dev, "%s: RP power: %d", prefix, rp->current_state); ++ dev_dbg(&pdev->dev, "%s: RP state saved: %d", prefix, rp->state_saved); ++ dev_dbg(&pdev->dev, "%s: RP state stored: %d", prefix, !!drvdata->dgpu_root_port_state); ++ dev_dbg(&pdev->dev, "%s: RP enabled: %d", prefix, atomic_read(&rp->enable_cnt)); ++ dev_dbg(&pdev->dev, "%s: RP mastered: %d", prefix, rp->is_busmaster); ++} ++ ++ ++static int shps_pm_prepare(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ bool pwrtgt; ++ int status = 0; ++ ++ dbg_dump_power_states(pdev, "shps_pm_prepare"); ++ ++ if (param_dgpu_power_susp != SHPS_DGPU_MP_POWER_ASIS) { ++ pwrtgt = test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ ++ status = shps_dgpu_set_power(pdev, param_dgpu_power_susp); ++ if (status) { ++ dev_err(&pdev->dev, "failed to power %s dGPU: %d\n", ++ param_dgpu_power_susp == SHPS_DGPU_MP_POWER_OFF ? "off" : "on", ++ status); ++ return status; ++ } ++ ++ if (pwrtgt) ++ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ else ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ ++ return 0; ++} ++ ++static void shps_pm_complete(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ dbg_dump_power_states(pdev, "shps_pm_complete"); ++ dbg_dump_pciesta(pdev, "shps_pm_complete"); ++ dbg_dump_drvsta(pdev, "shps_pm_complete.1"); ++ ++ // update power target, dGPU may have been detached while suspended ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) { ++ dev_err(&pdev->dev, "failed to get dGPU presence: %d\n", status); ++ return; ++ } else if (status == 0) { ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ } ++ ++ /* ++ * During resume, the PCIe core will power on the root-port, which in turn ++ * will power on the dGPU. Most of the state synchronization is already ++ * handled via the SAN RQSG handler, so it is in a fully consistent ++ * on-state here. If requested, turn it off here. ++ * ++ * As there seem to be some synchronization issues turning off the dGPU ++ * directly after the power-on SAN RQSG notification during the resume ++ * process, let's do this here. ++ * ++ * TODO/FIXME: ++ * This does not combat unhandled power-ons when the device is not fully ++ * resumed, i.e. re-suspended before shps_pm_complete is called. Those ++ * should normally not be an issue, but the dGPU does get hot even though ++ * it is suspended, so ideally we want to keep it off. ++ */ ++ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { ++ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); ++ if (status) ++ dev_err(&pdev->dev, "failed to power-off dGPU: %d\n", status); ++ } ++ ++ dbg_dump_drvsta(pdev, "shps_pm_complete.2"); ++} ++ ++static int shps_pm_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ if (device_may_wakeup(dev)) { ++ status = enable_irq_wake(drvdata->irq_base_presence); ++ if (status) ++ return status; ++ ++ set_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state); ++ } ++ ++ return 0; ++} ++ ++static int shps_pm_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status = 0; ++ ++ if (test_and_clear_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state)) { ++ status = disable_irq_wake(drvdata->irq_base_presence); ++ } ++ ++ return status; ++} ++ ++static void shps_shutdown(struct platform_device *pdev) ++{ ++ int status; ++ ++ /* ++ * Turn on dGPU before shutting down. This allows the core drivers to ++ * properly shut down the device. If we don't do this, the pcieport driver ++ * will complain that the device has already been disabled. ++ */ ++ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_ON); ++ if (status) ++ dev_err(&pdev->dev, "failed to turn on dGPU: %d\n", status); ++} ++ ++static int shps_dgpu_detached(struct platform_device *pdev) ++{ ++ dbg_dump_power_states(pdev, "shps_dgpu_detached"); ++ return shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); ++} ++ ++static int shps_dgpu_attached(struct platform_device *pdev) ++{ ++ dbg_dump_power_states(pdev, "shps_dgpu_attached"); ++ return 0; ++} ++ ++static int shps_dgpu_powered_on(struct platform_device *pdev) ++{ ++ /* ++ * This function gets called directly after a power-state transition of ++ * the dGPU root port out of D3cold state, indicating a power-on of the ++ * dGPU. Specifically, this function is called from the RQSG handler of ++ * SAN, invoked by the ACPI _ON method of the dGPU root port. This means ++ * that this function is run inside `pci_set_power_state(rp, ...)` ++ * syncrhonously and thus returns before the `pci_set_power_state` call ++ * does. ++ * ++ * `pci_set_power_state` may either be called by us or when the PCI ++ * subsystem decides to power up the root port (e.g. during resume). Thus ++ * we should use this function to ensure that the dGPU and root port ++ * states are consistent when an unexpected power-up is encountered. ++ */ ++ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct pci_dev *rp = drvdata->dgpu_root_port; ++ int status; ++ ++ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.1"); ++ ++ // if we caused the root port to power-on, return ++ if (test_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state)) ++ return 0; ++ ++ // if dGPU is not present, force power-target to off and return ++ status = shps_dgpu_is_present(pdev); ++ if (status == 0) ++ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); ++ if (status <= 0) ++ return status; ++ ++ mutex_lock(&drvdata->lock); ++ ++ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.1"); ++ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.1"); ++ if (drvdata->dgpu_root_port_state) ++ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); ++ pci_restore_state(rp); ++ if (!pci_is_enabled(rp)) ++ pci_enable_device(rp); ++ pci_set_master(rp); ++ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.2"); ++ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.2"); ++ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.2"); ++ ++ mutex_unlock(&drvdata->lock); ++ ++ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { ++ dev_warn(&pdev->dev, "unexpected dGPU power-on detected"); ++ // TODO: schedule state re-check and update ++ } ++ ++ return 0; ++} ++ ++ ++static int shps_dgpu_handle_rqsg(struct surface_sam_san_rqsg *rqsg, void *data) ++{ ++ struct platform_device *pdev = data; ++ ++ if (rqsg->tc == SAM_DGPU_TC && rqsg->cid == SAM_DGPU_CID_POWERON) ++ return shps_dgpu_powered_on(pdev); ++ ++ dev_warn(&pdev->dev, "unimplemented dGPU request: RQSG(0x%02x, 0x%02x, 0x%02x)", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ return 0; ++} ++ ++static irqreturn_t shps_dgpu_presence_irq(int irq, void *data) ++{ ++ struct platform_device *pdev = data; ++ bool dgpu_present; ++ int status; ++ ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) { ++ dev_err(&pdev->dev, "failed to check physical dGPU presence: %d\n", status); ++ return IRQ_HANDLED; ++ } ++ ++ dgpu_present = status != 0; ++ dev_info(&pdev->dev, "dGPU physically %s\n", dgpu_present ? "attached" : "detached"); ++ ++ if (dgpu_present) ++ status = shps_dgpu_attached(pdev); ++ else ++ status = shps_dgpu_detached(pdev); ++ ++ if (status) ++ dev_err(&pdev->dev, "error handling dGPU interrupt: %d\n", status); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t shps_base_presence_irq(int irq, void *data) ++{ ++ return IRQ_HANDLED; // nothing to do, just wake ++} ++ ++ ++static int shps_gpios_setup(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ struct gpio_desc *gpio_dgpu_power; ++ struct gpio_desc *gpio_dgpu_presence; ++ struct gpio_desc *gpio_base_presence; ++ int status; ++ ++ // get GPIOs ++ gpio_dgpu_power = devm_gpiod_get(&pdev->dev, "dgpu_power", GPIOD_IN); ++ if (IS_ERR(gpio_dgpu_power)) { ++ status = PTR_ERR(gpio_dgpu_power); ++ goto err_out; ++ } ++ ++ gpio_dgpu_presence = devm_gpiod_get(&pdev->dev, "dgpu_presence", GPIOD_IN); ++ if (IS_ERR(gpio_dgpu_presence)) { ++ status = PTR_ERR(gpio_dgpu_presence); ++ goto err_out; ++ } ++ ++ gpio_base_presence = devm_gpiod_get(&pdev->dev, "base_presence", GPIOD_IN); ++ if (IS_ERR(gpio_base_presence)) { ++ status = PTR_ERR(gpio_base_presence); ++ goto err_out; ++ } ++ ++ // export GPIOs ++ status = gpiod_export(gpio_dgpu_power, false); ++ if (status) ++ goto err_out; ++ ++ status = gpiod_export(gpio_dgpu_presence, false); ++ if (status) ++ goto err_export_dgpu_presence; ++ ++ status = gpiod_export(gpio_base_presence, false); ++ if (status) ++ goto err_export_base_presence; ++ ++ // create sysfs links ++ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_power", gpio_dgpu_power); ++ if (status) ++ goto err_link_dgpu_power; ++ ++ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_presence", gpio_dgpu_presence); ++ if (status) ++ goto err_link_dgpu_presence; ++ ++ status = gpiod_export_link(&pdev->dev, "gpio-base_presence", gpio_base_presence); ++ if (status) ++ goto err_link_base_presence; ++ ++ drvdata->gpio_dgpu_power = gpio_dgpu_power; ++ drvdata->gpio_dgpu_presence = gpio_dgpu_presence; ++ drvdata->gpio_base_presence = gpio_base_presence; ++ return 0; ++ ++err_link_base_presence: ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); ++err_link_dgpu_presence: ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); ++err_link_dgpu_power: ++ gpiod_unexport(gpio_base_presence); ++err_export_base_presence: ++ gpiod_unexport(gpio_dgpu_presence); ++err_export_dgpu_presence: ++ gpiod_unexport(gpio_dgpu_power); ++err_out: ++ return status; ++} ++ ++static void shps_gpios_remove(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-base_presence"); ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); ++ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); ++ gpiod_unexport(drvdata->gpio_base_presence); ++ gpiod_unexport(drvdata->gpio_dgpu_presence); ++ gpiod_unexport(drvdata->gpio_dgpu_power); ++} ++ ++static int shps_gpios_setup_irq(struct platform_device *pdev) ++{ ++ const int irqf_dgpu = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ const int irqf_base = IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ status = gpiod_to_irq(drvdata->gpio_base_presence); ++ if (status < 0) ++ return status; ++ drvdata->irq_base_presence = status; ++ ++ status = gpiod_to_irq(drvdata->gpio_dgpu_presence); ++ if (status < 0) ++ return status; ++ drvdata->irq_dgpu_presence = status; ++ ++ status = request_irq(drvdata->irq_base_presence, ++ shps_base_presence_irq, irqf_base, ++ "shps_base_presence_irq", pdev); ++ if (status) ++ return status; ++ ++ status = request_threaded_irq(drvdata->irq_dgpu_presence, ++ NULL, shps_dgpu_presence_irq, irqf_dgpu, ++ "shps_dgpu_presence_irq", pdev); ++ if (status) { ++ free_irq(drvdata->irq_base_presence, pdev); ++ return status; ++ } ++ ++ return 0; ++} ++ ++static void shps_gpios_remove_irq(struct platform_device *pdev) ++{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ ++ free_irq(drvdata->irq_base_presence, pdev); ++ free_irq(drvdata->irq_dgpu_presence, pdev); ++} ++ ++static int shps_probe(struct platform_device *pdev) ++{ ++ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); ++ struct shps_driver_data *drvdata; ++ struct device_link *link; ++ int power, status; ++ ++ if (gpiod_count(&pdev->dev, NULL) < 0) ++ return -ENODEV; ++ ++ // link to SSH ++ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ if (status) { ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ } ++ ++ // link to SAN ++ status = surface_sam_san_consumer_register(&pdev->dev, 0); ++ if (status) { ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ } ++ ++ status = acpi_dev_add_driver_gpios(shps_dev, shps_acpi_gpios); ++ if (status) ++ return status; ++ ++ drvdata = kzalloc(sizeof(struct shps_driver_data), GFP_KERNEL); ++ if (!drvdata) { ++ status = -ENOMEM; ++ goto err_drvdata; ++ } ++ mutex_init(&drvdata->lock); ++ platform_set_drvdata(pdev, drvdata); ++ ++ drvdata->dgpu_root_port = shps_dgpu_dsm_get_pci_dev(pdev, SHPS_DSM_GPU_ADDRS_RP); ++ if (IS_ERR(drvdata->dgpu_root_port)) { ++ status = PTR_ERR(drvdata->dgpu_root_port); ++ goto err_rp_lookup; ++ } ++ ++ status = shps_gpios_setup(pdev); ++ if (status) ++ goto err_gpio; ++ ++ status = shps_gpios_setup_irq(pdev); ++ if (status) ++ goto err_gpio_irqs; ++ ++ status = device_add_groups(&pdev->dev, shps_power_groups); ++ if (status) ++ goto err_devattr; ++ ++ link = device_link_add(&pdev->dev, &drvdata->dgpu_root_port->dev, ++ DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER); ++ if (!link) ++ goto err_devlink; ++ ++ surface_sam_san_set_rqsg_handler(shps_dgpu_handle_rqsg, pdev); ++ ++ // if dGPU is not present turn-off root-port, else obey module param ++ status = shps_dgpu_is_present(pdev); ++ if (status < 0) ++ goto err_devlink; ++ ++ power = status == 0 ? SHPS_DGPU_POWER_OFF : param_dgpu_power_init; ++ if (power != SHPS_DGPU_MP_POWER_ASIS) { ++ status = shps_dgpu_set_power(pdev, power); ++ if (status) ++ goto err_devlink; ++ } ++ ++ device_init_wakeup(&pdev->dev, true); ++ return 0; ++ ++err_devlink: ++ device_remove_groups(&pdev->dev, shps_power_groups); ++err_devattr: ++ shps_gpios_remove_irq(pdev); ++err_gpio_irqs: ++ shps_gpios_remove(pdev); ++err_gpio: ++ pci_dev_put(drvdata->dgpu_root_port); ++err_rp_lookup: ++ platform_set_drvdata(pdev, NULL); ++ kfree(drvdata); ++err_drvdata: ++ acpi_dev_remove_driver_gpios(shps_dev); ++ return status; ++} ++ ++static int shps_remove(struct platform_device *pdev) ++{ ++ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); ++ int status; ++ ++ if (param_dgpu_power_exit != SHPS_DGPU_MP_POWER_ASIS) { ++ status = shps_dgpu_set_power(pdev, param_dgpu_power_exit); ++ if (status) ++ dev_err(&pdev->dev, "failed to set dGPU power state: %d\n", status); ++ } ++ ++ device_set_wakeup_capable(&pdev->dev, false); ++ surface_sam_san_set_rqsg_handler(NULL, NULL); ++ device_remove_groups(&pdev->dev, shps_power_groups); ++ shps_gpios_remove_irq(pdev); ++ shps_gpios_remove(pdev); ++ pci_dev_put(drvdata->dgpu_root_port); ++ platform_set_drvdata(pdev, NULL); ++ kfree(drvdata); ++ ++ acpi_dev_remove_driver_gpios(shps_dev); ++ return 0; ++} ++ ++ ++static const struct dev_pm_ops shps_pm_ops = { ++ .prepare = shps_pm_prepare, ++ .complete = shps_pm_complete, ++ .suspend = shps_pm_suspend, ++ .resume = shps_pm_resume, ++}; ++ ++static const struct acpi_device_id shps_acpi_match[] = { ++ { "MSHW0153", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, shps_acpi_match); ++ ++struct platform_driver surface_sam_hps = { ++ .probe = shps_probe, ++ .remove = shps_remove, ++ .shutdown = shps_shutdown, ++ .driver = { ++ .name = "surface_dgpu_hps", ++ .acpi_match_table = ACPI_PTR(shps_acpi_match), ++ .pm = &shps_pm_ops, ++ }, ++}; ++module_platform_driver(surface_sam_hps); ++ ++MODULE_AUTHOR("Maximilian Luz "); ++MODULE_DESCRIPTION("Surface Hot-Plug System (HPS) and dGPU power-state Driver for Surface Book 2"); ++MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.c b/drivers/platform/x86/surface_sam/surface_sam_san.c new file mode 100644 -index 000000000000..810165083e0e +index 000000000000..aa0cfc4262be --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_san.c -@@ -0,0 +1,791 @@ +@@ -0,0 +1,901 @@ +/* + * Surface ACPI Notify (SAN) and ACPI integration driver for SAM. + * Translates communication from ACPI to SSH and back. @@ -924,6 +2056,7 @@ index 000000000000..810165083e0e +#include + +#include "surface_sam_ssh.h" ++#include "surface_sam_san.h" + + +#define SAN_RQST_RETRY 5 @@ -949,7 +2082,8 @@ index 000000000000..810165083e0e +#define SAM_EVENT_TEMP_RQID 0x0003 +#define SAM_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT 0x0b + -+#define SAN_RQST_TAG "surface_sam_san_rqst: " ++#define SAN_RQST_TAG "surface_sam_san: rqst: " ++#define SAN_RQSG_TAG "surface_sam_san: rqsg: " + +#define SAN_QUIRK_BASE_STATE_DELAY 1000 + @@ -967,17 +2101,17 @@ index 000000000000..810165083e0e + +struct san_consumer_link { + const struct san_acpi_consumer *properties; -+ struct device_link *link; ++ struct device_link *link; +}; + +struct san_consumers { -+ u32 num; ++ u32 num; + struct san_consumer_link *links; +}; + +struct san_drvdata { + struct san_opreg_context opreg_ctx; -+ struct san_consumers consumers; ++ struct san_consumers consumers; + bool has_power_events; +}; + @@ -1034,6 +2168,79 @@ index 000000000000..810165083e0e +}; + + ++static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data); ++ ++struct sam_san_rqsg_if { ++ struct mutex lock; ++ struct device *san_dev; ++ surface_sam_san_rqsg_handler_fn handler; ++ void *handler_data; ++}; ++ ++static struct sam_san_rqsg_if rqsg_if = { ++ .lock = __MUTEX_INITIALIZER(rqsg_if.lock), ++ .san_dev = NULL, ++ .handler = sam_san_default_rqsg_handler, ++ .handler_data = NULL, ++}; ++ ++int surface_sam_san_consumer_register(struct device *consumer, u32 flags) ++{ ++ const u32 valid = DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE; ++ int status; ++ ++ if ((flags | valid) != valid) ++ return -EINVAL; ++ ++ flags |= DL_FLAG_AUTOREMOVE_CONSUMER; ++ ++ mutex_lock(&rqsg_if.lock); ++ if (rqsg_if.san_dev) ++ status = device_link_add(consumer, rqsg_if.san_dev, flags) ? 0 : -EINVAL; ++ else ++ status = -ENXIO; ++ mutex_unlock(&rqsg_if.lock); ++ return status; ++} ++EXPORT_SYMBOL_GPL(surface_sam_san_consumer_register); ++ ++int surface_sam_san_set_rqsg_handler(surface_sam_san_rqsg_handler_fn fn, void *data) ++{ ++ int status = -EBUSY; ++ ++ mutex_lock(&rqsg_if.lock); ++ ++ if (rqsg_if.handler == sam_san_default_rqsg_handler || !fn) { ++ rqsg_if.handler = fn ? fn : sam_san_default_rqsg_handler; ++ rqsg_if.handler_data = data; ++ status = 0; ++ } ++ ++ mutex_unlock(&rqsg_if.lock); ++ return status; ++} ++EXPORT_SYMBOL_GPL(surface_sam_san_set_rqsg_handler); ++ ++int san_call_rqsg_handler(struct surface_sam_san_rqsg *rqsg) ++{ ++ int status; ++ ++ mutex_lock(&rqsg_if.lock); ++ status = rqsg_if.handler(rqsg, rqsg_if.handler_data); ++ mutex_unlock(&rqsg_if.lock); ++ ++ return status; ++} ++ ++static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data) ++{ ++ pr_warn(SAN_RQSG_TAG "unhandled request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", ++ rqsg->tc, rqsg->cid, rqsg->iid); ++ ++ return 0; ++} ++ ++ +static int san_acpi_notify_power_event(struct device *dev, enum san_pwr_event event) +{ + acpi_handle san = ACPI_HANDLE(dev); @@ -1369,16 +2576,33 @@ index 000000000000..810165083e0e +static acpi_status +san_rqsg(struct san_opreg_context *ctx, struct gsb_buffer *buffer) +{ -+ struct gsb_data_rqsx *rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ struct gsb_data_rqsx *gsb_rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); ++ struct surface_sam_san_rqsg rqsg = {}; ++ int status; + -+ if (!rqsg) { ++ if (!gsb_rqsg) { + return AE_OK; + } + -+ // TODO: RQSG handler ++ rqsg.tc = gsb_rqsg->tc; ++ rqsg.cid = gsb_rqsg->cid; ++ rqsg.iid = gsb_rqsg->iid; ++ rqsg.cdl = gsb_rqsg->cdl; ++ rqsg.pld = &gsb_rqsg->pld[0]; + -+ dev_warn(ctx->dev, "unsupported request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", -+ rqsg->tc, rqsg->cid, rqsg->iid); ++ status = san_call_rqsg_handler(&rqsg); ++ if (!status) { ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x00; ++ buffer->data.out.len = 0x00; ++ } else { ++ dev_err(ctx->dev, SAN_RQSG_TAG "failed with error %d\n", status); ++ buffer->status = 0x00; ++ buffer->len = 0x02; ++ buffer->data.out.status = 0x01; // indicate _SSH error ++ buffer->data.out.len = 0x00; ++ } + + return AE_OK; +} @@ -1645,9 +2869,23 @@ index 000000000000..810165083e0e + goto err_enable_events; + } + ++ mutex_lock(&rqsg_if.lock); ++ if (!rqsg_if.san_dev) { ++ rqsg_if.san_dev = &pdev->dev; ++ } else { ++ status = -EBUSY; ++ } ++ mutex_unlock(&rqsg_if.lock); ++ ++ if (status) { ++ goto err_install_dev; ++ } ++ + acpi_walk_dep_device_list(san); + return 0; + ++err_install_dev: ++ san_disable_events(pdev); +err_enable_events: + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); +err_install_handler: @@ -1664,6 +2902,10 @@ index 000000000000..810165083e0e + acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node + acpi_status status = AE_OK; + ++ mutex_lock(&rqsg_if.lock); ++ rqsg_if.san_dev = NULL; ++ mutex_unlock(&rqsg_if.lock); ++ + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); + san_disable_events(pdev); + @@ -1703,6 +2945,41 @@ index 000000000000..810165083e0e +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface ACPI Notify Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.h b/drivers/platform/x86/surface_sam/surface_sam_san.h +new file mode 100644 +index 000000000000..1ea8713db367 +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_san.h +@@ -0,0 +1,29 @@ ++/* ++ * Interface for Surface ACPI/Notify (SAN). ++ * ++ * The SAN is the main interface between the Surface Serial Hub (SSH) and the ++ * Surface/System Aggregator Module (SAM). It allows requests to be translated ++ * from ACPI to SSH/SAM. It also interfaces with the discrete GPU hot-plug ++ * driver. ++ */ ++ ++#ifndef _SURFACE_SAM_SAN_H ++#define _SURFACE_SAM_SAN_H ++ ++#include ++ ++ ++struct surface_sam_san_rqsg { ++ u8 tc; // target category ++ u8 cid; // command ID ++ u8 iid; // instance ID ++ u8 cdl; // command data length (lenght of payload) ++ u8 *pld; // pointer to payload of length cdl ++}; ++ ++typedef int (*surface_sam_san_rqsg_handler_fn)(struct surface_sam_san_rqsg *rqsg, void *data); ++ ++int surface_sam_san_consumer_register(struct device *consumer, u32 flags); ++int surface_sam_san_set_rqsg_handler(surface_sam_san_rqsg_handler_fn fn, void *data); ++ ++#endif /* _SURFACE_SAM_SAN_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid.c b/drivers/platform/x86/surface_sam/surface_sam_sid.c new file mode 100644 index 000000000000..f64dcd590494 @@ -2284,10 +3561,10 @@ index 000000000000..880a2567cf1b +MODULE_ALIAS("platform:surface_sam_sid_perfmode"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_power.c b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c new file mode 100644 -index 000000000000..ce4274709d68 +index 000000000000..1f2c88eda394 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c -@@ -0,0 +1,1258 @@ +@@ -0,0 +1,1259 @@ +/* + * Surface SID Battery/AC Driver. + * Provides support for the battery and AC on 7th generation Surface devices. @@ -3545,7 +4822,8 @@ index 000000000000..ce4274709d68 +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Surface Battery/AC Driver for 7th Generation Surface Devices"); +MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:surface_sam_sid_power"); ++MODULE_ALIAS("platform:surface_sam_sid_ac"); ++MODULE_ALIAS("platform:surface_sam_sid_battery"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c new file mode 100644 index 000000000000..dc5be3a14a8c @@ -3994,10 +5272,10 @@ index 000000000000..dc5be3a14a8c +MODULE_ALIAS("platform:surface_sam_sid_vhf"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c new file mode 100644 -index 000000000000..665d956eec01 +index 000000000000..34905cf29a51 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c -@@ -0,0 +1,1779 @@ +@@ -0,0 +1,1773 @@ +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System + * Aggregator Module. @@ -4080,8 +5358,8 @@ index 000000000000..665d956eec01 + +/* + * A note on Request IDs (RQIDs): -+ * 0x0000 is not a valid RQID -+ * 0x0001 is valid, but reserved for Surface Laptop keyboard events ++ * 0x0000 is not a valid RQID ++ * 0x0001 is valid, but reserved for Surface Laptop keyboard events + */ +#define SAM_NUM_EVENT_TYPES ((1 << SURFACE_SAM_SSH_RQID_EVENT_BITS) - 1) + @@ -5334,11 +6612,6 @@ index 000000000000..665d956eec01 + +static irqreturn_t surface_sam_irq_handler(int irq, void *dev_id) +{ -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t surface_sam_irq_handler_th(int irq, void *dev_id) -+{ + struct serdev_device *serdev = dev_id; + + dev_info(&serdev->dev, "wake irq triggered\n"); @@ -5347,7 +6620,7 @@ index 000000000000..665d956eec01 + +static int surface_sam_setup_irq(struct serdev_device *serdev) +{ -+ const int irqf = IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING; + struct gpio_desc *gpiod; + int irq; + int status; @@ -5362,8 +6635,7 @@ index 000000000000..665d956eec01 + if (irq < 0) + return irq; + -+ status = request_threaded_irq(irq, surface_sam_irq_handler, -+ surface_sam_irq_handler_th, ++ status = request_threaded_irq(irq, NULL, surface_sam_irq_handler, + irqf, "surface_sam_wakeup", serdev); + if (status) + return status; @@ -6314,5 +7586,5 @@ index a0ac16ee6575..226adeec2aed 100644 if (!ctrl->serdev) return -ENODEV; -- -2.24.0 +2.24.1 diff --git a/patches/5.4/0005-surface3-power.patch b/patches/5.4/0004-surface3-power.patch similarity index 98% rename from patches/5.4/0005-surface3-power.patch rename to patches/5.4/0004-surface3-power.patch index 8390a3e22..c325bd6ea 100644 --- a/patches/5.4/0005-surface3-power.patch +++ b/patches/5.4/0004-surface3-power.patch @@ -1,7 +1,7 @@ -From 1fe372a463e7db7bf57acbab2f9456797f6f5451 Mon Sep 17 00:00:00 2001 +From bd28b11662cc9fba2f92d69e3d132c5a50b3537a Mon Sep 17 00:00:00 2001 From: qzed Date: Tue, 17 Sep 2019 17:17:56 +0200 -Subject: [PATCH 5/7] surface3-power +Subject: [PATCH 4/7] surface3-power --- drivers/platform/x86/Kconfig | 7 + @@ -11,10 +11,10 @@ Subject: [PATCH 5/7] surface3-power create mode 100644 drivers/platform/x86/surface3_power.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 1cef3c858d24..b8f019db9a55 100644 +index 675ec12cbc0e..cf709b3dd0cd 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -1219,6 +1219,13 @@ config SURFACE_3_BUTTON +@@ -1210,6 +1210,13 @@ config SURFACE_3_BUTTON ---help--- This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. @@ -29,10 +29,10 @@ index 1cef3c858d24..b8f019db9a55 100644 tristate "Intel P-Unit IPC Driver" ---help--- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 725dedf5fbfe..705525ff99a7 100644 +index 18f5a4ba7244..19b56f2181eb 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile -@@ -86,6 +86,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o +@@ -85,6 +85,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o @@ -651,5 +651,5 @@ index 000000000000..e0af01a60302 +MODULE_DESCRIPTION("mshw0011 driver"); +MODULE_LICENSE("GPL v2"); -- -2.24.0 +2.24.1 diff --git a/patches/5.4/0004-surfacebook2-dgpu.patch b/patches/5.4/0004-surfacebook2-dgpu.patch deleted file mode 100644 index 06cb98151..000000000 --- a/patches/5.4/0004-surfacebook2-dgpu.patch +++ /dev/null @@ -1,359 +0,0 @@ -From 553685a989f06c01aef953dcd9f2ea8f7f8ebf7a Mon Sep 17 00:00:00 2001 -From: Maximilian Luz -Date: Tue, 2 Jul 2019 22:17:46 +0200 -Subject: [PATCH 4/7] surfacebook2-dgpu - ---- - drivers/platform/x86/Kconfig | 9 + - drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surfacebook2_dgpu_hps.c | 306 +++++++++++++++++++ - 3 files changed, 316 insertions(+) - create mode 100644 drivers/platform/x86/surfacebook2_dgpu_hps.c - -diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 675ec12cbc0e..1cef3c858d24 100644 ---- a/drivers/platform/x86/Kconfig -+++ b/drivers/platform/x86/Kconfig -@@ -481,6 +481,15 @@ config SURFACE3_WMI - To compile this driver as a module, choose M here: the module will - be called surface3-wmi. - -+config SURFACE_BOOK2_DGPU_HPS -+ tristate "Surface Book 2 dGPU Hot-Plug System Driver" -+ depends on ACPI -+ ---help--- -+ This is an experimetnal driver to control the power-state of the -+ Surface Book 2 dGPU. -+ -+ If you have a Surface Book 2, say Y or M here. -+ - config THINKPAD_ACPI - tristate "ThinkPad ACPI Laptop Extras" - depends on ACPI -diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 18f5a4ba7244..725dedf5fbfe 100644 ---- a/drivers/platform/x86/Makefile -+++ b/drivers/platform/x86/Makefile -@@ -48,6 +48,7 @@ obj-$(CONFIG_ACPI_WMI) += wmi.o - obj-$(CONFIG_MSI_WMI) += msi-wmi.o - obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o - obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o -+obj-$(CONFIG_SURFACE_BOOK2_DGPU_HPS) += surfacebook2_dgpu_hps.o - obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o - obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o - obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o -diff --git a/drivers/platform/x86/surfacebook2_dgpu_hps.c b/drivers/platform/x86/surfacebook2_dgpu_hps.c -new file mode 100644 -index 000000000000..7639fb0029d8 ---- /dev/null -+++ b/drivers/platform/x86/surfacebook2_dgpu_hps.c -@@ -0,0 +1,306 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+ -+#define SB2_SHPS_DSM_REVISION 1 -+#define SB2_SHPS_DSM_GPU_STATE 0x05 -+ -+static const guid_t SB2_SHPS_DSM_UUID = -+ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, -+ 0x32, 0x0e, 0x10, 0x36, 0x0a); -+ -+#define SB2_PARAM_PERM (S_IRUGO | S_IWUSR) -+ -+ -+static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; -+static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; -+ -+static const struct acpi_gpio_mapping sb2_mshw0153_acpi_gpios[] = { -+ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, -+ { "base_presence-gpio", &gpio_base_presence, 1 }, -+ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, -+ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, -+ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, -+ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, -+ { }, -+}; -+ -+ -+enum sb2_dgpu_power { -+ SB2_DGPU_POWER_OFF = 0, -+ SB2_DGPU_POWER_ON = 1, -+ -+ __SB2_DGPU_POWER__START = 0, -+ __SB2_DGPU_POWER__END = 1, -+}; -+ -+enum sb2_param_dgpu_power { -+ SB2_PARAM_DGPU_POWER_OFF = SB2_DGPU_POWER_OFF, -+ SB2_PARAM_DGPU_POWER_ON = SB2_DGPU_POWER_ON, -+ SB2_PARAM_DGPU_POWER_AS_IS = 2, -+ -+ __SB2_PARAM_DGPU_POWER__START = 0, -+ __SB2_PARAM_DGPU_POWER__END = 2, -+}; -+ -+static const char* sb2_dgpu_power_str(enum sb2_dgpu_power power) { -+ if (power == SB2_DGPU_POWER_OFF) { -+ return "off"; -+ } else if (power == SB2_DGPU_POWER_ON) { -+ return "on"; -+ } else { -+ return ""; -+ } -+} -+ -+ -+struct sb2_shps_driver_data { -+ struct mutex dgpu_power_lock; -+ enum sb2_dgpu_power dgpu_power; -+}; -+ -+ -+static int __sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ acpi_handle handle = ACPI_HANDLE(&pdev->dev); -+ union acpi_object *result; -+ union acpi_object param; -+ -+ param.type = ACPI_TYPE_INTEGER; -+ param.integer.value = power == SB2_DGPU_POWER_ON; -+ -+ result = acpi_evaluate_dsm_typed(handle, &SB2_SHPS_DSM_UUID, SB2_SHPS_DSM_REVISION, -+ SB2_SHPS_DSM_GPU_STATE, ¶m, ACPI_TYPE_BUFFER); -+ -+ if (IS_ERR_OR_NULL(result)) { -+ return result ? PTR_ERR(result) : -EFAULT; -+ } -+ -+ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { -+ return -EIO; -+ } -+ -+ drvdata->dgpu_power = power; -+ -+ printk(KERN_INFO "sb2_shps: dGPU power state set to \'%s\'\n", sb2_dgpu_power_str(power)); -+ -+ ACPI_FREE(result); -+ return 0; -+} -+ -+static int sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status = 0; -+ -+ if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&drvdata->dgpu_power_lock); -+ if (power != drvdata->dgpu_power) { -+ status = __sb2_shps_dgpu_set_power(pdev, power); -+ } -+ mutex_unlock(&drvdata->dgpu_power_lock); -+ -+ return status; -+} -+ -+static int sb2_shps_dgpu_force_power(struct platform_device *pdev, enum sb2_dgpu_power power) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&drvdata->dgpu_power_lock); -+ status = __sb2_shps_dgpu_set_power(pdev, power); -+ mutex_unlock(&drvdata->dgpu_power_lock); -+ -+ return status; -+} -+ -+ -+static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) -+{ -+ int power = SB2_PARAM_DGPU_POWER_OFF; -+ int status; -+ -+ status = kstrtoint(val, 0, &power); -+ if (status) { -+ return status; -+ } -+ -+ if (power < __SB2_PARAM_DGPU_POWER__START || power > __SB2_PARAM_DGPU_POWER__END) { -+ return -EINVAL; -+ } -+ -+ return param_set_int(val, kp); -+} -+ -+static const struct kernel_param_ops param_dgpu_power_ops = { -+ .set = param_dgpu_power_set, -+ .get = param_get_int, -+}; -+ -+static int param_dgpu_power_init = SB2_PARAM_DGPU_POWER_OFF; -+static int param_dgpu_power_exit = SB2_PARAM_DGPU_POWER_OFF; -+ -+module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SB2_PARAM_PERM); -+module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SB2_PARAM_PERM); -+ -+MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is)"); -+MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is)"); -+ -+ -+static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ return sprintf(data, "%s\n", sb2_dgpu_power_str(drvdata->dgpu_power)); -+} -+ -+static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, -+ const char *data, size_t count) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ bool power = false; -+ int status; -+ -+ status = kstrtobool(data, &power); -+ if (status) { -+ return status; -+ } -+ -+ if (power) { -+ status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_ON); -+ } else { -+ status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_OFF); -+ } -+ -+ return status < 0 ? status : count; -+} -+ -+const static DEVICE_ATTR_RW(dgpu_power); -+ -+ -+#ifdef CONFIG_PM -+ -+static int sb2_shps_resume(struct device *dev) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ return sb2_shps_dgpu_force_power(pdev, drvdata->dgpu_power); -+} -+ -+static SIMPLE_DEV_PM_OPS(sb2_shps_pm_ops, NULL, sb2_shps_resume); -+ -+#endif -+ -+ -+static int sb2_shps_probe(struct platform_device *pdev) -+{ -+ struct sb2_shps_driver_data *drvdata; -+ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); -+ int status = 0; -+ -+ if (gpiod_count(&pdev->dev, NULL) < 0) { -+ return -ENODEV; -+ } -+ -+ status = acpi_dev_add_driver_gpios(shps_dev, sb2_mshw0153_acpi_gpios); -+ if (status) { -+ return status; -+ } -+ -+ drvdata = kzalloc(sizeof(struct sb2_shps_driver_data), GFP_KERNEL); -+ if (!drvdata) { -+ status = -ENOMEM; -+ goto err_alloc_drvdata; -+ } -+ -+ mutex_init(&drvdata->dgpu_power_lock); -+ drvdata->dgpu_power = SB2_DGPU_POWER_OFF; -+ platform_set_drvdata(pdev, drvdata); -+ -+ if (param_dgpu_power_init != SB2_PARAM_DGPU_POWER_AS_IS) { -+ status = sb2_shps_dgpu_force_power(pdev, param_dgpu_power_init); -+ if (status) { -+ goto err_set_power; -+ } -+ } -+ -+ status = sysfs_create_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr); -+ if (status) { -+ goto err_sysfs; -+ } -+ -+ return 0; -+ -+err_sysfs: -+ sb2_shps_dgpu_force_power(pdev, SB2_DGPU_POWER_OFF); -+err_set_power: -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+err_alloc_drvdata: -+ acpi_dev_remove_driver_gpios(shps_dev); -+ return status; -+} -+ -+static int sb2_shps_remove(struct platform_device *pdev) -+{ -+ struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); -+ -+ sysfs_remove_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr); -+ -+ if (param_dgpu_power_exit != SB2_PARAM_DGPU_POWER_AS_IS) { -+ sb2_shps_dgpu_set_power(pdev, param_dgpu_power_exit); -+ } -+ acpi_dev_remove_driver_gpios(shps_dev); -+ -+ platform_set_drvdata(pdev, NULL); -+ kfree(drvdata); -+ -+ return 0; -+} -+ -+ -+static const struct acpi_device_id sb2_shps_acpi_match[] = { -+ { "MSHW0153", 0 }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(acpi, sb2_shps_acpi_match); -+ -+static struct platform_driver sb2_shps_driver = { -+ .probe = sb2_shps_probe, -+ .remove = sb2_shps_remove, -+ .driver = { -+ .name = "sb2_shps", -+ .acpi_match_table = ACPI_PTR(sb2_shps_acpi_match), -+#ifdef CONFIG_PM -+ .pm = &sb2_shps_pm_ops, -+#endif -+ }, -+}; -+module_platform_driver(sb2_shps_driver); -+ -+MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("Surface Book 2 Hot-Plug System Driver"); -+MODULE_LICENSE("GPL v2"); --- -2.24.0 - diff --git a/patches/5.4/0006-surface-lte.patch b/patches/5.4/0005-surface-lte.patch similarity index 88% rename from patches/5.4/0006-surface-lte.patch rename to patches/5.4/0005-surface-lte.patch index 796e152b6..6bfb45b0e 100644 --- a/patches/5.4/0006-surface-lte.patch +++ b/patches/5.4/0005-surface-lte.patch @@ -1,7 +1,7 @@ -From 686f6202a26afc35e9ee45e2106207f6a50d3bac Mon Sep 17 00:00:00 2001 +From 1ac8e97c8765f1e89ede2a981559b10571b5b5a2 Mon Sep 17 00:00:00 2001 From: qzed Date: Tue, 17 Sep 2019 17:21:43 +0200 -Subject: [PATCH 6/7] surface-lte +Subject: [PATCH 5/7] surface-lte --- drivers/usb/serial/qcserial.c | 1 + @@ -20,5 +20,5 @@ index 613f91add03d..e1428222dd73 100644 /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ -- -2.24.0 +2.24.1 diff --git a/patches/5.4/0006-wifi.patch b/patches/5.4/0006-wifi.patch new file mode 100644 index 000000000..d2e304d5d --- /dev/null +++ b/patches/5.4/0006-wifi.patch @@ -0,0 +1,168 @@ +From 2bd84c6967b2741d012844717f46f514b2122f4f Mon Sep 17 00:00:00 2001 +From: sebanc <22224731+sebanc@users.noreply.github.com> +Date: Mon, 4 Nov 2019 09:30:57 +0100 +Subject: [PATCH 6/7] wifi + +--- + drivers/net/wireless/marvell/mwifiex/pcie.c | 74 ++++++++++--------- + .../net/wireless/marvell/mwifiex/sta_cmd.c | 15 +--- + 2 files changed, 41 insertions(+), 48 deletions(-) + +diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c +index eff06d59e9df..eeda5de9173f 100644 +--- a/drivers/net/wireless/marvell/mwifiex/pcie.c ++++ b/drivers/net/wireless/marvell/mwifiex/pcie.c +@@ -149,35 +149,38 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *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); +- ++ 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) + 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; ++ ++ if (fw_status == FIRMWARE_READY_PCIE && !adapter->mfg_mode) { ++ mwifiex_deauthenticate_all(adapter); + +- /* Enable the Host Sleep */ +- if (!mwifiex_enable_hs(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; +- } ++ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + +- flush_workqueue(adapter->workqueue); ++ mwifiex_disable_auto_ds(priv); + +- /* 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; + } +@@ -192,28 +195,29 @@ static int mwifiex_pcie_suspend(struct device *dev) + */ + 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); ++ 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); + +- adapter = card->adapter; ++ card->dev = pdev; + +- 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; ++ } + + return 0; + } +@@ -267,6 +271,8 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, + return -1; + } + ++ pdev->bus->self->bridge_d3 = false; ++ + return 0; + } + +diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +index 4ed10cf82f9a..013db4386c39 100644 +--- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c ++++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +@@ -2265,14 +2265,13 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, + 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; + u16 packet_aggr_enable; + int data; ++ int ret; + + if (first_sta) { + if (priv->adapter->iface_type == MWIFIEX_PCIE) { +@@ -2395,18 +2394,6 @@ 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; +- } +- + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Send cmd to FW to enable/disable 11D function */ + state_11d = ENABLE_11D; +-- +2.24.1 + diff --git a/patches/5.4/0007-surface3-spi-dma.patch b/patches/5.4/0007-surface3-spi-dma.patch new file mode 100644 index 000000000..651c650ff --- /dev/null +++ b/patches/5.4/0007-surface3-spi-dma.patch @@ -0,0 +1,63 @@ +From 995dc220a596617eaf5ccd543a9a9b2e3d1e7bf2 Mon Sep 17 00:00:00 2001 +From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> +Date: Fri, 6 Dec 2019 23:10:30 +0900 +Subject: [PATCH 7/7] surface3-spi dma + +--- + drivers/input/touchscreen/surface3_spi.c | 26 ++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c +index ce4828b1415a..63b0b8ddf090 100644 +--- a/drivers/input/touchscreen/surface3_spi.c ++++ b/drivers/input/touchscreen/surface3_spi.c +@@ -25,6 +25,12 @@ + #define SURFACE3_REPORT_TOUCH 0xd2 + #define SURFACE3_REPORT_PEN 0x16 + ++bool use_dma = false; ++module_param(use_dma, bool, 0644); ++MODULE_PARM_DESC(use_dma, ++ "Disable DMA mode if you encounter touch input crash. " ++ "(default: false, disabled to avoid crash)"); ++ + struct surface3_ts_data { + struct spi_device *spi; + struct gpio_desc *gpiod_rst[2]; +@@ -326,6 +332,13 @@ static int surface3_spi_create_pen_input(struct surface3_ts_data *data) + return 0; + } + ++static bool surface3_spi_can_dma(struct spi_controller *ctlr, ++ struct spi_device *spi, ++ struct spi_transfer *tfr) ++{ ++ return use_dma; ++} ++ + static int surface3_spi_probe(struct spi_device *spi) + { + struct surface3_ts_data *data; +@@ -368,6 +381,19 @@ static int surface3_spi_probe(struct spi_device *spi) + if (error) + return error; + ++ /* ++ * Set up DMA ++ * ++ * TODO: Currently, touch input with DMA seems to be broken. ++ * On 4.19 LTS, touch input will crash after suspend. ++ * On recent stable kernel (at least after 5.1), touch input will crash after ++ * the first touch. No problem with PIO on those kernels. ++ * Maybe we need to configure DMA here. ++ * ++ * Link to issue: https://github.com/jakeday/linux-surface/issues/596 ++ */ ++ spi->controller->can_dma = surface3_spi_can_dma; ++ + return 0; + } + +-- +2.24.1 + diff --git a/patches/5.4/0007-wifi.patch b/patches/5.4/0007-wifi.patch deleted file mode 100644 index 0906a1262..000000000 --- a/patches/5.4/0007-wifi.patch +++ /dev/null @@ -1,267 +0,0 @@ -From 4336f2d8b36012ff7eac888dee18e9d35bbff90e Mon Sep 17 00:00:00 2001 -From: qzed -Date: Wed, 18 Sep 2019 03:18:25 +0200 -Subject: [PATCH 7/7] wifi - ---- - drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 3 +-- - drivers/net/wireless/marvell/mwifiex/cfg80211.c | 5 ++++- - drivers/net/wireless/marvell/mwifiex/cmdevt.c | 10 ++++++---- - drivers/net/wireless/marvell/mwifiex/fw.h | 1 + - drivers/net/wireless/marvell/mwifiex/main.c | 17 +++++++++++++---- - drivers/net/wireless/marvell/mwifiex/main.h | 2 ++ - drivers/net/wireless/marvell/mwifiex/pcie.c | 9 +++++++++ - drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 4 ++-- - .../net/wireless/marvell/mwifiex/sta_cmdresp.c | 10 +++++++--- - drivers/net/wireless/marvell/mwifiex/usb.c | 2 ++ - scripts/leaking_addresses.pl | 0 - 11 files changed, 47 insertions(+), 16 deletions(-) - mode change 100755 => 100644 scripts/leaking_addresses.pl - -diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -index 088612438530..4386e657dfdb 100644 ---- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c -@@ -198,8 +198,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - - do { - /* Check if AMSDU can accommodate this MSDU */ -- if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) > -- adapter->tx_buf_size) -+ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) - break; - - skb_src = skb_dequeue(&pra_list->skb_head); -diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index d89684168500..1545bae9d6cf 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c -+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -@@ -437,7 +437,10 @@ mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, - mwifiex_dbg(priv->adapter, INFO, - "info: ignore timeout value for IEEE Power Save\n"); - -- ps_mode = enabled; -+ //ps_mode = enabled; -+ -+ mwifiex_dbg(priv->adapter, INFO, "overriding ps_mode to false\n"); -+ ps_mode = 0; - - return mwifiex_drv_set_power(priv, &ps_mode); - } -diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c -index e8788c35a453..82d25b3ca914 100644 ---- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c -+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c -@@ -1004,6 +1004,7 @@ mwifiex_cmd_timeout_func(struct timer_list *t) - if (cmd_node->wait_q_enabled) { - adapter->cmd_wait_q.status = -ETIMEDOUT; - mwifiex_cancel_pending_ioctl(adapter); -+ adapter->cmd_sent = false; - } - } - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { -@@ -1011,11 +1012,11 @@ mwifiex_cmd_timeout_func(struct timer_list *t) - return; - } - -- if (adapter->if_ops.device_dump) -- adapter->if_ops.device_dump(adapter); -+ //if (adapter->if_ops.device_dump) -+ // adapter->if_ops.device_dump(adapter); - -- if (adapter->if_ops.card_reset) -- adapter->if_ops.card_reset(adapter); -+ //if (adapter->if_ops.card_reset) -+ // adapter->if_ops.card_reset(adapter); - } - - void -@@ -1578,6 +1579,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - adapter->key_api_minor_ver); - break; - case FW_API_VER_ID: -+ case FW_KEY_API_VER_ID: - adapter->fw_api_ver = - api_rev->major_ver; - mwifiex_dbg(adapter, INFO, -diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h -index 1fb76d2f5d3f..fb32379da99d 100644 ---- a/drivers/net/wireless/marvell/mwifiex/fw.h -+++ b/drivers/net/wireless/marvell/mwifiex/fw.h -@@ -1052,6 +1052,7 @@ struct host_cmd_ds_802_11_ps_mode_enh { - enum API_VER_ID { - KEY_API_VER_ID = 1, - FW_API_VER_ID = 2, -+ FW_KEY_API_VER_ID = 4, - }; - - struct hw_spec_api_rev { -diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c -index a9657ae6d782..ba99d84a31ef 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.c -+++ b/drivers/net/wireless/marvell/mwifiex/main.c -@@ -163,6 +163,7 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->mwifiex_processing) { - adapter->more_task_flag = true; -+ adapter->more_rx_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); -@@ -171,16 +172,18 @@ void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) - } - EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); - --static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) -+void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) - { - spin_lock_bh(&adapter->rx_proc_lock); - if (adapter->rx_processing) { -+ adapter->more_rx_task_flag = true; - spin_unlock_bh(&adapter->rx_proc_lock); - } else { - spin_unlock_bh(&adapter->rx_proc_lock); - queue_work(adapter->rx_workqueue, &adapter->rx_work); - } - } -+EXPORT_SYMBOL_GPL(mwifiex_queue_rx_work); - - static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - { -@@ -189,6 +192,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - - spin_lock_bh(&adapter->rx_proc_lock); - if (adapter->rx_processing || adapter->rx_locked) { -+ adapter->more_rx_task_flag = true; - spin_unlock_bh(&adapter->rx_proc_lock); - goto exit_rx_proc; - } else { -@@ -196,6 +200,7 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - spin_unlock_bh(&adapter->rx_proc_lock); - } - -+rx_process_start: - /* Check for Rx data */ - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - atomic_dec(&adapter->rx_pending); -@@ -217,6 +222,11 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) - } - } - spin_lock_bh(&adapter->rx_proc_lock); -+ if (adapter->more_rx_task_flag) { -+ adapter->more_rx_task_flag = false; -+ spin_unlock_bh(&adapter->rx_proc_lock); -+ goto rx_process_start; -+ } - adapter->rx_processing = false; - spin_unlock_bh(&adapter->rx_proc_lock); - -@@ -280,11 +290,10 @@ int mwifiex_main_process(struct mwifiex_adapter *adapter) - mwifiex_process_hs_config(adapter); - if (adapter->if_ops.process_int_status) - adapter->if_ops.process_int_status(adapter); -+ if (adapter->rx_work_enabled && adapter->data_received) -+ mwifiex_queue_rx_work(adapter); - } - -- if (adapter->rx_work_enabled && adapter->data_received) -- mwifiex_queue_rx_work(adapter); -- - /* Need to wake up the card ? */ - if ((adapter->ps_state == PS_STATE_SLEEP) && - (adapter->pm_wakeup_card_req && -diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h -index 095837fba300..5dca5c25c601 100644 ---- a/drivers/net/wireless/marvell/mwifiex/main.h -+++ b/drivers/net/wireless/marvell/mwifiex/main.h -@@ -909,6 +909,7 @@ struct mwifiex_adapter { - spinlock_t main_proc_lock; - u32 mwifiex_processing; - u8 more_task_flag; -+ u8 more_rx_task_flag; - u16 tx_buf_size; - u16 curr_tx_buf_size; - /* sdio single port rx aggregation capability */ -@@ -1695,6 +1696,7 @@ void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); - void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); - void mwifiex_fw_dump_event(struct mwifiex_private *priv); - void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); -+void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter); - int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action, - int cmd_type, - struct mwifiex_ds_wakeup_reason *wakeup_reason); -diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index eff06d59e9df..76e76a6a7ab1 100644 ---- a/drivers/net/wireless/marvell/mwifiex/pcie.c -+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c -@@ -1739,6 +1739,15 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) - } - - rx_len = get_unaligned_le16(skb->data); -+ -+ if (rx_len == 0) { -+ mwifiex_dbg(adapter, ERROR, -+ "0 byte cmdrsp\n"); -+ mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, -+ PCI_DMA_FROMDEVICE); -+ return 0; -+ } -+ - skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); - skb_trim(skb, rx_len); - -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -index 4ed10cf82f9a..485360e8534b 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -@@ -30,8 +30,8 @@ static bool drcs; - module_param(drcs, bool, 0644); - MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); - --static bool disable_auto_ds; --module_param(disable_auto_ds, bool, 0); -+static bool disable_auto_ds = 1; -+module_param(disable_auto_ds, bool, 0644); - MODULE_PARM_DESC(disable_auto_ds, - "deepsleep enabled=0(default), deepsleep disabled=1"); - /* -diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -index 20c206da0631..0e58da83417c 100644 ---- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c -@@ -47,9 +47,13 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ps_mode_enh *pm; - -- mwifiex_dbg(adapter, ERROR, -- "CMD_RESP: cmd %#x error, result=%#x\n", -- resp->command, resp->result); -+ if (resp->command == 271 && resp->result == 2) { -+ // ignore this command as the firmware does not support it -+ } else { -+ mwifiex_dbg(adapter, ERROR, -+ "CMD_RESP: cmd %#x error, result=%#x\n", -+ resp->command, resp->result); -+ } - - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; -diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c -index c2365eeb7016..0a219ba378dd 100644 ---- a/drivers/net/wireless/marvell/mwifiex/usb.c -+++ b/drivers/net/wireless/marvell/mwifiex/usb.c -@@ -144,6 +144,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); -+ if (adapter->rx_work_enabled) -+ mwifiex_queue_rx_work(adapter); - break; - default: - mwifiex_dbg(adapter, ERROR, -diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl -old mode 100755 -new mode 100644 --- -2.24.0 -