ソースを参照

add surface-acpi module

Add surface-acpi module, restructure patches, fix whitespace errors.
qzed 6 年 前
コミット
6bbdaf3679

+ 2996 - 0
patches/4.18/0001-surface-acpi.patch

@@ -0,0 +1,2996 @@
+From f742e9a20013dbf17f0d7bacb69047f8b7f77894 Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 13:31:00 +0100
+Subject: [PATCH 1/8] surface-acpi
+
+---
+ drivers/acpi/acpica/dsopcode.c      |    2 +-
+ drivers/acpi/acpica/exfield.c       |   26 +-
+ drivers/platform/x86/Kconfig        |   56 +
+ drivers/platform/x86/Makefile       |    1 +
+ drivers/platform/x86/surface_acpi.c | 2705 +++++++++++++++++++++++++++
+ drivers/tty/serdev/core.c           |   90 +-
+ 6 files changed, 2861 insertions(+), 19 deletions(-)
+ create mode 100644 drivers/platform/x86/surface_acpi.c
+
+diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
+index e9fb0bf3c8d2..cad7d94fa8d8 100644
+--- a/drivers/acpi/acpica/dsopcode.c
++++ b/drivers/acpi/acpica/dsopcode.c
+@@ -123,7 +123,7 @@ acpi_ds_init_buffer_field(u16 aml_opcode,
+ 
+ 		/* Offset is in bits, count is in bits */
+ 
+-		field_flags = AML_FIELD_ACCESS_BYTE;
++		field_flags = AML_FIELD_ACCESS_BUFFER;
+ 		bit_offset = offset;
+ 		bit_count = (u32) length_desc->integer.value;
+ 
+diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
+index b272c329d45d..cf547883a993 100644
+--- a/drivers/acpi/acpica/exfield.c
++++ b/drivers/acpi/acpica/exfield.c
+@@ -102,6 +102,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
+ 	void *buffer;
+ 	u32 function;
+ 	u16 accessor_type;
++	u8 field_flags;
+ 
+ 	ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc);
+ 
+@@ -199,11 +200,16 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
+ 	 * Note: Field.length is in bits.
+ 	 */
+ 	length =
+-	    (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
++	    (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
++	field_flags = obj_desc->common_field.field_flags;
+ 
+-	if (length > acpi_gbl_integer_byte_width) {
++	if (length > acpi_gbl_integer_byte_width ||
++	    (field_flags & AML_FIELD_ACCESS_TYPE_MASK) == AML_FIELD_ACCESS_BUFFER) {
+ 
+-		/* Field is too large for an Integer, create a Buffer instead */
++		/*
++		 * Field is either too large for an Integer, or a actually of type
++		 * buffer, so create a Buffer.
++		 */
+ 
+ 		buffer_desc = acpi_ut_create_buffer_object(length);
+ 		if (!buffer_desc) {
+@@ -366,19 +372,7 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
+ 		} else if (obj_desc->field.region_obj->region.space_id ==
+ 			   ACPI_ADR_SPACE_GSBUS) {
+ 			accessor_type = obj_desc->field.attribute;
+-			length =
+-			    acpi_ex_get_serial_access_length(accessor_type,
+-							     obj_desc->field.
+-							     access_length);
+-
+-			/*
+-			 * Add additional 2 bytes for the generic_serial_bus data buffer:
+-			 *
+-			 *     Status;    (Byte 0 of the data buffer)
+-			 *     Length;    (Byte 1 of the data buffer)
+-			 *     Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
+-			 */
+-			length += 2;
++			length = source_desc->buffer.length;
+ 			function = ACPI_WRITE | (accessor_type << 16);
+ 		} else {	/* IPMI */
+ 
+diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
+index ac4d48830415..d36316429293 100644
+--- a/drivers/platform/x86/Kconfig
++++ b/drivers/platform/x86/Kconfig
+@@ -573,6 +573,62 @@ config THINKPAD_ACPI_HOTKEY_POLL
+ 	  If you are not sure, say Y here.  The driver enables polling only if
+ 	  it is strictly necessary to do so.
+ 
++config SURFACE_ACPI
++	depends on ACPI
++	tristate "Microsoft Surface ACPI/Platform Drivers"
++	---help---
++	  ACPI and platform drivers for Microsoft Surface devices.
++
++config SURFACE_ACPI_SSH
++	bool "Surface Serial Hub Driver"
++	depends on SURFACE_ACPI
++	depends on X86_INTEL_LPSS
++	depends on SERIAL_8250_DW
++	depends on SERIAL_8250_DMA
++	depends on SERIAL_DEV_CTRL_TTYPORT
++	select CRC_CCITT
++	default y
++	---help---
++	  Surface Serial Hub driver for 5th generation (or later) Microsoft
++	  Surface devices.
++
++	  This is the base driver for the embedded serial controller found on
++	  5th generation (and later) Microsoft Surface devices (e.g. Book 2,
++	  Laptop, Laptop 2, Pro 2017, Pro 6, ...). This driver itself only
++	  provides access to the embedded controller and subsequent drivers are
++	  required for the respective functionalities.
++
++	  If you have a 5th generation (or later) Microsoft Surface device, say
++	  Y or M here.
++
++config SURFACE_ACPI_SAN
++	bool "Surface ACPI Notify Driver"
++	depends on SURFACE_ACPI_SSH
++	default y
++	---help---
++	  Surface ACPI Notify driver for 5th generation (or later) Microsoft
++	  Surface devices.
++
++	  This driver enables basic ACPI events and requests, such as battery
++	  status requests/events, thermal events, lid status, and possibly more,
++	  which would otherwise not work on these devices.
++
++	  If you are not sure, say Y here.
++
++config SURFACE_ACPI_VHF
++	bool "Surface Virtual HID Framework Driver"
++	depends on SURFACE_ACPI_SSH
++	depends on HID
++	default y
++	---help---
++	  Surface Virtual HID Framework driver for 5th generation (or later)
++	  Microsoft Surface devices.
++
++	  This driver provides support for the Microsoft Virtual HID framework,
++	  which is required for the Surface Laptop (1 and newer) keyboard.
++
++	  If you are not sure, say Y here.
++
+ config SENSORS_HDAPS
+ 	tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
+ 	depends on INPUT
+diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
+index 2ba6cb795338..4a76e3fca5d5 100644
+--- a/drivers/platform/x86/Makefile
++++ b/drivers/platform/x86/Makefile
+@@ -35,6 +35,7 @@ obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
+ obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
+ obj-$(CONFIG_IDEAPAD_LAPTOP)	+= ideapad-laptop.o
+ obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
++obj-$(CONFIG_SURFACE_ACPI)	+= surface_acpi.o
+ obj-$(CONFIG_SENSORS_HDAPS)	+= hdaps.o
+ obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
+ obj-$(CONFIG_FUJITSU_TABLET)	+= fujitsu-tablet.o
+diff --git a/drivers/platform/x86/surface_acpi.c b/drivers/platform/x86/surface_acpi.c
+new file mode 100644
+index 000000000000..e7d3c21e6f42
+--- /dev/null
++++ b/drivers/platform/x86/surface_acpi.c
+@@ -0,0 +1,2705 @@
++#include <asm/unaligned.h>
++#include <linux/acpi.h>
++#include <linux/completion.h>
++#include <linux/crc-ccitt.h>
++#include <linux/device.h>
++#include <linux/dmaengine.h>
++#include <linux/hid.h>
++#include <linux/input.h>
++#include <linux/jiffies.h>
++#include <linux/kernel.h>
++#include <linux/kfifo.h>
++#include <linux/mutex.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/refcount.h>
++#include <linux/serdev.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++#include <linux/workqueue.h>
++
++
++/*************************************************************************
++ * Surface Serial Hub driver (cross-driver interface)
++ */
++
++#ifdef CONFIG_SURFACE_ACPI_SSH
++
++/*
++ * Maximum request payload size in bytes.
++ * Value based on ACPI (255 bytes minus header/status bytes).
++ */
++#define SURFACEGEN5_MAX_RQST_PAYLOAD	(255 - 10)
++
++/*
++ * Maximum response payload size in bytes.
++ * Value based on ACPI (255 bytes minus header/status bytes).
++ */
++#define SURFACEGEN5_MAX_RQST_RESPONSE	(255 - 4)
++
++#define SURFACEGEN5_RQID_EVENT_BITS	5
++
++#define SURFACEGEN5_EVENT_IMMEDIATE	((unsigned long) -1)
++
++
++struct surfacegen5_buf {
++	u8 cap;
++	u8 len;
++	u8 *data;
++};
++
++struct surfacegen5_rqst {
++	u8 tc;
++	u8 iid;
++	u8 cid;
++	u8 snc;
++	u8 cdl;
++	u8 *pld;
++};
++
++struct surfacegen5_event {
++	u16 rqid;
++	u8  tc;
++	u8  iid;
++	u8  cid;
++	u8  len;
++	u8 *pld;
++};
++
++
++typedef int (*surfacegen5_ec_event_handler_fn)(struct surfacegen5_event *event, void *data);
++typedef unsigned long (*surfacegen5_ec_event_handler_delay)(struct surfacegen5_event *event, void *data);
++
++struct device_link *surfacegen5_ec_consumer_add(struct device *consumer, u32 flags);
++int surfacegen5_ec_consumer_remove(struct device_link *link);
++
++int surfacegen5_ec_rqst(struct surfacegen5_rqst *rqst, struct surfacegen5_buf *result);
++
++int surfacegen5_ec_enable_event_source(u8 tc, u8 unknown, u16 rqid);
++int surfacegen5_ec_disable_event_source(u8 tc, u8 unknown, u16 rqid);
++int surfacegen5_ec_remove_event_handler(u16 rqid);
++int surfacegen5_ec_set_event_handler(u16 rqid, surfacegen5_ec_event_handler_fn fn, void *data);
++int surfacegen5_ec_set_delayed_event_handler(u16 rqid,
++		surfacegen5_ec_event_handler_fn fn,
++		surfacegen5_ec_event_handler_delay delay,
++		void *data);
++
++#endif /* CONFIG_SURFACE_ACPI_SSH */
++
++
++/*************************************************************************
++ * Surface Serial Hub driver (private implementation)
++ */
++
++#ifdef CONFIG_SURFACE_ACPI_SSH
++
++#define SG5_RQST_TAG_FULL		"surfacegen5_ec_rqst: "
++#define SG5_RQST_TAG			"rqst: "
++#define SG5_EVENT_TAG			"event: "
++#define SG5_RECV_TAG			"recv: "
++
++#define SG5_SUPPORTED_FLOW_CONTROL_MASK		(~((u8) ACPI_UART_FLOW_CONTROL_HW))
++
++#define SG5_BYTELEN_SYNC		2
++#define SG5_BYTELEN_TERM		2
++#define SG5_BYTELEN_CRC			2
++#define SG5_BYTELEN_CTRL		4	// command-header, ACK, or RETRY
++#define SG5_BYTELEN_CMDFRAME		8	// without payload
++
++#define SG5_MAX_WRITE (                 \
++	  SG5_BYTELEN_SYNC              \
++	+ SG5_BYTELEN_CTRL              \
++	+ SG5_BYTELEN_CRC               \
++	+ SG5_BYTELEN_CMDFRAME          \
++	+ SURFACEGEN5_MAX_RQST_PAYLOAD  \
++	+ SG5_BYTELEN_CRC               \
++)
++
++#define SG5_MSG_LEN_CTRL (              \
++	  SG5_BYTELEN_SYNC              \
++	+ SG5_BYTELEN_CTRL              \
++	+ SG5_BYTELEN_CRC               \
++	+ SG5_BYTELEN_TERM              \
++)
++
++#define SG5_MSG_LEN_CMD_BASE (          \
++	  SG5_BYTELEN_SYNC              \
++	+ SG5_BYTELEN_CTRL              \
++	+ SG5_BYTELEN_CRC               \
++	+ SG5_BYTELEN_CRC               \
++)	// without payload and command-frame
++
++#define SG5_WRITE_TIMEOUT		msecs_to_jiffies(1000)
++#define SG5_READ_TIMEOUT		msecs_to_jiffies(1000)
++#define SG5_NUM_RETRY			3
++
++#define SG5_WRITE_BUF_LEN		SG5_MAX_WRITE
++#define SG5_READ_BUF_LEN		512		// must be power of 2
++#define SG5_EVAL_BUF_LEN		SG5_MAX_WRITE	// also works for reading
++
++#define SG5_FRAME_TYPE_CMD		0x80
++#define SG5_FRAME_TYPE_ACK		0x40
++#define SG5_FRAME_TYPE_RETRY		0x04
++
++#define SG5_FRAME_OFFS_CTRL		SG5_BYTELEN_SYNC
++#define SG5_FRAME_OFFS_CTRL_CRC		(SG5_FRAME_OFFS_CTRL + SG5_BYTELEN_CTRL)
++#define SG5_FRAME_OFFS_TERM		(SG5_FRAME_OFFS_CTRL_CRC + SG5_BYTELEN_CRC)
++#define SG5_FRAME_OFFS_CMD		SG5_FRAME_OFFS_TERM	// either TERM or CMD
++#define SG5_FRAME_OFFS_CMD_PLD		(SG5_FRAME_OFFS_CMD + SG5_BYTELEN_CMDFRAME)
++
++/*
++ * A note on Request IDs (RQIDs):
++ * 	0x0000 is not a valid RQID
++ * 	0x0001 is valid, but reserved for Surface Laptop keyboard events
++ */
++#define SG5_NUM_EVENT_TYPES		((1 << SURFACEGEN5_RQID_EVENT_BITS) - 1)
++
++/*
++ * Sync:			aa 55
++ * Terminate:			ff ff
++ *
++ * Request Message:		sync cmd-hdr crc(cmd-hdr) cmd-rqst-frame crc(cmd-rqst-frame)
++ * Ack Message:			sync ack crc(ack) terminate
++ * Retry Message:		sync retry crc(retry) terminate
++ * Response Message:		sync cmd-hdr crc(cmd-hdr) cmd-resp-frame crc(cmd-resp-frame)
++ *
++ * Command Header:		80 LEN 00 SEQ
++ * Ack:                 	40 00 00 SEQ
++ * Retry:			04 00 00 00
++ * Command Request Frame:	80 RTC 01 00 RIID RQID RCID PLD
++ * Command Response Frame:	80 RTC 00 01 RIID RQID RCID PLD
++ */
++
++struct surfacegen5_frame_ctrl {
++	u8 type;
++	u8 len;			// without crc
++	u8 pad;
++	u8 seq;
++} __packed;
++
++struct surfacegen5_frame_cmd {
++	u8 type;
++	u8 tc;
++	u8 unknown1;
++	u8 unknown2;
++	u8 iid;
++	u8 rqid_lo;		// id for request/response matching (low byte)
++	u8 rqid_hi;		// id for request/response matching (high byte)
++	u8 cid;
++} __packed;
++
++
++enum surfacegen5_ec_state {
++	SG5_EC_UNINITIALIZED,
++	SG5_EC_INITIALIZED,
++	SG5_EC_SUSPENDED,
++};
++
++struct surfacegen5_ec_counters {
++	u8  seq;		// control sequence id
++	u16 rqid;		// id for request/response matching
++};
++
++struct surfacegen5_ec_writer {
++	u8 *data;
++	u8 *ptr;
++} __packed;
++
++enum surfacegen5_ec_receiver_state {
++	SG5_RCV_DISCARD,
++	SG5_RCV_CONTROL,
++	SG5_RCV_COMMAND,
++};
++
++struct surfacegen5_ec_receiver {
++	spinlock_t        lock;
++	enum surfacegen5_ec_receiver_state state;
++	struct completion signal;
++	struct kfifo      fifo;
++	struct {
++		bool pld;
++		u8   seq;
++		u16  rqid;
++	} expect;
++	struct {
++		u16 cap;
++		u16 len;
++		u8 *ptr;
++	} eval_buf;
++};
++
++struct surfacegen5_ec_event_handler {
++	surfacegen5_ec_event_handler_fn handler;
++	surfacegen5_ec_event_handler_delay delay;
++	void *data;
++};
++
++struct surfacegen5_ec_events {
++	spinlock_t lock;
++	struct workqueue_struct *queue_ack;
++	struct workqueue_struct *queue_evt;
++	struct surfacegen5_ec_event_handler handler[SG5_NUM_EVENT_TYPES];
++};
++
++struct surfacegen5_ec {
++	struct mutex                   lock;
++	enum surfacegen5_ec_state      state;
++	struct serdev_device          *serdev;
++	struct surfacegen5_ec_counters counter;
++	struct surfacegen5_ec_writer   writer;
++	struct surfacegen5_ec_receiver receiver;
++	struct surfacegen5_ec_events   events;
++};
++
++struct surfacegen5_fifo_packet {
++	u8 type;	// packet type (ACK/RETRY/CMD)
++	u8 seq;
++	u8 len;
++};
++
++struct surfacegen5_event_work {
++	refcount_t               refcount;
++	struct surfacegen5_ec   *ec;
++	struct work_struct       work_ack;
++	struct delayed_work      work_evt;
++	struct surfacegen5_event event;
++	u8                       seq;
++};
++
++
++static struct surfacegen5_ec surfacegen5_ec = {
++	.lock   = __MUTEX_INITIALIZER(surfacegen5_ec.lock),
++	.state  = SG5_EC_UNINITIALIZED,
++	.serdev = NULL,
++	.counter = {
++		.seq  = 0,
++		.rqid = 0,
++	},
++	.writer = {
++		.data = NULL,
++		.ptr  = NULL,
++	},
++	.receiver = {
++		.lock = __SPIN_LOCK_UNLOCKED(),
++		.state = SG5_RCV_DISCARD,
++		.expect = {},
++	},
++	.events = {
++		.lock = __SPIN_LOCK_UNLOCKED(),
++		.handler = {},
++	}
++};
++
++
++static int surfacegen5_ec_rqst_unlocked(struct surfacegen5_ec *ec,
++                                 struct surfacegen5_rqst *rqst,
++				 struct surfacegen5_buf *result);
++
++
++inline static struct surfacegen5_ec *surfacegen5_ec_acquire(void)
++{
++	struct surfacegen5_ec *ec = &surfacegen5_ec;
++
++	mutex_lock(&ec->lock);
++	return ec;
++}
++
++inline static void surfacegen5_ec_release(struct surfacegen5_ec *ec)
++{
++	mutex_unlock(&ec->lock);
++}
++
++inline static struct surfacegen5_ec *surfacegen5_ec_acquire_init(void)
++{
++	struct surfacegen5_ec *ec = surfacegen5_ec_acquire();
++
++	if (ec->state == SG5_EC_UNINITIALIZED) {
++		surfacegen5_ec_release(ec);
++		return NULL;
++	}
++
++	return ec;
++}
++
++struct device_link *surfacegen5_ec_consumer_add(struct device *consumer, u32 flags)
++{
++	struct surfacegen5_ec *ec;
++	struct device_link *link;
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		return ERR_PTR(-ENXIO);
++	}
++
++	link = device_link_add(consumer, &ec->serdev->dev, flags);
++
++	surfacegen5_ec_release(ec);
++	return link;
++}
++
++int surfacegen5_ec_consumer_remove(struct device_link *link)
++{
++	struct surfacegen5_ec *ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		return -ENXIO;
++	}
++
++	device_link_del(link);
++
++	surfacegen5_ec_release(ec);
++	return 0;
++}
++
++
++inline static u16 surfacegen5_rqid_to_rqst(u16 rqid) {
++	return rqid << SURFACEGEN5_RQID_EVENT_BITS;
++}
++
++inline static bool surfacegen5_rqid_is_event(u16 rqid) {
++	const u16 mask = (1 << SURFACEGEN5_RQID_EVENT_BITS) - 1;
++	return rqid != 0 && (rqid | mask) == mask;
++}
++
++int surfacegen5_ec_enable_event_source(u8 tc, u8 unknown, u16 rqid)
++{
++	struct surfacegen5_ec *ec;
++
++	u8 pld[4] = { tc, unknown, rqid & 0xff, rqid >> 8 };
++	u8 buf[1] = { 0x00 };
++
++	struct surfacegen5_rqst rqst = {
++		.tc  = 0x01,
++		.iid = 0x00,
++		.cid = 0x0b,
++		.snc = 0x01,
++		.cdl = 0x04,
++		.pld = pld,
++	};
++
++	struct surfacegen5_buf result = {
++		result.cap = ARRAY_SIZE(buf),
++		result.len = 0,
++		result.data = buf,
++	};
++
++	int status;
++
++	// only allow RQIDs that lie within event spectrum
++	if (!surfacegen5_rqid_is_event(rqid)) {
++		return -EINVAL;
++	}
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n");
++		return -ENXIO;
++	}
++
++	if (ec->state == SG5_EC_SUSPENDED) {
++		dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n");
++
++		surfacegen5_ec_release(ec);
++		return -EPERM;
++	}
++
++	status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result);
++
++	if (buf[0] != 0x00) {
++		dev_warn(&ec->serdev->dev,
++		         "unexpected result while enabling event source: 0x%02x\n",
++			 buf[0]);
++	}
++
++	surfacegen5_ec_release(ec);
++	return status;
++
++}
++
++int surfacegen5_ec_disable_event_source(u8 tc, u8 unknown, u16 rqid)
++{
++	struct surfacegen5_ec *ec;
++
++	u8 pld[4] = { tc, unknown, rqid & 0xff, rqid >> 8 };
++	u8 buf[1] = { 0x00 };
++
++	struct surfacegen5_rqst rqst = {
++		.tc  = 0x01,
++		.iid = 0x00,
++		.cid = 0x0c,
++		.snc = 0x01,
++		.cdl = 0x04,
++		.pld = pld,
++	};
++
++	struct surfacegen5_buf result = {
++		result.cap = ARRAY_SIZE(buf),
++		result.len = 0,
++		result.data = buf,
++	};
++
++	int status;
++
++	// only allow RQIDs that lie within event spectrum
++	if (!surfacegen5_rqid_is_event(rqid)) {
++		return -EINVAL;
++	}
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n");
++		return -ENXIO;
++	}
++
++	if (ec->state == SG5_EC_SUSPENDED) {
++		dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n");
++
++		surfacegen5_ec_release(ec);
++		return -EPERM;
++	}
++
++	status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result);
++
++	if (buf[0] != 0x00) {
++		dev_warn(&ec->serdev->dev,
++		         "unexpected result while disabling event source: 0x%02x\n",
++			 buf[0]);
++	}
++
++	surfacegen5_ec_release(ec);
++	return status;
++}
++
++int surfacegen5_ec_set_delayed_event_handler(
++		u16 rqid, surfacegen5_ec_event_handler_fn fn,
++		surfacegen5_ec_event_handler_delay delay,
++		void *data)
++{
++	struct surfacegen5_ec *ec;
++	unsigned long flags;
++
++	if (!surfacegen5_rqid_is_event(rqid)) {
++		return -EINVAL;
++	}
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		return -ENXIO;
++	}
++
++	spin_lock_irqsave(&ec->events.lock, flags);
++
++	// 0 is not a valid event RQID
++	ec->events.handler[rqid - 1].handler = fn;
++	ec->events.handler[rqid - 1].delay = delay;
++	ec->events.handler[rqid - 1].data = data;
++
++	spin_unlock_irqrestore(&ec->events.lock, flags);
++	surfacegen5_ec_release(ec);
++
++	return 0;
++}
++
++int surfacegen5_ec_set_event_handler(
++		u16 rqid, surfacegen5_ec_event_handler_fn fn, void *data)
++{
++	return surfacegen5_ec_set_delayed_event_handler(rqid, fn, NULL, data);
++}
++
++int surfacegen5_ec_remove_event_handler(u16 rqid)
++{
++	struct surfacegen5_ec *ec;
++	unsigned long flags;
++
++	if (!surfacegen5_rqid_is_event(rqid)) {
++		return -EINVAL;
++	}
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		return -ENXIO;
++	}
++
++	spin_lock_irqsave(&ec->events.lock, flags);
++
++	// 0 is not a valid event RQID
++	ec->events.handler[rqid - 1].handler = NULL;
++	ec->events.handler[rqid - 1].delay = NULL;
++	ec->events.handler[rqid - 1].data = NULL;
++
++	spin_unlock_irqrestore(&ec->events.lock, flags);
++	surfacegen5_ec_release(ec);
++
++	/*
++	 * Make sure that the handler is not in use any more after we've
++	 * removed it.
++	 */
++	flush_workqueue(ec->events.queue_evt);
++
++	return 0;
++}
++
++
++inline static u16 surfacegen5_ssh_crc(const u8 *buf, size_t size)
++{
++	return crc_ccitt_false(0xffff, buf, size);
++}
++
++inline static void surfacegen5_ssh_write_u16(struct surfacegen5_ec_writer *writer, u16 in)
++{
++	put_unaligned_le16(in, writer->ptr);
++	writer->ptr += 2;
++}
++
++inline static void surfacegen5_ssh_write_crc(struct surfacegen5_ec_writer *writer,
++                                             const u8 *buf, size_t size)
++{
++	surfacegen5_ssh_write_u16(writer, surfacegen5_ssh_crc(buf, size));
++}
++
++inline static void surfacegen5_ssh_write_syn(struct surfacegen5_ec_writer *writer)
++{
++	u8 *w = writer->ptr;
++
++	*w++ = 0xaa;
++	*w++ = 0x55;
++
++	writer->ptr = w;
++}
++
++inline static void surfacegen5_ssh_write_ter(struct surfacegen5_ec_writer *writer)
++{
++	u8 *w = writer->ptr;
++
++	*w++ = 0xff;
++	*w++ = 0xff;
++
++	writer->ptr = w;
++}
++
++inline static void surfacegen5_ssh_write_buf(struct surfacegen5_ec_writer *writer,
++                                             u8 *in, size_t len)
++{
++	writer->ptr = memcpy(writer->ptr, in, len) + len;
++}
++
++inline static void surfacegen5_ssh_write_hdr(struct surfacegen5_ec_writer *writer,
++                                             struct surfacegen5_rqst *rqst,
++                                             struct surfacegen5_ec *ec)
++{
++	struct surfacegen5_frame_ctrl *hdr = (struct surfacegen5_frame_ctrl *)writer->ptr;
++	u8 *begin = writer->ptr;
++
++	hdr->type = SG5_FRAME_TYPE_CMD;
++	hdr->len  = SG5_BYTELEN_CMDFRAME + rqst->cdl;	// without CRC
++	hdr->pad  = 0x00;
++	hdr->seq  = ec->counter.seq;
++
++	writer->ptr += sizeof(*hdr);
++
++	surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin);
++}
++
++inline static void surfacegen5_ssh_write_cmd(struct surfacegen5_ec_writer *writer,
++                                             struct surfacegen5_rqst *rqst,
++                                             struct surfacegen5_ec *ec)
++{
++	struct surfacegen5_frame_cmd *cmd = (struct surfacegen5_frame_cmd *)writer->ptr;
++	u8 *begin = writer->ptr;
++
++	u16 rqid = surfacegen5_rqid_to_rqst(ec->counter.rqid);
++	u8 rqid_lo = rqid & 0xFF;
++	u8 rqid_hi = rqid >> 8;
++
++	cmd->type     = SG5_FRAME_TYPE_CMD;
++	cmd->tc       = rqst->tc;
++	cmd->unknown1 = 0x01;
++	cmd->unknown2 = 0x00;
++	cmd->iid      = rqst->iid;
++	cmd->rqid_lo  = rqid_lo;
++	cmd->rqid_hi  = rqid_hi;
++	cmd->cid      = rqst->cid;
++
++	writer->ptr += sizeof(*cmd);
++
++	surfacegen5_ssh_write_buf(writer, rqst->pld, rqst->cdl);
++	surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin);
++}
++
++inline static void surfacegen5_ssh_write_ack(struct surfacegen5_ec_writer *writer, u8 seq)
++{
++	struct surfacegen5_frame_ctrl *ack = (struct surfacegen5_frame_ctrl *)writer->ptr;
++	u8 *begin = writer->ptr;
++
++	ack->type = SG5_FRAME_TYPE_ACK;
++	ack->len  = 0x00;
++	ack->pad  = 0x00;
++	ack->seq  = seq;
++
++	writer->ptr += sizeof(*ack);
++
++	surfacegen5_ssh_write_crc(writer, begin, writer->ptr - begin);
++}
++
++inline static void surfacegen5_ssh_writer_reset(struct surfacegen5_ec_writer *writer)
++{
++	writer->ptr = writer->data;
++}
++
++inline static int surfacegen5_ssh_writer_flush(struct surfacegen5_ec *ec)
++{
++	struct surfacegen5_ec_writer *writer = &ec->writer;
++	struct serdev_device *serdev = ec->serdev;
++
++	size_t len = writer->ptr - writer->data;
++
++	dev_dbg(&ec->serdev->dev, "sending message\n");
++	print_hex_dump_debug("send: ", DUMP_PREFIX_OFFSET, 16, 1,
++	                     writer->data, writer->ptr - writer->data, false);
++
++	return serdev_device_write(serdev, writer->data, len, SG5_WRITE_TIMEOUT);
++}
++
++inline static void surfacegen5_ssh_write_msg_cmd(struct surfacegen5_ec *ec,
++                                                 struct surfacegen5_rqst *rqst)
++{
++	surfacegen5_ssh_writer_reset(&ec->writer);
++	surfacegen5_ssh_write_syn(&ec->writer);
++	surfacegen5_ssh_write_hdr(&ec->writer, rqst, ec);
++	surfacegen5_ssh_write_cmd(&ec->writer, rqst, ec);
++}
++
++inline static void surfacegen5_ssh_write_msg_ack(struct surfacegen5_ec *ec, u8 seq)
++{
++	surfacegen5_ssh_writer_reset(&ec->writer);
++	surfacegen5_ssh_write_syn(&ec->writer);
++	surfacegen5_ssh_write_ack(&ec->writer, seq);
++	surfacegen5_ssh_write_ter(&ec->writer);
++}
++
++inline static void surfacegen5_ssh_receiver_restart(struct surfacegen5_ec *ec,
++                                                    struct surfacegen5_rqst *rqst)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&ec->receiver.lock, flags);
++	reinit_completion(&ec->receiver.signal);
++	ec->receiver.state = SG5_RCV_CONTROL;
++	ec->receiver.expect.pld = rqst->snc;
++	ec->receiver.expect.seq = ec->counter.seq;
++	ec->receiver.expect.rqid = surfacegen5_rqid_to_rqst(ec->counter.rqid);
++	ec->receiver.eval_buf.len = 0;
++	spin_unlock_irqrestore(&ec->receiver.lock, flags);
++}
++
++inline static void surfacegen5_ssh_receiver_discard(struct surfacegen5_ec *ec)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&ec->receiver.lock, flags);
++	ec->receiver.state = SG5_RCV_DISCARD;
++	ec->receiver.eval_buf.len = 0;
++	kfifo_reset(&ec->receiver.fifo);
++	spin_unlock_irqrestore(&ec->receiver.lock, flags);
++}
++
++static int surfacegen5_ec_rqst_unlocked(struct surfacegen5_ec *ec,
++                                 struct surfacegen5_rqst *rqst,
++				 struct surfacegen5_buf *result)
++{
++	struct device *dev = &ec->serdev->dev;
++	struct surfacegen5_fifo_packet packet = {};
++	int status;
++	int try;
++	unsigned int rem;
++
++	if (rqst->cdl > SURFACEGEN5_MAX_RQST_PAYLOAD) {
++		dev_err(dev, SG5_RQST_TAG "request payload too large\n");
++		return -EINVAL;
++	}
++
++	// write command in buffer, we may need it multiple times
++	surfacegen5_ssh_write_msg_cmd(ec, rqst);
++	surfacegen5_ssh_receiver_restart(ec, rqst);
++
++	// send command, try to get an ack response
++	for (try = 0; try < SG5_NUM_RETRY; try++) {
++		status = surfacegen5_ssh_writer_flush(ec);
++		if (status) {
++			goto ec_rqst_out;
++		}
++
++		rem = wait_for_completion_timeout(&ec->receiver.signal, SG5_READ_TIMEOUT);
++		if (rem) {
++			kfifo_out(&ec->receiver.fifo, &packet, sizeof(packet));
++
++			if (packet.type == SG5_FRAME_TYPE_ACK) {
++				break;
++			}
++		}
++	}
++
++	// check if we ran out of tries?
++	if (try >= SG5_NUM_RETRY) {
++		dev_err(dev, SG5_RQST_TAG "communication failed %d times, giving up\n", try);
++		status = -EIO;
++		goto ec_rqst_out;
++	}
++
++	ec->counter.seq  += 1;
++	ec->counter.rqid += 1;
++
++	// get command response/payload
++	if (rqst->snc && result) {
++		rem = wait_for_completion_timeout(&ec->receiver.signal, SG5_READ_TIMEOUT);
++		if (rem) {
++			kfifo_out(&ec->receiver.fifo, &packet, sizeof(packet));
++
++			if (result->cap < packet.len) {
++				status = -EINVAL;
++				goto ec_rqst_out;
++			}
++
++			kfifo_out(&ec->receiver.fifo, result->data, packet.len);
++			result->len = packet.len;
++		} else {
++			dev_err(dev, SG5_RQST_TAG "communication timed out\n");
++			status = -EIO;
++			goto ec_rqst_out;
++		}
++
++		// send ACK
++		surfacegen5_ssh_write_msg_ack(ec, packet.seq);
++		status = surfacegen5_ssh_writer_flush(ec);
++		if (status) {
++			goto ec_rqst_out;
++		}
++	}
++
++ec_rqst_out:
++	surfacegen5_ssh_receiver_discard(ec);
++	return status;
++}
++
++int surfacegen5_ec_rqst(struct surfacegen5_rqst *rqst, struct surfacegen5_buf *result)
++{
++	struct surfacegen5_ec *ec;
++	int status;
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		printk(KERN_WARNING SG5_RQST_TAG_FULL "embedded controller is uninitialized\n");
++		return -ENXIO;
++	}
++
++	if (ec->state == SG5_EC_SUSPENDED) {
++		dev_warn(&ec->serdev->dev, SG5_RQST_TAG "embedded controller is suspended\n");
++
++		surfacegen5_ec_release(ec);
++		return -EPERM;
++	}
++
++	status = surfacegen5_ec_rqst_unlocked(ec, rqst, result);
++
++	surfacegen5_ec_release(ec);
++	return status;
++}
++
++
++static int surfacegen5_ssh_ec_resume(struct surfacegen5_ec *ec)
++{
++	u8 buf[1] = { 0x00 };
++
++	struct surfacegen5_rqst rqst = {
++		.tc  = 0x01,
++		.iid = 0x00,
++		.cid = 0x16,
++		.snc = 0x01,
++		.cdl = 0x00,
++		.pld = NULL,
++	};
++
++	struct surfacegen5_buf result = {
++		result.cap = ARRAY_SIZE(buf),
++		result.len = 0,
++		result.data = buf,
++	};
++
++	int status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result);
++	if (status) {
++		return status;
++	}
++
++	if (buf[0] != 0x00) {
++		dev_warn(&ec->serdev->dev,
++		         "unexpected result while trying to resume EC: 0x%02x\n",
++			 buf[0]);
++	}
++
++	return 0;
++}
++
++static int surfacegen5_ssh_ec_suspend(struct surfacegen5_ec *ec)
++{
++	u8 buf[1] = { 0x00 };
++
++	struct surfacegen5_rqst rqst = {
++		.tc  = 0x01,
++		.iid = 0x00,
++		.cid = 0x15,
++		.snc = 0x01,
++		.cdl = 0x00,
++		.pld = NULL,
++	};
++
++	struct surfacegen5_buf result = {
++		result.cap = ARRAY_SIZE(buf),
++		result.len = 0,
++		result.data = buf,
++	};
++
++	int status = surfacegen5_ec_rqst_unlocked(ec, &rqst, &result);
++	if (status) {
++		return status;
++	}
++
++	if (buf[0] != 0x00) {
++		dev_warn(&ec->serdev->dev,
++		         "unexpected result while trying to suspend EC: 0x%02x\n",
++			 buf[0]);
++	}
++
++	return 0;
++}
++
++
++inline static bool surfacegen5_ssh_is_valid_syn(const u8 *ptr)
++{
++	return ptr[0] == 0xaa && ptr[1] == 0x55;
++}
++
++inline static bool surfacegen5_ssh_is_valid_ter(const u8 *ptr)
++{
++	return ptr[0] == 0xff && ptr[1] == 0xff;
++}
++
++inline static bool surfacegen5_ssh_is_valid_crc(const u8 *begin, const u8 *end)
++{
++	u16 crc = surfacegen5_ssh_crc(begin, end - begin);
++	return (end[0] == (crc & 0xff)) && (end[1] == (crc >> 8));
++}
++
++
++static int surfacegen5_ssh_send_ack(struct surfacegen5_ec *ec, u8 seq)
++{
++	u8 buf[SG5_MSG_LEN_CTRL];
++	u16 crc;
++
++	buf[0] = 0xaa;
++	buf[1] = 0x55;
++	buf[2] = 0x40;
++	buf[3] = 0x00;
++	buf[4] = 0x00;
++	buf[5] = seq;
++
++	crc = surfacegen5_ssh_crc(buf + SG5_FRAME_OFFS_CTRL, SG5_BYTELEN_CTRL);
++	buf[6] = crc & 0xff;
++	buf[7] = crc >> 8;
++
++	buf[8] = 0xff;
++	buf[9] = 0xff;
++
++	dev_dbg(&ec->serdev->dev, "sending message\n");
++	print_hex_dump_debug("send: ", DUMP_PREFIX_OFFSET, 16, 1,
++	                     buf, SG5_MSG_LEN_CTRL, false);
++
++	return serdev_device_write(ec->serdev, buf, SG5_MSG_LEN_CTRL, SG5_WRITE_TIMEOUT);
++}
++
++static void surfacegen5_event_work_ack_handler(struct work_struct *_work)
++{
++	struct surfacegen5_event_work *work;
++	struct surfacegen5_event *event;
++	struct surfacegen5_ec *ec;
++	struct device *dev;
++	int status;
++
++	work = container_of(_work, struct surfacegen5_event_work, work_ack);
++	event = &work->event;
++	ec = work->ec;
++	dev = &ec->serdev->dev;
++
++	// make sure we load a fresh ec state
++	smp_mb();
++
++	if (ec->state == SG5_EC_INITIALIZED) {
++		status = surfacegen5_ssh_send_ack(ec, work->seq);
++		if (status) {
++			dev_err(dev, SG5_EVENT_TAG "failed to send ACK: %d\n", status);
++		}
++	}
++
++	if (refcount_dec_and_test(&work->refcount)) {
++		kfree(work);
++	}
++}
++
++static void surfacegen5_event_work_evt_handler(struct work_struct *_work)
++{
++	struct delayed_work *dwork = (struct delayed_work *)_work;
++	struct surfacegen5_event_work *work;
++	struct surfacegen5_event *event;
++	struct surfacegen5_ec *ec;
++	struct device *dev;
++	unsigned long flags;
++
++	surfacegen5_ec_event_handler_fn handler;
++	void *handler_data;
++
++	int status = 0;
++
++	work = container_of(dwork, struct surfacegen5_event_work, work_evt);
++	event = &work->event;
++	ec = work->ec;
++	dev = &ec->serdev->dev;
++
++	spin_lock_irqsave(&ec->events.lock, flags);
++	handler       = ec->events.handler[event->rqid - 1].handler;
++	handler_data  = ec->events.handler[event->rqid - 1].data;
++	spin_unlock_irqrestore(&ec->events.lock, flags);
++
++	/*
++	 * During handler removal or driver release, we ensure every event gets
++	 * handled before return of that function. Thus a handler obtained here is
++	 * guaranteed to be valid at least until this function returns.
++	 */
++
++	if (handler) {
++		status = handler(event, handler_data);
++	} else {
++		dev_warn(dev, SG5_EVENT_TAG "unhandled event (rqid: %04x)\n", event->rqid);
++	}
++
++	if (status) {
++		dev_err(dev, SG5_EVENT_TAG "error handling event: %d\n", status);
++	}
++
++	if (refcount_dec_and_test(&work->refcount)) {
++		kfree(work);
++	}
++}
++
++static void surfacegen5_ssh_handle_event(struct surfacegen5_ec *ec, const u8 *buf)
++{
++	struct device *dev = &ec->serdev->dev;
++	const struct surfacegen5_frame_ctrl *ctrl;
++	const struct surfacegen5_frame_cmd *cmd;
++	struct surfacegen5_event_work *work;
++	unsigned long flags;
++	u16 pld_len;
++
++	surfacegen5_ec_event_handler_delay delay_fn;
++	void *handler_data;
++	unsigned long delay = 0;
++
++	ctrl = (const struct surfacegen5_frame_ctrl *)(buf + SG5_FRAME_OFFS_CTRL);
++	cmd  = (const struct surfacegen5_frame_cmd  *)(buf + SG5_FRAME_OFFS_CMD);
++
++	pld_len = ctrl->len - SG5_BYTELEN_CMDFRAME;
++
++	work = kzalloc(sizeof(struct surfacegen5_event_work) + pld_len, GFP_ATOMIC);
++	if (!work) {
++		dev_warn(dev, SG5_EVENT_TAG "failed to allocate memory, dropping event\n");
++		return;
++	}
++
++	refcount_set(&work->refcount, 2);
++	work->ec         = ec;
++	work->seq        = ctrl->seq;
++	work->event.rqid = (cmd->rqid_hi << 8) | cmd->rqid_lo;
++	work->event.tc   = cmd->tc;
++	work->event.iid  = cmd->iid;
++	work->event.cid  = cmd->cid;
++	work->event.len  = pld_len;
++	work->event.pld  = ((u8*) work) + sizeof(struct surfacegen5_event_work);
++
++	memcpy(work->event.pld, buf + SG5_FRAME_OFFS_CMD_PLD, pld_len);
++
++	INIT_WORK(&work->work_ack, surfacegen5_event_work_ack_handler);
++	queue_work(ec->events.queue_ack, &work->work_ack);
++
++	spin_lock_irqsave(&ec->events.lock, flags);
++	handler_data = ec->events.handler[work->event.rqid - 1].data;
++	delay_fn     = ec->events.handler[work->event.rqid - 1].delay;
++	if (delay_fn) {
++		delay = delay_fn(&work->event, handler_data);
++	}
++	spin_unlock_irqrestore(&ec->events.lock, flags);
++
++	// immediate execution for high priority events (e.g. keyboard)
++	if (delay == SURFACEGEN5_EVENT_IMMEDIATE) {
++		surfacegen5_event_work_evt_handler(&work->work_evt.work);
++	} else {
++		INIT_DELAYED_WORK(&work->work_evt, surfacegen5_event_work_evt_handler);
++		queue_delayed_work(ec->events.queue_evt, &work->work_evt, delay);
++	}
++}
++
++static int surfacegen5_ssh_receive_msg_ctrl(struct surfacegen5_ec *ec,
++                                            const u8 *buf, size_t size)
++{
++	struct device *dev = &ec->serdev->dev;
++	struct surfacegen5_ec_receiver *rcv = &ec->receiver;
++	const struct surfacegen5_frame_ctrl *ctrl;
++	struct surfacegen5_fifo_packet packet;
++
++	const u8 *ctrl_begin = buf + SG5_FRAME_OFFS_CTRL;
++	const u8 *ctrl_end   = buf + SG5_FRAME_OFFS_CTRL_CRC;
++
++	ctrl = (const struct surfacegen5_frame_ctrl *)(ctrl_begin);
++
++	// actual length check
++	if (size < SG5_MSG_LEN_CTRL) {
++		return 0;			// need more bytes
++	}
++
++	// validate TERM
++	if (!surfacegen5_ssh_is_valid_ter(buf + SG5_FRAME_OFFS_TERM)) {
++		dev_err(dev, SG5_RECV_TAG "invalid end of message\n");
++		return size;			// discard everything
++	}
++
++	// validate CRC
++	if (!surfacegen5_ssh_is_valid_crc(ctrl_begin, ctrl_end)) {
++		dev_err(dev, SG5_RECV_TAG "invalid checksum (ctrl)\n");
++		return SG5_MSG_LEN_CTRL;	// only discard message
++	}
++
++	// check if we expect the message
++	if (rcv->state != SG5_RCV_CONTROL) {
++		dev_err(dev, SG5_RECV_TAG "discarding message: ctrl not expected\n");
++		return SG5_MSG_LEN_CTRL;	// discard message
++	}
++
++	// check if it is for our request
++	if (ctrl->type == SG5_FRAME_TYPE_ACK && ctrl->seq != rcv->expect.seq) {
++		dev_err(dev, SG5_RECV_TAG "discarding message: ack does not match\n");
++		return SG5_MSG_LEN_CTRL;	// discard message
++	}
++
++	// we now have a valid & expected ACK/RETRY message
++	dev_dbg(dev, SG5_RECV_TAG "valid control message received (type: 0x%02x)\n", ctrl->type);
++
++	packet.type = ctrl->type;
++	packet.seq  = ctrl->seq;
++	packet.len  = 0;
++
++	if (kfifo_avail(&rcv->fifo) >= sizeof(packet)) {
++		kfifo_in(&rcv->fifo, (u8 *) &packet, sizeof(packet));
++
++	} else {
++		dev_warn(dev, SG5_RECV_TAG
++			 "dropping frame: not enough space in fifo (type = %d)\n",
++			 SG5_FRAME_TYPE_CMD);
++
++		return SG5_MSG_LEN_CTRL;	// discard message
++	}
++
++	// update decoder state
++	if (ctrl->type == SG5_FRAME_TYPE_ACK) {
++		rcv->state = rcv->expect.pld
++			? SG5_RCV_COMMAND
++			: SG5_RCV_DISCARD;
++	}
++
++	complete(&rcv->signal);
++	return SG5_MSG_LEN_CTRL;		// handled message
++}
++
++static int surfacegen5_ssh_receive_msg_cmd(struct surfacegen5_ec *ec,
++                                           const u8 *buf, size_t size)
++{
++	struct device *dev = &ec->serdev->dev;
++	struct surfacegen5_ec_receiver *rcv = &ec->receiver;
++	const struct surfacegen5_frame_ctrl *ctrl;
++	const struct surfacegen5_frame_cmd *cmd;
++	struct surfacegen5_fifo_packet packet;
++
++	const u8 *ctrl_begin     = buf + SG5_FRAME_OFFS_CTRL;
++	const u8 *ctrl_end       = buf + SG5_FRAME_OFFS_CTRL_CRC;
++	const u8 *cmd_begin      = buf + SG5_FRAME_OFFS_CMD;
++	const u8 *cmd_begin_pld  = buf + SG5_FRAME_OFFS_CMD_PLD;
++	const u8 *cmd_end;
++
++	size_t msg_len;
++
++	ctrl = (const struct surfacegen5_frame_ctrl *)(ctrl_begin);
++	cmd  = (const struct surfacegen5_frame_cmd  *)(cmd_begin);
++
++	// we need at least a full control frame
++	if (size < (SG5_BYTELEN_SYNC + SG5_BYTELEN_CTRL + SG5_BYTELEN_CRC)) {
++		return 0;		// need more bytes
++	}
++
++	// validate control-frame CRC
++	if (!surfacegen5_ssh_is_valid_crc(ctrl_begin, ctrl_end)) {
++		dev_err(dev, SG5_RECV_TAG "invalid checksum (cmd-ctrl)\n");
++		/*
++		 * We can't be sure here if length is valid, thus
++		 * discard everything.
++		 */
++		return size;
++	}
++
++	// actual length check (ctrl->len contains command-frame but not crc)
++	msg_len = SG5_MSG_LEN_CMD_BASE + ctrl->len;
++	if (size < msg_len) {
++		return 0;			// need more bytes
++	}
++
++	cmd_end = cmd_begin + ctrl->len;
++
++	// validate command-frame type
++	if (cmd->type != SG5_FRAME_TYPE_CMD) {
++		dev_err(dev, SG5_RECV_TAG "expected command frame type but got 0x%02x\n", cmd->type);
++		return size;			// discard everything
++	}
++
++	// validate command-frame CRC
++	if (!surfacegen5_ssh_is_valid_crc(cmd_begin, cmd_end)) {
++		dev_err(dev, SG5_RECV_TAG "invalid checksum (cmd-pld)\n");
++
++		/*
++		 * The message length is provided in the control frame. As we
++		 * already validated that, we can be sure here that it's
++		 * correct, so we only need to discard the message.
++		 */
++		return msg_len;
++	}
++
++	// check if we received an event notification
++	if (surfacegen5_rqid_is_event((cmd->rqid_hi << 8) | cmd->rqid_lo)) {
++		surfacegen5_ssh_handle_event(ec, buf);
++		return msg_len;			// handled message
++	}
++
++	// check if we expect the message
++	if (rcv->state != SG5_RCV_COMMAND) {
++		dev_dbg(dev, SG5_RECV_TAG "discarding message: command not expected\n");
++		return msg_len;			// discard message
++	}
++
++	// check if response is for our request
++	if (rcv->expect.rqid != (cmd->rqid_lo | (cmd->rqid_hi << 8))) {
++		dev_dbg(dev, SG5_RECV_TAG "discarding message: command not a match\n");
++		return msg_len;			// discard message
++	}
++
++	// we now have a valid & expected command message
++	dev_dbg(dev, SG5_RECV_TAG "valid command message received\n");
++
++	packet.type = ctrl->type;
++	packet.seq = ctrl->seq;
++	packet.len = cmd_end - cmd_begin_pld;
++
++	if (kfifo_avail(&rcv->fifo) >= sizeof(packet) + packet.len) {
++		kfifo_in(&rcv->fifo, &packet, sizeof(packet));
++		kfifo_in(&rcv->fifo, cmd_begin_pld, packet.len);
++
++	} else {
++		dev_warn(dev, SG5_RECV_TAG
++			 "dropping frame: not enough space in fifo (type = %d)\n",
++			 SG5_FRAME_TYPE_CMD);
++
++		return SG5_MSG_LEN_CTRL;	// discard message
++	}
++
++	rcv->state = SG5_RCV_DISCARD;
++
++	complete(&rcv->signal);
++	return msg_len;				// handled message
++}
++
++static int surfacegen5_ssh_eval_buf(struct surfacegen5_ec *ec,
++                                    const u8 *buf, size_t size)
++{
++	struct device *dev = &ec->serdev->dev;
++	struct surfacegen5_frame_ctrl *ctrl;
++
++	// we need at least a control frame to check what to do
++	if (size < (SG5_BYTELEN_SYNC + SG5_BYTELEN_CTRL)) {
++		return 0;		// need more bytes
++	}
++
++	// make sure we're actually at the start of a new message
++	if (!surfacegen5_ssh_is_valid_syn(buf)) {
++		dev_err(dev, SG5_RECV_TAG "invalid start of message\n");
++		return size;		// discard everything
++	}
++
++	// handle individual message types seperately
++	ctrl = (struct surfacegen5_frame_ctrl *)(buf + SG5_FRAME_OFFS_CTRL);
++
++	switch (ctrl->type) {
++	case SG5_FRAME_TYPE_ACK:
++	case SG5_FRAME_TYPE_RETRY:
++		return surfacegen5_ssh_receive_msg_ctrl(ec, buf, size);
++
++	case SG5_FRAME_TYPE_CMD:
++		return surfacegen5_ssh_receive_msg_cmd(ec, buf, size);
++
++	default:
++		dev_err(dev, SG5_RECV_TAG "unknown frame type 0x%02x\n", ctrl->type);
++		return size;		// discard everything
++	}
++}
++
++static int surfacegen5_ssh_receive_buf(struct serdev_device *serdev,
++                                       const unsigned char *buf, size_t size)
++{
++	struct surfacegen5_ec *ec = serdev_device_get_drvdata(serdev);
++	struct surfacegen5_ec_receiver *rcv = &ec->receiver;
++	unsigned long flags;
++	int offs = 0;
++	int used, n;
++
++	dev_dbg(&serdev->dev, SG5_RECV_TAG "received buffer (size: %zu)\n", size);
++	print_hex_dump_debug(SG5_RECV_TAG, DUMP_PREFIX_OFFSET, 16, 1, buf, size, false);
++
++	/*
++	 * The battery _BIX message gets a bit long, thus we have to add some
++	 * additional buffering here.
++	 */
++
++	spin_lock_irqsave(&rcv->lock, flags);
++
++	// copy to eval-buffer
++	used = min(size, (size_t)(rcv->eval_buf.cap - rcv->eval_buf.len));
++	memcpy(rcv->eval_buf.ptr + rcv->eval_buf.len, buf, used);
++	rcv->eval_buf.len += used;
++
++	// evaluate buffer until we need more bytes or eval-buf is empty
++	while (offs < rcv->eval_buf.len) {
++		n = rcv->eval_buf.len - offs;
++		n = surfacegen5_ssh_eval_buf(ec, rcv->eval_buf.ptr + offs, n);
++		if (n <= 0) break;	// need more bytes
++
++		offs += n;
++	}
++
++	// throw away the evaluated parts
++	rcv->eval_buf.len -= offs;
++	memmove(rcv->eval_buf.ptr, rcv->eval_buf.ptr + offs, rcv->eval_buf.len);
++
++	spin_unlock_irqrestore(&rcv->lock, flags);
++
++	return used;
++}
++
++
++static acpi_status
++surfacegen5_ssh_setup_from_resource(struct acpi_resource *resource, void *context)
++{
++	struct serdev_device *serdev = context;
++	struct acpi_resource_common_serialbus *serial;
++	struct acpi_resource_uart_serialbus *uart;
++	int status = 0;
++
++	if (resource->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
++		return AE_OK;
++	}
++
++	serial = &resource->data.common_serial_bus;
++	if (serial->type != ACPI_RESOURCE_SERIAL_TYPE_UART) {
++		return AE_OK;
++	}
++
++	uart = &resource->data.uart_serial_bus;
++
++	// set up serdev device
++	serdev_device_set_baudrate(serdev, uart->default_baud_rate);
++
++	// serdev currently only supports RTSCTS flow control
++	if (uart->flow_control & SG5_SUPPORTED_FLOW_CONTROL_MASK) {
++		dev_warn(&serdev->dev, "unsupported flow control (value: 0x%02x)\n", uart->flow_control);
++	}
++
++	// set RTSCTS flow control
++	serdev_device_set_flow_control(serdev, uart->flow_control & ACPI_UART_FLOW_CONTROL_HW);
++
++	// serdev currently only supports EVEN/ODD parity
++	switch (uart->parity) {
++	case ACPI_UART_PARITY_NONE:
++		status = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
++		break;
++	case ACPI_UART_PARITY_EVEN:
++		status = serdev_device_set_parity(serdev, SERDEV_PARITY_EVEN);
++		break;
++	case ACPI_UART_PARITY_ODD:
++		status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD);
++		break;
++	default:
++		dev_warn(&serdev->dev, "unsupported parity (value: 0x%02x)\n", uart->parity);
++		break;
++	}
++
++	if (status) {
++		dev_err(&serdev->dev, "failed to set parity (value: 0x%02x)\n", uart->parity);
++		return status;
++	}
++
++	return AE_CTRL_TERMINATE;       // we've found the resource and are done
++}
++
++
++static bool surfacegen5_idma_filter(struct dma_chan *chan, void *param)
++{
++	// see dw8250_idma_filter
++	return param == chan->device->dev->parent;
++}
++
++static int surfacegen5_ssh_check_dma(struct serdev_device *serdev)
++{
++	struct device *dev = serdev->ctrl->dev.parent;
++	struct dma_chan *rx, *tx;
++	dma_cap_mask_t mask;
++	int status = 0;
++
++	/*
++	 * The EC UART requires DMA for proper communication. If we don't use DMA,
++	 * we'll drop bytes when the system has high load, e.g. during boot. This
++	 * causes some ugly behaviour, i.e. battery information (_BIX) messages
++	 * failing frequently. We're making sure the required DMA channels are
++	 * available here so serial8250_do_startup is able to grab them later
++	 * instead of silently falling back to a non-DMA approach.
++	 */
++
++	dma_cap_zero(mask);
++	dma_cap_set(DMA_SLAVE, mask);
++
++	rx = dma_request_slave_channel_compat(mask, surfacegen5_idma_filter, dev->parent, dev, "rx");
++	if (IS_ERR_OR_NULL(rx)) {
++		status = rx ? PTR_ERR(rx) : -EPROBE_DEFER;
++		if (status != -EPROBE_DEFER) {
++			dev_err(&serdev->dev, "sg5_dma: error requesting rx channel: %d\n", status);
++		} else {
++			dev_dbg(&serdev->dev, "sg5_dma: rx channel not found, deferring probe\n");
++		}
++		goto check_dma_out;
++	}
++
++	tx = dma_request_slave_channel_compat(mask, surfacegen5_idma_filter, dev->parent, dev, "tx");
++	if (IS_ERR_OR_NULL(tx)) {
++		status = tx ? PTR_ERR(tx) : -EPROBE_DEFER;
++		if (status != -EPROBE_DEFER) {
++			dev_err(&serdev->dev, "sg5_dma: error requesting tx channel: %d\n", status);
++		} else {
++			dev_dbg(&serdev->dev, "sg5_dma: tx channel not found, deferring probe\n");
++		}
++		goto check_dma_release_rx;
++	}
++
++	dma_release_channel(tx);
++check_dma_release_rx:
++	dma_release_channel(rx);
++check_dma_out:
++	return status;
++}
++
++
++static int surfacegen5_ssh_suspend(struct device *dev)
++{
++	struct surfacegen5_ec *ec;
++	int status = 0;
++
++	dev_dbg(dev, "suspending\n");
++
++	ec = surfacegen5_ec_acquire_init();
++	if (ec) {
++		status = surfacegen5_ssh_ec_suspend(ec);
++		if (status) {
++			dev_err(dev, "failed to suspend EC: %d\n", status);
++		}
++
++		ec->state = SG5_EC_SUSPENDED;
++		surfacegen5_ec_release(ec);
++	}
++
++	return status;
++}
++
++static int surfacegen5_ssh_resume(struct device *dev)
++{
++	struct surfacegen5_ec *ec;
++	int status = 0;
++
++	dev_dbg(dev, "resuming\n");
++
++	ec = surfacegen5_ec_acquire_init();
++	if (ec) {
++		ec->state = SG5_EC_INITIALIZED;
++
++		status = surfacegen5_ssh_ec_resume(ec);
++		if (status) {
++			dev_err(dev, "failed to resume EC: %d\n", status);
++		}
++
++		surfacegen5_ec_release(ec);
++	}
++
++	return status;
++}
++
++static SIMPLE_DEV_PM_OPS(surfacegen5_ssh_pm_ops, surfacegen5_ssh_suspend, surfacegen5_ssh_resume);
++
++
++static const struct serdev_device_ops surfacegen5_ssh_device_ops = {
++	.receive_buf  = surfacegen5_ssh_receive_buf,
++	.write_wakeup = serdev_device_write_wakeup,
++};
++
++static int surfacegen5_acpi_ssh_probe(struct serdev_device *serdev)
++{
++	struct surfacegen5_ec *ec;
++	struct workqueue_struct *event_queue_ack;
++	struct workqueue_struct *event_queue_evt;
++	u8 *write_buf;
++	u8 *read_buf;
++	u8 *eval_buf;
++	acpi_handle *ssh = ACPI_HANDLE(&serdev->dev);
++	acpi_status status;
++
++	dev_dbg(&serdev->dev, "probing\n");
++
++	// ensure DMA is ready before we set up the device
++	status = surfacegen5_ssh_check_dma(serdev);
++	if (status) {
++		return status;
++	}
++
++	// allocate buffers
++	write_buf = kzalloc(SG5_WRITE_BUF_LEN, GFP_KERNEL);
++	if (!write_buf) {
++		status = -ENOMEM;
++		goto err_ssh_probe_write_buf;
++	}
++
++	read_buf = kzalloc(SG5_READ_BUF_LEN, GFP_KERNEL);
++	if (!read_buf) {
++		status = -ENOMEM;
++		goto err_ssh_probe_read_buf;
++	}
++
++	eval_buf = kzalloc(SG5_EVAL_BUF_LEN, GFP_KERNEL);
++	if (!eval_buf) {
++		status = -ENOMEM;
++		goto err_ssh_probe_eval_buf;
++	}
++
++	event_queue_ack = create_singlethread_workqueue("sg5_ackq");
++	if (!event_queue_ack) {
++		status = -ENOMEM;
++		goto err_ssh_probe_ackq;
++	}
++
++	event_queue_evt = create_workqueue("sg5_evtq");
++	if (!event_queue_evt) {
++		status = -ENOMEM;
++		goto err_ssh_probe_evtq;
++	}
++
++	// set up EC
++	ec = surfacegen5_ec_acquire();
++	if (ec->state != SG5_EC_UNINITIALIZED) {
++		dev_err(&serdev->dev, "embedded controller already initialized\n");
++		surfacegen5_ec_release(ec);
++
++		status = -EBUSY;
++		goto err_ssh_probe_busy;
++	}
++
++	ec->serdev      = serdev;
++	ec->writer.data = write_buf;
++	ec->writer.ptr  = write_buf;
++
++	// initialize receiver
++	init_completion(&ec->receiver.signal);
++	kfifo_init(&ec->receiver.fifo, read_buf, SG5_READ_BUF_LEN);
++	ec->receiver.eval_buf.ptr = eval_buf;
++	ec->receiver.eval_buf.cap = SG5_EVAL_BUF_LEN;
++	ec->receiver.eval_buf.len = 0;
++
++	// initialize event handling
++	ec->events.queue_ack = event_queue_ack;
++	ec->events.queue_evt = event_queue_evt;
++
++	ec->state = SG5_EC_INITIALIZED;
++
++	serdev_device_set_drvdata(serdev, ec);
++
++	// ensure everything is properly set-up before we open the device
++	smp_mb();
++
++	serdev_device_set_client_ops(serdev, &surfacegen5_ssh_device_ops);
++	status = serdev_device_open(serdev);
++	if (status) {
++		goto err_ssh_probe_open;
++	}
++
++	status = acpi_walk_resources(ssh, METHOD_NAME__CRS,
++	                             surfacegen5_ssh_setup_from_resource, serdev);
++	if (ACPI_FAILURE(status)) {
++		goto err_ssh_probe_devinit;
++	}
++
++	status = surfacegen5_ssh_ec_resume(ec);
++	if (status) {
++		goto err_ssh_probe_devinit;
++	}
++
++	surfacegen5_ec_release(ec);
++	acpi_walk_dep_device_list(ssh);
++
++	return 0;
++
++err_ssh_probe_devinit:
++	serdev_device_close(serdev);
++err_ssh_probe_open:
++	ec->state = SG5_EC_UNINITIALIZED;
++	serdev_device_set_drvdata(serdev, NULL);
++	surfacegen5_ec_release(ec);
++err_ssh_probe_busy:
++	destroy_workqueue(event_queue_evt);
++err_ssh_probe_evtq:
++	destroy_workqueue(event_queue_ack);
++err_ssh_probe_ackq:
++	kfree(eval_buf);
++err_ssh_probe_eval_buf:
++	kfree(read_buf);
++err_ssh_probe_read_buf:
++	kfree(write_buf);
++err_ssh_probe_write_buf:
++	return status;
++}
++
++static void surfacegen5_acpi_ssh_remove(struct serdev_device *serdev)
++{
++	struct surfacegen5_ec *ec;
++	unsigned long flags;
++	int status;
++
++	ec = surfacegen5_ec_acquire_init();
++	if (!ec) {
++		return;
++	}
++
++	// suspend EC and disable events
++	status = surfacegen5_ssh_ec_suspend(ec);
++	if (status) {
++		dev_err(&serdev->dev, "failed to suspend EC: %d\n", status);
++	}
++
++	// make sure all events (received up to now) have been properly handled
++	flush_workqueue(ec->events.queue_ack);
++	flush_workqueue(ec->events.queue_evt);
++
++	// remove event handlers
++	spin_lock_irqsave(&ec->events.lock, flags);
++	memset(ec->events.handler, 0,
++	       sizeof(struct surfacegen5_ec_event_handler)
++	        * SG5_NUM_EVENT_TYPES);
++	spin_unlock_irqrestore(&ec->events.lock, flags);
++
++	// set device to deinitialized state
++	ec->state  = SG5_EC_UNINITIALIZED;
++	ec->serdev = NULL;
++
++	// ensure state and serdev get set before continuing
++	smp_mb();
++
++	/*
++	 * Flush any event that has not been processed yet to ensure we're not going to
++	 * use the serial device any more (e.g. for ACKing).
++	 */
++	flush_workqueue(ec->events.queue_ack);
++	flush_workqueue(ec->events.queue_evt);
++
++	serdev_device_close(serdev);
++
++	/*
++         * Only at this point, no new events can be received. Destroying the
++         * workqueue here flushes all remaining events. Those events will be
++         * silently ignored and neither ACKed nor any handler gets called.
++	 */
++	destroy_workqueue(ec->events.queue_ack);
++	destroy_workqueue(ec->events.queue_evt);
++
++	// free writer
++	kfree(ec->writer.data);
++	ec->writer.data = NULL;
++	ec->writer.ptr  = NULL;
++
++	// free receiver
++	spin_lock_irqsave(&ec->receiver.lock, flags);
++	ec->receiver.state = SG5_RCV_DISCARD;
++	kfifo_free(&ec->receiver.fifo);
++
++	kfree(ec->receiver.eval_buf.ptr);
++	ec->receiver.eval_buf.ptr = NULL;
++	ec->receiver.eval_buf.cap = 0;
++	ec->receiver.eval_buf.len = 0;
++	spin_unlock_irqrestore(&ec->receiver.lock, flags);
++
++	serdev_device_set_drvdata(serdev, NULL);
++	surfacegen5_ec_release(ec);
++}
++
++
++static const struct acpi_device_id surfacegen5_acpi_ssh_match[] = {
++	{ "MSHW0084", 0 },
++	{ },
++};
++MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_ssh_match);
++
++static struct serdev_device_driver surfacegen5_acpi_ssh = {
++	.probe = surfacegen5_acpi_ssh_probe,
++	.remove = surfacegen5_acpi_ssh_remove,
++	.driver = {
++		.name = "surfacegen5_acpi_ssh",
++		.acpi_match_table = ACPI_PTR(surfacegen5_acpi_ssh_match),
++		.pm = &surfacegen5_ssh_pm_ops,
++	},
++};
++
++#endif /* CONFIG_SURFACE_ACPI_SSH */
++
++
++/*************************************************************************
++ * Surface ACPI Notify driver
++ */
++
++#ifdef CONFIG_SURFACE_ACPI_SAN
++
++#define SG5_RQST_RETRY			5
++
++#define SG5_SAN_DSM_REVISION		0
++#define SG5_SAN_DSM_FN_NOTIFY_SENSOR_TRIP_POINT	0x09
++
++static const guid_t SG5_SAN_DSM_UUID =
++	GUID_INIT(0x93b666c5, 0x70c6, 0x469f, 0xa2, 0x15, 0x3d,
++	          0x48, 0x7c, 0x91, 0xab, 0x3c);
++
++#define SG5_EVENT_DELAY_POWER		msecs_to_jiffies(5000)
++
++#define SG5_EVENT_PWR_TC		0x02
++#define SG5_EVENT_PWR_RQID		0x0002
++#define SG5_EVENT_PWR_CID_HWCHANGE	0x15
++#define SG5_EVENT_PWR_CID_CHARGING	0x16
++#define SG5_EVENT_PWR_CID_ADAPTER	0x17
++#define SG5_EVENT_PWR_CID_STATE		0x4f
++
++#define SG5_EVENT_TEMP_TC		0x03
++#define SG5_EVENT_TEMP_RQID		0x0003
++#define SG5_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT	0x0b
++
++#define SG5_SAN_RQST_TAG            	"san_rqst: "
++
++
++struct surfacegen5_san_acpi_consumer {
++	char *path;
++	bool  required;
++	u32   flags;
++};
++
++struct surfacegen5_san_opreg_context {
++	struct acpi_connection_info connection;
++	struct device *dev;
++};
++
++struct surfacegen5_san_consumers {
++	u32                  num;
++	struct device_link **links;
++};
++
++struct surfacegen5_san_drvdata {
++	struct surfacegen5_san_opreg_context opreg_ctx;
++	struct surfacegen5_san_consumers     consumers;
++	struct device_link                  *ec_link;
++};
++
++struct gsb_data_in {
++	u8 cv;
++} __packed;
++
++struct gsb_data_rqsx {
++	u8 cv;				// command value (should be 0x01 or 0x03)
++	u8 tc;				// target controller
++	u8 tid;				// expected to be 0x01, could be revision
++	u8 iid;				// target sub-controller (e.g. primary vs. secondary battery)
++	u8 snc;				// expect-response-flag
++	u8 cid;				// command ID
++	u8 cdl;				// payload length
++	u8 _pad;			// padding
++	u8 pld[0];			// payload
++} __packed;
++
++struct gsb_data_etwl {
++	u8 cv;				// command value (should be 0x02)
++	u8 etw3;			// ?
++	u8 etw4;			// ?
++	u8 msg[0];			// error message (ASCIIZ)
++} __packed;
++
++struct gsb_data_out {
++	u8 status;			// _SSH communication status
++	u8 len;				// _SSH payload length
++	u8 pld[0];			// _SSH payload
++} __packed;
++
++union gsb_buffer_data {
++	struct gsb_data_in   in;	// common input
++	struct gsb_data_rqsx rqsx;	// RQSX input
++	struct gsb_data_etwl etwl;	// ETWL input
++	struct gsb_data_out  out;	// output
++};
++
++struct gsb_buffer {
++	u8 status;			// GSB AttribRawProcess status
++	u8 len;				// GSB AttribRawProcess length
++	union gsb_buffer_data data;
++} __packed;
++
++
++enum surfacegen5_pwr_event {
++	SURFACEGEN5_PWR_EVENT_BAT1_STAT	= 0x03,
++	SURFACEGEN5_PWR_EVENT_BAT1_INFO	= 0x04,
++	SURFACEGEN5_PWR_EVENT_ADP1_STAT	= 0x05,
++	SURFACEGEN5_PWR_EVENT_ADP1_INFO	= 0x06,
++	SURFACEGEN5_PWR_EVENT_BAT2_STAT	= 0x07,
++	SURFACEGEN5_PWR_EVENT_BAT2_INFO	= 0x08,
++};
++
++
++static int surfacegen5_acpi_notify_power_event(struct device *dev, enum surfacegen5_pwr_event event)
++{
++	acpi_handle san = ACPI_HANDLE(dev);
++	union acpi_object *obj;
++
++	obj = acpi_evaluate_dsm_typed(san, &SG5_SAN_DSM_UUID, SG5_SAN_DSM_REVISION,
++	                              (u8) event, NULL, ACPI_TYPE_BUFFER);
++
++	if (IS_ERR_OR_NULL(obj)) {
++		return obj ? PTR_ERR(obj) : -ENXIO;
++	}
++
++	if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) {
++		dev_err(dev, "got unexpected result from _DSM\n");
++		return -EFAULT;
++	}
++
++	ACPI_FREE(obj);
++	return 0;
++}
++
++static int surfacegen5_acpi_notify_sensor_trip_point(struct device *dev, u8 iid)
++{
++	acpi_handle san = ACPI_HANDLE(dev);
++	union acpi_object *obj;
++	union acpi_object param;
++
++	param.type = ACPI_TYPE_INTEGER;
++	param.integer.value = iid;
++
++	obj = acpi_evaluate_dsm_typed(san, &SG5_SAN_DSM_UUID, SG5_SAN_DSM_REVISION,
++	                              SG5_SAN_DSM_FN_NOTIFY_SENSOR_TRIP_POINT,
++				      &param, ACPI_TYPE_BUFFER);
++
++	if (IS_ERR_OR_NULL(obj)) {
++		return obj ? PTR_ERR(obj) : -ENXIO;
++	}
++
++	if (obj->buffer.length != 1 || obj->buffer.pointer[0] != 0) {
++		dev_err(dev, "got unexpected result from _DSM\n");
++		return -EFAULT;
++	}
++
++	ACPI_FREE(obj);
++	return 0;
++}
++
++
++inline static int surfacegen5_evt_power_adapter(struct device *dev, struct surfacegen5_event *event)
++{
++	int status;
++
++	status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_ADP1_STAT);
++	if (status) {
++		dev_err(dev, "error handling power event (cid = %x)\n", event->cid);
++		return status;
++	}
++
++	return 0;
++}
++
++inline static int surfacegen5_evt_power_hwchange(struct device *dev, struct surfacegen5_event *event)
++{
++	enum surfacegen5_pwr_event evcode;
++	int status;
++
++	if (event->iid == 0x02) {
++		evcode = SURFACEGEN5_PWR_EVENT_BAT2_INFO;
++	} else {
++		evcode = SURFACEGEN5_PWR_EVENT_BAT1_INFO;
++	}
++
++	status = surfacegen5_acpi_notify_power_event(dev, evcode);
++	if (status) {
++		dev_err(dev, "error handling power event (cid = %x)\n", event->cid);
++		return status;
++	}
++
++	return 0;
++}
++
++inline static int surfacegen5_evt_power_state(struct device *dev, struct surfacegen5_event *event)
++{
++	int status;
++
++	status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_BAT1_STAT);
++	if (status) {
++		dev_err(dev, "error handling power event (cid = %x)\n", event->cid);
++		return status;
++	}
++
++	status = surfacegen5_acpi_notify_power_event(dev, SURFACEGEN5_PWR_EVENT_BAT2_STAT);
++	if (status) {
++		dev_err(dev, "error handling power event (cid = %x)\n", event->cid);
++		return status;
++	}
++
++	return 0;
++}
++
++static unsigned long surfacegen5_evt_power_delay(struct surfacegen5_event *event, void *data)
++{
++	switch (event->cid) {
++	case SG5_EVENT_PWR_CID_CHARGING:
++	case SG5_EVENT_PWR_CID_STATE:
++		return SG5_EVENT_DELAY_POWER;
++
++	case SG5_EVENT_PWR_CID_ADAPTER:
++	case SG5_EVENT_PWR_CID_HWCHANGE:
++	default:
++		return 0;
++	}
++}
++
++static int surfacegen5_evt_power(struct surfacegen5_event *event, void *data)
++{
++	struct device *dev = (struct device *)data;
++
++	switch (event->cid) {
++	case SG5_EVENT_PWR_CID_HWCHANGE:
++		return surfacegen5_evt_power_hwchange(dev, event);
++
++	case SG5_EVENT_PWR_CID_ADAPTER:
++		return surfacegen5_evt_power_adapter(dev, event);
++
++	case SG5_EVENT_PWR_CID_CHARGING:
++	case SG5_EVENT_PWR_CID_STATE:
++		return surfacegen5_evt_power_state(dev, event);
++
++	default:
++		dev_warn(dev, "unhandled power event (cid = %x)\n", event->cid);
++	}
++
++	return 0;
++}
++
++
++inline static int surfacegen5_evt_thermal_notify(struct device *dev, struct surfacegen5_event *event)
++{
++	int status;
++
++	status = surfacegen5_acpi_notify_sensor_trip_point(dev, event->iid);
++	if (status) {
++		dev_err(dev, "error handling thermal event (cid = %x)\n", event->cid);
++		return status;
++	}
++
++	return 0;
++}
++
++static int surfacegen5_evt_thermal(struct surfacegen5_event *event, void *data)
++{
++	struct device *dev = (struct device *)data;
++
++	switch (event->cid) {
++	case SG5_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT:
++		return surfacegen5_evt_thermal_notify(dev, event);
++
++	default:
++		dev_warn(dev, "unhandled thermal event (cid = %x)\n", event->cid);
++	}
++
++	return 0;
++}
++
++
++static struct gsb_data_rqsx *surfacegen5_san_validate_rqsx(
++	struct device *dev, const char *type, struct gsb_buffer *buffer)
++{
++	struct gsb_data_rqsx *rqsx = &buffer->data.rqsx;
++
++	if (buffer->len < 8) {
++		dev_err(dev, "invalid %s package (len = %d)\n",
++			type, buffer->len);
++		return NULL;
++	}
++
++	if (rqsx->cdl != buffer->len - 8) {
++		dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n",
++			type, buffer->len, rqsx->cdl);
++		return NULL;
++	}
++
++	if (rqsx->tid != 0x01) {
++		dev_warn(dev, "unsupported %s package (tid = 0x%02x)\n",
++			 type, rqsx->tid);
++		return NULL;
++	}
++
++	return rqsx;
++}
++
++static acpi_status
++surfacegen5_san_etwl(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer)
++{
++	struct gsb_data_etwl *etwl = &buffer->data.etwl;
++
++	if (buffer->len < 3) {
++		dev_err(ctx->dev, "invalid ETWL package (len = %d)\n", buffer->len);
++		return AE_OK;
++	}
++
++	dev_err(ctx->dev, "ETWL(0x%02x, 0x%02x): %.*s\n",
++		etwl->etw3, etwl->etw4,
++		buffer->len - 3, (char *)etwl->msg);
++
++	// indicate success
++	buffer->status = 0x00;
++	buffer->len = 0x00;
++
++	return AE_OK;
++}
++
++static acpi_status
++surfacegen5_san_rqst(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer)
++{
++	struct gsb_data_rqsx *gsb_rqst = surfacegen5_san_validate_rqsx(ctx->dev, "RQST", buffer);
++	struct surfacegen5_rqst rqst = {};
++	struct surfacegen5_buf result = {};
++	int status = 0;
++	int try;
++
++	if (!gsb_rqst) {
++		return AE_OK;
++	}
++
++	rqst.tc  = gsb_rqst->tc;
++	rqst.iid = gsb_rqst->iid;
++	rqst.cid = gsb_rqst->cid;
++	rqst.snc = gsb_rqst->snc;
++	rqst.cdl = gsb_rqst->cdl;
++	rqst.pld = &gsb_rqst->pld[0];
++
++	result.cap  = SURFACEGEN5_MAX_RQST_RESPONSE;
++	result.len  = 0;
++	result.data = kzalloc(result.cap, GFP_KERNEL);
++
++	if (!result.data) {
++		return AE_NO_MEMORY;
++	}
++
++	for (try = 0; try < SG5_RQST_RETRY; try++) {
++		if (try) {
++			dev_warn(ctx->dev, SG5_SAN_RQST_TAG "IO error occured, trying again\n");
++		}
++
++		status = surfacegen5_ec_rqst(&rqst, &result);
++		if (status != -EIO) break;
++	}
++
++	if (!status) {
++		buffer->status          = 0x00;
++		buffer->len             = result.len + 2;
++		buffer->data.out.status = 0x00;
++		buffer->data.out.len    = result.len;
++		memcpy(&buffer->data.out.pld[0], result.data, result.len);
++
++	} else {
++		dev_err(ctx->dev, SG5_SAN_RQST_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;
++	}
++
++	kfree(result.data);
++
++	return AE_OK;
++}
++
++static acpi_status
++surfacegen5_san_rqsg(struct surfacegen5_san_opreg_context *ctx, struct gsb_buffer *buffer)
++{
++	struct gsb_data_rqsx *rqsg = surfacegen5_san_validate_rqsx(ctx->dev, "RQSG", buffer);
++
++	if (!rqsg) {
++		return AE_OK;
++	}
++
++	// TODO: RQSG handler
++
++	dev_warn(ctx->dev, "unsupported request: RQSG(0x%02x, 0x%02x, 0x%02x)\n",
++		 rqsg->tc, rqsg->cid, rqsg->iid);
++
++	return AE_OK;
++}
++
++
++static acpi_status
++surfacegen5_san_opreg_handler(u32 function, acpi_physical_address command,
++                              u32 bits, u64 *value64,
++                              void *opreg_context, void *region_context)
++{
++	struct surfacegen5_san_opreg_context *context = opreg_context;
++	struct gsb_buffer *buffer = (struct gsb_buffer *)value64;
++	int accessor_type = (0xFFFF0000 & function) >> 16;
++
++	if (command != 0) {
++		dev_warn(context->dev, "unsupported command: 0x%02llx\n", command);
++		return AE_OK;
++	}
++
++	if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) {
++		dev_err(context->dev, "invalid access type: 0x%02x\n", accessor_type);
++		return AE_OK;
++	}
++
++	// buffer must have at least contain the command-value
++	if (buffer->len == 0) {
++		dev_err(context->dev, "request-package too small\n");
++		return AE_OK;
++	}
++
++	switch (buffer->data.in.cv) {
++	case 0x01:  return surfacegen5_san_rqst(context, buffer);
++	case 0x02:  return surfacegen5_san_etwl(context, buffer);
++	case 0x03:  return surfacegen5_san_rqsg(context, buffer);
++	}
++
++	dev_warn(context->dev, "unsupported SAN0 request (cv: 0x%02x)\n", buffer->data.in.cv);
++	return AE_OK;
++}
++
++static int surfacegen5_san_enable_events(struct device *dev)
++{
++	int status;
++
++	status = surfacegen5_ec_set_delayed_event_handler(
++			SG5_EVENT_PWR_RQID, surfacegen5_evt_power,
++			surfacegen5_evt_power_delay, dev);
++	if (status) {
++		goto err_san_event_handler_power;
++	}
++
++	status = surfacegen5_ec_set_event_handler(
++			SG5_EVENT_TEMP_RQID, surfacegen5_evt_thermal,
++			dev);
++	if (status) {
++		goto err_san_event_handler_thermal;
++	}
++
++	status = surfacegen5_ec_enable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID);
++	if (status) {
++		goto err_san_event_source_power;
++	}
++
++	status = surfacegen5_ec_enable_event_source(SG5_EVENT_TEMP_TC, 0x01, SG5_EVENT_TEMP_RQID);
++	if (status) {
++		goto err_san_event_source_thermal;
++	}
++
++	return 0;
++
++err_san_event_source_thermal:
++	surfacegen5_ec_disable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID);
++err_san_event_source_power:
++	surfacegen5_ec_remove_event_handler(SG5_EVENT_TEMP_RQID);
++err_san_event_handler_thermal:
++	surfacegen5_ec_remove_event_handler(SG5_EVENT_PWR_RQID);
++err_san_event_handler_power:
++	return status;
++}
++
++static void surfacegen5_san_disable_events(void)
++{
++	surfacegen5_ec_disable_event_source(SG5_EVENT_TEMP_TC, 0x01, SG5_EVENT_TEMP_RQID);
++	surfacegen5_ec_disable_event_source(SG5_EVENT_PWR_TC, 0x01, SG5_EVENT_PWR_RQID);
++	surfacegen5_ec_remove_event_handler(SG5_EVENT_TEMP_RQID);
++	surfacegen5_ec_remove_event_handler(SG5_EVENT_PWR_RQID);
++}
++
++
++static int surfacegen5_san_consumers_link(struct platform_device *pdev,
++                                          const struct surfacegen5_san_acpi_consumer *cons,
++                                          struct surfacegen5_san_consumers *out)
++{
++	const struct surfacegen5_san_acpi_consumer *con;
++	struct device_link **links, **link;
++	struct acpi_device *adev;
++	acpi_handle handle;
++	u32 max_links = 0;
++	int status;
++
++	if (!cons) {
++		return 0;
++	}
++
++	// count links
++	for (con = cons; con->path; ++con) {
++		max_links += 1;
++	}
++
++	// allocate
++	links = kzalloc(max_links * sizeof(struct device_link *), GFP_KERNEL);
++	link = &links[0];
++
++	if (!links) {
++		return -ENOMEM;
++	}
++
++	// create links
++	for (con = cons; con->path; ++con) {
++		status = acpi_get_handle(NULL, con->path, &handle);
++		if (status) {
++			if (con->required || status != AE_NOT_FOUND) {
++				status = -ENXIO;
++				goto san_consumers_link_cleanup;
++			} else {
++				continue;
++			}
++		}
++
++		status = acpi_bus_get_device(handle, &adev);
++		if (status) {
++			goto san_consumers_link_cleanup;
++		}
++
++		*link = device_link_add(&adev->dev, &pdev->dev, con->flags);
++		if (!(*link)) {
++			status = -EFAULT;
++			goto san_consumers_link_cleanup;
++		}
++
++		link += 1;
++	}
++
++	out->num = link - links;
++	out->links = links;
++
++	return 0;
++
++san_consumers_link_cleanup:
++	for (link = link - 1; link >= links; --link) {
++		device_link_del(*link);
++	}
++
++	return status;
++}
++
++static void surfacegen5_san_consumers_unlink(struct surfacegen5_san_consumers *consumers) {
++	u32 i;
++
++	if (!consumers) {
++		return;
++	}
++
++	for (i = 0; i < consumers->num; ++i) {
++		device_link_del(consumers->links[i]);
++	}
++
++	kfree(consumers->links);
++
++	consumers->num = 0;
++	consumers->links = NULL;
++}
++
++static int surfacegen5_acpi_san_probe(struct platform_device *pdev)
++{
++	const struct surfacegen5_san_acpi_consumer *cons;
++	struct surfacegen5_san_drvdata *drvdata;
++	struct device_link *ec_link;
++	acpi_handle san = ACPI_HANDLE(&pdev->dev);	// _SAN device node
++	int status;
++
++	drvdata = kzalloc(sizeof(struct surfacegen5_san_drvdata), GFP_KERNEL);
++	if (!drvdata) {
++		return -ENOMEM;
++	}
++
++	/*
++	 * Defer probe if the _SSH driver has not set up the controller yet. This
++	 * makes sure we do not fail any initial requests (e.g. _STA request without
++	 * which the battery does not get set up correctly). Otherwise register as
++	 * consumer to set up a device_link.
++	 */
++	ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME);
++	if (IS_ERR_OR_NULL(ec_link)) {
++		if (PTR_ERR(ec_link) == -ENXIO) {
++			status = -EPROBE_DEFER;
++		} else {
++			status = -EFAULT;
++		}
++
++		goto err_san_probe_ec_link;
++	}
++
++	drvdata->ec_link = ec_link;
++	drvdata->opreg_ctx.dev = &pdev->dev;
++
++	cons = acpi_device_get_match_data(&pdev->dev);
++	status = surfacegen5_san_consumers_link(pdev, cons, &drvdata->consumers);
++	if (status) {
++		goto err_san_probe_consumers;
++	}
++
++	platform_set_drvdata(pdev, drvdata);
++
++	status = acpi_install_address_space_handler(san,
++			ACPI_ADR_SPACE_GSBUS,
++			&surfacegen5_san_opreg_handler,
++			NULL, &drvdata->opreg_ctx);
++
++	if (ACPI_FAILURE(status)) {
++		status = -ENODEV;
++		goto err_san_probe_install_handler;
++	}
++
++	status = surfacegen5_san_enable_events(&pdev->dev);
++	if (status) {
++		goto err_san_probe_enable_events;
++	}
++
++	acpi_walk_dep_device_list(san);
++	return 0;
++
++err_san_probe_enable_events:
++	acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &surfacegen5_san_opreg_handler);
++err_san_probe_install_handler:
++	platform_set_drvdata(san, NULL);
++	surfacegen5_san_consumers_unlink(&drvdata->consumers);
++err_san_probe_consumers:
++	surfacegen5_ec_consumer_remove(drvdata->ec_link);
++err_san_probe_ec_link:
++	kfree(drvdata);
++	return status;
++}
++
++static int surfacegen5_acpi_san_remove(struct platform_device *pdev)
++{
++	struct surfacegen5_san_drvdata *drvdata = platform_get_drvdata(pdev);
++	acpi_handle san = ACPI_HANDLE(&pdev->dev);	// _SAN device node
++	acpi_status status = AE_OK;
++
++	acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &surfacegen5_san_opreg_handler);
++	surfacegen5_san_disable_events();
++
++	surfacegen5_san_consumers_unlink(&drvdata->consumers);
++	surfacegen5_ec_consumer_remove(drvdata->ec_link);
++	kfree(drvdata);
++
++	platform_set_drvdata(pdev, NULL);
++	return status;
++}
++
++
++static const struct surfacegen5_san_acpi_consumer surfacegen5_mshw0091_consumers[] = {
++	{ "\\_SB.SRTC", true,  DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS },
++	{ "\\ADP1",     true,  DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS },
++	{ "\\_SB.BAT1", true,  DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS },
++	{ "\\_SB.BAT2", false, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS },
++	{ },
++};
++
++static const struct acpi_device_id surfacegen5_acpi_san_match[] = {
++	{ "MSHW0091", (long unsigned int) surfacegen5_mshw0091_consumers },
++	{ },
++};
++MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_san_match);
++
++static struct platform_driver surfacegen5_acpi_san = {
++	.probe = surfacegen5_acpi_san_probe,
++	.remove = surfacegen5_acpi_san_remove,
++	.driver = {
++		.name = "surfacegen5_acpi_san",
++		.acpi_match_table = ACPI_PTR(surfacegen5_acpi_san_match),
++	},
++};
++
++#endif /* CONFIG_SURFACE_ACPI_SAN */
++
++
++/*************************************************************************
++ * Virtual HID Framework driver
++ */
++
++#ifdef CONFIG_SURFACE_ACPI_VHF
++
++#define USB_VENDOR_ID_MICROSOFT		0x045e
++#define USB_DEVICE_ID_MS_VHF		0xf001
++
++/*
++ * Request ID for VHF events. This value is based on the output of the Surface
++ * EC and should not be changed.
++ */
++#define SG5_VHF_RQID			0x0001
++
++
++struct surfacegen5_vhf_evtctx {
++	struct device     *dev;
++	struct hid_device *hid;
++};
++
++struct surfacegen5_vhf_drvdata {
++	struct device_link           *ec_link;
++	struct surfacegen5_vhf_evtctx event_ctx;
++};
++
++
++/*
++ * These report descriptors have been extracted from a Surface Book 2.
++ * They seems to be similar enough to be usable on the Surface Laptop.
++ */
++static const u8 vhf_hid_desc[] = {
++	// keyboard descriptor (event command ID 0x03)
++	0x05, 0x01,             /*  Usage Page (Desktop),                   */
++	0x09, 0x06,             /*  Usage (Keyboard),                       */
++	0xA1, 0x01,             /*  Collection (Application),               */
++	0x85, 0x01,             /*      Report ID (1),                      */
++	0x15, 0x00,             /*      Logical Minimum (0),                */
++	0x25, 0x01,             /*      Logical Maximum (1),                */
++	0x75, 0x01,             /*      Report Size (1),                    */
++	0x95, 0x08,             /*      Report Count (8),                   */
++	0x05, 0x07,             /*      Usage Page (Keyboard),              */
++	0x19, 0xE0,             /*      Usage Minimum (KB Leftcontrol),     */
++	0x29, 0xE7,             /*      Usage Maximum (KB Right GUI),       */
++	0x81, 0x02,             /*      Input (Variable),                   */
++	0x75, 0x08,             /*      Report Size (8),                    */
++	0x95, 0x0A,             /*      Report Count (10),                  */
++	0x19, 0x00,             /*      Usage Minimum (None),               */
++	0x29, 0x91,             /*      Usage Maximum (KB LANG2),           */
++	0x26, 0xFF, 0x00,       /*      Logical Maximum (255),              */
++	0x81, 0x00,             /*      Input,                              */
++	0x05, 0x0C,             /*      Usage Page (Consumer),              */
++	0x0A, 0xC0, 0x02,       /*      Usage (02C0h),                      */
++	0xA1, 0x02,             /*      Collection (Logical),               */
++	0x1A, 0xC1, 0x02,       /*          Usage Minimum (02C1h),          */
++	0x2A, 0xC6, 0x02,       /*          Usage Maximum (02C6h),          */
++	0x95, 0x06,             /*          Report Count (6),               */
++	0xB1, 0x03,             /*          Feature (Constant, Variable),   */
++	0xC0,                   /*      End Collection,                     */
++	0x05, 0x08,             /*      Usage Page (LED),                   */
++	0x19, 0x01,             /*      Usage Minimum (01h),                */
++	0x29, 0x03,             /*      Usage Maximum (03h),                */
++	0x75, 0x01,             /*      Report Size (1),                    */
++	0x95, 0x03,             /*      Report Count (3),                   */
++	0x25, 0x01,             /*      Logical Maximum (1),                */
++	0x91, 0x02,             /*      Output (Variable),                  */
++	0x95, 0x05,             /*      Report Count (5),                   */
++	0x91, 0x01,             /*      Output (Constant),                  */
++	0xC0,                   /*  End Collection,                         */
++
++	// media key descriptor (event command ID 0x04)
++	0x05, 0x0C,             /*  Usage Page (Consumer),                  */
++	0x09, 0x01,             /*  Usage (Consumer Control),               */
++	0xA1, 0x01,             /*  Collection (Application),               */
++	0x85, 0x03,             /*      Report ID (3),                      */
++	0x75, 0x10,             /*      Report Size (16),                   */
++	0x15, 0x00,             /*      Logical Minimum (0),                */
++	0x26, 0xFF, 0x03,       /*      Logical Maximum (1023),             */
++	0x19, 0x00,             /*      Usage Minimum (00h),                */
++	0x2A, 0xFF, 0x03,       /*      Usage Maximum (03FFh),              */
++	0x81, 0x00,             /*      Input,                              */
++	0xC0,                   /*  End Collection,                         */
++};
++
++
++static int vhf_hid_start(struct hid_device *hid)
++{
++	hid_dbg(hid, "%s\n", __func__);
++	return 0;
++}
++
++static void vhf_hid_stop(struct hid_device *hid)
++{
++	hid_dbg(hid, "%s\n", __func__);
++}
++
++static int vhf_hid_open(struct hid_device *hid)
++{
++	hid_dbg(hid, "%s\n", __func__);
++	return 0;
++}
++
++static void vhf_hid_close(struct hid_device *hid)
++{
++	hid_dbg(hid, "%s\n", __func__);
++}
++
++static int vhf_hid_parse(struct hid_device *hid)
++{
++	return hid_parse_report(hid, (u8 *)vhf_hid_desc, ARRAY_SIZE(vhf_hid_desc));
++}
++
++static int vhf_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
++			       u8 *buf, size_t len, unsigned char rtype,
++			       int reqtype)
++{
++	hid_dbg(hid, "%s\n", __func__);
++	return 0;
++}
++
++static int vhf_hid_output_report(struct hid_device *hid, u8 *buf, size_t len)
++{
++	hid_dbg(hid, "%s\n", __func__);
++	print_hex_dump_debug("report:", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false);
++
++	return len;
++}
++
++static struct hid_ll_driver vhf_hid_ll_driver = {
++	.start         = vhf_hid_start,
++	.stop          = vhf_hid_stop,
++	.open          = vhf_hid_open,
++	.close         = vhf_hid_close,
++	.parse         = vhf_hid_parse,
++	.raw_request   = vhf_hid_raw_request,
++	.output_report = vhf_hid_output_report,
++};
++
++
++static struct hid_device *surfacegen5_vhf_create_hid_device(struct platform_device *pdev)
++{
++	struct hid_device *hid;
++
++	hid = hid_allocate_device();
++	if (IS_ERR(hid)) {
++		return hid;
++	}
++
++	hid->dev.parent = &pdev->dev;
++
++	hid->bus     = BUS_VIRTUAL;
++	hid->vendor  = USB_VENDOR_ID_MICROSOFT;
++	hid->product = USB_DEVICE_ID_MS_VHF;
++
++	hid->ll_driver = &vhf_hid_ll_driver;
++
++	sprintf(hid->name, "%s", "Microsoft Virtual HID Framework Device");
++
++	return hid;
++}
++
++static int surfacegen5_vhf_event_handler(struct surfacegen5_event *event, void *data)
++{
++	struct surfacegen5_vhf_evtctx *ctx = (struct surfacegen5_vhf_evtctx *)data;
++
++	if (event->tc == 0x08 && (event->cid == 0x03 || event->cid == 0x04)) {
++		return hid_input_report(ctx->hid, HID_INPUT_REPORT, event->pld, event->len, 1);
++	}
++
++	dev_warn(ctx->dev, "unsupported event (tc = %d, cid = %d)\n", event->tc, event->cid);
++	return 0;
++}
++
++static unsigned long surfacegen5_vhf_event_delay(struct surfacegen5_event *event, void *data)
++{
++	// high priority immediate execution for keyboard events
++	if (event->tc == 0x08 && (event->cid == 0x03 || event->cid == 0x04)) {
++		return SURFACEGEN5_EVENT_IMMEDIATE;
++	}
++
++	return 0;
++}
++
++static int surfacegen5_acpi_vhf_probe(struct platform_device *pdev)
++{
++	struct surfacegen5_vhf_drvdata *drvdata;
++	struct device_link *ec_link;
++	struct hid_device *hid;
++	int status;
++
++	drvdata = kzalloc(sizeof(struct surfacegen5_vhf_drvdata), GFP_KERNEL);
++	if (!drvdata) {
++		return -ENOMEM;
++	}
++
++	// add device link to EC
++	ec_link = surfacegen5_ec_consumer_add(&pdev->dev, DL_FLAG_PM_RUNTIME);
++	if (IS_ERR_OR_NULL(ec_link)) {
++		if (PTR_ERR(ec_link) == -ENXIO) {
++			status = -EPROBE_DEFER;
++		} else {
++			status = -EFAULT;
++		}
++
++		goto err_probe_ec_link;
++	}
++
++	hid = surfacegen5_vhf_create_hid_device(pdev);
++	if (IS_ERR(hid)) {
++		status = PTR_ERR(hid);
++		goto err_probe_hid;
++	}
++
++	status = hid_add_device(hid);
++	if (status) {
++		goto err_add_hid;
++	}
++
++	drvdata->ec_link = ec_link;
++	drvdata->event_ctx.dev = &pdev->dev;
++	drvdata->event_ctx.hid = hid;
++
++	platform_set_drvdata(pdev, drvdata);
++
++	/*
++         * Set event hanlder for VHF events. They seem to be enabled by
++         * default, thus there should be no need to explicitly enable them.
++	 */
++	status = surfacegen5_ec_set_delayed_event_handler(
++			SG5_VHF_RQID,
++	                surfacegen5_vhf_event_handler,
++	                surfacegen5_vhf_event_delay,
++			&drvdata->event_ctx);
++	if (status) {
++		goto err_add_hid;
++	}
++
++	return 0;
++
++err_add_hid:
++	hid_destroy_device(hid);
++	platform_set_drvdata(pdev, NULL);
++err_probe_hid:
++	surfacegen5_ec_consumer_remove(drvdata->ec_link);
++err_probe_ec_link:
++	kfree(drvdata);
++	return status;
++}
++
++static int surfacegen5_acpi_vhf_remove(struct platform_device *pdev)
++{
++	struct surfacegen5_vhf_drvdata *drvdata = platform_get_drvdata(pdev);
++
++	surfacegen5_ec_remove_event_handler(SG5_VHF_RQID);
++
++	hid_destroy_device(drvdata->event_ctx.hid);
++	surfacegen5_ec_consumer_remove(drvdata->ec_link);
++	kfree(drvdata);
++
++	platform_set_drvdata(pdev, NULL);
++	return 0;
++}
++
++
++static const struct acpi_device_id surfacegen5_acpi_vhf_match[] = {
++	{ "MSHW0096" },
++	{ },
++};
++MODULE_DEVICE_TABLE(acpi, surfacegen5_acpi_vhf_match);
++
++static struct platform_driver surfacegen5_acpi_vhf = {
++	.probe = surfacegen5_acpi_vhf_probe,
++	.remove = surfacegen5_acpi_vhf_remove,
++	.driver = {
++		.name = "surfacegen5_acpi_vhf",
++		.acpi_match_table = ACPI_PTR(surfacegen5_acpi_vhf_match),
++	},
++};
++
++#endif /* CONFIG_SURFACE_ACPI_VHF */
++
++
++/*************************************************************************
++ * Module initialization
++ */
++
++int __init surface_acpi_init(void)
++{
++        int status;
++
++#ifdef CONFIG_SURFACE_ACPI_SSH
++	status = serdev_device_driver_register(&surfacegen5_acpi_ssh);
++	if (status) {
++		goto err_modinit_ssh;
++	}
++#endif /* CONFIG_SURFACE_ACPI_SSH */
++
++#ifdef CONFIG_SURFACE_ACPI_SAN
++	status = platform_driver_register(&surfacegen5_acpi_san);
++	if (status) {
++		goto err_modinit_san;
++	}
++#endif /* CONFIG_SURFACE_ACPI_SAN */
++
++#ifdef CONFIG_SURFACE_ACPI_VHF
++	status = platform_driver_register(&surfacegen5_acpi_vhf);
++	if (status) {
++		goto err_modinit_vhf;
++	}
++#endif /* CONFIG_SURFACE_ACPI_VHF */
++
++        return 0;
++
++#ifdef CONFIG_SURFACE_ACPI_VHF
++	platform_driver_unregister(&surfacegen5_acpi_vhf);
++err_modinit_vhf:
++#endif /* CONFIG_SURFACE_ACPI_VHF */
++
++#ifdef CONFIG_SURFACE_ACPI_SAN
++	platform_driver_unregister(&surfacegen5_acpi_san);
++err_modinit_san:
++#endif /* CONFIG_SURFACE_ACPI_SAN */
++
++#ifdef CONFIG_SURFACE_ACPI_SSH
++	serdev_device_driver_unregister(&surfacegen5_acpi_ssh);
++err_modinit_ssh:
++#endif /* CONFIG_SURFACE_ACPI_SSH */
++
++        return status;
++}
++
++void __exit surface_acpi_exit(void)
++{
++#ifdef CONFIG_SURFACE_ACPI_VHF
++	platform_driver_unregister(&surfacegen5_acpi_vhf);
++#endif /* CONFIG_SURFACE_ACPI_VHF */
++
++#ifdef CONFIG_SURFACE_ACPI_SAN
++	platform_driver_unregister(&surfacegen5_acpi_san);
++#endif /* CONFIG_SURFACE_ACPI_SAN */
++
++#ifdef CONFIG_SURFACE_ACPI_SSH
++	serdev_device_driver_unregister(&surfacegen5_acpi_ssh);
++#endif /* CONFIG_SURFACE_ACPI_SSH */
++}
++
++
++module_init(surface_acpi_init)
++module_exit(surface_acpi_exit)
++
++MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
++MODULE_DESCRIPTION("ACPI/Platform Drivers for Microsoft Surface Devices");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
+index 9e59f4788589..d9f4700722b6 100644
+--- a/drivers/tty/serdev/core.c
++++ b/drivers/tty/serdev/core.c
+@@ -466,8 +466,7 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl,
+ 	struct serdev_device *serdev = NULL;
+ 	int err;
+ 
+-	if (acpi_bus_get_status(adev) || !adev->status.present ||
+-	    acpi_device_enumerated(adev))
++	if (acpi_bus_get_status(adev) || !adev->status.present)
+ 		return AE_OK;
+ 
+ 	serdev = serdev_device_alloc(ctrl);
+@@ -490,6 +489,81 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl,
+ 	return AE_OK;
+ }
+ 
++struct acpi_serdev_add_device_from_resource_ctx {
++	acpi_handle ctrl_handle;
++	acpi_handle device_handle;
++	struct serdev_controller *ctrl;
++	struct acpi_device *device;
++};
++
++static acpi_status
++acpi_serdev_add_device_from_resource(struct acpi_resource *resource, void *data)
++{
++	struct acpi_serdev_add_device_from_resource_ctx* ctx = data;
++	struct acpi_resource_source *ctrl_name;
++	acpi_handle ctrl_handle;
++	acpi_status status;
++
++	if (resource->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
++		return AE_OK;
++
++	if (resource->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART)
++		return AE_OK;
++
++	ctrl_name = &resource->data.common_serial_bus.resource_source;
++	if (ctrl_name->string_length == 0 || !ctrl_name->string_ptr) {
++		return AE_OK;
++	}
++
++	status = acpi_get_handle(ctx->device_handle, ctrl_name->string_ptr,
++				 &ctrl_handle);
++	if (ACPI_FAILURE(status)) {
++		return AE_OK;
++	}
++
++	if (ctrl_handle == ctx->ctrl_handle) {
++		return acpi_serdev_register_device(ctx->ctrl, ctx->device);
++	}
++
++	return AE_OK;
++}
++
++static acpi_status
++acpi_serdev_add_devices_from_resources(acpi_handle handle, u32 level,
++				       void *data, void **return_value)
++{
++	struct acpi_serdev_add_device_from_resource_ctx *ctx = data;
++	acpi_status status;
++
++	ctx->device_handle = handle;
++
++	status = acpi_bus_get_device(handle, &ctx->device);
++	if (status) return AE_OK;
++
++	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
++	                             acpi_serdev_add_device_from_resource,
++				     ctx);
++
++	if (status == AE_NOT_FOUND)
++		return AE_OK;		// not finding _CRS is not an error
++	else
++		return status;
++}
++
++static int
++acpi_serdev_register_devices_from_resources(acpi_handle handle,
++					    struct serdev_controller *ctrl)
++{
++	struct acpi_serdev_add_device_from_resource_ctx ctx = {
++		.ctrl = ctrl,
++		.ctrl_handle = handle,
++	};
++
++	return acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 128,
++				   acpi_serdev_add_devices_from_resources,
++				   NULL, &ctx, NULL);
++}
++
+ static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level,
+ 				       void *data, void **return_value)
+ {
+@@ -499,6 +573,9 @@ static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level,
+ 	if (acpi_bus_get_device(handle, &adev))
+ 		return AE_OK;
+ 
++	if (acpi_device_enumerated(adev))
++		return AE_OK;
++
+ 	return acpi_serdev_register_device(ctrl, adev);
+ }
+ 
+@@ -516,6 +593,15 @@ static int acpi_serdev_register_devices(struct serdev_controller *ctrl)
+ 	if (ACPI_FAILURE(status))
+ 		dev_dbg(&ctrl->dev, "failed to enumerate serdev slaves\n");
+ 
++	if (!ctrl->serdev) {
++		status = acpi_serdev_register_devices_from_resources(handle, ctrl);
++		if (ACPI_FAILURE(status)) {
++			dev_dbg(&ctrl->dev,
++			        "failed to register serdev slaves from resources: %x\n",
++				status);
++		}
++	}
++
+ 	if (!ctrl->serdev)
+ 		return -ENODEV;
+ 
+-- 
+2.20.1
+

