2018-03-03 20:25:08 +00:00
|
|
|
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
|
|
|
|
index 2c745e8..3b746c5 100644
|
|
|
|
--- a/drivers/platform/x86/Kconfig
|
|
|
|
+++ b/drivers/platform/x86/Kconfig
|
|
|
|
@@ -1113,6 +1113,15 @@ config SURFACE_3_BUTTON
|
|
|
|
---help---
|
|
|
|
This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet.
|
|
|
|
|
|
|
|
+config ACPI_SURFACE
|
|
|
|
+ tristate "Microsoft Surface Extras"
|
|
|
|
+ depends on ACPI
|
|
|
|
+ depends on ACPI_WMI
|
|
|
|
+ depends on INPUT
|
|
|
|
+ ---help---
|
|
|
|
+ This driver adds support for access to certain system events
|
|
|
|
+ on Microsoft Surface devices.
|
|
|
|
+
|
|
|
|
config INTEL_PUNIT_IPC
|
|
|
|
tristate "Intel P-Unit IPC Driver"
|
|
|
|
---help---
|
|
|
|
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
|
|
|
|
index c32b34a..6b04d7f 100644
|
|
|
|
--- a/drivers/platform/x86/Makefile
|
|
|
|
+++ b/drivers/platform/x86/Makefile
|
|
|
|
@@ -78,6 +78,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
|
|
|
|
obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o
|
|
|
|
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
|
|
|
|
obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
|
|
|
|
+obj-$(CONFIG_ACPI_SURFACE) += surface_acpi.o
|
|
|
|
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
|
|
|
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
|
|
|
|
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
|
|
|
|
diff --git a/drivers/platform/x86/surface_acpi.c b/drivers/platform/x86/surface_acpi.c
|
|
|
|
new file mode 100644
|
2018-03-04 15:41:25 +00:00
|
|
|
index 0000000..f9666d9
|
2018-03-03 20:25:08 +00:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/drivers/platform/x86/surface_acpi.c
|
2018-03-04 15:41:25 +00:00
|
|
|
@@ -0,0 +1,345 @@
|
2018-03-03 20:25:08 +00:00
|
|
|
+/*
|
|
|
|
+ * surface_acpi.c - Microsoft Surface ACPI Notify
|
|
|
|
+ *
|
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
|
+ * (at your option) any later version.
|
|
|
|
+ *
|
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
+ * GNU General Public License for more details.
|
|
|
|
+ *
|
|
|
|
+ * The full GNU General Public License is included in this distribution in
|
|
|
|
+ * the file called "COPYING".
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define SURFACE_ACPI_VERSION "0.1"
|
|
|
|
+#define SURFACE_EVENT_GUID "93b666c5-70c6-469f-a215-3d487c91ab3c"
|
|
|
|
+#define SURFACE_GEN_VERSION 0x08
|
|
|
|
+#define PROC_SURFACE "surface"
|
|
|
|
+
|
|
|
|
+#include <linux/kernel.h>
|
|
|
|
+#include <linux/module.h>
|
|
|
|
+#include <linux/init.h>
|
|
|
|
+#include <linux/types.h>
|
|
|
|
+#include <linux/acpi.h>
|
|
|
|
+#include <linux/power_supply.h>
|
|
|
|
+#include <linux/thermal.h>
|
|
|
|
+#include <linux/dmi.h>
|
|
|
|
+#include <acpi/acpi_bus.h>
|
|
|
|
+#include <acpi/acpi_drivers.h>
|
|
|
|
+
|
|
|
|
+MODULE_AUTHOR("Jake Day");
|
|
|
|
+MODULE_DESCRIPTION("Microsoft Surface ACPI Notify Driver");
|
|
|
|
+MODULE_LICENSE("GPL");
|
|
|
|
+
|
|
|
|
+#define SUR_METHOD_DSM "_DSM"
|
|
|
|
+#define SUR_METHOD_REG "_REG"
|
|
|
|
+
|
|
|
|
+#define SUR_QUERY_DEVICE 0x00
|
|
|
|
+#define SUR_SET_DVER 0x01
|
|
|
|
+#define SUR_GET_BOARD_REVID 0x02
|
|
|
|
+#define SUR_BAT1_STATE_CHANGE 0x03
|
|
|
|
+#define SUR_BAT1_INFO_CHANGE 0x04
|
|
|
|
+#define SUR_PSU_STATE_CHANGE 0x05
|
|
|
|
+#define SUR_PSU_INFO_CHANGE 0x06
|
|
|
|
+#define SUR_BAT2_STATE_CHANGE 0x07
|
|
|
|
+#define SUR_BAT2_INFO_CHANGE 0x08
|
|
|
|
+#define SUR_SENSOR_TRIP_POINT 0x09
|
|
|
|
+
|
|
|
|
+#define REG_AVAILABLE 0x01
|
|
|
|
+#define REG_INIT 0x09
|
|
|
|
+
|
|
|
|
+struct surface_acpi_dev {
|
|
|
|
+ struct acpi_device *acpi_dev;
|
|
|
|
+ struct acpi_device *bat1_dev;
|
|
|
|
+ struct acpi_device *bat2_dev;
|
|
|
|
+ struct acpi_device *psu_dev;
|
|
|
|
+
|
2018-03-04 15:41:25 +00:00
|
|
|
+ unsigned int bat1_attached:1;
|
|
|
|
+ unsigned int bat2_attached:1;
|
|
|
|
+ unsigned int psu_registered:1;
|
2018-03-03 20:25:08 +00:00
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct surface_acpi_dev *surface_acpi;
|
|
|
|
+
|
|
|
|
+static struct proc_dir_entry *surface_proc_dir;
|
|
|
|
+
|
|
|
|
+static int surface_acpi_reg(void)
|
|
|
|
+{
|
|
|
|
+ union acpi_object in_objs[2], out_objs[1];
|
|
|
|
+ struct acpi_object_list params;
|
|
|
|
+ struct acpi_buffer results;
|
|
|
|
+ acpi_status status;
|
|
|
|
+
|
|
|
|
+ params.count = ARRAY_SIZE(in_objs);
|
|
|
|
+ params.pointer = in_objs;
|
|
|
|
+ in_objs[0].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_objs[0].integer.value = REG_INIT;
|
|
|
|
+ in_objs[1].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_objs[1].integer.value = REG_AVAILABLE;
|
|
|
|
+ results.length = sizeof(out_objs);
|
|
|
|
+ results.pointer = out_objs;
|
|
|
|
+
|
|
|
|
+ if (acpi_has_method(surface_acpi->acpi_dev->handle, SUR_METHOD_REG)) {
|
|
|
|
+ status = acpi_evaluate_object(surface_acpi->acpi_dev->handle,
|
|
|
|
+ SUR_METHOD_REG, ¶ms, &results);
|
|
|
|
+
|
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
|
+ pr_err("surface_acpi: ACPI event failure status %s\n",
|
|
|
|
+ acpi_format_exception(status));
|
|
|
|
+ return AE_ERROR;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ return AE_NOT_FOUND;
|
|
|
|
+
|
|
|
|
+ return AE_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int surface_acpi_event_handler(u32 event)
|
|
|
|
+{
|
|
|
|
+ union acpi_object in_objs[4], out_objs[5];
|
|
|
|
+ struct acpi_object_list params;
|
|
|
|
+ struct acpi_buffer results;
|
|
|
|
+ acpi_status status;
|
|
|
|
+
|
|
|
|
+ params.count = ARRAY_SIZE(in_objs);
|
|
|
|
+ params.pointer = in_objs;
|
|
|
|
+ in_objs[0].type = ACPI_TYPE_STRING;
|
|
|
|
+ in_objs[0].string.pointer = SURFACE_EVENT_GUID;
|
|
|
|
+ in_objs[1].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_objs[1].integer.value = SUR_QUERY_DEVICE;
|
|
|
|
+ in_objs[2].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_objs[2].integer.value = event;
|
|
|
|
+ in_objs[3].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_objs[3].integer.value = SURFACE_GEN_VERSION;
|
|
|
|
+ results.length = sizeof(out_objs);
|
|
|
|
+ results.pointer = out_objs;
|
|
|
|
+
|
|
|
|
+ if (acpi_has_method(surface_acpi->acpi_dev->handle, SUR_METHOD_DSM)) {
|
|
|
|
+ status = acpi_evaluate_object(surface_acpi->acpi_dev->handle,
|
|
|
|
+ SUR_METHOD_DSM, ¶ms, &results);
|
|
|
|
+
|
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
|
+ pr_err("surface_acpi: ACPI event failure status %s\n",
|
|
|
|
+ acpi_format_exception(status));
|
|
|
|
+ return AE_ERROR;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ return AE_NOT_FOUND;
|
|
|
|
+
|
|
|
|
+ return AE_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void surface_acpi_load(void)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = surface_acpi_event_handler(SUR_SET_DVER);
|
|
|
|
+ if (ACPI_FAILURE(ret))
|
|
|
|
+ pr_err("surface_acpi: Error setting Driver Version\n");
|
|
|
|
+
|
|
|
|
+ ret = surface_acpi_event_handler(SUR_SENSOR_TRIP_POINT);
|
|
|
|
+ if (ACPI_FAILURE(ret))
|
|
|
|
+ pr_err("surface_acpi: Error setting Sensor Trip Point\n");
|
|
|
|
+
|
|
|
|
+ ret = surface_acpi_event_handler(SUR_BAT1_INFO_CHANGE);
|
|
|
|
+ if (ACPI_FAILURE(ret))
|
|
|
|
+ pr_err("surface_acpi: Error attaching BAT1\n");
|
|
|
|
+ else
|
2018-03-04 15:41:25 +00:00
|
|
|
+ surface_acpi->bat1_attached = 1;
|
2018-03-03 20:25:08 +00:00
|
|
|
+
|
|
|
|
+ ret = surface_acpi_event_handler(SUR_BAT2_INFO_CHANGE);
|
|
|
|
+ if (ACPI_FAILURE(ret))
|
|
|
|
+ pr_err("surface_acpi: Error attaching BAT2\n");
|
|
|
|
+ else
|
2018-03-04 15:41:25 +00:00
|
|
|
+ surface_acpi->bat2_attached = 1;
|
2018-03-03 20:25:08 +00:00
|
|
|
+
|
|
|
|
+ ret = surface_acpi_event_handler(SUR_PSU_INFO_CHANGE);
|
|
|
|
+ if (ACPI_FAILURE(ret))
|
|
|
|
+ pr_err("surface_acpi: Error registering PSU\n");
|
|
|
|
+ else
|
2018-03-04 15:41:25 +00:00
|
|
|
+ surface_acpi->psu_registered = 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bat1_proc_show(struct seq_file *m, void *v)
|
|
|
|
+{
|
|
|
|
+ seq_printf(m, "attached: %d\n", surface_acpi->bat1_attached);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bat1_proc_open(struct inode *inode, struct file *file)
|
|
|
|
+{
|
|
|
|
+ return single_open(file, bat1_proc_show, PDE_DATA(inode));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct file_operations bat1_proc_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = bat1_proc_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = single_release,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int bat2_proc_show(struct seq_file *m, void *v)
|
|
|
|
+{
|
|
|
|
+ seq_printf(m, "attached: %d\n", surface_acpi->bat2_attached);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bat2_proc_open(struct inode *inode, struct file *file)
|
|
|
|
+{
|
|
|
|
+ return single_open(file, bat2_proc_show, PDE_DATA(inode));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct file_operations bat2_proc_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = bat2_proc_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = single_release,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int psu_proc_show(struct seq_file *m, void *v)
|
|
|
|
+{
|
|
|
|
+ seq_printf(m, "registered: %d\n", surface_acpi->psu_registered);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int psu_proc_open(struct inode *inode, struct file *file)
|
|
|
|
+{
|
|
|
|
+ return single_open(file, psu_proc_show, PDE_DATA(inode));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct file_operations psu_proc_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = psu_proc_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = single_release,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int version_proc_show(struct seq_file *m, void *v)
|
|
|
|
+{
|
|
|
|
+ seq_printf(m, "driver: %s\n", SURFACE_ACPI_VERSION);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int version_proc_open(struct inode *inode, struct file *file)
|
|
|
|
+{
|
|
|
|
+ return single_open(file, version_proc_show, PDE_DATA(inode));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct file_operations version_proc_fops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = version_proc_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = single_release,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void create_surface_proc_entries(void)
|
|
|
|
+{
|
|
|
|
+ proc_create_data("BAT1", 0, surface_proc_dir,
|
|
|
|
+ &bat1_proc_fops, surface_acpi->bat1_attached);
|
|
|
|
+ proc_create_data("BAT2", 0, surface_proc_dir,
|
|
|
|
+ &bat2_proc_fops, surface_acpi->bat2_attached);
|
|
|
|
+ proc_create_data("ADP1", 0, surface_proc_dir,
|
|
|
|
+ &psu_proc_fops, surface_acpi->psu_registered);
|
|
|
|
+ proc_create_data("version", 0, surface_proc_dir,
|
|
|
|
+ &version_proc_fops, SURFACE_ACPI_VERSION);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void remove_surface_proc_entries(void)
|
|
|
|
+{
|
|
|
|
+ remove_proc_entry("BAT1", surface_proc_dir);
|
|
|
|
+ remove_proc_entry("BAT2", surface_proc_dir);
|
|
|
|
+ remove_proc_entry("ADP1", surface_proc_dir);
|
|
|
|
+ remove_proc_entry("version", surface_proc_dir);
|
2018-03-03 20:25:08 +00:00
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int surface_acpi_add(struct acpi_device *acpi_dev)
|
|
|
|
+{
|
|
|
|
+ if (surface_acpi)
|
|
|
|
+ return AE_ALREADY_ACQUIRED;
|
|
|
|
+
|
|
|
|
+ pr_info("surface_acpi: Microsoft Surface ACPI Notify version %s\n",
|
|
|
|
+ SURFACE_ACPI_VERSION);
|
|
|
|
+
|
|
|
|
+ surface_acpi = kzalloc(sizeof(*surface_acpi), GFP_KERNEL);
|
|
|
|
+ if (!surface_acpi)
|
|
|
|
+ return AE_NO_MEMORY;
|
|
|
|
+
|
|
|
|
+ surface_acpi->acpi_dev = acpi_dev;
|
|
|
|
+
|
|
|
|
+ surface_acpi_reg();
|
|
|
|
+
|
|
|
|
+ surface_acpi_load();
|
|
|
|
+
|
2018-03-04 15:41:25 +00:00
|
|
|
+ create_surface_proc_entries();
|
|
|
|
+
|
2018-03-03 20:25:08 +00:00
|
|
|
+ return AE_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int surface_acpi_remove(struct acpi_device *dev)
|
|
|
|
+{
|
2018-03-04 15:41:25 +00:00
|
|
|
+ remove_surface_proc_entries();
|
|
|
|
+
|
2018-03-03 20:25:08 +00:00
|
|
|
+ return AE_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void surface_acpi_notify(struct acpi_device *dev, u32 event)
|
|
|
|
+{
|
|
|
|
+ pr_info("surface_acpi: Event received %x\n", event);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct acpi_device_id surface_device_ids[] = {
|
|
|
|
+ {"MSHW0091", 0},
|
|
|
|
+ {"", 0},
|
|
|
|
+};
|
|
|
|
+MODULE_DEVICE_TABLE(acpi, surface_device_ids);
|
|
|
|
+
|
|
|
|
+static struct acpi_driver surface_acpi_driver = {
|
|
|
|
+ .name = "Microsoft Surface ACPI Notify Driver",
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .ids = surface_device_ids,
|
|
|
|
+ .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
|
|
|
+ .ops = {
|
|
|
|
+ .add = surface_acpi_add,
|
|
|
|
+ .remove = surface_acpi_remove,
|
|
|
|
+ .notify = surface_acpi_notify,
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int __init surface_acpi_init(void)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ surface_proc_dir = proc_mkdir(PROC_SURFACE, acpi_root_dir);
|
|
|
|
+ if (!surface_proc_dir) {
|
|
|
|
+ pr_err("surface_acpi: Unable to create proc dir " PROC_SURFACE "\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = acpi_bus_register_driver(&surface_acpi_driver);
|
|
|
|
+ if (ret) {
|
|
|
|
+ pr_err("surface_acpi: Failed to register ACPI driver: %d\n", ret);
|
|
|
|
+ remove_proc_entry(PROC_SURFACE, acpi_root_dir);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __exit surface_acpi_exit(void)
|
|
|
|
+{
|
|
|
|
+ acpi_bus_unregister_driver(&surface_acpi_driver);
|
|
|
|
+ if (surface_proc_dir)
|
|
|
|
+ remove_proc_entry(PROC_SURFACE, acpi_root_dir);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+module_init(surface_acpi_init);
|
|
|
|
+module_exit(surface_acpi_exit);
|