Changes: - SAM: - Update DTX driver state after resume. - Add DTX Documentation, misc. fixes, and cleanup. - IPTS: This implements a new and refined UAPI interface that should improve stability during suspend and move some responsibility into userspace, making the driver simpler. It also fixes some sleep issues due to improper shutdown of the device. Shortlog: 0a4a44c Add missing include 31ae03d Improve error handling of ipts_control_* functions 287dea0 Prevent lockups if stop is called from the receiver thread b737a9c On remove, wait until CLEAR_MEM_WINDOW returned. c5b66a5 Add GET_DEVICE_READY ioctl af0f84a Seperate UAPI initialization and device probing 4ae7674 Patch the MEI bus to allow sending commands on remove 27772bc Just a few refactorings... Links: - SAM:af4bb01042
- IPTS:0a4a44c2a9
- kernel:6e8bb10ad8
335 lines
9.9 KiB
Diff
335 lines
9.9 KiB
Diff
From 256ed7b2d0a06dd15968828ac55d355773b19958 Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Sat, 25 Jul 2020 17:19:53 +0200
|
|
Subject: [PATCH] i2c: acpi: Implement RawBytes read access
|
|
|
|
Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C
|
|
device via a generic serial bus operation region and RawBytes read
|
|
access. On the Surface Book 1, this access is required to turn on (and
|
|
off) the discrete GPU.
|
|
|
|
Multiple things are to note here:
|
|
|
|
a) The RawBytes access is device/driver dependent. The ACPI
|
|
specification states:
|
|
|
|
> Raw accesses assume that the writer has knowledge of the bus that
|
|
> the access is made over and the device that is being accessed. The
|
|
> protocol may only ensure that the buffer is transmitted to the
|
|
> appropriate driver, but the driver must be able to interpret the
|
|
> buffer to communicate to a register.
|
|
|
|
Thus this implementation may likely not work on other devices
|
|
accessing I2C via the RawBytes accessor type.
|
|
|
|
b) The MSHW0030 I2C device is an HID-over-I2C device which seems to
|
|
serve multiple functions:
|
|
|
|
1. It is the main access point for the legacy-type Surface Aggregator
|
|
Module (also referred to as SAM-over-HID, as opposed to the newer
|
|
SAM-over-SSH/UART). It has currently not been determined on how
|
|
support for the legacy SAM should be implemented. Likely via a
|
|
custom HID driver.
|
|
|
|
2. It seems to serve as the HID device for the Integrated Sensor Hub.
|
|
This might complicate matters with regards to implementing a
|
|
SAM-over-HID driver required by legacy SAM.
|
|
|
|
In light of this, the simplest approach has been chosen for now.
|
|
However, it may make more sense regarding breakage and compatibility to
|
|
either provide functionality for replacing or enhancing the default
|
|
operation region handler via some additional API functions, or even to
|
|
completely blacklist MSHW0030 from the I2C core and provide a custom
|
|
driver for it.
|
|
|
|
Replacing/enhancing the default operation region handler would, however,
|
|
either require some sort of secondary driver and access point for it,
|
|
from which the new API functions would be called and the new handler
|
|
(part) would be installed, or hard-coding them via some sort of
|
|
quirk-like interface into the I2C core.
|
|
|
|
Patchset: surface-sam-over-hid
|
|
---
|
|
drivers/i2c/i2c-core-acpi.c | 35 +++++++++++++++++++++++++++++++++++
|
|
1 file changed, 35 insertions(+)
|
|
|
|
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
|
|
index 2ade99b105b9..60b9cb51d5f7 100644
|
|
--- a/drivers/i2c/i2c-core-acpi.c
|
|
+++ b/drivers/i2c/i2c-core-acpi.c
|
|
@@ -574,6 +574,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
|
|
return (ret == 1) ? 0 : -EIO;
|
|
}
|
|
|
|
+static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client,
|
|
+ u8 *data, u8 data_len)
|
|
+{
|
|
+ struct i2c_msg msgs[1];
|
|
+ int ret = AE_OK;
|
|
+
|
|
+ msgs[0].addr = client->addr;
|
|
+ msgs[0].flags = client->flags;
|
|
+ msgs[0].len = data_len + 1;
|
|
+ msgs[0].buf = data;
|
|
+
|
|
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
|
+
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* 1 transfer must have completed successfully */
|
|
+ return (ret == 1) ? 0 : -EIO;
|
|
+}
|
|
+
|
|
static acpi_status
|
|
i2c_acpi_space_handler(u32 function, acpi_physical_address command,
|
|
u32 bits, u64 *value64,
|
|
@@ -675,6 +697,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
|
|
}
|
|
break;
|
|
|
|
+ case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES:
|
|
+ if (action == ACPI_READ) {
|
|
+ dev_warn(&adapter->dev,
|
|
+ "protocol 0x%02x not supported for client 0x%02x\n",
|
|
+ accessor_type, client->addr);
|
|
+ ret = AE_BAD_PARAMETER;
|
|
+ goto err;
|
|
+ } else {
|
|
+ status = acpi_gsb_i2c_write_raw_bytes(client,
|
|
+ gsb->data, info->access_length);
|
|
+ }
|
|
+ break;
|
|
+
|
|
default:
|
|
dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
|
|
accessor_type, client->addr);
|
|
--
|
|
2.28.0
|
|
|
|
From 03be5ddae354964501f25958d53525b23c9c6243 Mon Sep 17 00:00:00 2001
|
|
From: Maximilian Luz <luzmaximilian@gmail.com>
|
|
Date: Sun, 6 Sep 2020 04:01:19 +0200
|
|
Subject: [PATCH] platform/x86: Add driver for Surface Book 1 dGPU switch
|
|
|
|
Add driver exposing the discrete GPU power-switch of the Microsoft
|
|
Surface Book 1 to user-space.
|
|
|
|
On the Surface Book 1, the dGPU power is controlled via the Surface
|
|
System Aggregator Module (SAM). The specific SAM-over-HID command for
|
|
this is exposed via ACPI. This module provides a simple driver exposing
|
|
the ACPI call via a sysfs parameter to user-space, so that users can
|
|
easily power-on/-off the dGPU.
|
|
|
|
Patchset: surface-sam-over-hid
|
|
---
|
|
drivers/platform/x86/Kconfig | 7 ++
|
|
drivers/platform/x86/Makefile | 1 +
|
|
drivers/platform/x86/sb1_dgpu_sw.c | 162 +++++++++++++++++++++++++++++
|
|
3 files changed, 170 insertions(+)
|
|
create mode 100644 drivers/platform/x86/sb1_dgpu_sw.c
|
|
|
|
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
|
|
index 01752d1f40f8..19a560c44511 100644
|
|
--- a/drivers/platform/x86/Kconfig
|
|
+++ b/drivers/platform/x86/Kconfig
|
|
@@ -889,6 +889,13 @@ config SURFACE_GPE
|
|
accordingly. It is required on those devices to allow wake-ups from
|
|
suspend by opening the lid.
|
|
|
|
+config SURFACE_BOOK1_DGPU_SWITCH
|
|
+ tristate "Surface Book 1 dGPU Switch Driver"
|
|
+ depends on ACPI && SYSFS
|
|
+ help
|
|
+ This driver provides a sysfs switch to set the power-state of the
|
|
+ discrete GPU found on the Microsoft Surface Book 1.
|
|
+
|
|
config MSI_LAPTOP
|
|
tristate "MSI Laptop Extras"
|
|
depends on ACPI
|
|
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
|
|
index a64ce216719a..f46d4a3ce746 100644
|
|
--- a/drivers/platform/x86/Makefile
|
|
+++ b/drivers/platform/x86/Makefile
|
|
@@ -86,6 +86,7 @@ obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
|
|
obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
|
|
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
|
|
obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
|
|
+obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += sb1_dgpu_sw.o
|
|
|
|
# MSI
|
|
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
|
diff --git a/drivers/platform/x86/sb1_dgpu_sw.c b/drivers/platform/x86/sb1_dgpu_sw.c
|
|
new file mode 100644
|
|
index 000000000000..8c66ed5110fd
|
|
--- /dev/null
|
|
+++ b/drivers/platform/x86/sb1_dgpu_sw.c
|
|
@@ -0,0 +1,162 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/platform_device.h>
|
|
+
|
|
+
|
|
+#ifdef pr_fmt
|
|
+#undef pr_fmt
|
|
+#endif
|
|
+#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
|
|
+
|
|
+
|
|
+static const guid_t dgpu_sw_guid = GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4,
|
|
+ 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35);
|
|
+
|
|
+#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI"
|
|
+#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON"
|
|
+#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF"
|
|
+
|
|
+
|
|
+static int sb1_dgpu_sw_dsmcall(void)
|
|
+{
|
|
+ union acpi_object *ret;
|
|
+ acpi_handle handle;
|
|
+ acpi_status status;
|
|
+
|
|
+ status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle);
|
|
+ if (status)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER);
|
|
+ if (!ret)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ACPI_FREE(ret);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sb1_dgpu_sw_hgon(void)
|
|
+{
|
|
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
+ acpi_status status;
|
|
+
|
|
+ status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf);
|
|
+ if (status) {
|
|
+ pr_err("failed to run HGON: %d\n", status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (buf.pointer)
|
|
+ ACPI_FREE(buf.pointer);
|
|
+
|
|
+ pr_info("turned-on dGPU via HGON\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sb1_dgpu_sw_hgof(void)
|
|
+{
|
|
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
+ acpi_status status;
|
|
+
|
|
+ status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf);
|
|
+ if (status) {
|
|
+ pr_err("failed to run HGOF: %d\n", status);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (buf.pointer)
|
|
+ ACPI_FREE(buf.pointer);
|
|
+
|
|
+ pr_info("turned-off dGPU via HGOF\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t len)
|
|
+{
|
|
+ int status, value;
|
|
+
|
|
+ status = kstrtoint(buf, 0, &value);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ if (value != 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ status = sb1_dgpu_sw_dsmcall();
|
|
+
|
|
+ return status < 0 ? status : len;
|
|
+}
|
|
+
|
|
+static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t len)
|
|
+{
|
|
+ bool power;
|
|
+ int status;
|
|
+
|
|
+ status = kstrtobool(buf, &power);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ if (power)
|
|
+ status = sb1_dgpu_sw_hgon();
|
|
+ else
|
|
+ status = sb1_dgpu_sw_hgof();
|
|
+
|
|
+ return status < 0 ? status : len;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR_WO(dgpu_dsmcall);
|
|
+static DEVICE_ATTR_WO(dgpu_power);
|
|
+
|
|
+static struct attribute *sb1_dgpu_sw_attrs[] = {
|
|
+ &dev_attr_dgpu_dsmcall.attr,
|
|
+ &dev_attr_dgpu_power.attr,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static const struct attribute_group sb1_dgpu_sw_attr_group = {
|
|
+ .attrs = sb1_dgpu_sw_attrs,
|
|
+};
|
|
+
|
|
+
|
|
+static int sb1_dgpu_sw_probe(struct platform_device *pdev)
|
|
+{
|
|
+ return sysfs_create_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group);
|
|
+}
|
|
+
|
|
+static int sb1_dgpu_sw_remove(struct platform_device *pdev)
|
|
+{
|
|
+ sysfs_remove_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The dGPU power seems to be actually handled by MSHW0040. However, that is
|
|
+ * also the power-/volume-button device with a mainline driver. So let's use
|
|
+ * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device.
|
|
+ */
|
|
+static const struct acpi_device_id sb1_dgpu_sw_match[] = {
|
|
+ { "MSHW0041", },
|
|
+ { },
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match);
|
|
+
|
|
+static struct platform_driver sb1_dgpu_sw = {
|
|
+ .probe = sb1_dgpu_sw_probe,
|
|
+ .remove = sb1_dgpu_sw_remove,
|
|
+ .driver = {
|
|
+ .name = "sb1_dgpu_sw",
|
|
+ .acpi_match_table = sb1_dgpu_sw_match,
|
|
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(sb1_dgpu_sw);
|
|
+
|
|
+MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
|
|
+MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1");
|
|
+MODULE_LICENSE("GPL");
|
|
--
|
|
2.28.0
|
|
|