+ 63 - 0
patches/4.18/0002-resume-delay.patch

@@ -0,0 +1,63 @@
+From 415511ab8182c8948ed31e38cb7d304c61d53277 Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 14:03:51 +0100
+Subject: [PATCH 2/8] resume-delay
+
+---
+ kernel/power/suspend.c | 11 +++++++++++
+ kernel/sysctl.c        |  9 +++++++++
+ 2 files changed, 20 insertions(+)
+
+diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
+index 87331565e505..d55f949ee25a 100644
+--- a/kernel/power/suspend.c
++++ b/kernel/power/suspend.c
+@@ -520,6 +520,8 @@ int suspend_devices_and_enter(suspend_state_t state)
+ 	goto Resume_devices;
+ }
+ 
++unsigned int resume_delay = 3000;
++
+ /**
+  * suspend_finish - Clean up before finishing the suspend sequence.
+  *
+@@ -528,6 +530,15 @@ int suspend_devices_and_enter(suspend_state_t state)
+  */
+ static void suspend_finish(void)
+ {
++	if (resume_delay) {
++		/* Give kernel threads a head start, such that usb-storage
++		 * can detect devices before syslog attempts to write log
++		 * messages from the suspend code.
++		 */
++		thaw_kernel_threads();
++		pr_debug("PM: Sleeping for %d milliseconds.\n", resume_delay);
++		msleep(resume_delay);
++	}
+ 	suspend_thaw_processes();
+ 	pm_notifier_call_chain(PM_POST_SUSPEND);
+ 	pm_restore_console();
+diff --git a/kernel/sysctl.c b/kernel/sysctl.c
+index 2d9837c0aff4..ac704bf71f45 100644
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -308,7 +308,16 @@ static int min_extfrag_threshold;
+ static int max_extfrag_threshold = 1000;
+ #endif
+ 
++extern unsigned int resume_delay;
++
+ static struct ctl_table kern_table[] = {
++	{
++		.procname	= "resume_delay",
++		.data		= &resume_delay,
++		.maxlen		= sizeof(unsigned int),
++		.mode		= 0644,
++		.proc_handler	= proc_dointvec,
++	},
+ 	{
+ 		.procname	= "sched_child_runs_first",
+ 		.data		= &sysctl_sched_child_runs_first,
+-- 
+2.20.1
+

+ 15 - 15
patches/4.18/buttons.patch → patches/4.18/0003-buttons.patch

@@ -1,7 +1,7 @@
-From 9c4948b06b2e8d23bbb4d65c7453b6b847d3f237 Mon Sep 17 00:00:00 2001
+From ac9ac2a05180af9e356f2f4d6840b132b1fb45f7 Mon Sep 17 00:00:00 2001
 From: qzed <qzed@users.noreply.github.com>
-Date: Wed, 19 Sep 2018 19:21:14 +0200
-Subject: [PATCH] Surface Book 2 / Surface Pro 2017 button support
+Date: Mon, 24 Dec 2018 14:05:07 +0100
+Subject: [PATCH 3/8] buttons
 
 ---
  drivers/input/misc/soc_button_array.c     | 67 +++++++++++++++++++++--
@@ -15,7 +15,7 @@ index 23520df7650f..d74f8c3dd54b 100644
 @@ -29,12 +29,20 @@ struct soc_button_info {
  	bool wakeup;
  };
-
+ 
 +struct soc_device_data {
 +	/* Button info, may be NULL. */
 +	struct soc_button_info *button_info;
@@ -31,7 +31,7 @@ index 23520df7650f..d74f8c3dd54b 100644
 -#define BUTTON_TYPES	2
 +#define BUTTON_TYPES            2
 +#define SURFACE_METHOD_DSM      "_DSM"
-
+ 
  struct soc_button_data {
  	struct platform_device *children[BUTTON_TYPES];
 @@ -310,6 +318,7 @@ static int soc_button_probe(struct platform_device *pdev)
@@ -45,7 +45,7 @@ index 23520df7650f..d74f8c3dd54b 100644
 @@ -320,12 +329,20 @@ static int soc_button_probe(struct platform_device *pdev)
  	if (!id)
  		return -ENODEV;
-
+ 
 -	if (!id->driver_data) {
 +	device_data = (struct soc_device_data *)id->driver_data;
 +	if (device_data && device_data->check) {
@@ -64,21 +64,21 @@ index 23520df7650f..d74f8c3dd54b 100644
 -	} else {
 -		button_info = (struct soc_button_info *)id->driver_data;
  	}
-
+ 
  	error = gpiod_count(dev, NULL);
 @@ -357,7 +374,7 @@ static int soc_button_probe(struct platform_device *pdev)
  	if (!priv->children[0] && !priv->children[1])
  		return -ENODEV;
-
+ 
 -	if (!id->driver_data)
 +	if (!device_data || !device_data->button_info)
  		devm_kfree(dev, button_info);
-
+ 
  	return 0;
 @@ -377,9 +394,47 @@ static struct soc_button_info soc_button_PNP0C40[] = {
  	{ }
  };
-
+ 
 +static struct soc_device_data soc_device_PNP0C40 = {
 +	.button_info = soc_button_PNP0C40,
 +	.check = NULL,
@@ -123,7 +123,7 @@ index 23520df7650f..d74f8c3dd54b 100644
 +	{ "MSHW0040", (unsigned long)&soc_device_MSHW0040 },
  	{ }
  };
-
+ 
 diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c
 index 1b491690ce07..006c94eda7be 100644
 --- a/drivers/platform/x86/surfacepro3_button.c
@@ -134,12 +134,12 @@ index 1b491690ce07..006c94eda7be 100644
  #define SURFACE_BUTTON_OBJ_NAME		"VGBI"
 +#define SURFACE_METHOD_DSM		"_DSM"
  #define SURFACE_BUTTON_DEVICE_NAME	"Surface Pro 3/4 Buttons"
-
+ 
  #define SURFACE_BUTTON_NOTIFY_TABLET_MODE	0xc8
 @@ -158,6 +159,14 @@ static int surface_button_add(struct acpi_device *device)
  	    strlen(SURFACE_BUTTON_OBJ_NAME)))
  		return -ENODEV;
-
+ 
 +	/*
 +	 * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device
 +	 * ID (MSHW0040) for the power/volume buttons. Make sure this is the right
@@ -151,6 +151,6 @@ index 1b491690ce07..006c94eda7be 100644
  	button = kzalloc(sizeof(struct surface_button), GFP_KERNEL);
  	if (!button)
  		return -ENOMEM;
---
-2.19.0
+-- 
+2.20.1
 

+ 29 - 6
patches/4.18/cameras.patch → patches/4.18/0004-cameras.patch

@@ -1,3 +1,25 @@
+From ddc414a8b8bb0df0763eb793019f5bc616e142bf Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 14:08:40 +0100
+Subject: [PATCH 4/8] cameras
+
+---
+ drivers/media/usb/uvc/uvc_driver.c |   40 +
+ drivers/staging/Makefile           |    1 +
+ drivers/staging/ov5693/Kconfig     |   10 +
+ drivers/staging/ov5693/Makefile    |    5 +
+ drivers/staging/ov5693/ad5823.c    |  218 +++++
+ drivers/staging/ov5693/ad5823.h    |   90 ++
+ drivers/staging/ov5693/ov5693.c    | 1461 ++++++++++++++++++++++++++++
+ drivers/staging/ov5693/ov5693.h    |  848 ++++++++++++++++
+ 8 files changed, 2673 insertions(+)
+ create mode 100644 drivers/staging/ov5693/Kconfig
+ create mode 100644 drivers/staging/ov5693/Makefile
+ create mode 100644 drivers/staging/ov5693/ad5823.c
+ create mode 100644 drivers/staging/ov5693/ad5823.h
+ create mode 100644 drivers/staging/ov5693/ov5693.c
+ 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 8e138201330f..db38a76af80b 100644
 --- a/drivers/media/usb/uvc/uvc_driver.c
@@ -60,10 +82,10 @@ index e84959a8a684..6a78734cfc5a 100644
 +obj-$(CONFIG_VIDEO_OV5693)	+= ov5693/
 diff --git a/drivers/staging/ov5693/Kconfig b/drivers/staging/ov5693/Kconfig
 new file mode 100644
-index 000000000000..9fb1bffbe9b3
+index 000000000000..96000f112c4d
 --- /dev/null
 +++ b/drivers/staging/ov5693/Kconfig
-@@ -0,0 +1,11 @@
+@@ -0,0 +1,10 @@
 +config VIDEO_OV5693
 +       tristate "Omnivision ov5693 sensor support"
 +       depends on I2C && VIDEO_V4L2
@@ -74,7 +96,6 @@ index 000000000000..9fb1bffbe9b3
 +         ov5693 is video camera sensor.
 +
 +         It currently only works with the atomisp driver.
-+
 diff --git a/drivers/staging/ov5693/Makefile b/drivers/staging/ov5693/Makefile
 new file mode 100644
 index 000000000000..d8a63faa591f
@@ -88,10 +109,10 @@ index 000000000000..d8a63faa591f
 +ccflags-y += -Werror
 diff --git a/drivers/staging/ov5693/ad5823.c b/drivers/staging/ov5693/ad5823.c
 new file mode 100644
-index 000000000000..fcb9f5b70f9f
+index 000000000000..7c34c36e77e5
 --- /dev/null
 +++ b/drivers/staging/ov5693/ad5823.c
-@@ -0,0 +1,219 @@
+@@ -0,0 +1,218 @@
 +#include <asm/intel-mid.h>
 +#include <linux/bitops.h>
 +#include <linux/device.h>
@@ -310,7 +331,6 @@ index 000000000000..fcb9f5b70f9f
 +	ad5823_dev.platform_data = camera_get_af_platform_data();
 +	return ad5823_dev.platform_data ? 0 : -ENODEV;
 +}
-+
 diff --git a/drivers/staging/ov5693/ad5823.h b/drivers/staging/ov5693/ad5823.h
 new file mode 100644
 index 000000000000..8b046c31f3af
@@ -2728,3 +2748,6 @@ index 000000000000..79aef69666e8
 +	.q_focus_abs = ad5823_q_focus_abs,
 +};
 +#endif
+-- 
+2.20.1
+

+ 123 - 50
patches/4.18/ipts.patch → patches/4.18/0005-ipts.patch

@@ -1,3 +1,74 @@
+From f2a58519921430701dc3fc4dca924e85e6c2d883 Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 14:14:01 +0100
+Subject: [PATCH 5/8] ipts
+
+---
+ drivers/gpu/drm/i915/Makefile               |    3 +
+ drivers/gpu/drm/i915/i915_drv.c             |    7 +
+ drivers/gpu/drm/i915/i915_drv.h             |    3 +
+ drivers/gpu/drm/i915/i915_gem_context.c     |   12 +
+ drivers/gpu/drm/i915/i915_irq.c             |    7 +-
+ drivers/gpu/drm/i915/i915_params.c          |    5 +-
+ drivers/gpu/drm/i915/i915_params.h          |    5 +-
+ drivers/gpu/drm/i915/intel_dp.c             |    4 +-
+ drivers/gpu/drm/i915/intel_guc.h            |    1 +
+ drivers/gpu/drm/i915/intel_guc_submission.c |   79 +-
+ drivers/gpu/drm/i915/intel_guc_submission.h |    4 +
+ drivers/gpu/drm/i915/intel_ipts.c           |  627 +++++++++++
+ drivers/gpu/drm/i915/intel_ipts.h           |   34 +
+ drivers/gpu/drm/i915/intel_lrc.c            |   11 +-
+ drivers/gpu/drm/i915/intel_lrc.h            |    8 +
+ drivers/gpu/drm/i915/intel_panel.c          |    7 +
+ drivers/hid/hid-multitouch.c                |   32 +-
+ drivers/misc/Kconfig                        |    1 +
+ drivers/misc/Makefile                       |    1 +
+ drivers/misc/ipts/Kconfig                   |    9 +
+ drivers/misc/ipts/Makefile                  |   13 +
+ drivers/misc/ipts/ipts-binary-spec.h        |  118 +++
+ drivers/misc/ipts/ipts-dbgfs.c              |  152 +++
+ drivers/misc/ipts/ipts-gfx.c                |  184 ++++
+ drivers/misc/ipts/ipts-gfx.h                |   24 +
+ drivers/misc/ipts/ipts-hid.c                |  456 ++++++++
+ drivers/misc/ipts/ipts-hid.h                |   34 +
+ drivers/misc/ipts/ipts-kernel.c             | 1050 +++++++++++++++++++
+ drivers/misc/ipts/ipts-kernel.h             |   23 +
+ drivers/misc/ipts/ipts-mei-msgs.h           |  585 +++++++++++
+ drivers/misc/ipts/ipts-mei.c                |  282 +++++
+ drivers/misc/ipts/ipts-msg-handler.c        |  431 ++++++++
+ drivers/misc/ipts/ipts-msg-handler.h        |   32 +
+ drivers/misc/ipts/ipts-resource.c           |  277 +++++
+ drivers/misc/ipts/ipts-resource.h           |   30 +
+ drivers/misc/ipts/ipts-sensor-regs.h        |  700 +++++++++++++
+ drivers/misc/ipts/ipts-state.h              |   29 +
+ drivers/misc/ipts/ipts.h                    |  200 ++++
+ drivers/misc/mei/hw-me-regs.h               |    1 +
+ drivers/misc/mei/pci-me.c                   |    1 +
+ include/linux/intel_ipts_if.h               |   75 ++
+ 41 files changed, 5532 insertions(+), 25 deletions(-)
+ create mode 100644 drivers/gpu/drm/i915/intel_ipts.c
+ create mode 100644 drivers/gpu/drm/i915/intel_ipts.h
+ create mode 100644 drivers/misc/ipts/Kconfig
+ create mode 100644 drivers/misc/ipts/Makefile
+ create mode 100644 drivers/misc/ipts/ipts-binary-spec.h
+ create mode 100644 drivers/misc/ipts/ipts-dbgfs.c
+ create mode 100644 drivers/misc/ipts/ipts-gfx.c
+ create mode 100644 drivers/misc/ipts/ipts-gfx.h
+ create mode 100644 drivers/misc/ipts/ipts-hid.c
+ create mode 100644 drivers/misc/ipts/ipts-hid.h
+ create mode 100644 drivers/misc/ipts/ipts-kernel.c
+ create mode 100644 drivers/misc/ipts/ipts-kernel.h
+ create mode 100644 drivers/misc/ipts/ipts-mei-msgs.h
+ create mode 100644 drivers/misc/ipts/ipts-mei.c
+ create mode 100644 drivers/misc/ipts/ipts-msg-handler.c
+ create mode 100644 drivers/misc/ipts/ipts-msg-handler.h
+ create mode 100644 drivers/misc/ipts/ipts-resource.c
+ create mode 100644 drivers/misc/ipts/ipts-resource.h
+ create mode 100644 drivers/misc/ipts/ipts-sensor-regs.h
+ create mode 100644 drivers/misc/ipts/ipts-state.h
+ create mode 100644 drivers/misc/ipts/ipts.h
+ create mode 100644 include/linux/intel_ipts_if.h
+
 diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
 index 4c6adae23e18..1171fbd3a823 100644
 --- a/drivers/gpu/drm/i915/Makefile
@@ -13,7 +84,7 @@ index 4c6adae23e18..1171fbd3a823 100644
  i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o
  i915-$(CONFIG_DRM_I915_SELFTEST) += \
 diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
-index 9c449b8d8eab..d5d91d196627 100644
+index 015f9e93419d..4ecb9b824fc1 100644
 --- a/drivers/gpu/drm/i915/i915_drv.c
 +++ b/drivers/gpu/drm/i915/i915_drv.c
 @@ -53,6 +53,7 @@
@@ -34,7 +105,7 @@ index 9c449b8d8eab..d5d91d196627 100644
  	return 0;
  
  cleanup_gem:
-@@ -1441,6 +1445,9 @@ void i915_driver_unload(struct drm_device *dev)
+@@ -1440,6 +1444,9 @@ void i915_driver_unload(struct drm_device *dev)
  	struct drm_i915_private *dev_priv = to_i915(dev);
  	struct pci_dev *pdev = dev_priv->drm.pdev;
  
@@ -45,10 +116,10 @@ index 9c449b8d8eab..d5d91d196627 100644
  
  	if (i915_gem_suspend(dev_priv))
 diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
-index 71e1aa54f774..62686739ff57 100644
+index 7c22fac3aa04..8cd24da0c6ea 100644
 --- a/drivers/gpu/drm/i915/i915_drv.h
 +++ b/drivers/gpu/drm/i915/i915_drv.h
-@@ -3230,6 +3230,9 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj,
+@@ -3222,6 +3222,9 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj,
  void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj,
  					 struct sg_table *pages);
  
@@ -114,7 +185,7 @@ index c16cb025755e..8fb9160d013b 100644
  			GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT |
  			GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
 diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
-index 66ea3552c63e..4f54d74febc8 100644
+index 66ea3552c63e..b256e8a900af 100644
 --- a/drivers/gpu/drm/i915/i915_params.c
 +++ b/drivers/gpu/drm/i915/i915_params.c
 @@ -152,7 +152,10 @@ i915_param_named_unsafe(edp_vswing, int, 0400,
@@ -130,7 +201,7 @@ index 66ea3552c63e..4f54d74febc8 100644
  i915_param_named(guc_log_level, int, 0400,
  	"GuC firmware logging level. Requires GuC to be loaded. "
 diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
-index 6684025b7af8..f7f0a63a87e5 100644
+index 6684025b7af8..ca1128dcebca 100644
 --- a/drivers/gpu/drm/i915/i915_params.h
 +++ b/drivers/gpu/drm/i915/i915_params.h
 @@ -47,7 +47,7 @@ struct drm_printer;
@@ -153,10 +224,10 @@ index 6684025b7af8..f7f0a63a87e5 100644
  #define MEMBER(T, member, ...) T member;
  struct i915_params {
 diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
-index 16faea30114a..e5c1e24a6072 100644
+index 5d6517d37236..0119a4663071 100644
 --- a/drivers/gpu/drm/i915/intel_dp.c
 +++ b/drivers/gpu/drm/i915/intel_dp.c
-@@ -2601,8 +2601,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
+@@ -2631,8 +2631,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
  		return;
  
  	if (mode != DRM_MODE_DPMS_ON) {
@@ -180,7 +251,7 @@ index f1265e122d30..1303090a4bd8 100644
  	struct guc_preempt_work preempt_work[I915_NUM_ENGINES];
  	struct workqueue_struct *preempt_wq;
 diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c
-index 2feb65096966..1b7b8dc27a28 100644
+index 2feb65096966..4f8f3845bbe9 100644
 --- a/drivers/gpu/drm/i915/intel_guc_submission.c
 +++ b/drivers/gpu/drm/i915/intel_guc_submission.c
 @@ -94,6 +94,7 @@ static inline bool is_high_priority(struct intel_guc_client *client)
@@ -191,7 +262,7 @@ index 2feb65096966..1b7b8dc27a28 100644
  	unsigned long offset;
  	unsigned long end;
  	u16 id;
-@@ -106,11 +107,16 @@ static int reserve_doorbell(struct intel_guc_client *client)
+@@ -106,10 +107,15 @@ static int reserve_doorbell(struct intel_guc_client *client)
  	 * priority contexts, the second half for high-priority ones.
  	 */
  	offset = 0;
@@ -201,17 +272,16 @@ index 2feb65096966..1b7b8dc27a28 100644
 -		end += offset;
 +	if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
 +		end = GUC_NUM_DOORBELLS;
- 	}
++	}
 +	else {
 +		end = GUC_NUM_DOORBELLS/2;
 +		if (is_high_priority(client)) {
 +			offset = end;
 +			end += offset;
 +		}
-+ 	}
+ 	}
  
  	id = find_next_zero_bit(client->guc->doorbell_bitmap, end, offset);
- 	if (id == end)
 @@ -355,8 +361,14 @@ static void guc_stage_desc_init(struct intel_guc *guc,
  	desc = __get_stage_desc(client);
  	memset(desc, 0, sizeof(*desc));
@@ -986,7 +1056,7 @@ index 000000000000..a6965d102417
 +
 +#endif //_INTEL_IPTS_H_
 diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
-index 7c4c8fb1dae4..88cab775a4d2 100644
+index 0328ee704ee5..5be61a9a7233 100644
 --- a/drivers/gpu/drm/i915/intel_lrc.c
 +++ b/drivers/gpu/drm/i915/intel_lrc.c
 @@ -163,8 +163,6 @@
@@ -998,7 +1068,7 @@ index 7c4c8fb1dae4..88cab775a4d2 100644
  static void execlists_init_reg_state(u32 *reg_state,
  				     struct i915_gem_context *ctx,
  				     struct intel_engine_cs *engine,
-@@ -1345,7 +1343,7 @@ static int __context_pin(struct i915_gem_context *ctx, struct i915_vma *vma)
+@@ -1357,7 +1355,7 @@ static int __context_pin(struct i915_gem_context *ctx, struct i915_vma *vma)
  	return i915_vma_pin(vma, 0, GEN8_LR_CONTEXT_ALIGN, flags);
  }
  
@@ -1007,7 +1077,7 @@ index 7c4c8fb1dae4..88cab775a4d2 100644
  execlists_context_pin(struct intel_engine_cs *engine,
  		      struct i915_gem_context *ctx)
  {
-@@ -1399,7 +1397,7 @@ execlists_context_pin(struct intel_engine_cs *engine,
+@@ -1411,7 +1409,7 @@ execlists_context_pin(struct intel_engine_cs *engine,
  	return ERR_PTR(ret);
  }
  
@@ -1016,7 +1086,7 @@ index 7c4c8fb1dae4..88cab775a4d2 100644
  				    struct i915_gem_context *ctx)
  {
  	struct intel_context *ce = to_intel_context(ctx, engine);
-@@ -2364,6 +2362,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine)
+@@ -2376,6 +2374,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine)
  
  	logical_ring_setup(engine);
  
@@ -1026,7 +1096,7 @@ index 7c4c8fb1dae4..88cab775a4d2 100644
  	if (HAS_L3_DPF(dev_priv))
  		engine->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
  
-@@ -2628,7 +2629,7 @@ populate_lr_context(struct i915_gem_context *ctx,
+@@ -2640,7 +2641,7 @@ populate_lr_context(struct i915_gem_context *ctx,
  	return ret;
  }
  
@@ -1085,7 +1155,7 @@ index b443278e569c..4e44ae7c3387 100644
  
  static void pch_enable_backlight(const struct intel_crtc_state *crtc_state,
 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index 45968f7970f8..a0e053c4dc1c 100644
+index 15c934ef6b18..468710ac7693 100644
 --- a/drivers/hid/hid-multitouch.c
 +++ b/drivers/hid/hid-multitouch.c
 @@ -151,6 +151,7 @@ struct mt_device {
@@ -1153,7 +1223,7 @@ index 45968f7970f8..a0e053c4dc1c 100644
  
  	/* let hid-core decide for the others */
  	return 0;
-@@ -1315,6 +1330,7 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+@@ -1327,6 +1342,7 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  			suffix = "Pen";
  			/* force BTN_STYLUS to allow tablet matching in udev */
  			__set_bit(BTN_STYLUS, hi->input->keybit);
@@ -1161,7 +1231,7 @@ index 45968f7970f8..a0e053c4dc1c 100644
  		}
  	}
  
-@@ -1330,12 +1346,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+@@ -1342,12 +1358,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
  			/* already handled by hid core */
  			break;
  		case HID_DG_TOUCHSCREEN:
@@ -1176,7 +1246,7 @@ index 45968f7970f8..a0e053c4dc1c 100644
  			break;
  		case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
  			suffix = "Custom Media Keys";
-@@ -1452,6 +1469,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
+@@ -1464,6 +1481,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  	td->cc_index = -1;
  	td->scantime_index = -1;
  	td->mt_report_id = -1;
@@ -1746,7 +1816,7 @@ index 000000000000..03a5f3551ddf
 +#endif // _IPTS_GFX_H_
 diff --git a/drivers/misc/ipts/ipts-hid.c b/drivers/misc/ipts/ipts-hid.c
 new file mode 100644
-index 000000000000..3b3be6177648
+index 000000000000..e85844dc1158
 --- /dev/null
 +++ b/drivers/misc/ipts/ipts-hid.c
 @@ -0,0 +1,456 @@
@@ -2158,7 +2228,7 @@ index 000000000000..3b3be6177648
 +						err_payload->code[2],
 +						err_payload->code[3],
 +						err_payload->string);
-+				
++
 +				break;
 +			}
 +			default:
@@ -2248,7 +2318,7 @@ index 000000000000..f1b22c912df7
 +#endif /* _IPTS_HID_H_ */
 diff --git a/drivers/misc/ipts/ipts-kernel.c b/drivers/misc/ipts/ipts-kernel.c
 new file mode 100644
-index 000000000000..ca5e24ce579e
+index 000000000000..86fd359d2eed
 --- /dev/null
 +++ b/drivers/misc/ipts/ipts-kernel.c
 @@ -0,0 +1,1050 @@
@@ -2526,7 +2596,7 @@ index 000000000000..ca5e24ce579e
 +		return -EINVAL;
 +
 +	ipts_dbg(ipts, "cmd buf size = %d\n", cmd->size);
-+		
++
 +	num_of_parallels = ipts_get_num_of_parallel_buffers(ipts);
 +	/* command buffers are located after the other allocations */
 +	cmdbuf_idx = num_of_parallels * alloc_info->num_of_allocations;
@@ -2598,7 +2668,7 @@ index 000000000000..ca5e24ce579e
 +	int buf_idx, num_of_alloc;
 +	u32 buf_size, flags, io_buf_type;
 +	bool initialize;
-+	
++
 +	parsed = parse_info->parsed;
 +	size = parse_info->size;
 +	bin_data = parse_info->data;
@@ -2611,7 +2681,7 @@ index 000000000000..ca5e24ce579e
 +
 +	ipts_dbg(ipts, "number of resources %u\n", res_list->num);
 +	for (i = 0; i < res_list->num; i++) {
-+		initialize = false; 
++		initialize = false;
 +		io_buf_type = 0;
 +		flags = 0;
 +
@@ -2792,7 +2862,7 @@ index 000000000000..ca5e24ce579e
 +			if(alloc_info->buffs[buf_idx].buf != NULL) {
 +				gtt_offset = (u32)(u64)
 +					alloc_info->buffs[buf_idx].buf->gfx_addr;
-+			} 
++			}
 +			gtt_offset += patch[i].alloc_offset;
 +
 +			batch += patch[i].patch_offset;
@@ -2816,7 +2886,7 @@ index 000000000000..ca5e24ce579e
 +	u8 *wi_data;
 +	int size, parsed, hdr_size, wi_size;
 +	int i, batch_offset;
-+	
++
 +	parsed = parse_info->parsed;
 +	size = parse_info->size;
 +	bin_guc_wq = (ipts_bin_guc_wq_info_t *)&parse_info->data[parsed];
@@ -2861,7 +2931,7 @@ index 000000000000..ca5e24ce579e
 +	bin_buffer_t *bin_buf;
 +	int wq_size, wi_size, parallel_idx, cmd_idx, k_idx, iter_size;
 +	int i, num_of_parallels, batch_offset, k_num, total_workload;
-+	
++
 +	wq_addr = (u8*)ipts->resource.wq_info.wq_addr;
 +	wq_size = ipts->resource.wq_info.wq_size;
 +	num_of_parallels = ipts_get_num_of_parallel_buffers(ipts);
@@ -2885,7 +2955,7 @@ index 000000000000..ca5e24ce579e
 +			batch_offset = kernel->guc_wq_item->batch_offset;
 +			wi_size = kernel->guc_wq_item->size;
 +			wi_data = &kernel->guc_wq_item->data[0];
-+			
++
 +			cmd_idx = wl[parallel_idx].cmdbuf_index;
 +			bin_buf = &alloc_info->buffs[cmd_idx];
 +
@@ -3028,38 +3098,38 @@ index 000000000000..ca5e24ce579e
 +
 +	ret = bin_read_allocation_list(ipts, parse_info, alloc_info);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error read_allocation_list\n");
++		ipts_dbg(ipts, "error read_allocation_list\n");
 +		goto setup_error;
 +	}
 +
 +	ret = bin_read_cmd_buffer(ipts, parse_info, alloc_info, wl);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error read_cmd_buffer\n");
++		ipts_dbg(ipts, "error read_cmd_buffer\n");
 +		goto setup_error;
 +	}
 +
 +	ret = bin_read_res_list(ipts, parse_info, alloc_info, wl);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error read_res_list\n");
++		ipts_dbg(ipts, "error read_res_list\n");
 +		goto setup_error;
 +	}
 +
 +	ret = bin_read_patch_list(ipts, parse_info, alloc_info, wl);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error read_patch_list\n");
++		ipts_dbg(ipts, "error read_patch_list\n");
 +		goto setup_error;
 +	}
 +
 +	ret = bin_read_guc_wq_item(ipts, parse_info, &guc_wq_item);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error read_guc_workqueue\n");
++		ipts_dbg(ipts, "error read_guc_workqueue\n");
 +		goto setup_error;
 +	}
 +
 +	memset(&bufid_patch, 0, sizeof(bufid_patch));
 +	ret = bin_read_bufid_patch(ipts, parse_info, &bufid_patch);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error read_bufid_patch\n");
++		ipts_dbg(ipts, "error read_bufid_patch\n");
 +		goto setup_error;
 +	}
 +
@@ -3069,7 +3139,7 @@ index 000000000000..ca5e24ce579e
 +	kernel->guc_wq_item = guc_wq_item;
 +	memcpy(&kernel->bufid_patch, &bufid_patch, sizeof(bufid_patch));
 +
-+        return 0;
++	return 0;
 +
 +setup_error:
 +	vfree(guc_wq_item);
@@ -3198,16 +3268,16 @@ index 000000000000..ca5e24ce579e
 +	}
 +
 +	ipts_set_wq_item_size(ipts, total_workload);
-+	
++
 +	ret = bin_setup_guc_workqueue(ipts, kernel_list);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error setup_guc_workqueue\n");
++		ipts_dbg(ipts, "error setup_guc_workqueue\n");
 +		goto error_exit;
 +	}
 +
 +	ret = bin_setup_bufid_buffer(ipts, kernel_list);
 +	if (ret) {
-+        	ipts_dbg(ipts, "error setup_lastbubmit_buffer\n");
++		ipts_dbg(ipts, "error setup_lastbubmit_buffer\n");
 +		goto error_exit;
 +	}
 +
@@ -3250,7 +3320,7 @@ index 000000000000..ca5e24ce579e
 +	for (k_idx = 0; k_idx < k_num; k_idx++) {
 +		unload_kernel(ipts, kernel);
 +		kernel++;
-+	}	
++	}
 +
 +	ipts_unmap_buffer(ipts, kernel_list->bufid_buf);
 +
@@ -3924,7 +3994,7 @@ index 000000000000..8ca146800a47
 +#endif // _IPTS_MEI_MSGS_H_
 diff --git a/drivers/misc/ipts/ipts-mei.c b/drivers/misc/ipts/ipts-mei.c
 new file mode 100644
-index 000000000000..39667e75dafd
+index 000000000000..199e49cb8d70
 --- /dev/null
 +++ b/drivers/misc/ipts/ipts-mei.c
 @@ -0,0 +1,282 @@
@@ -4152,7 +4222,7 @@ index 000000000000..39667e75dafd
 +
 +disable_mei :
 +	mei_cldev_disable(cldev);
-+	
++
 +	return ret;
 +}
 +
@@ -4212,7 +4282,7 @@ index 000000000000..39667e75dafd
 +MODULE_LICENSE("GPL");
 diff --git a/drivers/misc/ipts/ipts-msg-handler.c b/drivers/misc/ipts/ipts-msg-handler.c
 new file mode 100644
-index 000000000000..1396ecc7197f
+index 000000000000..8b214f975c03
 --- /dev/null
 +++ b/drivers/misc/ipts/ipts-msg-handler.c
 @@ -0,0 +1,431 @@
@@ -4374,7 +4444,7 @@ index 000000000000..1396ecc7197f
 +
 +	if (old_state == IPTS_STA_RAW_DATA_STARTED ||
 +					old_state == IPTS_STA_HID_STARTED) {
-+        	ipts_free_default_resource(ipts);
++		ipts_free_default_resource(ipts);
 +		ipts_free_raw_data_resource(ipts);
 +
 +		return;
@@ -4390,7 +4460,7 @@ index 000000000000..1396ecc7197f
 +	ipts_stop(ipts);
 +
 +	ipts->retry++;
-+	if (ipts->retry == IPTS_MAX_RETRY && 
++	if (ipts->retry == IPTS_MAX_RETRY &&
 +			ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) {
 +		/* try with HID mode */
 +		ipts->sensor_mode = TOUCH_SENSOR_MODE_HID;
@@ -4649,7 +4719,7 @@ index 000000000000..1396ecc7197f
 +}
 diff --git a/drivers/misc/ipts/ipts-msg-handler.h b/drivers/misc/ipts/ipts-msg-handler.h
 new file mode 100644
-index 000000000000..b8e27d30c63e
+index 000000000000..15038814dfec
 --- /dev/null
 +++ b/drivers/misc/ipts/ipts-msg-handler.h
 @@ -0,0 +1,32 @@
@@ -4677,7 +4747,7 @@ index 000000000000..b8e27d30c63e
 +void ipts_stop(ipts_info_t *ipts);
 +int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode);
 +int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg,
-+                        					u32 msg_len);
++                     u32 msg_len);
 +int ipts_handle_processed_data(ipts_info_t *ipts);
 +int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id);
 +int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts);
@@ -6056,3 +6126,6 @@ index 000000000000..f329bbfb8079
 +void intel_ipts_disconnect(uint64_t gfx_handle);
 +
 +#endif // INTEL_IPTS_IF_H
+-- 
+2.20.1
+

+ 144 - 0
patches/4.18/0006-hid.patch

@@ -0,0 +1,144 @@
+From 756ee28254567d058b3f671451cf2b9652cf88db Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 14:19:55 +0100
+Subject: [PATCH 6/8] hid
+
+---
+ drivers/hid/hid-ids.h        | 20 ++++++++++----
+ drivers/hid/hid-microsoft.c  |  3 ++-
+ drivers/hid/hid-multitouch.c | 52 ++++++++++++++++++++++++++++++++++++
+ drivers/hid/hid-quirks.c     | 10 +++++++
+ 4 files changed, 79 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
+index ae5b72269e27..c7ec848f630a 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -792,11 +792,21 @@
+ #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732
+ #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600  0x0750
+ #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500	0x076c
+-#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3
+-#define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
+-#define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
+-#define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
+-#define USB_DEVICE_ID_MS_POWER_COVER     0x07da
++#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD	0x00e3
++#define USB_DEVICE_ID_MS_SURFACE_PRO_2		0x0799
++#define USB_DEVICE_ID_MS_TOUCH_COVER_2		0x07a7
++#define USB_DEVICE_ID_MS_TYPE_COVER_2		0x07a9
++#define USB_DEVICE_ID_MS_TYPE_COVER_3		0x07de
++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3	0x07dc
++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1	0x07de
++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2	0x07e2
++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP	0x07dd
++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4	0x07e8
++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1	0x07e4
++#define USB_DEVICE_ID_MS_SURFACE_BOOK		0x07cd
++#define USB_DEVICE_ID_MS_SURFACE_BOOK_2		0x0922
++#define USB_DEVICE_ID_MS_SURFACE_VHF		0xf001
++#define USB_DEVICE_ID_MS_POWER_COVER		0x07da
+ 
+ #define USB_VENDOR_ID_MOJO		0x8282
+ #define USB_DEVICE_ID_RETRO_ADAPTER	0x3201
+diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
+index 96e7d3231d2f..36f13a867f0f 100644
+--- a/drivers/hid/hid-microsoft.c
++++ b/drivers/hid/hid-microsoft.c
+@@ -278,7 +278,8 @@ static const struct hid_device_id ms_devices[] = {
+ 		.driver_data = MS_HIDINPUT },
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
+ 		.driver_data = MS_ERGONOMY},
+-
++	{ HID_DEVICE(BUS_VIRTUAL, 0, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_VHF),
++		.driver_data = MS_HIDINPUT},
+ 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
+ 		.driver_data = MS_PRESENTER },
+ 	{ }
+diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
+index 468710ac7693..130fc1432bc2 100644
+--- a/drivers/hid/hid-multitouch.c
++++ b/drivers/hid/hid-multitouch.c
+@@ -1769,6 +1769,58 @@ static const struct hid_device_id mt_devices[] = {
+ 		HID_USB_DEVICE(USB_VENDOR_ID_LG,
+ 			USB_DEVICE_ID_LG_MELFAS_MT) },
+ 
++	/* Microsoft Touch Cover */
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++		USB_DEVICE_ID_MS_TOUCH_COVER_2) },
++
++	/* Microsoft Type Cover */
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_2) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_3) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) },
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1) },
++
++	/* Microsoft Surface Book */
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++		USB_DEVICE_ID_MS_SURFACE_BOOK) },
++
++	/* Microsoft Surface Book 2 */
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++		USB_DEVICE_ID_MS_SURFACE_BOOK_2) },
++
++	/* Microsoft Surface Laptop */
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
++			USB_VENDOR_ID_MICROSOFT,
++			USB_DEVICE_ID_MS_SURFACE_VHF) },
++
++	/* Microsoft Power Cover */
++	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
++		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
++		USB_DEVICE_ID_MS_POWER_COVER) },
++
+ 	/* MosArt panels */
+ 	{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
+ 		MT_USB_DEVICE(USB_VENDOR_ID_ASUS,
+diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
+index 249d49b6b16c..f284f08ba73b 100644
+--- a/drivers/hid/hid-quirks.c
++++ b/drivers/hid/hid-quirks.c
+@@ -111,6 +111,16 @@ static const struct hid_device_id hid_quirks[] = {
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS },
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS },
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK_2), HID_QUIRK_NO_INIT_REPORTS },
++	{ HID_DEVICE(BUS_VIRTUAL, 0, USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_VHF), HID_QUIRK_ALWAYS_POLL },
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER), HID_QUIRK_MULTI_INPUT },
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS },
+ 	{ HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT },
+-- 
+2.20.1
+

+ 12 - 0
patches/4.18/sdcard_reader.patch → patches/4.18/0007-sdcard_reader.patch

@@ -1,3 +1,12 @@
+From c9eb294b2f6e5e16c09e2b0b9e43fb1982f2d723 Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 14:20:52 +0100
+Subject: [PATCH 7/8] sdcard_reader
+
+---
+ drivers/usb/core/hub.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
 index 1fb266809966..916a323ca79f 100644
 --- a/drivers/usb/core/hub.c
@@ -12,3 +21,6 @@ index 1fb266809966..916a323ca79f 100644
  		return;
  
  	udev->lpm_disable_count--;
+-- 
+2.20.1
+

+ 16 - 0
patches/4.18/0008-wifi.patch

@@ -0,0 +1,16 @@
+From fcdceb24f246ff4ffebfcced57e8c10d332a2a0c Mon Sep 17 00:00:00 2001
+From: qzed <qzed@users.noreply.github.com>
+Date: Mon, 24 Dec 2018 14:21:12 +0100
+Subject: [PATCH 8/8] wifi
+
+---
+ scripts/leaking_addresses.pl | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ mode change 100755 => 100644 scripts/leaking_addresses.pl
+
+diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl
+old mode 100755
+new mode 100644
+-- 
+2.20.1
+

+ 0 - 1374
patches/4.18/acpi.patch

@@ -1,1374 +0,0 @@
-diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
-index ac4d48830415..2025f56446a0 100644
---- a/drivers/platform/x86/Kconfig
-+++ b/drivers/platform/x86/Kconfig
-@@ -1158,6 +1158,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 2ba6cb795338..8fd5b93bb20d 100644
---- a/drivers/platform/x86/Makefile
-+++ b/drivers/platform/x86/Makefile
-@@ -81,6 +81,9 @@ 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_ACPI_SURFACE)	+= surface_i2c.o
-+obj-$(CONFIG_ACPI_SURFACE)	+= surface_platform.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
-index 000000000000..c969bda99464
---- /dev/null
-+++ b/drivers/platform/x86/surface_acpi.c
-@@ -0,0 +1,485 @@
-+/*
-+ *  surface_acpi.c - Microsoft Surface ACPI Driver
-+ *
-+ *  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_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 <linux/seq_file.h>
-+#include <acpi/acpi_bus.h>
-+#include <acpi/acpi_drivers.h>
-+
-+#include "surface_acpi.h"
-+
-+#define SUR_METHOD_DSM			"_DSM"
-+#define SUR_METHOD_REG			"_REG"
-+#define SUR_METHOD_STA			"_STA"
-+#define SUR_METHOD_INI			"_INI"
-+#define SUR_METHOD_CRS			"_CRS"
-+
-+#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
-+
-+static char SURFACE_EVENT_GUID[] = "93b666c5-70c6-469f-a215-3d487c91ab3c";
-+static char SUR_SAN_RQST[] = "\\_SB._SAN.RQST";
-+static char SUR_SAN_RQSX[] = "\\_SB._SAN.RQSX";
-+
-+struct surface_acpi_dev {
-+	acpi_handle handle;
-+	acpi_handle rqst_handle;
-+	acpi_handle rqsx_handle;
-+
-+	struct acpi_device *san_dev;
-+	struct acpi_device *ssh_dev;
-+	struct acpi_device *bat1_dev;
-+	struct acpi_device *bat2_dev;
-+	struct acpi_device *psu_dev;
-+
-+	unsigned int bat1_attached:1;
-+	unsigned int bat2_attached:1;
-+	unsigned int psu_registered:1;
-+};
-+
-+static struct surface_acpi_dev *surface_acpi;
-+
-+static struct proc_dir_entry *surface_proc_dir;
-+
-+static acpi_status surface_acpi_check_status(struct acpi_device *dev)
-+{
-+	unsigned long long value;
-+	acpi_status status;
-+
-+	if (acpi_has_method(dev->handle, SUR_METHOD_STA)) {
-+		status = acpi_evaluate_integer(dev->handle,
-+				SUR_METHOD_STA, NULL, &value);
-+
-+		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 acpi_status surface_acpi_san_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->handle, SUR_METHOD_REG)) {
-+		status = acpi_evaluate_object(surface_acpi->handle,
-+				SUR_METHOD_REG, &params, &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;
-+}
-+
-+acpi_status 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_BUFFER;
-+	in_objs[0].buffer.length = sizeof(SURFACE_EVENT_GUID);
-+	in_objs[0].buffer.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_PACKAGE;
-+	in_objs[3].package.count = 0;
-+	in_objs[3].package.elements = SURFACE_GEN_VERSION;
-+	results.length = sizeof(out_objs);
-+	results.pointer = out_objs;
-+
-+	if (acpi_has_method(surface_acpi->handle, SUR_METHOD_DSM)) {
-+		status = acpi_evaluate_object(surface_acpi->handle,
-+				SUR_METHOD_DSM, &params, &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;
-+}
-+EXPORT_SYMBOL(surface_acpi_event_handler);
-+
-+static void surface_acpi_san_load(void)
-+{
-+	acpi_status 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
-+		surface_acpi->bat1_attached = 1;
-+
-+	ret = surface_acpi_event_handler(SUR_BAT2_INFO_CHANGE);
-+	if (ACPI_FAILURE(ret))
-+		pr_err("surface_acpi: Error attaching BAT2\n");
-+	else
-+		surface_acpi->bat2_attached = 1;
-+
-+	ret = surface_acpi_event_handler(SUR_PSU_INFO_CHANGE);
-+	if (ACPI_FAILURE(ret))
-+		pr_err("surface_acpi: Error registering PSU\n");
-+	else
-+		surface_acpi->psu_registered = 1;
-+}
-+
-+static acpi_status surface_acpi_ssh_initialize(void)
-+{
-+	acpi_status status;
-+
-+	if (acpi_has_method(surface_acpi->ssh_dev->handle, SUR_METHOD_INI)) {
-+		status = acpi_evaluate_object(surface_acpi->ssh_dev->handle,
-+				SUR_METHOD_INI, NULL, NULL);
-+
-+		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 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);
-+}
-+
-+static void surface_acpi_notify(struct acpi_device *dev, u32 event)
-+{
-+	pr_info("surface_acpi: Event received %x\n", event);
-+}
-+
-+static void surface_acpi_register_rqst_handler(void)
-+{
-+	acpi_status status;
-+
-+	status = acpi_get_handle(NULL, SUR_SAN_RQST, &surface_acpi->rqst_handle);
-+	if (ACPI_FAILURE(status)) {
-+		pr_err("surface_acpi: ACPI event failure status %s\n",
-+					acpi_format_exception(status));
-+	}
-+}
-+
-+static void surface_acpi_register_rqsx_handler(void)
-+{
-+	acpi_status status;
-+
-+	status = acpi_get_handle(NULL, SUR_SAN_RQSX, &surface_acpi->rqsx_handle);
-+	if (ACPI_FAILURE(status)) {
-+		pr_err("surface_acpi: ACPI event failure status %s\n",
-+					acpi_format_exception(status));
-+	}
-+}
-+
-+static acpi_status surface_acpi_walk_callback(acpi_handle handle, u32 level,
-+						void *context, void **return_value)
-+{
-+	struct acpi_device_info *info;
-+
-+	if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) {
-+		pr_warn("method: name: %4.4s, args %X\n",
-+			(char *)&info->name, info->param_count);
-+
-+		kfree(info);
-+	}
-+
-+	return AE_OK;
-+}
-+
-+static void surface_acpi_walk_namespace(struct acpi_device *dev)
-+{
-+	acpi_status status;
-+
-+	status = acpi_walk_namespace(ACPI_TYPE_METHOD,
-+			dev->handle, 1, surface_acpi_walk_callback,
-+			NULL, NULL, NULL);
-+	if (ACPI_FAILURE(status))
-+		pr_warn("surface_acpi: Unable to walk acpi resources\n");
-+}
-+
-+static int surface_acpi_add(struct acpi_device *dev)
-+{
-+	if (!surface_acpi)
-+	{
-+		surface_acpi = kzalloc(sizeof(*surface_acpi), GFP_KERNEL);
-+		if (!surface_acpi)
-+			return AE_NO_MEMORY;
-+	}
-+
-+	if (acpi_has_method(dev->handle, SUR_METHOD_DSM))
-+	{
-+		pr_info("surface_acpi: Attaching device MSHW0091\n");
-+
-+		surface_acpi->san_dev = dev;
-+		surface_acpi->handle = dev->handle;
-+
-+		surface_acpi_walk_namespace(surface_acpi->san_dev);
-+		surface_acpi_check_status(surface_acpi->san_dev);
-+
-+		surface_acpi_register_rqst_handler();
-+		surface_acpi_register_rqsx_handler();
-+
-+		surface_acpi_san_reg();
-+		surface_acpi_san_load();
-+
-+		create_surface_proc_entries();
-+	}
-+	else if (acpi_has_method(dev->handle, SUR_METHOD_CRS))
-+	{
-+		pr_info("surface_acpi: Attaching device MSHW0084\n");
-+
-+		surface_acpi->ssh_dev = dev;
-+
-+		surface_acpi_walk_namespace(surface_acpi->ssh_dev);
-+		surface_acpi_check_status(surface_acpi->ssh_dev);
-+
-+		surface_acpi_ssh_initialize();
-+		//surface_acpi_ssh_load();
-+	}
-+	else
-+	{
-+		pr_info("surface_acpi: Attaching device\n");
-+	}
-+
-+	device_init_wakeup(&dev->dev, true);
-+
-+	return AE_OK;
-+}
-+
-+static int surface_acpi_remove(struct acpi_device *dev)
-+{
-+	remove_surface_proc_entries();
-+
-+	return AE_OK;
-+}
-+
-+static const struct acpi_device_id surface_device_ids[] = {
-+	{"MSHW0084", 0},
-+	{"MSHW0091", 0},
-+	{"MSHW0124", 0},
-+	{"INT3403", 0},
-+	{"LNXTHERM", 0},
-+	{"PNP0C0A", 0},
-+	{"", 0},
-+};
-+MODULE_DEVICE_TABLE(acpi, surface_device_ids);
-+
-+static struct acpi_driver surface_acpi_driver = {
-+	.name	= "surface_acpi",
-+	.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;
-+
-+	pr_info("surface_acpi: Microsoft Surface ACPI Driver version %s\n",
-+	       SURFACE_ACPI_VERSION);
-+
-+	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);
-+
-+MODULE_AUTHOR("Jake Day");
-+MODULE_DESCRIPTION("Microsoft Surface ACPI Driver");
-+MODULE_LICENSE("GPL");
-diff --git a/drivers/platform/x86/surface_acpi.h b/drivers/platform/x86/surface_acpi.h
-new file mode 100644
-index 000000000000..5b6627c4d6f1
---- /dev/null
-+++ b/drivers/platform/x86/surface_acpi.h
-@@ -0,0 +1,18 @@
-+/*
-+ *  surface_acpi.h - Microsoft Surface ACPI Driver
-+ *
-+ *  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".
-+ */
-+
-+acpi_status surface_acpi_event_handler(u32 event);
-diff --git a/drivers/platform/x86/surface_i2c.c b/drivers/platform/x86/surface_i2c.c
-new file mode 100644
-index 000000000000..fb2cf0cae72f
---- /dev/null
-+++ b/drivers/platform/x86/surface_i2c.c
-@@ -0,0 +1,696 @@
-+/*
-+ *  surface_i2c.c - Microsoft Surface I2C Driver
-+ *
-+ *  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".
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/i2c.h>
-+#include <linux/slab.h>
-+#include <linux/acpi.h>
-+#include <linux/module.h>
-+#include <linux/uuid.h>
-+#include <linux/kthread.h>
-+#include <linux/freezer.h>
-+#include <asm/unaligned.h>
-+
-+#include "surface_acpi.h"
-+
-+#define POLL_INTERVAL		(HZ * 2)
-+
-+struct surface_i2c_data {
-+	struct i2c_client *adp1;
-+	struct i2c_client *bat0;
-+	unsigned short notify_version;
-+	struct task_struct *poll_task;
-+	bool kthread_running;
-+	bool charging;
-+	bool bat_charging;
-+	u8 trip_point;
-+	s32 full_capacity;
-+};
-+
-+struct surface_i2c_lookup {
-+	struct surface_i2c_data	*cdata;
-+	unsigned int n;
-+	unsigned int index;
-+	int addr;
-+};
-+
-+struct surface_i2c_handler_data {
-+	struct acpi_connection_info info;
-+	struct i2c_client *client;
-+};
-+
-+struct bix {
-+	u32	revision;
-+	u32	power_unit;
-+	u32	design_capacity;
-+	u32	last_full_charg_capacity;
-+	u32	battery_technology;
-+	u32	design_voltage;
-+	u32	design_capacity_of_warning;
-+	u32	design_capacity_of_low;
-+	u32	cycle_count;
-+	u32	measurement_accuracy;
-+	u32	max_sampling_time;
-+	u32	min_sampling_time;
-+	u32	max_average_interval;
-+	u32	min_average_interval;
-+	u32	battery_capacity_granularity_1;
-+	u32	battery_capacity_granularity_2;
-+	char model[10];
-+	char serial[10];
-+	char type[10];
-+	char OEM[10];
-+} __packed;
-+
-+struct bst {
-+	u32 battery_state;
-+	s32 battery_present_rate;
-+	u32 battery_remaining_capacity;
-+	u32 battery_present_voltage;
-+} __packed;
-+
-+struct gsb_command {
-+	u8 arg0;
-+	u8 arg1;
-+	u8 arg2;
-+} __packed;
-+
-+struct gsb_buffer {
-+	u8 status;
-+	u8 len;
-+	u8 ret;
-+	union {
-+		struct gsb_command cmd;
-+		struct bst bst;
-+		struct bix bix;
-+	} __packed;
-+} __packed;
-+
-+#define ACPI_BATTERY_STATE_DISCHARGING	0x1
-+#define ACPI_BATTERY_STATE_CHARGING		0x2
-+#define ACPI_BATTERY_STATE_CRITICAL		0x4
-+
-+#define surface_i2c_CMD_DEST_BAT0		0x01
-+#define surface_i2c_CMD_DEST_ADP1		0x03
-+
-+#define surface_i2c_CMD_BAT0_STA		0x01
-+#define surface_i2c_CMD_BAT0_BIX		0x02
-+#define surface_i2c_CMD_BAT0_BCT		0x03
-+#define surface_i2c_CMD_BAT0_BTM		0x04
-+#define surface_i2c_CMD_BAT0_BST		0x05
-+#define surface_i2c_CMD_BAT0_BTP		0x06
-+#define surface_i2c_CMD_ADP1_PSR		0x07
-+#define surface_i2c_CMD_BAT0_PSOC		0x09
-+#define surface_i2c_CMD_BAT0_PMAX		0x0A
-+#define surface_i2c_CMD_BAT0_PSRC		0x0B
-+#define surface_i2c_CMD_BAT0_CHGI		0x0C
-+#define surface_i2c_CMD_BAT0_ARTG		0x0D
-+
-+#define surface_i2c_NOTIFY_GET_VERSION	0x00
-+#define surface_i2c_NOTIFY_ADP1			0x01
-+#define surface_i2c_NOTIFY_BAT0_BST		0x02
-+#define surface_i2c_NOTIFY_BAT0_BIX		0x05
-+
-+#define surface_i2c_ADP1_REG_PSR		0x03
-+
-+#define surface_i2c_BAT0_REG_CAPACITY			0x0c
-+#define surface_i2c_BAT0_REG_FULL_CHG_CAPACITY	0x0e
-+#define surface_i2c_BAT0_REG_DESIGN_CAPACITY	0x40
-+#define surface_i2c_BAT0_REG_VOLTAGE			0x08
-+#define surface_i2c_BAT0_REG_RATE				0x14
-+#define surface_i2c_BAT0_REG_OEM				0x45
-+#define surface_i2c_BAT0_REG_TYPE				0x4e
-+#define surface_i2c_BAT0_REG_SERIAL_NO			0x56
-+#define surface_i2c_BAT0_REG_CYCLE_CNT			0x6e
-+
-+#define surface_i2c_EV_2_5				0x1ff
-+
-+static int surface_i2c_read_block(struct i2c_client *client, u8 reg, u8 *buf,
-+				   int len)
-+{
-+	int status, i;
-+
-+	for (i = 0; i < len; i++) {
-+		status = i2c_smbus_read_byte_data(client, reg + i);
-+		if (status < 0) {
-+			buf[i] = 0xff;
-+			continue;
-+		}
-+
-+		buf[i] = (u8)status;
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+surface_i2c_notify(struct surface_i2c_data *cdata, u8 arg1, u8 arg2,
-+		unsigned int *ret_value)
-+{
-+	/*static const guid_t surface_i2c_guid =
-+	GUID_INIT(0x93b666c5, 0x70c6, 0x469f,
-+		  0xa2, 0x15, 0x3d, 0x48, 0x7c, 0x91, 0xab, 0x3c);*/
-+
-+	struct acpi_device *adev;
-+	acpi_handle handle;
-+	acpi_status status;
-+
-+	handle = ACPI_HANDLE(&cdata->adp1->dev);
-+	if (!handle || acpi_bus_get_device(handle, &adev))
-+		return -ENODEV;
-+
-+	*ret_value = 0;
-+
-+	status = surface_acpi_event_handler(arg2);
-+	if (ACPI_FAILURE(status)) {
-+		pr_err("surface_i2c: ACPI event failure status %s\n",
-+					acpi_format_exception(status));
-+	}
-+
-+	return 0;
-+}
-+
-+static const struct bix default_bix = {
-+	.revision = 0x00,
-+	.power_unit = 0x00,
-+	.design_capacity = 0x1734,
-+	.last_full_charg_capacity = 0x1734,
-+	.battery_technology = 0x01,
-+	.design_voltage = 0x1d92,
-+	.design_capacity_of_warning = 0xc8,
-+	.design_capacity_of_low = 0xc8,
-+	.battery_capacity_granularity_1 = 0x45,
-+	.battery_capacity_granularity_2 = 0x11,
-+	.cycle_count = 0x01,
-+	.measurement_accuracy = 0x00015F90,
-+	.max_sampling_time = 0x03E8,
-+	.min_sampling_time = 0x03E8,
-+	.max_average_interval = 0x03E8,
-+	.min_average_interval = 0x03E8,
-+	.model = "PNP0C0A",
-+	.serial = "1234567890",
-+	.type = "SDS-BAT",
-+	.OEM = "MICROSOFT",
-+};
-+
-+static int surface_i2c_bix(struct surface_i2c_data *cdata, struct bix *bix)
-+{
-+	struct i2c_client *client = cdata->bat0;
-+	int ret;
-+	char buf[10];
-+
-+	*bix = default_bix;
-+
-+	/* get design capacity */
-+	ret = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_DESIGN_CAPACITY);
-+	if (ret < 0) {
-+		dev_err(&client->dev, "Error reading design capacity: %d\n", ret);
-+		return ret;
-+	}
-+	bix->design_capacity = le16_to_cpu(ret);
-+
-+	/* get last full charge capacity */
-+	ret = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_FULL_CHG_CAPACITY);
-+	if (ret < 0) {
-+		dev_err(&client->dev, "Error reading last full charge capacity: %d\n", ret);
-+		return ret;
-+	}
-+	bix->last_full_charg_capacity = le16_to_cpu(ret);
-+
-+	/* get serial number */
-+	ret = surface_i2c_read_block(client, surface_i2c_BAT0_REG_SERIAL_NO,
-+				      buf, 10);
-+	if (ret) {
-+		dev_err(&client->dev, "Error reading serial no: %d\n", ret);
-+		return ret;
-+	}
-+	memcpy(bix->serial, buf + 7, 3);
-+	memcpy(bix->serial + 3, buf, 6);
-+	bix->serial[9] = '\0';
-+
-+	/* get cycle count */
-+	ret = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_CYCLE_CNT);
-+	if (ret < 0) {
-+		dev_err(&client->dev, "Error reading cycle count: %d\n", ret);
-+		return ret;
-+	}
-+	bix->cycle_count = le16_to_cpu(ret);
-+
-+	/* get OEM name */
-+	ret = surface_i2c_read_block(client, surface_i2c_BAT0_REG_OEM, buf, 4);
-+	if (ret) {
-+		dev_err(&client->dev, "Error reading cycle count: %d\n", ret);
-+		return ret;
-+	}
-+	memcpy(bix->OEM, buf, 3);
-+	bix->OEM[4] = '\0';
-+
-+	return 0;
-+}
-+
-+static int surface_i2c_bst(struct surface_i2c_data *cdata, struct bst *bst)
-+{
-+	struct i2c_client *client = cdata->bat0;
-+	int rate, capacity, voltage, state;
-+	s16 tmp;
-+
-+	rate = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_RATE);
-+	if (rate < 0)
-+		return rate;
-+
-+	capacity = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_CAPACITY);
-+	if (capacity < 0)
-+		return capacity;
-+
-+	voltage = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_VOLTAGE);
-+	if (voltage < 0)
-+		return voltage;
-+
-+	tmp = le16_to_cpu(rate);
-+	bst->battery_present_rate = abs((s32)tmp);
-+
-+	state = 0;
-+	if ((s32) tmp > 0)
-+		state |= ACPI_BATTERY_STATE_CHARGING;
-+	else if ((s32) tmp < 0)
-+		state |= ACPI_BATTERY_STATE_DISCHARGING;
-+	bst->battery_state = state;
-+
-+	bst->battery_remaining_capacity = le16_to_cpu(capacity);
-+	bst->battery_present_voltage = le16_to_cpu(voltage);
-+
-+	return 0;
-+}
-+
-+static int surface_i2c_adp_psr(struct surface_i2c_data *cdata)
-+{
-+	struct i2c_client *client = cdata->adp1;
-+	int ret;
-+
-+	ret = i2c_smbus_read_byte_data(client, surface_i2c_ADP1_REG_PSR);
-+	if (ret < 0)
-+		return ret;
-+
-+	return ret;
-+}
-+
-+static int surface_i2c_isr(struct surface_i2c_data *cdata)
-+{
-+	struct bst bst;
-+	struct bix bix;
-+	int ret;
-+	bool status, bat_status;
-+
-+	ret = surface_i2c_adp_psr(cdata);
-+	if (ret < 0)
-+		return ret;
-+
-+	status = ret;
-+
-+	if (status != cdata->charging)
-+		surface_i2c_notify(cdata, cdata->notify_version,
-+				surface_i2c_NOTIFY_ADP1, &ret);
-+
-+	cdata->charging = status;
-+
-+	ret = surface_i2c_bst(cdata, &bst);
-+	if (ret < 0)
-+		return ret;
-+
-+	bat_status = bst.battery_state;
-+
-+	if (bat_status != cdata->bat_charging)
-+		surface_i2c_notify(cdata, cdata->notify_version,
-+				surface_i2c_NOTIFY_BAT0_BST, &ret);
-+
-+	cdata->bat_charging = bat_status;
-+
-+	ret = surface_i2c_bix(cdata, &bix);
-+	if (ret < 0)
-+		return ret;
-+	if (bix.last_full_charg_capacity != cdata->full_capacity)
-+		surface_i2c_notify(cdata, cdata->notify_version,
-+				surface_i2c_NOTIFY_BAT0_BIX, &ret);
-+
-+	cdata->full_capacity = bix.last_full_charg_capacity;
-+
-+	return 0;
-+}
-+
-+static int surface_i2c_poll_task(void *data)
-+{
-+	struct surface_i2c_data *cdata = data;
-+	int ret = 0;
-+
-+	cdata->kthread_running = true;
-+
-+	set_freezable();
-+
-+	while (!kthread_should_stop()) {
-+		schedule_timeout_interruptible(POLL_INTERVAL);
-+		try_to_freeze();
-+		ret = surface_i2c_isr(data);
-+		if (ret)
-+			goto out;
-+	}
-+
-+out:
-+	cdata->kthread_running = false;
-+	return ret;
-+}
-+
-+static acpi_status
-+surface_i2c_space_handler(u32 function, acpi_physical_address command,
-+			u32 bits, u64 *value64,
-+			void *handler_context, void *region_context)
-+{
-+	struct gsb_buffer *gsb = (struct gsb_buffer *)value64;
-+	struct surface_i2c_handler_data *data = handler_context;
-+	struct acpi_connection_info *info = &data->info;
-+	struct acpi_resource_i2c_serialbus *sb;
-+	struct i2c_client *client = data->client;
-+	struct surface_i2c_data *cdata = i2c_get_clientdata(client);
-+	struct acpi_resource *ares;
-+	u32 accessor_type = function >> 16;
-+	acpi_status ret;
-+	int status = 1;
-+
-+	ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
-+	if (ACPI_FAILURE(ret))
-+		return ret;
-+
-+	if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
-+		ret = AE_BAD_PARAMETER;
-+		goto err;
-+	}
-+
-+	sb = &ares->data.i2c_serial_bus;
-+	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
-+		ret = AE_BAD_PARAMETER;
-+		goto err;
-+	}
-+
-+	if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) {
-+		ret = AE_BAD_PARAMETER;
-+		goto err;
-+	}
-+
-+	if (gsb->cmd.arg0 == surface_i2c_CMD_DEST_ADP1 &&
-+	    gsb->cmd.arg1 == surface_i2c_CMD_ADP1_PSR) {
-+		ret = surface_i2c_adp_psr(cdata);
-+		if (ret >= 0) {
-+			status = ret;
-+			ret = 0;
-+		}
-+		goto out;
-+	}
-+
-+	if (gsb->cmd.arg0 != surface_i2c_CMD_DEST_BAT0) {
-+		ret = AE_BAD_PARAMETER;
-+		goto err;
-+	}
-+
-+	switch (gsb->cmd.arg1) {
-+	case surface_i2c_CMD_BAT0_STA:
-+		status = 1;
-+		ret = 0;
-+		break;
-+	case surface_i2c_CMD_BAT0_BIX:
-+		status = 1;
-+		ret = surface_i2c_bix(cdata, &gsb->bix);
-+		break;
-+	case surface_i2c_CMD_BAT0_BTP:
-+		status = 1;
-+		ret = 0;
-+		cdata->trip_point = gsb->cmd.arg2;
-+		break;
-+	case surface_i2c_CMD_BAT0_BST:
-+		status = 1;
-+		ret = surface_i2c_bst(cdata, &gsb->bst);
-+		break;
-+	default:
-+		pr_info("command(0x%02x) is not supported.\n", gsb->cmd.arg1);
-+		ret = AE_BAD_PARAMETER;
-+		goto err;
-+	}
-+
-+ out:
-+	gsb->ret = status;
-+	gsb->status = 0;
-+
-+ err:
-+	ACPI_FREE(ares);
-+	return ret;
-+}
-+
-+static int surface_i2c_install_space_handler(struct i2c_client *client)
-+{
-+	acpi_handle handle;
-+	struct surface_i2c_handler_data *data;
-+	acpi_status status;
-+
-+	handle = ACPI_HANDLE(&client->dev);
-+
-+	if (!handle)
-+		return -ENODEV;
-+
-+	data = kzalloc(sizeof(struct surface_i2c_handler_data),
-+			    GFP_KERNEL);
-+	if (!data)
-+		return -ENOMEM;
-+
-+	data->client = client;
-+	status = acpi_bus_attach_private_data(handle, (void *)data);
-+	if (ACPI_FAILURE(status)) {
-+		kfree(data);
-+		return -ENOMEM;
-+	}
-+
-+	status = acpi_install_address_space_handler(handle,
-+				ACPI_ADR_SPACE_GSBUS,
-+				&surface_i2c_space_handler,
-+				NULL,
-+				data);
-+	if (ACPI_FAILURE(status)) {
-+		dev_err(&client->dev, "Error installing i2c space handler\n");
-+		acpi_bus_detach_private_data(handle);
-+		kfree(data);
-+		return -ENOMEM;
-+	}
-+
-+	acpi_walk_dep_device_list(handle);
-+	return 0;
-+}
-+
-+static void surface_i2c_remove_space_handler(struct i2c_client *client)
-+{
-+	acpi_handle handle;
-+	struct surface_i2c_handler_data *data;
-+	acpi_status status;
-+
-+	handle = ACPI_HANDLE(&client->dev);
-+
-+	if (!handle)
-+		return;
-+
-+	acpi_remove_address_space_handler(handle,
-+				ACPI_ADR_SPACE_GSBUS,
-+				&surface_i2c_space_handler);
-+
-+	status = acpi_bus_get_private_data(handle, (void **)&data);
-+	if (ACPI_SUCCESS(status))
-+		kfree(data);
-+
-+	acpi_bus_detach_private_data(handle);
-+}
-+
-+static int acpi_find_i2c(struct acpi_resource *ares, void *data)
-+{
-+	struct surface_i2c_lookup *lookup = data;
-+
-+	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
-+		return 1;
-+
-+	if (lookup->n++ == lookup->index && !lookup->addr)
-+		lookup->addr = ares->data.i2c_serial_bus.slave_address;
-+
-+	return 1;
-+}
-+
-+static int surface_i2c_resource_lookup(struct surface_i2c_data *cdata,
-+					unsigned int index)
-+{
-+	struct i2c_client *client = cdata->adp1;
-+	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
-+	struct surface_i2c_lookup lookup = {
-+		.cdata = cdata,
-+		.index = index,
-+	};
-+	struct list_head res_list;
-+	int ret;
-+
-+	INIT_LIST_HEAD(&res_list);
-+
-+	ret = acpi_dev_get_resources(adev, &res_list, acpi_find_i2c, &lookup);
-+	if (ret < 0)
-+		return ret;
-+
-+	acpi_dev_free_resource_list(&res_list);
-+
-+	if (!lookup.addr)
-+		return -ENOENT;
-+
-+	return lookup.addr;
-+}
-+
-+static void surface_i2c_dump_registers(struct i2c_client *client,
-+				    struct i2c_client *bat0)
-+{
-+	char rd_buf[60];
-+	int error, i, c;
-+	char buff[17 * 3 * 2] = {0};
-+
-+	dev_info(&client->dev, "dumping registers 0x00 to 0x7F:\n");
-+
-+	for (i = 0; i < 0x80; i += 0x20) {
-+		memset(rd_buf, 0, sizeof(rd_buf));
-+		error = surface_i2c_read_block(bat0, i, rd_buf, 0x20);
-+		dev_info(&client->dev, " read 0x%02x: %*ph|%*ph\n",
-+			 i,
-+			 0x10, rd_buf,
-+			 0x10, rd_buf + 0x10);
-+		for (c = 0; c < 0x20; c++) {
-+			if (rd_buf[c] >= 0x20 && rd_buf[c] <= 0x7e) {
-+				buff[c * 3 + 0] = ' ';
-+				buff[c * 3 + 1] = rd_buf[c];
-+			} else {
-+				buff[c * 3 + 0] = '-';
-+				buff[c * 3 + 1] = '-';
-+			}
-+			buff[c * 3 + 2] = (c + 1) % 0x10 ? ' ' : '|';
-+		}
-+		buff[0x1f * 3 + 2] = '\0';
-+		dev_info(&client->dev, "ascii 0x%02x: %s\n", i, buff);
-+	}
-+}
-+
-+static int surface_i2c_probe(struct i2c_client *client)
-+{
-+	struct device *dev = &client->dev;
-+	struct i2c_client *bat0;
-+	struct surface_i2c_data *data;
-+	int error, version, addr;
-+
-+	pr_info("surface_i2c: Probing for surface i2c device...\n");
-+
-+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
-+	if (!data)
-+		return -ENOMEM;
-+
-+	data->adp1 = client;
-+	i2c_set_clientdata(client, data);
-+
-+	addr = surface_i2c_resource_lookup(data, 1);
-+	if (addr < 0)
-+		return addr;
-+
-+	bat0 = i2c_new_dummy(client->adapter, addr);
-+	if (!bat0)
-+		return -ENOMEM;
-+
-+	data->bat0 = bat0;
-+	i2c_set_clientdata(bat0, data);
-+
-+	// debugging
-+	surface_i2c_dump_registers(client, bat0);
-+
-+	pr_info("surface_i2c: Attaching device MSHW0124...");
-+
-+	error = surface_i2c_notify(data, 1, surface_i2c_NOTIFY_GET_VERSION, &version);
-+	if (error)
-+		goto out_err;
-+
-+	data->notify_version = version == surface_i2c_EV_2_5;
-+
-+	data->poll_task = kthread_run(surface_i2c_poll_task, data, "surface_i2c_adp");
-+	if (IS_ERR(data->poll_task)) {
-+		error = PTR_ERR(data->poll_task);
-+		dev_err(&client->dev, "Unable to run kthread err %d\n", error);
-+		goto out_err;
-+	}
-+
-+	//error = surface_i2c_install_space_handler(client);
-+	//if (error)
-+	//	goto out_err;
-+
-+	return 0;
-+
-+out_err:
-+	if (data->kthread_running)
-+		kthread_stop(data->poll_task);
-+	i2c_unregister_device(data->bat0);
-+	return error;
-+}
-+
-+static int surface_i2c_remove(struct i2c_client *client)
-+{
-+	struct surface_i2c_data *cdata = i2c_get_clientdata(client);
-+
-+	surface_i2c_remove_space_handler(client);
-+
-+	if (cdata->kthread_running)
-+		kthread_stop(cdata->poll_task);
-+
-+	i2c_unregister_device(cdata->bat0);
-+
-+	return 0;
-+}
-+
-+int surface_i2c_detect(struct i2c_client* client, struct i2c_board_info* board_info)
-+{
-+    pr_info("surface_i2c: Detecting surface_i2c device...");
-+    return 0;
-+}
-+
-+static const struct acpi_device_id surface_i2c_acpi_match[] = {
-+	{ "MSHW0124", 0 },
-+	{ "", 0 }
-+};
-+MODULE_DEVICE_TABLE(acpi, surface_i2c_acpi_match);
-+
-+static const struct i2c_device_id surface_i2c_id[] = {
-+	{ "MSHW0124:00", 0 },
-+	{ "MSHW0124:01", 0 },
-+	{ "", 0 }
-+};
-+MODULE_DEVICE_TABLE(i2c, surface_i2c_id);
-+
-+static struct i2c_driver surface_i2c_driver = {
-+	.probe_new = surface_i2c_probe,
-+	.remove = surface_i2c_remove,
-+	.detect = surface_i2c_detect,
-+	.id_table = surface_i2c_id,
-+	.driver = {
-+		.name = "surface_i2c",
-+		.acpi_match_table = ACPI_PTR(surface_i2c_acpi_match),
-+	},
-+};
-+module_i2c_driver(surface_i2c_driver);
-+
-+MODULE_AUTHOR("Jake Day");
-+MODULE_DESCRIPTION("Microsoft Surface I2C Driver");
-+MODULE_LICENSE("GPL");
-diff --git a/drivers/platform/x86/surface_platform.c b/drivers/platform/x86/surface_platform.c
-new file mode 100644
-index 000000000000..7a84340b04bb
---- /dev/null
-+++ b/drivers/platform/x86/surface_platform.c
-@@ -0,0 +1,67 @@
-+/*
-+ *  surface_platform.c - Microsoft Surface Platform Driver
-+ *
-+ *  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".
-+ */
-+
-+#include <linux/acpi.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/platform_device.h>
-+#include <linux/power_supply.h>
-+#include <linux/thermal.h>
-+#include <linux/uuid.h>
-+#include <linux/workqueue.h>
-+
-+#include <asm/unaligned.h>
-+
-+struct surface_platform_data {
-+	struct device *dev;
-+};
-+
-+static int surface_platform_probe(struct platform_device *pdev)
-+{
-+	struct surface_platform_data *pdata;
-+
-+	platform_set_drvdata(pdev, pdata);
-+	return 0;
-+}
-+
-+static int surface_platform_remove(struct platform_device *pdev)
-+{
-+	struct surface_platform_data *pdata = platform_get_drvdata(pdev);
-+}
-+
-+static const struct acpi_device_id surface_platform_acpi_match[] = {
-+	{ "MSHW0091", 0 },
-+	{ "INT3403", 0 },
-+	{ "", 0 }
-+};
-+MODULE_DEVICE_TABLE(acpi, surface_platform_acpi_match);
-+
-+static struct platform_driver surface_platform_driver = {
-+	.probe = surface_platform_probe,
-+	.remove = surface_platform_remove,
-+	.driver = {
-+		.name = "surface_platform",
-+		.acpi_match_table = ACPI_PTR(surface_platform_acpi_match),
-+	},
-+};
-+module_platform_driver(surface_platform_driver);
-+
-+MODULE_AUTHOR("Jake Day");
-+MODULE_DESCRIPTION("Microsoft Surface Platform Driver");
-+MODULE_LICENSE("GPL");
-diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
-index 87331565e505..d55f949ee25a 100644
---- a/kernel/power/suspend.c
-+++ b/kernel/power/suspend.c
-@@ -520,6 +520,8 @@ int suspend_devices_and_enter(suspend_state_t state)
- 	goto Resume_devices;
- }
- 
-+unsigned int resume_delay = 3000;
-+
- /**
-  * suspend_finish - Clean up before finishing the suspend sequence.
-  *
-@@ -528,6 +530,15 @@ int suspend_devices_and_enter(suspend_state_t state)
-  */
- static void suspend_finish(void)
- {
-+	if (resume_delay) {
-+		/* Give kernel threads a head start, such that usb-storage
-+		 * can detect devices before syslog attempts to write log
-+		 * messages from the suspend code.
-+		 */
-+		thaw_kernel_threads();
-+		pr_debug("PM: Sleeping for %d milliseconds.\n", resume_delay);
-+		msleep(resume_delay);
-+	}
- 	suspend_thaw_processes();
- 	pm_notifier_call_chain(PM_POST_SUSPEND);
- 	pm_restore_console();
-diff --git a/kernel/sysctl.c b/kernel/sysctl.c
-index 2d9837c0aff4..ac704bf71f45 100644
---- a/kernel/sysctl.c
-+++ b/kernel/sysctl.c
-@@ -308,7 +308,16 @@ static int min_extfrag_threshold;
- static int max_extfrag_threshold = 1000;
- #endif
- 
-+extern unsigned int resume_delay;
-+
- static struct ctl_table kern_table[] = {
-+	{
-+		.procname	= "resume_delay",
-+		.data		= &resume_delay,
-+		.maxlen		= sizeof(unsigned int),
-+		.mode		= 0644,
-+		.proc_handler	= proc_dointvec,
-+	},
- 	{
- 		.procname	= "sched_child_runs_first",
- 		.data		= &sysctl_sched_child_runs_first,

+ 0 - 555
patches/4.18/keyboards_and_covers.patch

@@ -1,555 +0,0 @@
-diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
-index c7981ddd8776..914a19b4290a 100644
---- a/drivers/hid/hid-ids.h
-+++ b/drivers/hid/hid-ids.h
-@@ -793,11 +793,21 @@
- #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732
- #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600  0x0750
- #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500	0x076c
--#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3
--#define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
--#define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
--#define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
--#define USB_DEVICE_ID_MS_POWER_COVER     0x07da
-+#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD	0x00e3
-+#define USB_DEVICE_ID_MS_SURFACE_PRO_2		0x0799
-+#define USB_DEVICE_ID_MS_TOUCH_COVER_2		0x07a7
-+#define USB_DEVICE_ID_MS_TYPE_COVER_2		0x07a9
-+#define USB_DEVICE_ID_MS_TYPE_COVER_3		0x07de
-+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3	0x07dc
-+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1	0x07de
-+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2	0x07e2
-+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP	0x07dd
-+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4	0x07e8
-+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1	0x07e4
-+#define USB_DEVICE_ID_MS_SURFACE_BOOK		0x07cd
-+#define USB_DEVICE_ID_MS_SURFACE_BOOK_2		0x0922
-+#define USB_DEVICE_ID_MS_SURFACE_LAPTOP		0xf001
-+#define USB_DEVICE_ID_MS_POWER_COVER		0x07da
- 
- #define USB_VENDOR_ID_MOJO		0x8282
- #define USB_DEVICE_ID_RETRO_ADAPTER	0x3201
-diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
-index 96e7d3231d2f..e55097221eec 100644
---- a/drivers/hid/hid-microsoft.c
-+++ b/drivers/hid/hid-microsoft.c
-@@ -278,7 +278,8 @@ static const struct hid_device_id ms_devices[] = {
- 		.driver_data = MS_HIDINPUT },
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
- 		.driver_data = MS_ERGONOMY},
--
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_LAPTOP),
-+		.driver_data = MS_HIDINPUT},
- 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
- 		.driver_data = MS_PRESENTER },
- 	{ }
-diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
-index a0e053c4dc1c..5298793b1eaf 100644
---- a/drivers/hid/hid-multitouch.c
-+++ b/drivers/hid/hid-multitouch.c
-@@ -1754,6 +1754,58 @@ static const struct hid_device_id mt_devices[] = {
- 		HID_USB_DEVICE(USB_VENDOR_ID_LG,
- 			USB_DEVICE_ID_LG_MELFAS_MT) },
- 
-+	/* Microsoft Touch Cover */
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+		USB_DEVICE_ID_MS_TOUCH_COVER_2) },
-+
-+	/* Microsoft Type Cover */
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_2) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_3) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) },
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1) },
-+
-+	/* Microsoft Surface Book */
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+		USB_DEVICE_ID_MS_SURFACE_BOOK) },
-+
-+	/* Microsoft Surface Book 2 */
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+		USB_DEVICE_ID_MS_SURFACE_BOOK_2) },
-+
-+	/* Microsoft Surface Laptop */
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
-+			USB_VENDOR_ID_MICROSOFT,
-+			USB_DEVICE_ID_MS_SURFACE_LAPTOP) },
-+
-+	/* Microsoft Power Cover */
-+	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
-+		MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT,
-+		USB_DEVICE_ID_MS_POWER_COVER) },
-+
- 	/* MosArt panels */
- 	{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
- 		MT_USB_DEVICE(USB_VENDOR_ID_ASUS,
-diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
-index 249d49b6b16c..28fa76011df3 100644
---- a/drivers/hid/hid-quirks.c
-+++ b/drivers/hid/hid-quirks.c
-@@ -111,6 +111,16 @@ static const struct hid_device_id hid_quirks[] = {
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS },
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS },
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK_2), HID_QUIRK_NO_INIT_REPORTS },
-+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_LAPTOP), HID_QUIRK_ALWAYS_POLL },
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER), HID_QUIRK_MULTI_INPUT },
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS },
- 	{ HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT },
-diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
-index 28c4cede2d91..4afa91fdb4da 100644
---- a/drivers/platform/x86/Makefile
-+++ b/drivers/platform/x86/Makefile
-@@ -85,6 +85,8 @@ obj-$(CONFIG_SURFACEBOOK2_BUTTON) += surfacebook2_button.o
- obj-$(CONFIG_ACPI_SURFACE)	+= surface_acpi.o
- obj-$(CONFIG_ACPI_SURFACE)	+= surface_i2c.o
- obj-$(CONFIG_ACPI_SURFACE)	+= surface_platform.o
-+obj-$(CONFIG_ACPI_SURFACE)	+= surface_vhf.o
-+obj-$(CONFIG_ACPI_SURFACE)	+= surface_vhf_keyboard.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_vhf.c b/drivers/platform/x86/surface_vhf.c
-new file mode 100644
-index 000000000000..2e9f2a670455
---- /dev/null
-+++ b/drivers/platform/x86/surface_vhf.c
-@@ -0,0 +1,328 @@
-+/*
-+ *  surface_vhf.c - Microsoft Surface Virtual HID Framework Driver
-+ *
-+ *  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 the distribution in
-+ * the file called "COPYING".
-+ */
-+
-+#include <linux/acpi.h>
-+#include <linux/dmi.h>
-+#include <linux/device.h>
-+#include <linux/hid.h>
-+#include <linux/input.h>
-+#include <linux/input/sparse-keymap.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+#include <linux/hid-sensor-hub.h>
-+#include <linux/suspend.h>
-+#include <linux/clk.h>
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/interrupt.h>
-+#include <linux/io.h>
-+#include <linux/irq.h>
-+#include <linux/pm_wakeup.h>
-+#include <linux/slab.h>
-+#include <linux/types.h>
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Jake Day");
-+
-+#define STATUS_REG	0x0C
-+#define DATA_REG	0x10
-+#define DATA_AVAIL	0x2
-+
-+static const struct acpi_device_id surface_vhf_ids[] = {
-+	{"MSHW0096", 0},
-+	{"", 0},
-+};
-+
-+struct surface_kbd {
-+	struct input_dev *input;
-+	struct resource *res;
-+	void __iomem *io_base;
-+	struct clk *clk;
-+	unsigned short keycodes[256];
-+};
-+
-+struct kbd_platform_data {
-+	const struct matrix_keymap_data *keymap;
-+	bool rep;
-+};
-+
-+static const struct key_entry surface_vhf_keymap[] = {
-+	{ KE_KEY, 1, { KEY_ESC } },
-+	{ KE_KEY, 2, { KEY_F1 } },
-+	{ KE_KEY, 3, { KEY_F2 } },
-+	{ KE_KEY, 4, { KEY_F3 } },
-+	{ KE_KEY, 5, { KEY_F4 } },
-+	{ KE_KEY, 6, { KEY_F5 } },
-+	{ KE_KEY, 7, { KEY_F6 } },
-+	{ KE_KEY, 8, { KEY_F7 } },
-+	{ KE_KEY, 9, { KEY_F8 } },
-+	{ KE_KEY, 10, { KEY_F9 } },
-+	{ KE_KEY, 11, { KEY_F10 } },
-+	{ KE_KEY, 12, { KEY_F11 } },
-+	{ KE_KEY, 13, { KEY_F12 } },
-+	{ KE_KEY, 14, { KEY_POWER } },
-+	{ KE_KEY, 15, { KEY_DELETE } },
-+	{ KE_KEY, 16, { KEY_GRAVE } },
-+	{ KE_KEY, 17, { KEY_1 } },
-+	{ KE_KEY, 18, { KEY_2 } },
-+	{ KE_KEY, 19, { KEY_3 } },
-+	{ KE_KEY, 20, { KEY_4 } },
-+	{ KE_KEY, 21, { KEY_5 } },
-+	{ KE_KEY, 23, { KEY_6 } },
-+	{ KE_KEY, 23, { KEY_7 } },
-+	{ KE_KEY, 24, { KEY_8 } },
-+	{ KE_KEY, 25, { KEY_9 } },
-+	{ KE_KEY, 26, { KEY_0 } },
-+	{ KE_KEY, 27, { KEY_MINUS } },
-+	{ KE_KEY, 28, { KEY_EQUAL } },
-+	{ KE_KEY, 29, { KEY_BACKSPACE } },
-+	{ KE_KEY, 30, { KEY_TAB } },
-+	{ KE_KEY, 31, { KEY_Q } },
-+	{ KE_KEY, 32, { KEY_W } },
-+	{ KE_KEY, 33, { KEY_E } },
-+	{ KE_KEY, 34, { KEY_R } },
-+	{ KE_KEY, 35, { KEY_T } },
-+	{ KE_KEY, 36, { KEY_Y } },
-+	{ KE_KEY, 37, { KEY_U } },
-+	{ KE_KEY, 38, { KEY_I } },
-+	{ KE_KEY, 39, { KEY_O } },
-+	{ KE_KEY, 40, { KEY_P } },
-+	{ KE_KEY, 41, { KEY_LEFTBRACE } },
-+	{ KE_KEY, 42, { KEY_RIGHTBRACE } },
-+	{ KE_KEY, 43, { KEY_BACKSLASH } },
-+	{ KE_KEY, 44, { KEY_CAPSLOCK } },
-+	{ KE_KEY, 45, { KEY_A } },
-+	{ KE_KEY, 46, { KEY_S } },
-+	{ KE_KEY, 47, { KEY_D } },
-+	{ KE_KEY, 48, { KEY_F } },
-+	{ KE_KEY, 49, { KEY_G } },
-+	{ KE_KEY, 50, { KEY_H } },
-+	{ KE_KEY, 51, { KEY_J } },
-+	{ KE_KEY, 52, { KEY_K } },
-+	{ KE_KEY, 53, { KEY_L } },
-+	{ KE_KEY, 54, { KEY_SEMICOLON } },
-+	{ KE_KEY, 55, { KEY_APOSTROPHE } },
-+	{ KE_KEY, 56, { KEY_ENTER } },
-+	{ KE_KEY, 57, { KEY_LEFTSHIFT } },
-+	{ KE_KEY, 58, { KEY_Z } },
-+	{ KE_KEY, 59, { KEY_X } },
-+	{ KE_KEY, 60, { KEY_C } },
-+	{ KE_KEY, 61, { KEY_V } },
-+	{ KE_KEY, 62, { KEY_B } },
-+	{ KE_KEY, 63, { KEY_N } },
-+	{ KE_KEY, 64, { KEY_M } },
-+	{ KE_KEY, 65, { KEY_COMMA } },
-+	{ KE_KEY, 66, { KEY_DOT } },
-+	{ KE_KEY, 67, { KEY_SLASH } },
-+	{ KE_KEY, 68, { KEY_RIGHTSHIFT } },
-+	{ KE_KEY, 69, { KEY_LEFTCTRL } },
-+	{ KE_KEY, 70, { KEY_FN } },
-+	{ KE_KEY, 71, { KEY_KPASTERISK } },
-+	{ KE_KEY, 72, { KEY_LEFTALT } },
-+	{ KE_KEY, 73, { KEY_SPACE } },
-+	{ KE_KEY, 74, { KEY_RIGHTALT } },
-+	{ KE_KEY, 75, { KEY_MENU } },
-+	{ KE_KEY, 76, { KEY_LEFT } },
-+	{ KE_KEY, 77, { KEY_UP } },
-+	{ KE_KEY, 78, { KEY_DOWN } },
-+	{ KE_KEY, 79, { KEY_RIGHT } },
-+	{ KE_END },
-+};
-+
-+
-+static irqreturn_t surface_kbd_interrupt(int irq, void *dev_id)
-+{
-+	struct surface_kbd *kbd = dev_id;
-+	struct input_dev *input = kbd->input;
-+	unsigned int key;
-+	u8 sts, val;
-+
-+	sts = readb(kbd->io_base + STATUS_REG);
-+	if (!(sts & DATA_AVAIL))
-+		return IRQ_NONE;
-+
-+	val = readb(kbd->io_base + DATA_REG);
-+	key = kbd->keycodes[val];
-+
-+	input_event(input, EV_MSC, MSC_SCAN, val);
-+	input_report_key(input, key, 1);
-+	input_sync(input);
-+
-+	writeb(0, kbd->io_base + STATUS_REG);
-+
-+	return IRQ_HANDLED;
-+}
-+
-+static int surface_vhf_probe(struct platform_device *pdev)
-+{
-+	struct resource *res;
-+	struct surface_kbd *kbd;
-+	struct input_dev *input_dev;
-+	int error;
-+	int ret;
-+	int irq;
-+
-+	pr_info("Surface VHF found\n");
-+
-+	pr_info("Surface VHF resources: %u\n", pdev->num_resources);
-+
-+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+	if (!res) {
-+		dev_err(&pdev->dev, "Surface VHF: No keyboard resource defined\n");
-+		return -EBUSY;
-+	}
-+
-+	irq = platform_get_irq(pdev, 0);
-+	if (irq < 0) {
-+		dev_err(&pdev->dev, "not able to get irq for the device\n");
-+		return irq;
-+	}
-+
-+	kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
-+	input_dev = input_allocate_device();
-+	if (!kbd || !input_dev) {
-+		dev_err(&pdev->dev, "Surface VHF: Out of memory\n");
-+		error = -ENOMEM;
-+		goto err_free_mem;
-+	}
-+
-+	kbd->input = input_dev;
-+
-+	/*kbd->res = request_mem_region(res->start, resource_size(res),
-+				      pdev->name);
-+	if (!kbd->res) {
-+		dev_err(&pdev->dev, "keyboard region already claimed\n");
-+		error = -EBUSY;
-+		goto err_free_mem;
-+	}*/
-+
-+	kbd->io_base = ioremap(res->start, resource_size(res));
-+	if (!kbd->io_base) {
-+		dev_err(&pdev->dev, "Surface VHF: ioremap failed for kbd region\n");
-+		error = -ENOMEM;
-+		goto err_release_mem_region;
-+	}
-+
-+	kbd->clk = clk_get(&pdev->dev, NULL);
-+	if (IS_ERR(kbd->clk)) {
-+		error = PTR_ERR(kbd->clk);
-+		goto err_iounmap;
-+	}
-+
-+	input_dev->name = "Surface Laptop Keyboard";
-+	input_dev->phys = "keyboard/input0";
-+	input_dev->dev.parent = &pdev->dev;
-+	input_dev->id.bustype = BUS_HOST;
-+	input_dev->id.vendor = 0x045e;
-+	input_dev->id.product = 0xf001;
-+	input_dev->id.version = 0x0001;
-+
-+	__set_bit(EV_KEY, input_dev->evbit);
-+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
-+
-+	input_dev->keycode = kbd->keycodes;
-+	input_dev->keycodesize = sizeof(kbd->keycodes[0]);
-+	input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes);
-+
-+	input_set_drvdata(input_dev, kbd);
-+
-+	ret = sparse_keymap_setup(input_dev, surface_vhf_keymap, NULL);
-+	if (ret)
-+		return ret;
-+
-+	error = request_irq(irq, surface_kbd_interrupt, 0, "keyboard", kbd);
-+	if (error) {
-+		dev_err(&pdev->dev, "Surface VHF: Request_irq fail\n");
-+		goto err_put_clk;
-+	}
-+
-+	error = input_register_device(input_dev);
-+	if (error) {
-+		dev_err(&pdev->dev, "Surface VHF: Unable to register keyboard device\n");
-+		return 0;
-+	}
-+
-+	device_init_wakeup(&pdev->dev, 1);
-+	platform_set_drvdata(pdev, kbd);
-+
-+	return 0;
-+
-+/*err_free_irq:
-+	free_irq(kbd->irq, kbd);*/
-+err_put_clk:
-+	clk_put(kbd->clk);
-+err_iounmap:
-+	iounmap(kbd->io_base);
-+err_release_mem_region:
-+	release_mem_region(res->start, resource_size(res));
-+err_free_mem:
-+	input_free_device(input_dev);
-+	kfree(kbd);
-+
-+	return error;
-+}
-+
-+static int surface_vhf_remove(struct platform_device *pdev)
-+{
-+	device_init_wakeup(&pdev->dev, false);
-+
-+	return 0;
-+}
-+
-+static struct platform_driver surface_vhf_driver = {
-+	.driver = {
-+		.name = "surface_vhf",
-+		.acpi_match_table = surface_vhf_ids,
-+	},
-+	.probe = surface_vhf_probe,
-+	.remove = surface_vhf_remove,
-+};
-+MODULE_DEVICE_TABLE(acpi, surface_vhf_ids);
-+
-+static acpi_status __init
-+check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
-+{
-+	const struct acpi_device_id *ids = context;
-+	struct acpi_device *dev;
-+
-+	if (acpi_bus_get_device(handle, &dev) != 0)
-+		return AE_OK;
-+
-+	if (acpi_match_device_ids(dev, ids) == 0)
-+		if (acpi_create_platform_device(dev, NULL))
-+			dev_info(&dev->dev,
-+				 "Surface VHF: Created platform device\n");
-+
-+	return AE_OK;
-+}
-+
-+static int __init surface_vhf_init(void)
-+{
-+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-+			    ACPI_UINT32_MAX, check_acpi_dev, NULL,
-+			    (void *)surface_vhf_ids, NULL);
-+
-+	return platform_driver_register(&surface_vhf_driver);
-+}
-+module_init(surface_vhf_init);
-+
-+static void __exit surface_vhf_exit(void)
-+{
-+	platform_driver_unregister(&surface_vhf_driver);
-+}
-+module_exit(surface_vhf_exit);
-diff --git a/drivers/platform/x86/surface_vhf_keyboard.c b/drivers/platform/x86/surface_vhf_keyboard.c
-new file mode 100644
-index 000000000000..c619d599e4ea
---- /dev/null
-+++ b/drivers/platform/x86/surface_vhf_keyboard.c
-@@ -0,0 +1,73 @@
-+/*
-+ *  surface_vhf_keyboard.c - Microsoft Surface Virtual HID Framework Keyboard Device
-+ *
-+ *  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 the distribution in
-+ * the file called "COPYING".
-+ */
-+
-+#include <linux/acpi.h>
-+#include <linux/dmi.h>
-+#include <linux/device.h>
-+#include <linux/hid.h>
-+#include <linux/input.h>
-+#include <linux/input/sparse-keymap.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+#include <linux/hid-sensor-hub.h>
-+#include <linux/suspend.h>
-+#include <linux/clk.h>
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/interrupt.h>
-+#include <linux/io.h>
-+#include <linux/irq.h>
-+#include <linux/pm_wakeup.h>
-+#include <linux/slab.h>
-+#include <linux/types.h>
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Jake Day");
-+
-+static struct resource surface_vhf_keyboard_resources[] = {
-+	{
-+		.start	= 0x1a7bbaf9,
-+		.end	= 0x2d356b9e,
-+		.flags	= IORESOURCE_MEM,
-+		.name	= "io-memory"
-+	},
-+	{
-+		.start	= 21,
-+		.end	= 21,
-+		.flags	= IORESOURCE_IRQ,
-+		.name	= "irq",
-+	}
-+};
-+
-+static struct platform_device surface_vhf_keyboard = {
-+	.name 		= "surface_vhf",
-+	.resource	= surface_vhf_keyboard_resources,
-+	.num_resources	= ARRAY_SIZE(surface_vhf_keyboard_resources),
-+};
-+
-+static int __init surface_hid_init(void)
-+{
-+	return platform_device_register(&surface_vhf_keyboard);
-+}
-+module_init(surface_hid_init);
-+
-+static void __exit surface_hid_exit(void)
-+{
-+	platform_device_unregister(&surface_vhf_keyboard);
-+}
-+module_exit(surface_hid_exit);

+ 0 - 3
patches/4.18/wifi.patch

@@ -1,3 +0,0 @@
-diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl
-old mode 100755
-new mode 100644