From 00e270fc8a4656ce25548c68dda913ff3ba9261c Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 30 Jul 2020 20:38:28 +0200 Subject: [PATCH] Update v5.4 patches Changes: SAM: - Enable performance modes on Surface Laptop 1 and 2. - Support for D0-exit/D0-entry notifications, sent to the SAM controller when suspending (D0-exit) or resuming (D0-entry) on newer (gen. 7+) devices. - Replace sysfs-based debug device with new debugfs-based debug device. - More internal restructuring and internal API changes. - Some small bug fixes. SAM-over-HID: - Add ACPI support for SAM-over-HID. This allows setting dGPU power on the Surface Book 1 via ACPI calls. Links: - kernel: https://github.com/linux-surface/kernel/commit/91f5a27a55b62e506b4faefdd84af2014de47d79 - SAM: https://github.com/linux-surface/surface-aggregator-module/commit/89b8232d49856a3a1e9710761a87d81c347b574f --- configs/surface-5.4.config | 3 +- patches/5.4/0001-surface3-power.patch | 12 +- patches/5.4/0002-surface3-oemb.patch | 12 +- patches/5.4/0003-surface-sam.patch | 4196 +++++++++-------- patches/5.4/0004-surface-sam-over-hid.patch | 65 + .../5.4/{0004-wifi.patch => 0005-wifi.patch} | 12 +- .../5.4/{0005-ipts.patch => 0006-ipts.patch} | 80 +- 7 files changed, 2466 insertions(+), 1914 deletions(-) create mode 100644 patches/5.4/0004-surface-sam-over-hid.patch rename patches/5.4/{0004-wifi.patch => 0005-wifi.patch} (97%) rename patches/5.4/{0005-ipts.patch => 0006-ipts.patch} (97%) diff --git a/configs/surface-5.4.config b/configs/surface-5.4.config index da9494aef..8838751af 100644 --- a/configs/surface-5.4.config +++ b/configs/surface-5.4.config @@ -4,7 +4,8 @@ CONFIG_GPIO_SYSFS=y # required for SURFACE_SAM_HPS CONFIG_SURFACE_SAM=m CONFIG_SURFACE_SAM_SSH=m -CONFIG_SURFACE_SAM_SSH_DEBUG_DEVICE=y +CONFIG_SURFACE_SAM_SSH_ERROR_INJECTION=n +CONFIG_SURFACE_SAM_DEBUGFS=m CONFIG_SURFACE_SAM_SAN=m CONFIG_SURFACE_SAM_VHF=m CONFIG_SURFACE_SAM_DTX=m diff --git a/patches/5.4/0001-surface3-power.patch b/patches/5.4/0001-surface3-power.patch index b9279eea3..0beb5ec2d 100644 --- a/patches/5.4/0001-surface3-power.patch +++ b/patches/5.4/0001-surface3-power.patch @@ -1,7 +1,7 @@ -From b7d0622c7e15d3caeede840e33efed1c7dbbfaa4 Mon Sep 17 00:00:00 2001 +From 7f67d3c72b8968df814d0b94c2f2f8c6b621fbdf Mon Sep 17 00:00:00 2001 From: qzed Date: Tue, 17 Sep 2019 17:17:56 +0200 -Subject: [PATCH 1/5] surface3-power +Subject: [PATCH 1/6] surface3-power --- drivers/platform/x86/Kconfig | 7 + @@ -11,7 +11,7 @@ Subject: [PATCH 1/5] surface3-power create mode 100644 drivers/platform/x86/surface3_power.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 1cab99320514..348c795019fa 100644 +index 1cab993205142..348c795019fa4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1209,6 +1209,13 @@ config SURFACE_3_BUTTON @@ -29,7 +29,7 @@ index 1cab99320514..348c795019fa 100644 tristate "Intel P-Unit IPC Driver" ---help--- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 415104033060..6dd955ad9bf1 100644 +index 4151040330601..6dd955ad9bf18 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o @@ -42,7 +42,7 @@ index 415104033060..6dd955ad9bf1 100644 obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ diff --git a/drivers/platform/x86/surface3_power.c b/drivers/platform/x86/surface3_power.c new file mode 100644 -index 000000000000..e0af01a60302 +index 0000000000000..e0af01a603025 --- /dev/null +++ b/drivers/platform/x86/surface3_power.c @@ -0,0 +1,604 @@ @@ -651,5 +651,5 @@ index 000000000000..e0af01a60302 +MODULE_DESCRIPTION("mshw0011 driver"); +MODULE_LICENSE("GPL v2"); -- -2.27.0 +2.28.0 diff --git a/patches/5.4/0002-surface3-oemb.patch b/patches/5.4/0002-surface3-oemb.patch index 2061420aa..936b61f49 100644 --- a/patches/5.4/0002-surface3-oemb.patch +++ b/patches/5.4/0002-surface3-oemb.patch @@ -1,7 +1,7 @@ -From f31a2997266f71cfe81a3cc9b1f27042ff0e76d8 Mon Sep 17 00:00:00 2001 +From e09db7fd5b8a8589a26b618fb1aa8903edc337fc Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Tue, 18 Sep 2018 11:01:37 +0800 -Subject: [PATCH 2/5] surface3-oemb +Subject: [PATCH 2/6] surface3-oemb --- drivers/platform/x86/surface3-wmi.c | 7 +++++++ @@ -10,7 +10,7 @@ Subject: [PATCH 2/5] surface3-oemb 3 files changed, 24 insertions(+) diff --git a/drivers/platform/x86/surface3-wmi.c b/drivers/platform/x86/surface3-wmi.c -index 130b6f52a600..801083aa56d6 100644 +index 130b6f52a6001..801083aa56d6d 100644 --- a/drivers/platform/x86/surface3-wmi.c +++ b/drivers/platform/x86/surface3-wmi.c @@ -37,6 +37,13 @@ static const struct dmi_system_id surface3_dmi_table[] = { @@ -28,7 +28,7 @@ index 130b6f52a600..801083aa56d6 100644 { } }; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c -index c83f7f5da96b..e6bfe684d6be 100644 +index c83f7f5da96b7..e6bfe684d6be8 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3681,6 +3681,15 @@ static const struct dmi_system_id dmi_platform_data[] = { @@ -48,7 +48,7 @@ index c83f7f5da96b..e6bfe684d6be 100644 /* * Match for the GPDwin which unfortunately uses somewhat diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c -index d0fb43c2b9f6..0e938713cb13 100644 +index d0fb43c2b9f68..0e938713cb133 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -27,6 +27,14 @@ static const struct dmi_system_id cht_table[] = { @@ -67,5 +67,5 @@ index d0fb43c2b9f6..0e938713cb13 100644 }; -- -2.27.0 +2.28.0 diff --git a/patches/5.4/0003-surface-sam.patch b/patches/5.4/0003-surface-sam.patch index e8933b386..32696a7ed 100644 --- a/patches/5.4/0003-surface-sam.patch +++ b/patches/5.4/0003-surface-sam.patch @@ -1,7 +1,7 @@ -From 0d17ea2d04af09c1689b6c892fc67b635b588423 Mon Sep 17 00:00:00 2001 +From 25788573eb6b46e5a525013489433a3a7a6ffffd Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:11:08 +0200 -Subject: [PATCH 3/5] surface-sam +Subject: [PATCH 3/6] surface-sam --- drivers/acpi/acpica/dsopcode.c | 2 +- @@ -9,26 +9,28 @@ Subject: [PATCH 3/5] surface-sam drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/surface_sam/Kconfig | 176 + - drivers/platform/x86/surface_sam/Makefile | 15 + - .../x86/surface_sam/surface_sam_dtx.c | 590 ++ - .../x86/surface_sam/surface_sam_hps.c | 1297 +++++ - .../x86/surface_sam/surface_sam_san.c | 913 +++ + drivers/platform/x86/surface_sam/Makefile | 16 + + .../x86/surface_sam/surface_sam_debugfs.c | 270 + + .../x86/surface_sam/surface_sam_dtx.c | 582 ++ + .../x86/surface_sam/surface_sam_hps.c | 1287 ++++ + .../x86/surface_sam/surface_sam_san.c | 930 +++ .../x86/surface_sam/surface_sam_san.h | 30 + - .../x86/surface_sam/surface_sam_sid.c | 281 + + .../x86/surface_sam/surface_sam_sid.c | 283 + .../x86/surface_sam/surface_sam_sid_gpelid.c | 232 + - .../surface_sam/surface_sam_sid_perfmode.c | 216 + - .../x86/surface_sam/surface_sam_sid_power.c | 1154 ++++ - .../x86/surface_sam/surface_sam_sid_power.h | 15 + - .../x86/surface_sam/surface_sam_sid_vhf.c | 432 ++ - .../x86/surface_sam/surface_sam_sid_vhf.h | 13 + - .../x86/surface_sam/surface_sam_ssh.c | 5111 +++++++++++++++++ - .../x86/surface_sam/surface_sam_ssh.h | 488 ++ - .../x86/surface_sam/surface_sam_ssh_trace.h | 536 ++ - .../x86/surface_sam/surface_sam_vhf.c | 261 + + .../surface_sam/surface_sam_sid_perfmode.c | 214 + + .../x86/surface_sam/surface_sam_sid_power.c | 1054 ++++ + .../x86/surface_sam/surface_sam_sid_power.h | 16 + + .../x86/surface_sam/surface_sam_sid_vhf.c | 429 ++ + .../x86/surface_sam/surface_sam_sid_vhf.h | 14 + + .../x86/surface_sam/surface_sam_ssh.c | 5190 +++++++++++++++++ + .../x86/surface_sam/surface_sam_ssh.h | 717 +++ + .../x86/surface_sam/surface_sam_ssh_trace.h | 532 ++ + .../x86/surface_sam/surface_sam_vhf.c | 266 + drivers/tty/serdev/core.c | 111 +- - 22 files changed, 11871 insertions(+), 16 deletions(-) + 23 files changed, 12349 insertions(+), 16 deletions(-) create mode 100644 drivers/platform/x86/surface_sam/Kconfig create mode 100644 drivers/platform/x86/surface_sam/Makefile + create mode 100644 drivers/platform/x86/surface_sam/surface_sam_debugfs.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_dtx.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_hps.c create mode 100644 drivers/platform/x86/surface_sam/surface_sam_san.c @@ -46,7 +48,7 @@ Subject: [PATCH 3/5] surface-sam create mode 100644 drivers/platform/x86/surface_sam/surface_sam_vhf.c diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c -index 10f32b62608e..7b2a4987f050 100644 +index 10f32b62608ee..7b2a4987f0507 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, @@ -59,7 +61,7 @@ index 10f32b62608e..7b2a4987f050 100644 bit_count = (u32) length_desc->integer.value; diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c -index d3d2dbfba680..0b7f617a6e9b 100644 +index d3d2dbfba680c..0b7f617a6e9b1 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -109,6 +109,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, @@ -91,7 +93,7 @@ index d3d2dbfba680..0b7f617a6e9b 100644 buffer_desc = acpi_ut_create_buffer_object(buffer_length); if (!buffer_desc) { diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 348c795019fa..d25067a838a3 100644 +index 348c795019fa4..d25067a838a33 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1342,6 +1342,7 @@ config PCENGINES_APU2 @@ -103,7 +105,7 @@ index 348c795019fa..d25067a838a3 100644 endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 6dd955ad9bf1..19b56f2181eb 100644 +index 6dd955ad9bf18..19b56f2181eb9 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -101,3 +101,4 @@ obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o @@ -113,7 +115,7 @@ index 6dd955ad9bf1..19b56f2181eb 100644 +obj-$(CONFIG_SURFACE_SAM) += surface_sam/ diff --git a/drivers/platform/x86/surface_sam/Kconfig b/drivers/platform/x86/surface_sam/Kconfig new file mode 100644 -index 000000000000..7781c5cd932c +index 0000000000000..b5bb55248a5d5 --- /dev/null +++ b/drivers/platform/x86/surface_sam/Kconfig @@ -0,0 +1,176 @@ @@ -150,18 +152,6 @@ index 000000000000..7781c5cd932c + If you have a 5th generation (or later) Microsoft Surface device, say + Y or M here. + -+config SURFACE_SAM_SSH_DEBUG_DEVICE -+ bool "Surface Serial Hub Debug Device" -+ depends on SURFACE_SAM_SSH -+ depends on SYSFS -+ default n -+ help -+ Debug device for direct communication with the embedded controller -+ found on 5th generation (and later) Microsoft Surface devices (e.g. -+ Book 2, Laptop, Laptop 2, Pro 2017, Pro 6, ...) via sysfs. -+ -+ If you are not sure, say N here. -+ +config SURFACE_SAM_SSH_ERROR_INJECTION + bool "Surface Serial Hub Error Injection Capabilities" + depends on SURFACE_SAM_SSH @@ -174,6 +164,18 @@ index 000000000000..7781c5cd932c + + If you are not sure, say N here. + ++config SURFACE_SAM_DEBUGFS ++ tristate "Surface Serial Hub Debug Device" ++ depends on SURFACE_SAM_SSH ++ depends on DEBUG_FS ++ default n ++ help ++ Debug device for direct communication with the embedded controller ++ found on 5th generation (and later) Microsoft Surface devices (e.g. ++ Book 2, Laptop, Laptop 2, Pro 2017, Pro 6, ...) via debugfs. ++ ++ If you are not sure, say N here. ++ +config SURFACE_SAM_SAN + tristate "Surface ACPI Notify Driver" + depends on SURFACE_SAM_SSH @@ -295,10 +297,10 @@ index 000000000000..7781c5cd932c + If you are not sure, say M here. diff --git a/drivers/platform/x86/surface_sam/Makefile b/drivers/platform/x86/surface_sam/Makefile new file mode 100644 -index 000000000000..1a5c1260639d +index 0000000000000..89bced46ebcdd --- /dev/null +++ b/drivers/platform/x86/surface_sam/Makefile -@@ -0,0 +1,15 @@ +@@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# For include/trace/define_trace.h to include surface_sam_ssh_trace.h @@ -314,12 +316,289 @@ index 000000000000..1a5c1260639d +obj-$(CONFIG_SURFACE_SAM_SID_PERFMODE) += surface_sam_sid_perfmode.o +obj-$(CONFIG_SURFACE_SAM_SID_POWER) += surface_sam_sid_power.o +obj-$(CONFIG_SURFACE_SAM_SID_VHF) += surface_sam_sid_vhf.o ++obj-$(CONFIG_SURFACE_SAM_DEBUGFS) += surface_sam_debugfs.o +diff --git a/drivers/platform/x86/surface_sam/surface_sam_debugfs.c b/drivers/platform/x86/surface_sam/surface_sam_debugfs.c +new file mode 100644 +index 0000000000000..13e93404775c5 +--- /dev/null ++++ b/drivers/platform/x86/surface_sam/surface_sam_debugfs.c +@@ -0,0 +1,270 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "surface_sam_ssh.h" ++ ++#define SSAM_DBGDEV_NAME "surface_sam_dbgdev" ++#define SSAM_DBGDEV_VERS 0x0100 ++ ++ ++struct ssam_dbgdev_request { ++ __u8 target_category; ++ __u8 command_id; ++ __u8 instance_id; ++ __u8 channel; ++ __u16 flags; ++ __s16 status; ++ ++ struct { ++ __u8 __pad[6]; ++ __u16 length; ++ const __u8 __user *data; ++ } payload; ++ ++ struct { ++ __u8 __pad[6]; ++ __u16 length; ++ __u8 __user *data; ++ } response; ++}; ++ ++#define SSAM_DBGDEV_IOCTL_GETVERSION _IOR(0xA5, 0, __u32) ++#define SSAM_DBGDEV_IOCTL_REQUEST _IOWR(0xA5, 1, struct ssam_dbgdev_request) ++ ++ ++struct ssam_dbgdev { ++ struct ssam_controller *ctrl; ++ struct dentry *dentry_dir; ++ struct dentry *dentry_dev; ++}; ++ ++ ++static int ssam_dbgdev_open(struct inode *inode, struct file *filp) ++{ ++ filp->private_data = inode->i_private; ++ return nonseekable_open(inode, filp); ++} ++ ++static long ssam_dbgdev_request(struct file *file, unsigned long arg) ++{ ++ struct ssam_dbgdev *ddev = file->private_data; ++ struct ssam_dbgdev_request __user *r; ++ struct ssam_dbgdev_request rqst; ++ struct ssam_request spec; ++ struct ssam_response rsp; ++ u8 *pldbuf = NULL; ++ u8 *rspbuf = NULL; ++ int status = 0, ret = 0, tmp; ++ ++ r = (struct ssam_dbgdev_request __user *)arg; ++ ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r)); ++ if (ret) ++ goto out; ++ ++ // setup basic request fields ++ spec.target_category = rqst.target_category; ++ spec.command_id = rqst.command_id; ++ spec.instance_id = rqst.instance_id; ++ spec.channel = rqst.channel; ++ spec.flags = rqst.flags; ++ spec.length = rqst.payload.length; ++ ++ rsp.capacity = rqst.response.length; ++ rsp.length = 0; ++ ++ // get request payload from user-space ++ if (spec.length) { ++ if (!rqst.payload.data) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ pldbuf = kzalloc(spec.length, GFP_KERNEL); ++ if (!pldbuf) { ++ status = -ENOMEM; ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ if (copy_from_user(pldbuf, rqst.payload.data, spec.length)) { ++ ret = -EFAULT; ++ goto out; ++ } ++ } ++ spec.payload = pldbuf; ++ ++ // allocate response buffer ++ if (rsp.capacity) { ++ if (!rqst.response.data) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ rspbuf = kzalloc(rsp.capacity, GFP_KERNEL); ++ if (!rspbuf) { ++ status = -ENOMEM; ++ ret = -EFAULT; ++ goto out; ++ } ++ } ++ rsp.pointer = rspbuf; ++ ++ // perform request ++ status = ssam_request_sync(ddev->ctrl, &spec, &rsp); ++ if (status) ++ goto out; ++ ++ // copy response to user-space ++ if (rsp.length) { ++ if (copy_to_user(rqst.response.data, rsp.pointer, rsp.length)) { ++ ret = -EFAULT; ++ goto out; ++ } ++ } ++ ++out: ++ // always try to set response-length and status ++ tmp = put_user(rsp.length, &r->response.length); ++ if (!ret) ++ ret = tmp; ++ ++ tmp = put_user(status, &r->status); ++ if (!ret) ++ ret = tmp; ++ ++ // cleanup ++ if (pldbuf) ++ kfree(pldbuf); ++ ++ if (rspbuf) ++ kfree(rspbuf); ++ ++ return ret; ++} ++ ++static long ssam_dbgdev_getversion(struct file *file, unsigned long arg) ++{ ++ put_user(SSAM_DBGDEV_VERS, (u32 __user *)arg); ++ return 0; ++} ++ ++static long ssam_dbgdev_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ switch (cmd) { ++ case SSAM_DBGDEV_IOCTL_GETVERSION: ++ return ssam_dbgdev_getversion(file, arg); ++ ++ case SSAM_DBGDEV_IOCTL_REQUEST: ++ return ssam_dbgdev_request(file, arg); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++const struct file_operations ssam_dbgdev_fops = { ++ .owner = THIS_MODULE, ++ .open = ssam_dbgdev_open, ++ .unlocked_ioctl = ssam_dbgdev_ioctl, ++ .compat_ioctl = ssam_dbgdev_ioctl, ++ .llseek = noop_llseek, ++}; ++ ++static int ssam_dbgdev_probe(struct platform_device *pdev) ++{ ++ struct ssam_dbgdev *ddev; ++ struct ssam_controller *ctrl; ++ int status; ++ ++ status = ssam_client_bind(&pdev->dev, &ctrl); ++ if (status) ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ ++ ddev = devm_kzalloc(&pdev->dev, sizeof(struct ssam_dbgdev), GFP_KERNEL); ++ if (!ddev) ++ return -ENOMEM; ++ ++ ddev->ctrl = ctrl; ++ ++ ddev->dentry_dir = debugfs_create_dir("surface_sam", NULL); ++ if (IS_ERR(ddev->dentry_dir)) ++ return PTR_ERR(ddev->dentry_dir); ++ ++ ddev->dentry_dev = debugfs_create_file("controller", 0600, ++ ddev->dentry_dir, ddev, ++ &ssam_dbgdev_fops); ++ if (IS_ERR(ddev->dentry_dev)) { ++ debugfs_remove(ddev->dentry_dir); ++ return PTR_ERR(ddev->dentry_dev); ++ } ++ ++ platform_set_drvdata(pdev, ddev); ++ return 0; ++} ++ ++static int ssam_dbgdev_remove(struct platform_device *pdev) ++{ ++ struct ssam_dbgdev *ddev = platform_get_drvdata(pdev); ++ ++ debugfs_remove(ddev->dentry_dev); ++ debugfs_remove(ddev->dentry_dir); ++ ++ platform_set_drvdata(pdev, NULL); ++ return 0; ++} ++ ++static void ssam_dbgdev_release(struct device *dev) ++{ ++ // nothing to do ++} ++ ++ ++static struct platform_device ssam_dbgdev_device = { ++ .name = SSAM_DBGDEV_NAME, ++ .id = PLATFORM_DEVID_NONE, ++ .dev.release = ssam_dbgdev_release, ++}; ++ ++static struct platform_driver ssam_dbgdev_driver = { ++ .probe = ssam_dbgdev_probe, ++ .remove = ssam_dbgdev_remove, ++ .driver = { ++ .name = SSAM_DBGDEV_NAME, ++ }, ++}; ++ ++static int __init surface_sam_debugfs_init(void) ++{ ++ int status; ++ ++ status = platform_device_register(&ssam_dbgdev_device); ++ if (status) ++ return status; ++ ++ status = platform_driver_register(&ssam_dbgdev_driver); ++ if (status) ++ platform_device_unregister(&ssam_dbgdev_device); ++ ++ return status; ++} ++ ++static void __exit surface_sam_debugfs_exit(void) ++{ ++ platform_driver_unregister(&ssam_dbgdev_driver); ++ platform_device_unregister(&ssam_dbgdev_device); ++} ++ ++module_init(surface_sam_debugfs_init); ++module_exit(surface_sam_debugfs_exit); ++ ++MODULE_AUTHOR("Maximilian Luz "); ++MODULE_DESCRIPTION("DebugFS entries for Surface Aggregator Module"); ++MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_dtx.c b/drivers/platform/x86/surface_sam/surface_sam_dtx.c new file mode 100644 -index 000000000000..88dba7bced3a +index 0000000000000..9c844bb0f7739 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_dtx.c -@@ -0,0 +1,590 @@ +@@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Detachment system (DTX) driver for Microsoft Surface Book 2. @@ -355,13 +634,6 @@ index 000000000000..88dba7bced3a +#define DTX_CMD_LATCH_OPEN _IO(0x11, 0x04) +#define DTX_CMD_GET_OPMODE _IOR(0x11, 0x05, int) + -+#define SAM_RQST_DTX_TC 0x11 -+#define SAM_RQST_DTX_CID_LATCH_LOCK 0x06 -+#define SAM_RQST_DTX_CID_LATCH_UNLOCK 0x07 -+#define SAM_RQST_DTX_CID_LATCH_REQUEST 0x08 -+#define SAM_RQST_DTX_CID_LATCH_OPEN 0x09 -+#define SAM_RQST_DTX_CID_GET_OPMODE 0x0D -+ +#define SAM_EVENT_DTX_CID_CONNECTION 0x0c +#define SAM_EVENT_DTX_CID_BUTTON 0x0e +#define SAM_EVENT_DTX_CID_ERROR 0x0f @@ -392,6 +664,8 @@ index 000000000000..88dba7bced3a +} __packed; + +struct surface_dtx_dev { ++ struct ssam_controller *ctrl; ++ + struct ssam_event_notifier notif; + struct delayed_work opmode_work; + wait_queue_head_t waitq; @@ -418,61 +692,51 @@ index 000000000000..88dba7bced3a +static struct surface_dtx_dev surface_dtx_dev; + + -+static int surface_sam_query_opmpde(void) ++static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x06, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x07, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_request, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x08, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_open, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x09, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x0d, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++ ++static int dtx_bas_get_opmode(struct ssam_controller *ctrl, int __user *buf) +{ -+ u8 result_buf[1]; ++ u8 opmode; + int status; + -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = SAM_RQST_DTX_TC, -+ .cid = SAM_RQST_DTX_CID_GET_OPMODE, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surface_sam_ssh_buf result = { -+ .cap = 1, -+ .len = 0, -+ .data = result_buf, -+ }; -+ -+ status = surface_sam_ssh_rqst(&rqst, &result); -+ if (status) ++ status = ssam_bas_query_opmode(ctrl, &opmode); ++ if (status < 0) + return status; + -+ if (result.len != 1) -+ return -EFAULT; -+ -+ return result.data[0]; -+} -+ -+ -+static int dtx_cmd_simple(u8 cid) -+{ -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = SAM_RQST_DTX_TC, -+ .cid = cid, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x00, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ return surface_sam_ssh_rqst(&rqst, NULL); -+} -+ -+static int dtx_cmd_get_opmode(int __user *buf) -+{ -+ int opmode; -+ -+ opmode = surface_sam_query_opmpde(); -+ if (opmode < 0) -+ return opmode; -+ + if (put_user(opmode, buf)) + return -EACCES; + @@ -617,23 +881,23 @@ index 000000000000..88dba7bced3a + + switch (cmd) { + case DTX_CMD_LATCH_LOCK: -+ status = dtx_cmd_simple(SAM_RQST_DTX_CID_LATCH_LOCK); ++ status = ssam_bas_latch_lock(ddev->ctrl); + break; + + case DTX_CMD_LATCH_UNLOCK: -+ status = dtx_cmd_simple(SAM_RQST_DTX_CID_LATCH_UNLOCK); ++ status = ssam_bas_latch_unlock(ddev->ctrl); + break; + + case DTX_CMD_LATCH_REQUEST: -+ status = dtx_cmd_simple(SAM_RQST_DTX_CID_LATCH_REQUEST); ++ status = ssam_bas_latch_request(ddev->ctrl); + break; + + case DTX_CMD_LATCH_OPEN: -+ status = dtx_cmd_simple(SAM_RQST_DTX_CID_LATCH_OPEN); ++ status = ssam_bas_latch_open(ddev->ctrl); + break; + + case DTX_CMD_GET_OPMODE: -+ status = dtx_cmd_get_opmode((int __user *)arg); ++ status = dtx_bas_get_opmode(ddev->ctrl, (int __user *)arg); + break; + + default: @@ -698,12 +962,15 @@ index 000000000000..88dba7bced3a +static void surface_dtx_update_opmpde(struct surface_dtx_dev *ddev) +{ + struct surface_dtx_event event; -+ int opmode; ++ u8 opmode; ++ int status; + + // get operation mode -+ opmode = surface_sam_query_opmpde(); -+ if (opmode < 0) -+ printk(DTX_ERR "EC request failed with error %d\n", opmode); ++ status = ssam_bas_query_opmode(ddev->ctrl, &opmode); ++ if (status < 0) { ++ printk(DTX_ERR "EC request failed with error %d\n", status); ++ return; ++ } + + // send DTX event + event.type = 0x11; @@ -765,9 +1032,11 @@ index 000000000000..88dba7bced3a +} + + -+static struct input_dev *surface_dtx_register_inputdev(struct platform_device *pdev) ++static struct input_dev *surface_dtx_register_inputdev( ++ struct platform_device *pdev, struct ssam_controller *ctrl) +{ + struct input_dev *input_dev; ++ u8 opmode; + int status; + + input_dev = input_allocate_device(); @@ -782,13 +1051,13 @@ index 000000000000..88dba7bced3a + + input_set_capability(input_dev, EV_SW, SW_TABLET_MODE); + -+ status = surface_sam_query_opmpde(); ++ status = ssam_bas_query_opmode(ctrl, &opmode); + if (status < 0) { + input_free_device(input_dev); + return ERR_PTR(status); + } + -+ input_report_switch(input_dev, SW_TABLET_MODE, status != DTX_OPMODE_LAPTOP); ++ input_report_switch(input_dev, SW_TABLET_MODE, opmode != DTX_OPMODE_LAPTOP); + + status = input_register_device(input_dev); + if (status) { @@ -803,15 +1072,16 @@ index 000000000000..88dba7bced3a +static int surface_sam_dtx_probe(struct platform_device *pdev) +{ + struct surface_dtx_dev *ddev = &surface_dtx_dev; ++ struct ssam_controller *ctrl; + struct input_dev *input_dev; + int status; + + // link to ec -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + -+ input_dev = surface_dtx_register_inputdev(pdev); ++ input_dev = surface_dtx_register_inputdev(pdev, ctrl); + if (IS_ERR(input_dev)) + return PTR_ERR(input_dev); + @@ -823,6 +1093,7 @@ index 000000000000..88dba7bced3a + goto err_register; + } + ++ ddev->ctrl = ctrl; + INIT_DELAYED_WORK(&ddev->opmode_work, surface_dtx_opmode_workfn); + INIT_LIST_HEAD(&ddev->client_list); + init_waitqueue_head(&ddev->waitq); @@ -842,7 +1113,7 @@ index 000000000000..88dba7bced3a + ddev->notif.event.id.instance = 0; + ddev->notif.event.flags = SSAM_EVENT_SEQUENCED; + -+ status = surface_sam_ssh_notifier_register(&ddev->notif); ++ status = ssam_notifier_register(ctrl, &ddev->notif); + if (status) + goto err_events_setup; + @@ -871,7 +1142,7 @@ index 000000000000..88dba7bced3a + mutex_unlock(&ddev->mutex); + + // After this call we're guaranteed that no more input events will arive -+ surface_sam_ssh_notifier_unregister(&ddev->notif); ++ ssam_notifier_unregister(ddev->ctrl, &ddev->notif); + + // wake up clients + spin_lock(&ddev->client_lock); @@ -912,10 +1183,10 @@ index 000000000000..88dba7bced3a +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_hps.c b/drivers/platform/x86/surface_sam/surface_sam_hps.c new file mode 100644 -index 000000000000..40f39f29113c +index 0000000000000..b11f9fa8095fb --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_hps.c -@@ -0,0 +1,1297 @@ +@@ -0,0 +1,1287 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface dGPU hot-plug system driver. @@ -953,11 +1224,7 @@ index 000000000000..40f39f29113c + +#define SAM_DGPU_TC 0x13 +#define SAM_DGPU_CID_POWERON 0x02 -+ -+#define SAM_DTX_TC 0x11 -+#define SAM_DTX_CID_LATCH_LOCK 0x06 -+#define SAM_DTX_CID_LATCH_UNLOCK 0x07 -+#define ACPI_SGCP_NOTIFY_POWER_ON 0x81 ++#define ACPI_SGCP_NOTIFY_POWER_ON 0x81 + +#define SHPS_DSM_GPU_ADDRS_RP "RP5_PCIE" +#define SHPS_DSM_GPU_ADDRS_DGPU "DGPU_PCIE" @@ -1010,6 +1277,8 @@ index 000000000000..40f39f29113c +}; + +struct shps_driver_data { ++ struct ssam_controller *ctrl; ++ + struct mutex lock; + struct pci_dev *dgpu_root_port; + struct pci_saved_state *dgpu_root_port_state; @@ -1097,30 +1366,19 @@ index 000000000000..40f39f29113c +MODULE_PARM_DESC(dgpu_power_susp, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: as-is)"); +MODULE_PARM_DESC(dtx_latch, "lock/unlock DTX base latch in accordance to power-state (Y/n)"); + -+static int dtx_cmd_simple(u8 cid) -+{ -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = SAM_DTX_TC, -+ .cid = cid, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x00, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; ++static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x06, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); + -+ return surface_sam_ssh_rqst(&rqst, NULL); -+} -+ -+static inline int shps_dtx_latch_lock(void) -+{ -+ return dtx_cmd_simple(SAM_DTX_CID_LATCH_LOCK); -+} -+ -+static inline int shps_dtx_latch_unlock(void) -+{ -+ return dtx_cmd_simple(SAM_DTX_CID_LATCH_UNLOCK); -+} ++static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, { ++ .target_category = SSAM_SSH_TC_BAS, ++ .command_id = 0x07, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); + +static int shps_dgpu_dsm_get_pci_addr_from_adr(struct platform_device *pdev, const char *entry) { + acpi_handle handle = ACPI_HANDLE(&pdev->dev); @@ -1434,26 +1692,27 @@ index 000000000000..40f39f29113c + +static int shps_dgpu_set_power(struct platform_device *pdev, enum shps_dgpu_power power) +{ ++ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); + int status; + + if (!param_dtx_latch) + return shps_dgpu_rp_set_power(pdev, power); + + if (power == SHPS_DGPU_POWER_ON) { -+ status = shps_dtx_latch_lock(); ++ status = ssam_bas_latch_lock(drvdata->ctrl); + if (status) + return status; + + status = shps_dgpu_rp_set_power(pdev, power); + if (status) -+ shps_dtx_latch_unlock(); ++ ssam_bas_latch_unlock(drvdata->ctrl); + + } else { + status = shps_dgpu_rp_set_power(pdev, power); + if (status) + return status; + -+ status = shps_dtx_latch_unlock(); ++ status = ssam_bas_latch_unlock(drvdata->ctrl); + } + + return status; @@ -2015,6 +2274,7 @@ index 000000000000..40f39f29113c +{ + struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev); + struct shps_driver_data *drvdata; ++ struct ssam_controller *ctrl; + struct device_link *link; + int power, status; + struct shps_hardware_traits detected_traits; @@ -2025,7 +2285,7 @@ index 000000000000..40f39f29113c + } + + // link to SSH -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) { + return status == -ENXIO ? -EPROBE_DEFER : status; + } @@ -2056,6 +2316,7 @@ index 000000000000..40f39f29113c + mutex_init(&drvdata->lock); + platform_set_drvdata(pdev, drvdata); + ++ drvdata->ctrl = ctrl; + drvdata->hardware_traits = detected_traits; + + drvdata->dgpu_root_port = shps_dgpu_dsm_get_pci_dev(pdev); @@ -2215,16 +2476,17 @@ index 000000000000..40f39f29113c +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.c b/drivers/platform/x86/surface_sam/surface_sam_san.c new file mode 100644 -index 000000000000..11dd6daedc3d +index 0000000000000..eab4e178a8450 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_san.c -@@ -0,0 +1,913 @@ +@@ -0,0 +1,930 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface ACPI Notify (SAN) and ACPI integration driver for SAM. + * Translates communication from ACPI to SSH and back. + */ + ++#include +#include +#include +#include @@ -2254,11 +2516,6 @@ index 000000000000..11dd6daedc3d + +#define SAM_EVENT_TEMP_CID_NOTIFY_SENSOR_TRIP_POINT 0x0b + -+#define SAN_RQST_TAG "surface_sam_san: rqst: " -+#define SAN_RQSG_TAG "surface_sam_san: rqsg: " -+ -+#define SAN_QUIRK_BASE_STATE_DELAY 1000 -+ + +struct san_acpi_consumer { + char *path; @@ -2266,9 +2523,8 @@ index 000000000000..11dd6daedc3d + u32 flags; +}; + -+struct san_opreg_context { -+ struct acpi_connection_info connection; -+ struct device *dev; ++struct san_handler_data { ++ struct acpi_connection_info info; // must be first +}; + +struct san_consumer_link { @@ -2281,18 +2537,23 @@ index 000000000000..11dd6daedc3d + struct san_consumer_link *links; +}; + -+struct san_drvdata { -+ struct san_opreg_context opreg_ctx; ++struct san_data { ++ struct device *dev; ++ struct ssam_controller *ctrl; ++ ++ struct san_handler_data context; + struct san_consumers consumers; + -+ struct platform_device *dev; + struct ssam_event_notifier nf_bat; + struct ssam_event_notifier nf_tmp; +}; + ++#define to_san_data(ptr, member) \ ++ container_of(ptr, struct san_data, member) ++ +struct san_event_work { + struct delayed_work work; -+ struct platform_device *dev; ++ struct device *dev; + struct ssam_event event; // must be last +}; + @@ -2303,12 +2564,11 @@ index 000000000000..11dd6daedc3d +struct gsb_data_rqsx { + u8 cv; // command value (should be 0x01 or 0x03) + u8 tc; // target controller -+ u8 tid; // transport channnel ID? ++ u8 tid; // transport channnel ID + 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 ++ u16 cdl; // payload length + u8 pld[0]; // payload +} __packed; + @@ -2338,6 +2598,12 @@ index 000000000000..11dd6daedc3d + union gsb_buffer_data data; +} __packed; + ++#define SAN_GSB_MAX_RQSX_PAYLOAD (U8_MAX - 2 - sizeof(struct gsb_data_rqsx)) ++#define SAN_GSB_MAX_RESPONSE (U8_MAX - 2 - sizeof(struct gsb_data_out)) ++ ++#define san_request_sync_onstack(ctrl, rqst, rsp) \ ++ ssam_request_sync_onstack(ctrl, rqst, rsp, SAN_GSB_MAX_RQSX_PAYLOAD) ++ + +enum san_pwr_event { + SAN_PWR_EVENT_BAT1_STAT = 0x03, @@ -2394,7 +2660,7 @@ index 000000000000..11dd6daedc3d + + if (rqsg_if.handler == sam_san_default_rqsg_handler || !fn) { + rqsg_if.handler = fn ? fn : sam_san_default_rqsg_handler; -+ rqsg_if.handler_data = data; ++ rqsg_if.handler_data = fn ? data : NULL; + status = 0; + } + @@ -2416,8 +2682,10 @@ index 000000000000..11dd6daedc3d + +static int sam_san_default_rqsg_handler(struct surface_sam_san_rqsg *rqsg, void *data) +{ -+ pr_warn(SAN_RQSG_TAG "unhandled request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", -+ rqsg->tc, rqsg->cid, rqsg->iid); ++ struct device *dev = rqsg_if.san_dev; ++ ++ dev_warn(dev, "unhandled request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", ++ rqsg->tc, rqsg->cid, rqsg->iid); + + return 0; +} @@ -2621,19 +2889,19 @@ index 000000000000..11dd6daedc3d +{ + struct san_event_work *ev = container_of(work, struct san_event_work, work.work); + -+ san_evt_power(&ev->event, &ev->dev->dev); ++ san_evt_power(&ev->event, ev->dev); + kfree(ev); +} + + +static u32 san_evt_power_nb(struct ssam_notifier_block *nb, const struct ssam_event *event) +{ -+ struct san_drvdata *drvdata = container_of(nb, struct san_drvdata, nf_bat.base); ++ struct san_data *d = to_san_data(nb, nf_bat.base); + struct san_event_work *work; + unsigned long delay = san_evt_power_delay(event->command_id); + + if (delay == 0) { -+ if (san_evt_power(event, &drvdata->dev->dev)) ++ if (san_evt_power(event, d->dev)) + return SSAM_NOTIF_HANDLED; + else + return 0; @@ -2644,7 +2912,7 @@ index 000000000000..11dd6daedc3d + return ssam_notifier_from_errno(-ENOMEM); + + INIT_DELAYED_WORK(&work->work, san_evt_power_workfn); -+ work->dev = drvdata->dev; ++ work->dev = d->dev; + + memcpy(&work->event, event, sizeof(struct ssam_event) + event->length); + @@ -2681,10 +2949,7 @@ index 000000000000..11dd6daedc3d + +static u32 san_evt_thermal_nb(struct ssam_notifier_block *nb, const struct ssam_event *event) +{ -+ struct san_drvdata *drvdata = container_of(nb, struct san_drvdata, nf_tmp.base); -+ struct platform_device *pdev = drvdata->dev; -+ -+ if (san_evt_thermal(event, &pdev->dev)) ++ if (san_evt_thermal(event, to_san_data(nb, nf_tmp.base)->dev)) + return SSAM_NOTIF_HANDLED; + else + return 0; @@ -2702,9 +2967,15 @@ index 000000000000..11dd6daedc3d + return NULL; + } + -+ if (rqsx->cdl != buffer->len - 8) { ++ if (get_unaligned(&rqsx->cdl) != buffer->len - sizeof(struct gsb_data_rqsx)) { + dev_err(dev, "bogus %s package (len = %d, cdl = %d)\n", -+ type, buffer->len, rqsx->cdl); ++ type, buffer->len, get_unaligned(&rqsx->cdl)); ++ return NULL; ++ } ++ ++ if (get_unaligned(&rqsx->cdl) > SAN_GSB_MAX_RQSX_PAYLOAD) { ++ dev_err(dev, "payload for %s package too large (cdl = %d)\n", ++ type, get_unaligned(&rqsx->cdl)); + return NULL; + } + @@ -2717,17 +2988,16 @@ index 000000000000..11dd6daedc3d + return rqsx; +} + -+static acpi_status -+san_etwl(struct san_opreg_context *ctx, struct gsb_buffer *buffer) ++static acpi_status san_etwl(struct san_data *d, 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); ++ dev_err(d->dev, "invalid ETWL package (len = %d)\n", buffer->len); + return AE_OK; + } + -+ dev_err(ctx->dev, "ETWL(0x%02x, 0x%02x): %.*s\n", ++ dev_err(d->dev, "ETWL(0x%02x, 0x%02x): %.*s\n", + etwl->etw3, etwl->etw4, + buffer->len - 3, (char *)etwl->msg); + @@ -2738,43 +3008,29 @@ index 000000000000..11dd6daedc3d + return AE_OK; +} + -+static acpi_status -+san_rqst(struct san_opreg_context *ctx, struct gsb_buffer *buffer) ++static void gsb_response_error(struct gsb_buffer *gsb, int status) +{ -+ struct gsb_data_rqsx *gsb_rqst = san_validate_rqsx(ctx->dev, "RQST", buffer); -+ struct surface_sam_ssh_rqst rqst = {}; -+ struct surface_sam_ssh_buf result = {}; -+ int status = 0; -+ int try; ++ gsb->status = 0x00; ++ gsb->len = 0x02; ++ gsb->data.out.status = (u8)(-status); ++ gsb->data.out.len = 0x00; ++} + -+ if (!gsb_rqst) -+ return AE_OK; ++static void gsb_response_success(struct gsb_buffer *gsb, u8 *ptr, size_t len) ++{ ++ gsb->status = 0x00; ++ gsb->len = len + 2; ++ gsb->data.out.status = 0x00; ++ gsb->data.out.len = len; + -+ rqst.tc = gsb_rqst->tc; -+ rqst.cid = gsb_rqst->cid; -+ rqst.iid = gsb_rqst->iid; -+ rqst.chn = gsb_rqst->tid; -+ rqst.snc = gsb_rqst->snc; -+ rqst.cdl = gsb_rqst->cdl; -+ rqst.pld = &gsb_rqst->pld[0]; ++ if (len) ++ memcpy(&gsb->data.out.pld[0], ptr, len); ++} + -+ result.cap = SURFACE_SAM_SSH_MAX_RQST_RESPONSE; -+ result.len = 0; -+ result.data = kzalloc(result.cap, GFP_KERNEL); -+ -+ if (!result.data) -+ return AE_NO_MEMORY; -+ -+ for (try = 0; try < SAN_RQST_RETRY; try++) { -+ if (try) -+ dev_warn(ctx->dev, SAN_RQST_TAG "IO error occurred, trying again\n"); -+ -+ status = surface_sam_ssh_rqst(&rqst, &result); -+ if (status != -EIO) -+ break; -+ } -+ -+ if (rqst.tc == 0x11 && rqst.cid == 0x0D && status == -EPERM) { ++static acpi_status san_rqst_fixup_suspended(struct ssam_request *rqst, ++ struct gsb_buffer *gsb) ++{ ++ if (rqst->target_category == 0x11 && rqst->command_id == 0x0D) { + /* Base state quirk: + * The base state may be queried from ACPI when the EC is still + * suspended. In this case it will return '-EPERM'. This query @@ -2790,60 +3046,87 @@ index 000000000000..11dd6daedc3d + * delay. + */ + -+ buffer->status = 0x00; -+ buffer->len = 0x03; -+ buffer->data.out.status = 0x00; -+ buffer->data.out.len = 0x01; -+ buffer->data.out.pld[0] = 0x01; -+ -+ } else if (!status) { // success -+ 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 { // failure -+ dev_err(ctx->dev, 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; ++ u8 base_state = 1; ++ gsb_response_success(gsb, &base_state, 1); ++ return AE_OK; + } + -+ kfree(result.data); ++ gsb_response_error(gsb, -ENXIO); ++ return AE_OK; ++} ++ ++static acpi_status san_rqst(struct san_data *d, struct gsb_buffer *buffer) ++{ ++ u8 rspbuf[SAN_GSB_MAX_RESPONSE]; ++ struct gsb_data_rqsx *gsb_rqst; ++ struct ssam_request rqst; ++ struct ssam_response rsp; ++ int status = 0; ++ int try; ++ ++ gsb_rqst = san_validate_rqsx(d->dev, "RQST", buffer); ++ if (!gsb_rqst) ++ return AE_OK; ++ ++ rqst.target_category = gsb_rqst->tc; ++ rqst.command_id = gsb_rqst->cid; ++ rqst.instance_id = gsb_rqst->iid; ++ rqst.channel = gsb_rqst->tid; ++ rqst.flags = gsb_rqst->snc ? SSAM_REQUEST_HAS_RESPONSE : 0; ++ rqst.length = get_unaligned(&gsb_rqst->cdl); ++ rqst.payload = &gsb_rqst->pld[0]; ++ ++ rsp.capacity = ARRAY_SIZE(rspbuf); ++ rsp.length = 0; ++ rsp.pointer = &rspbuf[0]; ++ ++ // handle suspended device ++ if (d->dev->power.is_suspended) { ++ dev_warn(d->dev, "rqst: device is suspended, not executing\n"); ++ return san_rqst_fixup_suspended(&rqst, buffer); ++ } ++ ++ for (try = 0; try < SAN_RQST_RETRY; try++) { ++ if (try) ++ dev_warn(d->dev, "rqst: IO error, trying again\n"); ++ ++ status = san_request_sync_onstack(d->ctrl, &rqst, &rsp); ++ if (status != -ETIMEDOUT && status != -EREMOTEIO) ++ break; ++ } ++ ++ if (!status) { ++ gsb_response_success(buffer, rsp.pointer, rsp.length); ++ } else { ++ dev_err(d->dev, "rqst: failed with error %d\n", status); ++ gsb_response_error(buffer, status); ++ } + + return AE_OK; +} + -+static acpi_status -+san_rqsg(struct san_opreg_context *ctx, struct gsb_buffer *buffer) ++static acpi_status san_rqsg(struct san_data *d, struct gsb_buffer *buffer) +{ -+ struct gsb_data_rqsx *gsb_rqsg = san_validate_rqsx(ctx->dev, "RQSG", buffer); -+ struct surface_sam_san_rqsg rqsg = {}; ++ struct gsb_data_rqsx *gsb_rqsg; ++ struct surface_sam_san_rqsg rqsg; + int status; + ++ gsb_rqsg = san_validate_rqsx(d->dev, "RQSG", buffer); + if (!gsb_rqsg) + return AE_OK; + + rqsg.tc = gsb_rqsg->tc; + rqsg.cid = gsb_rqsg->cid; + rqsg.iid = gsb_rqsg->iid; -+ rqsg.cdl = gsb_rqsg->cdl; ++ rqsg.cdl = get_unaligned(&gsb_rqsg->cdl); + rqsg.pld = &gsb_rqsg->pld[0]; + + status = san_call_rqsg_handler(&rqsg); + if (!status) { -+ buffer->status = 0x00; -+ buffer->len = 0x02; -+ buffer->data.out.status = 0x00; -+ buffer->data.out.len = 0x00; ++ gsb_response_success(buffer, NULL, 0); + } else { -+ dev_err(ctx->dev, SAN_RQSG_TAG "failed with error %d\n", status); -+ buffer->status = 0x00; -+ buffer->len = 0x02; -+ buffer->data.out.status = 0x01; // indicate _SSH error -+ buffer->data.out.len = 0x00; ++ dev_err(d->dev, "rqsg: failed with error %d\n", status); ++ gsb_response_error(buffer, status); + } + + return AE_OK; @@ -2855,72 +3138,72 @@ index 000000000000..11dd6daedc3d + u32 bits, u64 *value64, + void *opreg_context, void *region_context) +{ -+ struct san_opreg_context *context = opreg_context; ++ struct san_data *d = to_san_data(opreg_context, 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); ++ dev_warn(d->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); ++ dev_err(d->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"); ++ dev_err(d->dev, "request-package too small\n"); + return AE_OK; + } + + switch (buffer->data.in.cv) { -+ case 0x01: return san_rqst(context, buffer); -+ case 0x02: return san_etwl(context, buffer); -+ case 0x03: return san_rqsg(context, buffer); ++ case 0x01: return san_rqst(d, buffer); ++ case 0x02: return san_etwl(d, buffer); ++ case 0x03: return san_rqsg(d, buffer); + } + -+ dev_warn(context->dev, "unsupported SAN0 request (cv: 0x%02x)\n", buffer->data.in.cv); ++ dev_warn(d->dev, "unsupported SAN0 request (cv: 0x%02x)\n", buffer->data.in.cv); + return AE_OK; +} + +static int san_events_register(struct platform_device *pdev) +{ -+ struct san_drvdata *drvdata = platform_get_drvdata(pdev); ++ struct san_data *d = platform_get_drvdata(pdev); + int status; + -+ drvdata->nf_bat.base.priority = 1; -+ drvdata->nf_bat.base.fn = san_evt_power_nb; -+ drvdata->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM; -+ drvdata->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT; -+ drvdata->nf_bat.event.id.instance = 0; -+ drvdata->nf_bat.event.flags = SSAM_EVENT_SEQUENCED; ++ d->nf_bat.base.priority = 1; ++ d->nf_bat.base.fn = san_evt_power_nb; ++ d->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM; ++ d->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT; ++ d->nf_bat.event.id.instance = 0; ++ d->nf_bat.event.flags = SSAM_EVENT_SEQUENCED; + -+ drvdata->nf_tmp.base.priority = 1; -+ drvdata->nf_tmp.base.fn = san_evt_thermal_nb; -+ drvdata->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM; -+ drvdata->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP; -+ drvdata->nf_tmp.event.id.instance = 0; -+ drvdata->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED; ++ d->nf_tmp.base.priority = 1; ++ d->nf_tmp.base.fn = san_evt_thermal_nb; ++ d->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM; ++ d->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP; ++ d->nf_tmp.event.id.instance = 0; ++ d->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED; + -+ status = surface_sam_ssh_notifier_register(&drvdata->nf_bat); ++ status = ssam_notifier_register(d->ctrl, &d->nf_bat); + if (status) + return status; + -+ status = surface_sam_ssh_notifier_register(&drvdata->nf_tmp); ++ status = ssam_notifier_register(d->ctrl, &d->nf_tmp); + if (status) -+ surface_sam_ssh_notifier_unregister(&drvdata->nf_bat); ++ ssam_notifier_unregister(d->ctrl, &d->nf_bat); + + return status; +} + +static void san_events_unregister(struct platform_device *pdev) +{ -+ struct san_drvdata *drvdata = platform_get_drvdata(pdev); ++ struct san_data *d = platform_get_drvdata(pdev); + -+ surface_sam_ssh_notifier_unregister(&drvdata->nf_bat); -+ surface_sam_ssh_notifier_unregister(&drvdata->nf_tmp); ++ ssam_notifier_unregister(d->ctrl, &d->nf_bat); ++ ssam_notifier_unregister(d->ctrl, &d->nf_tmp); +} + + @@ -3010,38 +3293,33 @@ index 000000000000..11dd6daedc3d +static int surface_sam_san_probe(struct platform_device *pdev) +{ + const struct san_acpi_consumer *cons; -+ struct san_drvdata *drvdata; + acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node ++ struct ssam_controller *ctrl; ++ struct san_data *data; + int status; + -+ /* -+ * 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. -+ */ -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + -+ drvdata = kzalloc(sizeof(struct san_drvdata), GFP_KERNEL); -+ if (!drvdata) ++ data = kzalloc(sizeof(struct san_data), GFP_KERNEL); ++ if (!data) + return -ENOMEM; + -+ drvdata->dev = pdev; -+ drvdata->opreg_ctx.dev = &pdev->dev; ++ data->dev = &pdev->dev; ++ data->ctrl = ctrl; + + cons = acpi_device_get_match_data(&pdev->dev); -+ status = san_consumers_link(pdev, cons, &drvdata->consumers); ++ status = san_consumers_link(pdev, cons, &data->consumers); + if (status) + goto err_consumers; + -+ platform_set_drvdata(pdev, drvdata); ++ platform_set_drvdata(pdev, data); + + status = acpi_install_address_space_handler(san, + ACPI_ADR_SPACE_GSBUS, + &san_opreg_handler, -+ NULL, &drvdata->opreg_ctx); ++ NULL, &data->context); + + if (ACPI_FAILURE(status)) { + status = -ENODEV; @@ -3071,15 +3349,15 @@ index 000000000000..11dd6daedc3d + acpi_remove_address_space_handler(san, ACPI_ADR_SPACE_GSBUS, &san_opreg_handler); +err_install_handler: + platform_set_drvdata(san, NULL); -+ san_consumers_unlink(&drvdata->consumers); ++ san_consumers_unlink(&data->consumers); +err_consumers: -+ kfree(drvdata); ++ kfree(data); + return status; +} + +static int surface_sam_san_remove(struct platform_device *pdev) +{ -+ struct san_drvdata *drvdata = platform_get_drvdata(pdev); ++ struct san_data *data = platform_get_drvdata(pdev); + acpi_handle san = ACPI_HANDLE(&pdev->dev); // _SAN device node + acpi_status status = AE_OK; + @@ -3096,8 +3374,8 @@ index 000000000000..11dd6daedc3d + */ + flush_scheduled_work(); + -+ san_consumers_unlink(&drvdata->consumers); -+ kfree(drvdata); ++ san_consumers_unlink(&data->consumers); ++ kfree(data); + + platform_set_drvdata(pdev, NULL); + return status; @@ -3134,7 +3412,7 @@ index 000000000000..11dd6daedc3d +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_san.h b/drivers/platform/x86/surface_sam/surface_sam_san.h new file mode 100644 -index 000000000000..2b9dee159bbb +index 0000000000000..3408dde964b3c --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_san.h @@ -0,0 +1,30 @@ @@ -3158,7 +3436,7 @@ index 000000000000..2b9dee159bbb + u8 tc; // target category + u8 cid; // command ID + u8 iid; // instance ID -+ u8 cdl; // command data length (length of payload) ++ u16 cdl; // command data length (length of payload) + u8 *pld; // pointer to payload of length cdl +}; + @@ -3170,10 +3448,10 @@ index 000000000000..2b9dee159bbb +#endif /* _SURFACE_SAM_SAN_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid.c b/drivers/platform/x86/surface_sam/surface_sam_sid.c new file mode 100644 -index 000000000000..caa2e6446b5f +index 0000000000000..bcf9a569ee719 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid.c -@@ -0,0 +1,281 @@ +@@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Integration Driver. @@ -3190,14 +3468,14 @@ index 000000000000..caa2e6446b5f +#include "surface_sam_sid_vhf.h" + + -+struct ssam_battery_properties ssam_battery_props_bat1 = { ++static const struct ssam_battery_properties ssam_battery_props_bat1 = { + .registry = SSAM_EVENT_REGISTRY_SAM, + .num = 0, + .channel = 1, + .instance = 1, +}; + -+struct ssam_battery_properties ssam_battery_props_bat2_sb3 = { ++static const struct ssam_battery_properties ssam_battery_props_bat2_sb3 = { + .registry = SSAM_EVENT_REGISTRY_KIP, + .num = 1, + .channel = 2, @@ -3245,7 +3523,7 @@ index 000000000000..caa2e6446b5f + { + .name = "surface_sam_sid_battery", + .id = -1, -+ .platform_data = &ssam_battery_props_bat1, ++ .platform_data = (void *)&ssam_battery_props_bat1, + .pdata_size = sizeof(struct ssam_battery_properties), + }, + { }, @@ -3269,13 +3547,13 @@ index 000000000000..caa2e6446b5f + { + .name = "surface_sam_sid_battery", + .id = 1, -+ .platform_data = &ssam_battery_props_bat1, ++ .platform_data = (void *)&ssam_battery_props_bat1, + .pdata_size = sizeof(struct ssam_battery_properties), + }, + { + .name = "surface_sam_sid_battery", + .id = 2, -+ .platform_data = &ssam_battery_props_bat2_sb3, ++ .platform_data = (void *)&ssam_battery_props_bat2_sb3, + .pdata_size = sizeof(struct ssam_battery_properties), + }, + { @@ -3306,12 +3584,14 @@ index 000000000000..caa2e6446b5f +}; + +static const struct mfd_cell sid_devs_sl1[] = { -+ { .name = "surface_sam_sid_gpelid", .id = -1 }, ++ { .name = "surface_sam_sid_gpelid", .id = -1 }, ++ { .name = "surface_sam_sid_perfmode", .id = -1 }, + { }, +}; + +static const struct mfd_cell sid_devs_sl2[] = { -+ { .name = "surface_sam_sid_gpelid", .id = -1 }, ++ { .name = "surface_sam_sid_gpelid", .id = -1 }, ++ { .name = "surface_sam_sid_perfmode", .id = -1 }, + { }, +}; + @@ -3322,7 +3602,7 @@ index 000000000000..caa2e6446b5f + { + .name = "surface_sam_sid_battery", + .id = -1, -+ .platform_data = &ssam_battery_props_bat1, ++ .platform_data = (void *)&ssam_battery_props_bat1, + .pdata_size = sizeof(struct ssam_battery_properties), + }, + { @@ -3352,7 +3632,7 @@ index 000000000000..caa2e6446b5f + { + .name = "surface_sam_sid_battery", + .id = -1, -+ .platform_data = &ssam_battery_props_bat1, ++ .platform_data = (void *)&ssam_battery_props_bat1, + .pdata_size = sizeof(struct ssam_battery_properties), + }, + { @@ -3457,7 +3737,7 @@ index 000000000000..caa2e6446b5f +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_gpelid.c b/drivers/platform/x86/surface_sam/surface_sam_sid_gpelid.c new file mode 100644 -index 000000000000..f0cee43c859b +index 0000000000000..f0cee43c859b4 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_gpelid.c @@ -0,0 +1,232 @@ @@ -3695,10 +3975,10 @@ index 000000000000..f0cee43c859b +MODULE_ALIAS("platform:surface_sam_sid_gpelid"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_perfmode.c b/drivers/platform/x86/surface_sam/surface_sam_sid_perfmode.c new file mode 100644 -index 000000000000..2e11efb166f2 +index 0000000000000..e0b1e42c2087f --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_perfmode.c -@@ -0,0 +1,216 @@ +@@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Performance Mode Driver. @@ -3736,57 +4016,39 @@ index 000000000000..2e11efb166f2 + __SID_PARAM_PERF_MODE__END = 4, +}; + ++struct spm_data { ++ struct ssam_controller *ctrl; ++}; + -+static int surface_sam_perf_mode_get(void) ++ ++struct ssam_perf_info { ++ __le32 mode; ++ __le16 unknown1; ++ __le16 unknown2; ++} __packed; ++ ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_tmp_perf_mode_get, struct ssam_perf_info, { ++ .target_category = SSAM_SSH_TC_TMP, ++ .command_id = 0x02, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, { ++ .target_category = SSAM_SSH_TC_TMP, ++ .command_id = 0x03, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static int ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, u32 mode) +{ -+ u8 result_buf[8] = { 0 }; -+ int status; ++ __le32 mode_le = cpu_to_le32(mode); + -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x03, -+ .cid = 0x02, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surface_sam_ssh_buf result = { -+ .cap = ARRAY_SIZE(result_buf), -+ .len = 0, -+ .data = result_buf, -+ }; -+ -+ status = surface_sam_ssh_rqst(&rqst, &result); -+ if (status) -+ return status; -+ -+ if (result.len != 8) -+ return -EFAULT; -+ -+ return get_unaligned_le32(&result.data[0]); -+} -+ -+static int surface_sam_perf_mode_set(int perf_mode) -+{ -+ u8 payload[4] = { 0 }; -+ -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x03, -+ .cid = 0x03, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x00, -+ .cdl = ARRAY_SIZE(payload), -+ .pld = payload, -+ }; -+ -+ if (perf_mode < __SAM_PERF_MODE__START || perf_mode > __SAM_PERF_MODE__END) ++ if (mode < __SAM_PERF_MODE__START || mode > __SAM_PERF_MODE__END) + return -EINVAL; + -+ put_unaligned_le32(perf_mode, &rqst.pld[0]); -+ return surface_sam_ssh_rqst(&rqst, NULL); ++ return __ssam_tmp_perf_mode_set(ctrl, &mode_le); +} + + @@ -3822,20 +4084,23 @@ index 000000000000..2e11efb166f2 + +static ssize_t perf_mode_show(struct device *dev, struct device_attribute *attr, char *data) +{ -+ int perf_mode; ++ struct spm_data *d = dev_get_drvdata(dev); ++ struct ssam_perf_info info; ++ int status; + -+ perf_mode = surface_sam_perf_mode_get(); -+ if (perf_mode < 0) { -+ dev_err(dev, "failed to get current performance mode: %d\n", perf_mode); ++ status = ssam_tmp_perf_mode_get(d->ctrl, &info); ++ if (status) { ++ dev_err(dev, "failed to get current performance mode: %d\n", status); + return -EIO; + } + -+ return sprintf(data, "%d\n", perf_mode); ++ return sprintf(data, "%d\n", le32_to_cpu(info.mode)); +} + +static ssize_t perf_mode_store(struct device *dev, struct device_attribute *attr, + const char *data, size_t count) +{ ++ struct spm_data *d = dev_get_drvdata(dev); + int perf_mode; + int status; + @@ -3843,7 +4108,7 @@ index 000000000000..2e11efb166f2 + if (status) + return status; + -+ status = surface_sam_perf_mode_set(perf_mode); ++ status = ssam_tmp_perf_mode_set(d->ctrl, perf_mode); + if (status) + return status; + @@ -3868,16 +4133,25 @@ index 000000000000..2e11efb166f2 + +static int surface_sam_sid_perfmode_probe(struct platform_device *pdev) +{ ++ struct ssam_controller *ctrl; ++ struct spm_data *data; + int status; + + // link to ec -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + ++ data = devm_kzalloc(&pdev->dev, sizeof(struct spm_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->ctrl = ctrl; ++ platform_set_drvdata(pdev, data); ++ + // set initial perf_mode + if (param_perf_mode_init != SID_PARAM_PERF_MODE_AS_IS) { -+ status = surface_sam_perf_mode_set(param_perf_mode_init); ++ status = ssam_tmp_perf_mode_set(ctrl, param_perf_mode_init); + if (status) + return status; + } @@ -3890,14 +4164,18 @@ index 000000000000..2e11efb166f2 + return 0; + +err_sysfs: -+ surface_sam_perf_mode_set(param_perf_mode_exit); ++ ssam_tmp_perf_mode_set(ctrl, param_perf_mode_exit); + return status; +} + +static int surface_sam_sid_perfmode_remove(struct platform_device *pdev) +{ ++ struct spm_data *data = platform_get_drvdata(pdev); ++ + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_perf_mode.attr); -+ surface_sam_perf_mode_set(param_perf_mode_exit); ++ ssam_tmp_perf_mode_set(data->ctrl, param_perf_mode_exit); ++ ++ platform_set_drvdata(pdev, NULL); + return 0; +} + @@ -3917,16 +4195,17 @@ index 000000000000..2e11efb166f2 +MODULE_ALIAS("platform:surface_sam_sid_perfmode"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_power.c b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c new file mode 100644 -index 000000000000..1d945c0a911a +index 0000000000000..64a3d46a128cc --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_power.c -@@ -0,0 +1,1154 @@ +@@ -0,0 +1,1054 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface SID Battery/AC Driver. + * Provides support for the battery and AC on 7th generation Surface devices. + */ + ++#include +#include +#include +#include @@ -3958,19 +4237,6 @@ index 000000000000..1d945c0a911a + * SAM Interface. + */ + -+#define SAM_PWR_TC 0x02 -+ -+#define SAM_RQST_PWR_CID_STA 0x01 -+#define SAM_RQST_PWR_CID_BIX 0x02 -+#define SAM_RQST_PWR_CID_BST 0x03 -+#define SAM_RQST_PWR_CID_BTP 0x04 -+ -+#define SAM_RQST_PWR_CID_PMAX 0x0b -+#define SAM_RQST_PWR_CID_PSOC 0x0c -+#define SAM_RQST_PWR_CID_PSRC 0x0d -+#define SAM_RQST_PWR_CID_CHGI 0x0e -+#define SAM_RQST_PWR_CID_ARTG 0x0f -+ +#define SAM_EVENT_PWR_CID_BIX 0x15 +#define SAM_EVENT_PWR_CID_BST 0x16 +#define SAM_EVENT_PWR_CID_ADAPTER 0x17 @@ -3988,21 +4254,21 @@ index 000000000000..1d945c0a911a +/* Equivalent to data returned in ACPI _BIX method */ +struct spwr_bix { + u8 revision; -+ u32 power_unit; -+ u32 design_cap; -+ u32 last_full_charge_cap; -+ u32 technology; -+ u32 design_voltage; -+ u32 design_cap_warn; -+ u32 design_cap_low; -+ u32 cycle_count; -+ u32 measurement_accuracy; -+ u32 max_sampling_time; -+ u32 min_sampling_time; -+ u32 max_avg_interval; -+ u32 min_avg_interval; -+ u32 bat_cap_granularity_1; -+ u32 bat_cap_granularity_2; ++ __le32 power_unit; ++ __le32 design_cap; ++ __le32 last_full_charge_cap; ++ __le32 technology; ++ __le32 design_voltage; ++ __le32 design_cap_warn; ++ __le32 design_cap_low; ++ __le32 cycle_count; ++ __le32 measurement_accuracy; ++ __le32 max_sampling_time; ++ __le32 min_sampling_time; ++ __le32 max_avg_interval; ++ __le32 min_avg_interval; ++ __le32 bat_cap_granularity_1; ++ __le32 bat_cap_granularity_2; + u8 model[21]; + u8 serial[11]; + u8 type[5]; @@ -4011,202 +4277,77 @@ index 000000000000..1d945c0a911a + +/* Equivalent to data returned in ACPI _BST method */ +struct spwr_bst { -+ u32 state; -+ u32 present_rate; -+ u32 remaining_cap; -+ u32 present_voltage; ++ __le32 state; ++ __le32 present_rate; ++ __le32 remaining_cap; ++ __le32 present_voltage; +} __packed; + +/* DPTF event payload */ +struct spwr_event_dptf { -+ u32 pmax; -+ u32 _1; /* currently unknown */ -+ u32 _2; /* currently unknown */ ++ __le32 pmax; ++ __le32 _1; /* currently unknown */ ++ __le32 _2; /* currently unknown */ +} __packed; + + +/* Get battery status (_STA) */ -+static int sam_psy_get_sta(u8 channel, u8 instance, u32 *sta) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_STA; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(u32); -+ result.len = 0; -+ result.data = (u8 *)sta; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_sta, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x01, ++}); + +/* Get battery static information (_BIX) */ -+static int sam_psy_get_bix(u8 channel, u8 instance, struct spwr_bix *bix) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_BIX; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(struct spwr_bix); -+ result.len = 0; -+ result.data = (u8 *)bix; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_bix, struct spwr_bix, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x02, ++}); + +/* Get battery dynamic information (_BST) */ -+static int sam_psy_get_bst(u8 channel, u8 instance, struct spwr_bst *bst) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_BST; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(struct spwr_bst); -+ result.len = 0; -+ result.data = (u8 *)bst; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_bst, struct spwr_bst, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x03, ++}); + +/* Set battery trip point (_BTP) */ -+static int sam_psy_set_btp(u8 channel, u8 instance, u32 btp) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_BTP; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x00; -+ rqst.cdl = sizeof(u32); -+ rqst.pld = (u8 *)&btp; -+ -+ return surface_sam_ssh_rqst(&rqst, NULL); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_W(ssam_bat_set_btp, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x04, ++}); + +/* Get platform power soruce for battery (DPTF PSRC) */ -+static int sam_psy_get_psrc(u8 channel, u8 instance, u32 *psrc) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_PSRC; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(u32); -+ result.len = 0; -+ result.data = (u8 *)psrc; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_psrc, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x0d, ++}); + +/* Get maximum platform power for battery (DPTF PMAX) */ +__always_unused -+static int sam_psy_get_pmax(u8 channel, u8 instance, u32 *pmax) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_PMAX; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(u32); -+ result.len = 0; -+ result.data = (u8 *)pmax; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_pmax, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x0b, ++}); + +/* Get adapter rating (DPTF ARTG) */ +__always_unused -+static int sam_psy_get_artg(u8 channel, u8 instance, u32 *artg) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_ARTG; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(u32); -+ result.len = 0; -+ result.data = (u8 *)artg; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_artg, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x0f, ++}); + +/* Unknown (DPTF PSOC) */ +__always_unused -+static int sam_psy_get_psoc(u8 channel, u8 instance, u32 *psoc) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_PSOC; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x01; -+ rqst.cdl = 0x00; -+ rqst.pld = NULL; -+ -+ result.cap = sizeof(u32); -+ result.len = 0; -+ result.data = (u8 *)psoc; -+ -+ return surface_sam_ssh_rqst(&rqst, &result); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_R(ssam_bat_get_psoc, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x0c, ++}); + +/* Unknown (DPTF CHGI/ INT3403 SPPC) */ +__always_unused -+static int sam_psy_set_chgi(u8 channel, u8 instance, u32 chgi) -+{ -+ struct surface_sam_ssh_rqst rqst; -+ -+ rqst.tc = SAM_PWR_TC; -+ rqst.cid = SAM_RQST_PWR_CID_CHGI; -+ rqst.iid = instance; -+ rqst.chn = channel; -+ rqst.snc = 0x00; -+ rqst.cdl = sizeof(u32); -+ rqst.pld = (u8 *)&chgi; -+ -+ return surface_sam_ssh_rqst(&rqst, NULL); -+} ++static SSAM_DEFINE_SYNC_REQUEST_MD_W(ssam_bat_set_chgi, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x0e, ++}); + + +/* @@ -4215,6 +4356,7 @@ index 000000000000..1d945c0a911a + +struct spwr_battery_device { + struct platform_device *pdev; ++ struct ssam_controller *ctrl; + const struct ssam_battery_properties *p; + + char name[32]; @@ -4228,7 +4370,7 @@ index 000000000000..1d945c0a911a + struct mutex lock; + unsigned long timestamp; + -+ u32 sta; ++ __le32 sta; + struct spwr_bix bix; + struct spwr_bst bst; + u32 alarm; @@ -4236,6 +4378,7 @@ index 000000000000..1d945c0a911a + +struct spwr_ac_device { + struct platform_device *pdev; ++ struct ssam_controller *ctrl; + + char name[32]; + struct power_supply *psy; @@ -4245,7 +4388,7 @@ index 000000000000..1d945c0a911a + + struct mutex lock; + -+ u32 state; ++ __le32 state; +}; + +static enum power_supply_property spwr_ac_props[] = { @@ -4291,6 +4434,7 @@ index 000000000000..1d945c0a911a + +static int spwr_battery_register(struct spwr_battery_device *bat, + struct platform_device *pdev, ++ struct ssam_controller *ctrl, + const struct ssam_battery_properties *p); + +static void spwr_battery_unregister(struct spwr_battery_device *bat); @@ -4298,13 +4442,14 @@ index 000000000000..1d945c0a911a + +static inline bool spwr_battery_present(struct spwr_battery_device *bat) +{ -+ return bat->sta & SAM_BATTERY_STA_PRESENT; ++ return le32_to_cpu(bat->sta) & SAM_BATTERY_STA_PRESENT; +} + + +static inline int spwr_battery_load_sta(struct spwr_battery_device *bat) +{ -+ return sam_psy_get_sta(bat->p->channel, bat->p->instance, &bat->sta); ++ return ssam_bat_get_sta(bat->ctrl, bat->p->channel, bat->p->instance, ++ &bat->sta); +} + +static inline int spwr_battery_load_bix(struct spwr_battery_device *bat) @@ -4312,7 +4457,8 @@ index 000000000000..1d945c0a911a + if (!spwr_battery_present(bat)) + return 0; + -+ return sam_psy_get_bix(bat->p->channel, bat->p->instance, &bat->bix); ++ return ssam_bat_get_bix(bat->ctrl, bat->p->channel, bat->p->instance, ++ &bat->bix); +} + +static inline int spwr_battery_load_bst(struct spwr_battery_device *bat) @@ -4320,14 +4466,18 @@ index 000000000000..1d945c0a911a + if (!spwr_battery_present(bat)) + return 0; + -+ return sam_psy_get_bst(bat->p->channel, bat->p->instance, &bat->bst); ++ return ssam_bat_get_bst(bat->ctrl, bat->p->channel, bat->p->instance, ++ &bat->bst); +} + + +static inline int spwr_battery_set_alarm_unlocked(struct spwr_battery_device *bat, u32 value) +{ ++ __le32 alarm = cpu_to_le32(value); ++ + bat->alarm = value; -+ return sam_psy_set_btp(bat->p->channel, bat->p->instance, bat->alarm); ++ return ssam_bat_set_btp(bat->ctrl, bat->p->channel, bat->p->instance, ++ &alarm); +} + +static inline int spwr_battery_set_alarm(struct spwr_battery_device *bat, u32 value) @@ -4405,7 +4555,7 @@ index 000000000000..1d945c0a911a + +static inline int spwr_ac_update_unlocked(struct spwr_ac_device *ac) +{ -+ return sam_psy_get_psrc(0x01, 0x01, &ac->state); ++ return ssam_bat_get_psrc(ac->ctrl, 0x01, 0x01, &ac->state); +} + +static int spwr_ac_update(struct spwr_ac_device *ac) @@ -4423,7 +4573,7 @@ index 000000000000..1d945c0a911a +static int spwr_battery_recheck(struct spwr_battery_device *bat) +{ + bool present = spwr_battery_present(bat); -+ u32 unit = bat->bix.power_unit; ++ u32 unit = get_unaligned_le32(&bat->bix.power_unit); + int status; + + status = spwr_battery_update_bix(bat); @@ -4432,15 +4582,16 @@ index 000000000000..1d945c0a911a + + // if battery has been attached, (re-)initialize alarm + if (!present && spwr_battery_present(bat)) { -+ status = spwr_battery_set_alarm(bat, bat->bix.design_cap_warn); ++ u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn); ++ status = spwr_battery_set_alarm(bat, cap_warn); + if (status) + return status; + } + + // if the unit has changed, re-add the battery -+ if (unit != bat->bix.power_unit) { ++ if (unit != get_unaligned_le32(&bat->bix.power_unit)) { + spwr_battery_unregister(bat); -+ status = spwr_battery_register(bat, bat->pdev, bat->p); ++ status = spwr_battery_register(bat, bat->pdev, bat->ctrl, bat->p); + } + + return status; @@ -4471,6 +4622,9 @@ index 000000000000..1d945c0a911a + +static inline int spwr_notify_adapter_bat(struct spwr_battery_device *bat) +{ ++ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); ++ + /* + * Handle battery update quirk: + * When the battery is fully charged and the adapter is plugged in or @@ -4479,7 +4633,7 @@ index 000000000000..1d945c0a911a + * the state is updated on the battery. Schedule an update to solve this. + */ + -+ if (bat->bst.remaining_cap >= bat->bix.last_full_charge_cap) ++ if (remaining_cap >= last_full_cap) + schedule_delayed_work(&bat->update_work, SPWR_AC_BAT_UPDATE_DELAY); + + return 0; @@ -4572,16 +4726,21 @@ index 000000000000..1d945c0a911a + +static inline int spwr_battery_prop_status(struct spwr_battery_device *bat) +{ -+ if (bat->bst.state & SAM_BATTERY_STATE_DISCHARGING) ++ u32 state = get_unaligned_le32(&bat->bst.state); ++ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); ++ u32 present_rate = get_unaligned_le32(&bat->bst.present_rate); ++ ++ if (state & SAM_BATTERY_STATE_DISCHARGING) + return POWER_SUPPLY_STATUS_DISCHARGING; + -+ if (bat->bst.state & SAM_BATTERY_STATE_CHARGING) ++ if (state & SAM_BATTERY_STATE_CHARGING) + return POWER_SUPPLY_STATUS_CHARGING; + -+ if (bat->bix.last_full_charge_cap == bat->bst.remaining_cap) ++ if (last_full_cap == remaining_cap) + return POWER_SUPPLY_STATUS_FULL; + -+ if (bat->bst.present_rate == 0) ++ if (present_rate == 0) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + + return POWER_SUPPLY_STATUS_UNKNOWN; @@ -4609,21 +4768,28 @@ index 000000000000..1d945c0a911a + +static inline int spwr_battery_prop_capacity(struct spwr_battery_device *bat) +{ -+ if (bat->bst.remaining_cap && bat->bix.last_full_charge_cap) -+ return bat->bst.remaining_cap * 100 / bat->bix.last_full_charge_cap; ++ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); ++ ++ if (remaining_cap && last_full_cap) ++ return remaining_cap * 100 / last_full_cap; + else + return 0; +} + +static inline int spwr_battery_prop_capacity_level(struct spwr_battery_device *bat) +{ -+ if (bat->bst.state & SAM_BATTERY_STATE_CRITICAL) ++ u32 state = get_unaligned_le32(&bat->bst.state); ++ u32 last_full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap); ++ u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap); ++ ++ if (state & SAM_BATTERY_STATE_CRITICAL) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + -+ if (bat->bst.remaining_cap >= bat->bix.last_full_charge_cap) ++ if (remaining_cap >= last_full_cap) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + -+ if (bat->bst.remaining_cap <= bat->alarm) ++ if (remaining_cap <= bat->alarm) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; @@ -4644,7 +4810,7 @@ index 000000000000..1d945c0a911a + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: -+ val->intval = ac->state == 1; ++ val->intval = le32_to_cpu(ac->state) == 1; + break; + + default: @@ -4690,35 +4856,39 @@ index 000000000000..1d945c0a911a + break; + + case POWER_SUPPLY_PROP_CYCLE_COUNT: -+ val->intval = bat->bix.cycle_count; ++ val->intval = get_unaligned_le32(&bat->bix.cycle_count); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: -+ val->intval = bat->bix.design_voltage * 1000; ++ val->intval = get_unaligned_le32(&bat->bix.design_voltage) ++ * 1000; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = bat->bst.present_voltage * 1000; ++ val->intval = get_unaligned_le32(&bat->bst.present_voltage) ++ * 1000; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_POWER_NOW: -+ val->intval = bat->bst.present_rate * 1000; ++ val->intval = get_unaligned_le32(&bat->bst.present_rate) * 1000; + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: -+ val->intval = bat->bix.design_cap * 1000; ++ val->intval = get_unaligned_le32(&bat->bix.design_cap) * 1000; + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: -+ val->intval = bat->bix.last_full_charge_cap * 1000; ++ val->intval = get_unaligned_le32(&bat->bix.last_full_charge_cap) ++ * 1000; + break; + + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: -+ val->intval = bat->bst.remaining_cap * 1000; ++ val->intval = get_unaligned_le32(&bat->bst.remaining_cap) ++ * 1000; + break; + + case POWER_SUPPLY_PROP_CAPACITY: @@ -4792,23 +4962,26 @@ index 000000000000..1d945c0a911a +}; + + -+static int spwr_ac_register(struct spwr_ac_device *ac, struct platform_device *pdev) ++static int spwr_ac_register(struct spwr_ac_device *ac, ++ struct platform_device *pdev, ++ struct ssam_controller *ctrl) +{ + struct power_supply_config psy_cfg = {}; -+ u32 sta; ++ __le32 sta; + int status; + + // make sure the device is there and functioning properly -+ status = sam_psy_get_sta(0x01, 0x01, &sta); ++ status = ssam_bat_get_sta(ctrl, 0x01, 0x01, &sta); + if (status) + return status; + -+ if ((sta & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK) ++ if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK) + return -ENODEV; + + psy_cfg.drv_data = ac; + + ac->pdev = pdev; ++ ac->ctrl = ctrl; + mutex_init(&ac->lock); + + snprintf(ac->name, ARRAY_SIZE(ac->name), "ADP0"); @@ -4832,7 +5005,7 @@ index 000000000000..1d945c0a911a + ac->notif.event.id.instance = 0; + ac->notif.event.flags = SSAM_EVENT_SEQUENCED; + -+ status = surface_sam_ssh_notifier_register(&ac->notif); ++ status = ssam_notifier_register(ctrl, &ac->notif); + if (status) + goto err_notif; + @@ -4847,7 +5020,7 @@ index 000000000000..1d945c0a911a + +static int spwr_ac_unregister(struct spwr_ac_device *ac) +{ -+ surface_sam_ssh_notifier_unregister(&ac->notif); ++ ssam_notifier_unregister(ac->ctrl, &ac->notif); + power_supply_unregister(ac->psy); + mutex_destroy(&ac->lock); + return 0; @@ -4855,21 +5028,23 @@ index 000000000000..1d945c0a911a + +static int spwr_battery_register(struct spwr_battery_device *bat, + struct platform_device *pdev, ++ struct ssam_controller *ctrl, + const struct ssam_battery_properties *p) +{ + struct power_supply_config psy_cfg = {}; -+ u32 sta; ++ __le32 sta; + int status; + + bat->pdev = pdev; ++ bat->ctrl = ctrl; + bat->p = p; + + // make sure the device is there and functioning properly -+ status = sam_psy_get_sta(bat->p->channel, bat->p->instance, &sta); ++ status = ssam_bat_get_sta(ctrl, bat->p->channel, bat->p->instance, &sta); + if (status) + return status; + -+ if ((sta & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK) ++ if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK) + return -ENODEV; + + status = spwr_battery_update_bix_unlocked(bat); @@ -4877,7 +5052,8 @@ index 000000000000..1d945c0a911a + return status; + + if (spwr_battery_present(bat)) { -+ status = spwr_battery_set_alarm_unlocked(bat, bat->bix.design_cap_warn); ++ u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn); ++ status = spwr_battery_set_alarm_unlocked(bat, cap_warn); + if (status) + return status; + } @@ -4886,7 +5062,7 @@ index 000000000000..1d945c0a911a + bat->psy_desc.name = bat->name; + bat->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; + -+ if (bat->bix.power_unit == SAM_BATTERY_POWER_UNIT_MA) { ++ if (get_unaligned_le32(&bat->bix.power_unit) == SAM_BATTERY_POWER_UNIT_MA) { + bat->psy_desc.properties = spwr_battery_props_chg; + bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_chg); + } else { @@ -4914,7 +5090,7 @@ index 000000000000..1d945c0a911a + bat->notif.event.id.instance = 0; + bat->notif.event.flags = SSAM_EVENT_SEQUENCED; + -+ status = surface_sam_ssh_notifier_register(&bat->notif); ++ status = ssam_notifier_register(ctrl, &bat->notif); + if (status) + goto err_notif; + @@ -4925,7 +5101,7 @@ index 000000000000..1d945c0a911a + return 0; + +err_file: -+ surface_sam_ssh_notifier_unregister(&bat->notif); ++ ssam_notifier_unregister(ctrl, &bat->notif); +err_notif: + power_supply_unregister(bat->psy); +err_psy: @@ -4935,7 +5111,7 @@ index 000000000000..1d945c0a911a + +static void spwr_battery_unregister(struct spwr_battery_device *bat) +{ -+ surface_sam_ssh_notifier_unregister(&bat->notif); ++ ssam_notifier_unregister(bat->ctrl, &bat->notif); + cancel_delayed_work_sync(&bat->update_work); + device_remove_file(&bat->psy->dev, &alarm_attr); + power_supply_unregister(bat->psy); @@ -4964,10 +5140,11 @@ index 000000000000..1d945c0a911a +static int surface_sam_sid_battery_probe(struct platform_device *pdev) +{ + struct spwr_battery_device *bat; ++ struct ssam_controller *ctrl; + int status; + + // link to ec -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + @@ -4976,7 +5153,7 @@ index 000000000000..1d945c0a911a + return -ENOMEM; + + platform_set_drvdata(pdev, bat); -+ return spwr_battery_register(bat, pdev, pdev->dev.platform_data); ++ return spwr_battery_register(bat, pdev, ctrl, pdev->dev.platform_data); +} + +static int surface_sam_sid_battery_remove(struct platform_device *pdev) @@ -5006,11 +5183,12 @@ index 000000000000..1d945c0a911a + +static int surface_sam_sid_ac_probe(struct platform_device *pdev) +{ -+ int status; + struct spwr_ac_device *ac; ++ struct ssam_controller *ctrl; ++ int status; + + // link to ec -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + @@ -5018,7 +5196,7 @@ index 000000000000..1d945c0a911a + if (!ac) + return -ENOMEM; + -+ status = spwr_ac_register(ac, pdev); ++ status = spwr_ac_register(ac, pdev, ctrl); + if (status) + return status; + @@ -5077,10 +5255,11 @@ index 000000000000..1d945c0a911a +MODULE_ALIAS("platform:surface_sam_sid_battery"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_power.h b/drivers/platform/x86/surface_sam/surface_sam_sid_power.h new file mode 100644 -index 000000000000..2e8f212086e1 +index 0000000000000..d8d9509b7d122 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_power.h -@@ -0,0 +1,15 @@ +@@ -0,0 +1,16 @@ ++ +#ifndef _SURFACE_SAM_SID_POWER_H +#define _SURFACE_SAM_SID_POWER_H + @@ -5098,10 +5277,10 @@ index 000000000000..2e8f212086e1 +#endif /* _SURFACE_SAM_SID_POWER_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c new file mode 100644 -index 000000000000..474221097eaf +index 0000000000000..a6059d6796619 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.c -@@ -0,0 +1,432 @@ +@@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microsofs Surface HID (VHF) driver for HID input events via SAM. @@ -5124,10 +5303,13 @@ index 000000000000..474221097eaf +#define VHF_HID_STARTED 0 + +struct sid_vhf { -+ const struct ssam_hid_properties *p; + struct platform_device *dev; -+ struct hid_device *hid; ++ struct ssam_controller *ctrl; ++ const struct ssam_hid_properties *p; ++ + struct ssam_event_notifier notif; ++ ++ struct hid_device *hid; + unsigned long state; +}; + @@ -5200,99 +5382,91 @@ index 000000000000..474221097eaf +} __packed; + + -+static int vhf_get_metadata(u8 iid, struct vhf_device_metadata *meta) ++static int vhf_get_metadata(struct ssam_controller *ctrl, u8 iid, ++ struct vhf_device_metadata *meta) +{ ++ struct surface_sam_sid_vhf_meta_resp data = {}; ++ struct ssam_request rqst; ++ struct ssam_response rsp; + int status; + -+ struct surface_sam_sid_vhf_meta_resp resp = { -+ .rqst = { -+ .id = 2, -+ .offset = 0, -+ .length = 0x76, -+ .end = 0 -+ } -+ }; ++ data.rqst.id = 2; ++ data.rqst.offset = 0; ++ data.rqst.length = 0x76; ++ data.rqst.end = 0; + -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x15, -+ .cid = 0x04, -+ .iid = iid, -+ .chn = 0x02, -+ .snc = 0x01, -+ .cdl = sizeof(struct surface_sam_sid_vhf_meta_rqst), -+ .pld = (u8 *)&resp.rqst, -+ }; ++ rqst.target_category = SSAM_SSH_TC_HID;; ++ rqst.command_id = 0x04; ++ rqst.instance_id = iid; ++ rqst.channel = 0x02; ++ rqst.flags = SSAM_REQUEST_HAS_RESPONSE; ++ rqst.length = sizeof(struct surface_sam_sid_vhf_meta_rqst); ++ rqst.payload = (u8 *)&data.rqst; + -+ struct surface_sam_ssh_buf result = { -+ .cap = sizeof(struct surface_sam_sid_vhf_meta_resp), -+ .len = 0, -+ .data = (u8 *)&resp, -+ }; ++ rsp.capacity = sizeof(struct surface_sam_sid_vhf_meta_resp); ++ rsp.length = 0; ++ rsp.pointer = (u8 *)&data; + -+ status = surface_sam_ssh_rqst(&rqst, &result); ++ status = ssam_request_sync(ctrl, &rqst, &rsp); + if (status) + return status; + -+ *meta = resp.data.meta; ++ *meta = data.data.meta; + + return 0; +} + +static int vhf_get_hid_descriptor(struct hid_device *hid, u8 iid, u8 **desc, int *size) +{ ++ struct sid_vhf *vhf = dev_get_drvdata(hid->dev.parent); ++ struct surface_sam_sid_vhf_meta_resp data = {}; ++ struct ssam_request rqst; ++ struct ssam_response rsp; + int status, len; + u8 *buf; + -+ struct surface_sam_sid_vhf_meta_resp resp = { -+ .rqst = { -+ .id = 0, -+ .offset = 0, -+ .length = 0x76, -+ .end = 0, -+ } -+ }; ++ data.rqst.id = 0; ++ data.rqst.offset = 0; ++ data.rqst.length = 0x76; ++ data.rqst.end = 0; + -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x15, -+ .cid = 0x04, -+ .iid = iid, -+ .chn = 0x02, -+ .snc = 0x01, -+ .cdl = sizeof(struct surface_sam_sid_vhf_meta_rqst), -+ .pld = (u8 *)&resp.rqst, -+ }; ++ rqst.target_category = SSAM_SSH_TC_HID; ++ rqst.command_id = 0x04; ++ rqst.instance_id = iid; ++ rqst.channel = 0x02; ++ rqst.flags = SSAM_REQUEST_HAS_RESPONSE; ++ rqst.length = sizeof(struct surface_sam_sid_vhf_meta_rqst); ++ rqst.payload = (u8 *)&data.rqst; + -+ struct surface_sam_ssh_buf result = { -+ .cap = sizeof(struct surface_sam_sid_vhf_meta_resp), -+ .len = 0, -+ .data = (u8 *)&resp, -+ }; ++ rsp.capacity = sizeof(struct surface_sam_sid_vhf_meta_resp); ++ rsp.length = 0; ++ rsp.pointer = (u8 *)&data; + + // first fetch 00 to get the total length -+ status = surface_sam_ssh_rqst(&rqst, &result); ++ status = ssam_request_sync(vhf->ctrl, &rqst, &rsp); + if (status) + return status; + -+ len = resp.data.info.hid_len; ++ len = data.data.info.hid_len; + + // allocate a buffer for the descriptor + buf = kzalloc(len, GFP_KERNEL); + + // then, iterate and write into buffer, copying out bytes -+ resp.rqst.id = 1; -+ resp.rqst.offset = 0; -+ resp.rqst.length = 0x76; -+ resp.rqst.end = 0; ++ data.rqst.id = 1; ++ data.rqst.offset = 0; ++ data.rqst.length = 0x76; ++ data.rqst.end = 0; + -+ while (!resp.rqst.end && resp.rqst.offset < len) { -+ status = surface_sam_ssh_rqst(&rqst, &result); ++ while (!data.rqst.end && data.rqst.offset < len) { ++ status = ssam_request_sync(vhf->ctrl, &rqst, &rsp); + if (status) { + kfree(buf); + return status; + } -+ memcpy(buf + resp.rqst.offset, resp.data.pld, resp.rqst.length); ++ memcpy(buf + data.rqst.offset, data.data.pld, data.rqst.length); + -+ resp.rqst.offset += resp.rqst.length; ++ data.rqst.offset += data.rqst.length; + } + + *desc = buf; @@ -5326,10 +5500,10 @@ index 000000000000..474221097eaf + reqtype) +{ + struct sid_vhf *vhf = dev_get_drvdata(hid->dev.parent); ++ struct ssam_request rqst; ++ struct ssam_response rsp; + int status; + u8 cid; -+ struct surface_sam_ssh_rqst rqst = {}; -+ struct surface_sam_ssh_buf result = {}; + + hid_dbg(hid, "%s: reportnum=%#04x rtype=%i reqtype=%i\n", __func__, reportnum, rtype, reqtype); + print_hex_dump_debug("report:", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false); @@ -5366,30 +5540,30 @@ index 000000000000..474221097eaf + return -EIO; + } + -+ rqst.tc = SAM_EVENT_SID_VHF_TC; -+ rqst.chn = 0x02; -+ rqst.iid = vhf->p->instance; -+ rqst.cid = cid; -+ rqst.snc = reqtype == HID_REQ_GET_REPORT ? 0x01 : 0x00; -+ rqst.cdl = reqtype == HID_REQ_GET_REPORT ? 0x01 : len; -+ rqst.pld = buf; ++ rqst.target_category = SSAM_SSH_TC_HID; ++ rqst.channel = 0x02; ++ rqst.instance_id = vhf->p->instance; ++ rqst.command_id = cid; ++ rqst.flags = reqtype == HID_REQ_GET_REPORT ? SSAM_REQUEST_HAS_RESPONSE : 0; ++ rqst.length = reqtype == HID_REQ_GET_REPORT ? 1 : len; ++ rqst.payload = buf; + -+ result.cap = len; -+ result.len = 0; -+ result.data = buf; ++ rsp.capacity = len; ++ rsp.length = 0; ++ rsp.pointer = buf; + + hid_dbg(hid, "%s: sending to cid=%#04x snc=%#04x\n", __func__, cid, HID_REQ_GET_REPORT == reqtype); + -+ status = surface_sam_ssh_rqst(&rqst, &result); ++ status = ssam_request_sync(vhf->ctrl, &rqst, &rsp); + hid_dbg(hid, "%s: status %i\n", __func__, status); + + if (status) + return status; + -+ if (result.len > 0) -+ print_hex_dump_debug("response:", DUMP_PREFIX_OFFSET, 16, 1, result.data, result.len, false); ++ if (rsp.length > 0) ++ print_hex_dump_debug("response:", DUMP_PREFIX_OFFSET, 16, 1, rsp.pointer, rsp.length, false); + -+ return result.len; ++ return rsp.length; +} + +static struct hid_ll_driver sid_vhf_hid_ll_driver = { @@ -5451,13 +5625,14 @@ index 000000000000..474221097eaf +static int surface_sam_sid_vhf_probe(struct platform_device *pdev) +{ + const struct ssam_hid_properties *p = pdev->dev.platform_data; ++ struct ssam_controller *ctrl; + struct sid_vhf *vhf; + struct vhf_device_metadata meta = {}; + struct hid_device *hid; + int status; + + // add device link to EC -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + @@ -5465,7 +5640,7 @@ index 000000000000..474221097eaf + if (!vhf) + return -ENOMEM; + -+ status = vhf_get_metadata(p->instance, &meta); ++ status = vhf_get_metadata(ctrl, p->instance, &meta); + if (status) + goto err_create_hid; + @@ -5475,8 +5650,9 @@ index 000000000000..474221097eaf + goto err_create_hid; + } + -+ vhf->p = pdev->dev.platform_data; + vhf->dev = pdev; ++ vhf->ctrl = ctrl; ++ vhf->p = pdev->dev.platform_data; + vhf->hid = hid; + + vhf->notif.base.priority = 1; @@ -5488,7 +5664,7 @@ index 000000000000..474221097eaf + + platform_set_drvdata(pdev, vhf); + -+ status = surface_sam_ssh_notifier_register(&vhf->notif); ++ status = ssam_notifier_register(ctrl, &vhf->notif); + if (status) + goto err_notif; + @@ -5499,7 +5675,7 @@ index 000000000000..474221097eaf + return 0; + +err_add_hid: -+ surface_sam_ssh_notifier_unregister(&vhf->notif); ++ ssam_notifier_unregister(ctrl, &vhf->notif); +err_notif: + hid_destroy_device(hid); + platform_set_drvdata(pdev, NULL); @@ -5512,7 +5688,7 @@ index 000000000000..474221097eaf +{ + struct sid_vhf *vhf = platform_get_drvdata(pdev); + -+ surface_sam_ssh_notifier_unregister(&vhf->notif); ++ ssam_notifier_unregister(vhf->ctrl, &vhf->notif); + hid_destroy_device(vhf->hid); + kfree(vhf); + @@ -5536,10 +5712,11 @@ index 000000000000..474221097eaf +MODULE_ALIAS("platform:surface_sam_sid_vhf"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.h b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.h new file mode 100644 -index 000000000000..eb55485ccb11 +index 0000000000000..d956de5cf877a --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_sid_vhf.h -@@ -0,0 +1,13 @@ +@@ -0,0 +1,14 @@ ++ +#ifndef _SURFACE_SAM_SID_VHF_H +#define _SURFACE_SAM_SID_VHF_H + @@ -5555,10 +5732,10 @@ index 000000000000..eb55485ccb11 +#endif /* _SURFACE_SAM_SID_VHF_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.c b/drivers/platform/x86/surface_sam/surface_sam_ssh.c new file mode 100644 -index 000000000000..9f44bdfbc4fd +index 0000000000000..4144fe0b3d192 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.c -@@ -0,0 +1,5111 @@ +@@ -0,0 +1,5190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System @@ -5594,14 +5771,6 @@ index 000000000000..9f44bdfbc4fd +#include "surface_sam_ssh_trace.h" + + -+/* -- TODO. ----------------------------------------------------------------- */ -+ -+#define SSH_RQST_TAG_FULL "surface_sam_ssh_rqst: " -+#define SSH_RQST_TAG "rqst: " -+ -+#define SSH_SUPPORTED_FLOW_CONTROL_MASK (~((u8) ACPI_UART_FLOW_CONTROL_HW)) -+ -+ +/* -- Error injection helpers. ---------------------------------------------- */ + +#ifdef CONFIG_SURFACE_SAM_SSH_ERROR_INJECTION @@ -5611,34 +5780,30 @@ index 000000000000..9f44bdfbc4fd +#endif /* CONFIG_SURFACE_SAM_SSH_ERROR_INJECTION */ + + -+/* -- Public interface. ----------------------------------------------------- */ ++/* -- SSH protocol utility functions and definitions. ----------------------- */ + -+enum ssam_request_flags { -+ SSAM_REQUEST_HAS_RESPONSE = BIT(0), -+ SSAM_REQUEST_UNSEQUENCED = BIT(1), -+}; ++/* ++ * The number of reserved event IDs, used for registering an SSH event ++ * handler. Valid event IDs are numbers below or equal to this value, with ++ * exception of zero, which is not an event ID. Thus, this is also the ++ * absolute maximum number of event handlers that can be registered. ++ */ ++#define SSH_NUM_EVENTS 34 + -+struct ssam_request { -+ u8 target_category; -+ u8 command_id; -+ u8 instance_id; -+ u8 channel; -+ u16 flags; -+ u16 length; -+ u8 *payload; -+}; ++/* ++ * The number of communication channels used in the protocol. ++ */ ++#define SSH_NUM_CHANNELS 2 + + -+/* -- Common/utility functions. --------------------------------------------- */ -+ +static inline u16 ssh_crc(const u8 *buf, size_t len) +{ + return crc_ccitt_false(0xffff, buf, len); +} + -+static inline u16 __ssh_rqid_next(u16 rqid) ++static inline u16 ssh_rqid_next_valid(u16 rqid) +{ -+ return rqid > 0 ? rqid + 1u : rqid + SURFACE_SAM_SSH_NUM_EVENTS + 1u; ++ return rqid > 0 ? rqid + 1u : rqid + SSH_NUM_EVENTS + 1u; +} + +static inline u16 ssh_event_to_rqid(u16 event) @@ -5653,23 +5818,11 @@ index 000000000000..9f44bdfbc4fd + +static inline bool ssh_rqid_is_event(u16 rqid) +{ -+ return ssh_rqid_to_event(rqid) < SURFACE_SAM_SSH_NUM_EVENTS; ++ return ssh_rqid_to_event(rqid) < SSH_NUM_EVENTS; +} + +static inline int ssh_tc_to_rqid(u8 tc) +{ -+#if 0 // TODO: check if it works without this -+ /* -+ * TC=0x08 represents the input subsystem on Surface Laptop 1 and 2. -+ * This is mapped on Windows to RQID=0x0001. As input events seem to be -+ * somewhat special with regards to enabling/disabling (they seem to be -+ * enabled by default with a fixed RQID), let's do the same here. -+ */ -+ if (tc == 0x08) -+ return 0x0001; -+ -+ /* Default path: Set RQID = TC. */ -+#endif + return tc; +} + @@ -5685,7 +5838,7 @@ index 000000000000..9f44bdfbc4fd + +static inline bool ssh_channel_is_valid(u8 channel) +{ -+ return ssh_channel_to_index(channel) < SURFACE_SAM_SSH_NUM_CHANNELS; ++ return ssh_channel_to_index(channel) < SSH_NUM_CHANNELS; +} + + @@ -5726,12 +5879,12 @@ index 000000000000..9f44bdfbc4fd +static inline u16 ssh_rqid_next(struct ssh_rqid_counter *c) +{ + u16 old = READ_ONCE(c->value); -+ u16 new = __ssh_rqid_next(old); ++ u16 new = ssh_rqid_next_valid(old); + u16 ret; + + while (unlikely((ret = cmpxchg(&c->value, old, new)) != old)) { + old = ret; -+ new = __ssh_rqid_next(old); ++ new = ssh_rqid_next_valid(old); + } + + return old; @@ -5741,16 +5894,16 @@ index 000000000000..9f44bdfbc4fd +/* -- Builder functions for SAM-over-SSH messages. -------------------------- */ + +struct msgbuf { -+ u8 *buffer; ++ u8 *begin; + u8 *end; + u8 *ptr; +}; + -+static inline void msgb_init(struct msgbuf *msgb, u8 *buffer, size_t cap) ++static inline void msgb_init(struct msgbuf *msgb, u8 *ptr, size_t cap) +{ -+ msgb->buffer = buffer; -+ msgb->end = buffer + cap; -+ msgb->ptr = buffer; ++ msgb->begin = ptr; ++ msgb->end = ptr + cap; ++ msgb->ptr = ptr; +} + +static inline int msgb_alloc(struct msgbuf *msgb, size_t cap, gfp_t flags) @@ -5767,26 +5920,25 @@ index 000000000000..9f44bdfbc4fd + +static inline void msgb_free(struct msgbuf *msgb) +{ -+ kfree(msgb->buffer); -+ msgb->buffer = NULL; ++ kfree(msgb->begin); ++ msgb->begin = NULL; + msgb->end = NULL; + msgb->ptr = NULL; +} + +static inline void msgb_reset(struct msgbuf *msgb) +{ -+ msgb->ptr = msgb->buffer; ++ msgb->ptr = msgb->begin; +} + +static inline size_t msgb_bytes_used(const struct msgbuf *msgb) +{ -+ return msgb->ptr - msgb->buffer; ++ return msgb->ptr - msgb->begin; +} + +static inline void msgb_push_u16(struct msgbuf *msgb, u16 value) +{ -+ WARN_ON(msgb->ptr + sizeof(u16) > msgb->end); -+ if (msgb->ptr + sizeof(u16) > msgb->end) ++ if (WARN_ON(msgb->ptr + sizeof(u16) > msgb->end)) + return; + + put_unaligned_le16(value, msgb->ptr); @@ -5813,8 +5965,7 @@ index 000000000000..9f44bdfbc4fd + struct ssh_frame *frame = (struct ssh_frame *)msgb->ptr; + const u8 *const begin = msgb->ptr; + -+ WARN_ON(msgb->ptr + sizeof(*frame) > msgb->end); -+ if (msgb->ptr + sizeof(*frame) > msgb->end) ++ if (WARN_ON(msgb->ptr + sizeof(*frame) > msgb->end)) + return; + + frame->type = ty; @@ -5849,9 +6000,8 @@ index 000000000000..9f44bdfbc4fd + msgb_push_crc(msgb, msgb->ptr, 0); +} + -+static inline void msgb_push_cmd(struct msgbuf *msgb, u8 seq, -+ const struct surface_sam_ssh_rqst *rqst, -+ u16 rqid) ++static inline void msgb_push_cmd(struct msgbuf *msgb, u8 seq, u16 rqid, ++ const struct ssam_request *rqst) +{ + struct ssh_command *cmd; + const u8 *cmd_begin; @@ -5861,28 +6011,27 @@ index 000000000000..9f44bdfbc4fd + msgb_push_syn(msgb); + + // command frame + crc -+ msgb_push_frame(msgb, type, sizeof(*cmd) + rqst->cdl, seq); ++ msgb_push_frame(msgb, type, sizeof(*cmd) + rqst->length, seq); + + // frame payload: command struct + payload -+ WARN_ON(msgb->ptr + sizeof(*cmd) > msgb->end); -+ if (msgb->ptr + sizeof(*cmd) > msgb->end) ++ if (WARN_ON(msgb->ptr + sizeof(*cmd) > msgb->end)) + return; + + cmd_begin = msgb->ptr; + cmd = (struct ssh_command *)msgb->ptr; + + cmd->type = SSH_PLD_TYPE_CMD; -+ cmd->tc = rqst->tc; -+ cmd->chn_out = rqst->chn; ++ cmd->tc = rqst->target_category; ++ cmd->chn_out = rqst->channel; + cmd->chn_in = 0x00; -+ cmd->iid = rqst->iid; ++ cmd->iid = rqst->instance_id; + put_unaligned_le16(rqid, &cmd->rqid); -+ cmd->cid = rqst->cid; ++ cmd->cid = rqst->command_id; + + msgb->ptr += sizeof(*cmd); + + // command payload -+ msgb_push_buf(msgb, rqst->pld, rqst->cdl); ++ msgb_push_buf(msgb, rqst->payload, rqst->length); + + // crc for command struct + payload + msgb_push_crc(msgb, cmd_begin, msgb->ptr - cmd_begin); @@ -5898,7 +6047,7 @@ index 000000000000..9f44bdfbc4fd +}; + + -+static inline bool sshp_validate_crc(const struct sshp_span *src, const u8 *crc) ++static inline bool sshp_validate_crc(const struct ssam_span *src, const u8 *crc) +{ + u16 actual = ssh_crc(src->ptr, src->len); + u16 expected = get_unaligned_le16(crc); @@ -5906,7 +6055,7 @@ index 000000000000..9f44bdfbc4fd + return actual == expected; +} + -+static bool sshp_find_syn(const struct sshp_span *src, struct sshp_span *rem) ++static bool sshp_find_syn(const struct ssam_span *src, struct ssam_span *rem) +{ + size_t i; + @@ -5929,19 +6078,19 @@ index 000000000000..9f44bdfbc4fd + } +} + -+static bool sshp_starts_with_syn(const struct sshp_span *src) ++static bool sshp_starts_with_syn(const struct ssam_span *src) +{ + return src->len >= 2 && get_unaligned_le16(src->ptr) == SSH_MSG_SYN; +} + +static int sshp_parse_frame(const struct device *dev, -+ const struct sshp_span *source, ++ const struct ssam_span *source, + struct ssh_frame **frame, -+ struct sshp_span *payload, ++ struct ssam_span *payload, + size_t maxlen) +{ -+ struct sshp_span sf; -+ struct sshp_span sp; ++ struct ssam_span sf; ++ struct ssam_span sp; + + // initialize output + *frame = NULL; @@ -6002,9 +6151,9 @@ index 000000000000..9f44bdfbc4fd +} + +static int sshp_parse_command(const struct device *dev, -+ const struct sshp_span *source, ++ const struct ssam_span *source, + struct ssh_command **command, -+ struct sshp_span *command_data) ++ struct ssam_span *command_data) +{ + // check for minimum length + if (unlikely(source->len < sizeof(struct ssh_command))) { @@ -6078,7 +6227,7 @@ index 000000000000..9f44bdfbc4fd +} + +static inline void sshp_buf_span_from(struct sshp_buf *buf, size_t offset, -+ struct sshp_span *span) ++ struct ssam_span *span) +{ + span->ptr = buf->ptr + offset; + span->len = buf->len - offset; @@ -6248,7 +6397,7 @@ index 000000000000..9f44bdfbc4fd +}; + +struct ssh_ptl_ops { -+ void (*data_received)(struct ssh_ptl *p, const struct sshp_span *data); ++ void (*data_received)(struct ssh_ptl *p, const struct ssam_span *data); +}; + +struct ssh_ptl { @@ -6284,7 +6433,7 @@ index 000000000000..9f44bdfbc4fd + struct { + u16 seqs[8]; + u16 offset; -+ } blacklist; ++ } blocked; + } rx; + + struct { @@ -6378,7 +6527,7 @@ index 000000000000..9f44bdfbc4fd + * data being sent to the EC + * + * Hook to simulate corrupt/invalid data being sent from host (driver) to EC. -+ * Causes the package data to be actively corrupted by overwriting it with ++ * Causes the packet data to be actively corrupted by overwriting it with + * pre-defined values, such that it becomes invalid, causing the EC to respond + * with a NAK packet. Useful to test handling of NAK packets received by the + * driver. @@ -6456,10 +6605,10 @@ index 000000000000..9f44bdfbc4fd +static bool ssh_ptl_should_drop_packet(struct ssh_packet *packet) +{ + // ignore packets that don't carry any data (i.e. flush) -+ if (!packet->data || !packet->data_length) ++ if (!packet->data.ptr || !packet->data.len) + return false; + -+ switch (packet->data[SSH_MSGOFFSET_FRAME(type)]) { ++ switch (packet->data.ptr[SSH_MSGOFFSET_FRAME(type)]) { + case SSH_FRAME_TYPE_ACK: + return __ssh_ptl_should_drop_ack_packet(packet); + @@ -6497,11 +6646,11 @@ index 000000000000..9f44bdfbc4fd +static inline void ssh_ptl_tx_inject_invalid_data(struct ssh_packet *packet) +{ + // ignore packets that don't carry any data (i.e. flush) -+ if (!packet->data || !packet->data_length) ++ if (!packet->data.ptr || !packet->data.len) + return; + + // only allow sequenced data packets to be modified -+ if (packet->data[SSH_MSGOFFSET_FRAME(type)] != SSH_FRAME_TYPE_DATA_SEQ) ++ if (packet->data.ptr[SSH_MSGOFFSET_FRAME(type)] != SSH_FRAME_TYPE_DATA_SEQ) + return; + + if (likely(!ssh_ptl_should_corrupt_tx_data())) @@ -6517,13 +6666,13 @@ index 000000000000..9f44bdfbc4fd + * doesn't have any (major) overlap with the SYN bytes (aa 55) and is + * non-trivial (i.e. non-zero, non-0xff). + */ -+ memset(packet->data, 0xb3, packet->data_length); ++ memset(packet->data.ptr, 0xb3, packet->data.len); +} + +static inline void ssh_ptl_rx_inject_invalid_syn(struct ssh_ptl *ptl, -+ struct sshp_span *data) ++ struct ssam_span *data) +{ -+ struct sshp_span frame; ++ struct ssam_span frame; + + // check if there actually is something to corrupt + if (!sshp_find_syn(data, &frame)) @@ -6538,7 +6687,7 @@ index 000000000000..9f44bdfbc4fd +} + +static inline void ssh_ptl_rx_inject_invalid_data(struct ssh_ptl *ptl, -+ struct sshp_span *frame) ++ struct ssam_span *frame) +{ + size_t payload_len, message_len; + struct ssh_frame *sshf; @@ -6588,12 +6737,12 @@ index 000000000000..9f44bdfbc4fd +} + +static inline void ssh_ptl_rx_inject_invalid_syn(struct ssh_ptl *ptl, -+ struct sshp_span *data) ++ struct ssam_span *data) +{ +} + +static inline void ssh_ptl_rx_inject_invalid_data(struct ssh_ptl *ptl, -+ struct sshp_span *frame) ++ struct ssam_span *frame) +{ +} + @@ -6610,25 +6759,26 @@ index 000000000000..9f44bdfbc4fd + p->ops->release(p); +} + -+static inline void ssh_packet_get(struct ssh_packet *packet) ++void ssh_packet_get(struct ssh_packet *packet) +{ + kref_get(&packet->refcnt); +} ++EXPORT_SYMBOL_GPL(ssh_packet_get); + -+static inline void ssh_packet_put(struct ssh_packet *packet) ++void ssh_packet_put(struct ssh_packet *packet) +{ + kref_put(&packet->refcnt, __ssh_ptl_packet_release); +} -+ ++EXPORT_SYMBOL_GPL(ssh_packet_put); + +static inline u8 ssh_packet_get_seq(struct ssh_packet *packet) +{ -+ return packet->data[SSH_MSGOFFSET_FRAME(seq)]; ++ return packet->data.ptr[SSH_MSGOFFSET_FRAME(seq)]; +} + + +struct ssh_packet_args { -+ u8 type; ++ unsigned long type; + u8 priority; + const struct ssh_packet_ops *ops; +}; @@ -6642,40 +6792,45 @@ index 000000000000..9f44bdfbc4fd + INIT_LIST_HEAD(&packet->queue_node); + INIT_LIST_HEAD(&packet->pending_node); + -+ packet->type = args->type; ++ packet->state = args->type & SSH_PACKET_FLAGS_TY_MASK; + packet->priority = args->priority; -+ packet->state = 0; + packet->timestamp = KTIME_MAX; + -+ packet->data_length = 0; -+ packet->data = NULL; ++ packet->data.ptr = NULL; ++ packet->data.len = 0; + + packet->ops = args->ops; +} + + -+static struct ssh_packet *ptl_alloc_ctrl_packet( -+ struct ssh_ptl *ptl, const struct ssh_packet_args *args, -+ gfp_t flags) ++static int ptl_alloc_ctrl_packet(struct ssh_ptl *ptl, ++ struct ssh_packet **packet, ++ struct ssam_span *buffer, gfp_t flags) +{ -+ struct ssh_packet *packet; ++ // TODO: cache packets ++ // - Potential problem with kmem_cache: Minimum alloc size of that is ++ // PAGE_SIZE (???), which is somewhat overkill here. ++ // - Note: Mempool always tries to allocate with alloc callback first. ++ // Buffered objects are only chosen on allocation failure. ++ // ++ // => either kmem_cache or custom, try kmem_cache first and check via ++ // /proc/slabinfo ++ // ++ // Note: kmem_cache_create needs unique name + -+ // TODO: chache packets ++ *packet = kzalloc(sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL, flags); ++ if (!*packet) ++ return -ENOMEM; + -+ packet = kzalloc(sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL, flags); -+ if (!packet) -+ return NULL; ++ buffer->ptr = (u8 *)(*packet + 1); ++ buffer->len = SSH_MSG_LEN_CTRL; + -+ ssh_packet_init(packet, args); -+ packet->data_length = SSH_MSG_LEN_CTRL; -+ packet->data = ((u8 *) packet) + sizeof(struct ssh_packet); -+ -+ return packet; ++ return 0; +} + +static void ptl_free_ctrl_packet(struct ssh_packet *p) +{ -+ // TODO: chache packets ++ // TODO: cache packets + + kfree(p); +} @@ -6862,6 +7017,9 @@ index 000000000000..9f44bdfbc4fd + trace_ssam_packet_complete(p, status); + + ptl_dbg_cond(ptl, "ptl: completing packet %p\n", p); ++ if (status && status != -ECANCELED) ++ ptl_dbg_cond(ptl, "ptl: packet error: %d\n", status); ++ + if (p->ops->complete) + p->ops->complete(p, status); +} @@ -6892,11 +7050,11 @@ index 000000000000..9f44bdfbc4fd +{ + struct ssh_ptl *ptl = packet->ptl; + -+ if (packet->type & SSH_PACKET_TY_FLUSH) ++ if (test_bit(SSH_PACKET_TY_FLUSH_BIT, &packet->state)) + return !atomic_read(&ptl->pending.count); + + // we can alwas process non-blocking packets -+ if (!(packet->type & SSH_PACKET_TY_BLOCKING)) ++ if (!test_bit(SSH_PACKET_TY_BLOCKING_BIT, &packet->state)) + return true; + + // if we are already waiting for this packet, send it again @@ -6965,7 +7123,7 @@ index 000000000000..9f44bdfbc4fd + if (IS_ERR(p)) + return p; + -+ if (p->type & SSH_PACKET_TY_SEQUENCED) { ++ if (test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &p->state)) { + ptl_dbg(ptl, "ptl: transmitting sequenced packet %p\n", p); + ssh_ptl_pending_push(p); + ssh_ptl_timeout_start(p); @@ -6999,7 +7157,7 @@ index 000000000000..9f44bdfbc4fd + clear_bit(SSH_PACKET_SF_TRANSMITTING_BIT, &packet->state); + + // if the packet is unsequenced, we're done: lock and complete -+ if (!(packet->type & SSH_PACKET_TY_SEQUENCED)) { ++ if (!test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &packet->state)) { + set_bit(SSH_PACKET_SF_LOCKED_BIT, &packet->state); + ssh_ptl_remove_and_complete(packet, 0); + } @@ -7071,9 +7229,9 @@ index 000000000000..9f44bdfbc4fd + ssh_ptl_tx_inject_invalid_data(ptl->tx.packet); + + // flush-packets don't have any data -+ if (likely(ptl->tx.packet->data && !drop)) { -+ buf = ptl->tx.packet->data + ptl->tx.offset; -+ len = ptl->tx.packet->data_length - ptl->tx.offset; ++ if (likely(ptl->tx.packet->data.ptr && !drop)) { ++ buf = ptl->tx.packet->data.ptr + ptl->tx.offset; ++ len = ptl->tx.packet->data.len - ptl->tx.offset; + + ptl_dbg(ptl, "tx: sending data (length: %zu)\n", len); + print_hex_dump_debug("tx: ", DUMP_PREFIX_OFFSET, 16, 1, @@ -7251,41 +7409,36 @@ index 000000000000..9f44bdfbc4fd +} + + -+static int ssh_ptl_submit(struct ssh_ptl *ptl, struct ssh_packet *packet) ++static int ssh_ptl_submit(struct ssh_ptl *ptl, struct ssh_packet *p) +{ ++ struct ssh_ptl *ptl_old; + int status; + -+ trace_ssam_packet_submit(packet); ++ trace_ssam_packet_submit(p); + + // validate packet fields -+ if (packet->type & SSH_PACKET_TY_FLUSH) { -+ if (packet->data || (packet->type & SSH_PACKET_TY_SEQUENCED)) ++ if (test_bit(SSH_PACKET_TY_FLUSH_BIT, &p->state)) { ++ if (p->data.ptr || test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &p->state)) + return -EINVAL; -+ } else if (!packet->data) { ++ } else if (!p->data.ptr) { + return -EINVAL; + } + + /* -+ * This function is currently not intended for re-submission. The ptl -+ * reference only gets set on the first submission. After the first -+ * submission, it has to be read-only. -+ * -+ * Use cmpxchg to ensure safety with regards to ssh_ptl_cancel and -+ * re-entry, where we can't guarantee that the packet has been submitted -+ * yet. -+ * -+ * The implicit barrier of cmpxchg is paired with barrier in -+ * ssh_ptl_cancel to guarantee cancelation in case the packet has never -+ * been submitted or is currently being submitted. ++ * The ptl reference only gets set on or before the first submission. ++ * After the first submission, it has to be read-only. + */ -+ if (cmpxchg(&packet->ptl, NULL, ptl) != NULL) ++ ptl_old = READ_ONCE(p->ptl); ++ if (ptl_old == NULL) ++ WRITE_ONCE(p->ptl, ptl); ++ else if (ptl_old != ptl) + return -EALREADY; + -+ status = ssh_ptl_queue_push(packet); ++ status = ssh_ptl_queue_push(p); + if (status) + return status; + -+ ssh_ptl_tx_wakeup(ptl, !(packet->type & SSH_PACKET_TY_BLOCKING)); ++ ssh_ptl_tx_wakeup(ptl, !test_bit(SSH_PACKET_TY_BLOCKING_BIT, &p->state)); + return 0; +} + @@ -7495,32 +7648,32 @@ index 000000000000..9f44bdfbc4fd +} + + -+static bool ssh_ptl_rx_blacklist_check(struct ssh_ptl *ptl, u8 seq) ++static bool ssh_ptl_rx_retransmit_check(struct ssh_ptl *ptl, u8 seq) +{ + int i; + -+ // check if SEQ is blacklisted -+ for (i = 0; i < ARRAY_SIZE(ptl->rx.blacklist.seqs); i++) { -+ if (likely(ptl->rx.blacklist.seqs[i] != seq)) ++ // check if SEQ has been seen recently (i.e. packet was re-transmitted) ++ for (i = 0; i < ARRAY_SIZE(ptl->rx.blocked.seqs); i++) { ++ if (likely(ptl->rx.blocked.seqs[i] != seq)) + continue; + + ptl_dbg(ptl, "ptl: ignoring repeated data packet\n"); + return true; + } + -+ // update blacklist -+ ptl->rx.blacklist.seqs[ptl->rx.blacklist.offset] = seq; -+ ptl->rx.blacklist.offset = (ptl->rx.blacklist.offset + 1) -+ % ARRAY_SIZE(ptl->rx.blacklist.seqs); ++ // update list of blocked seuence IDs ++ ptl->rx.blocked.seqs[ptl->rx.blocked.offset] = seq; ++ ptl->rx.blocked.offset = (ptl->rx.blocked.offset + 1) ++ % ARRAY_SIZE(ptl->rx.blocked.seqs); + + return false; +} + +static void ssh_ptl_rx_dataframe(struct ssh_ptl *ptl, + const struct ssh_frame *frame, -+ const struct sshp_span *payload) ++ const struct ssam_span *payload) +{ -+ if (ssh_ptl_rx_blacklist_check(ptl, frame->seq)) ++ if (ssh_ptl_rx_retransmit_check(ptl, frame->seq)) + return; + + ptl->ops.data_received(ptl, payload); @@ -7530,21 +7683,24 @@ index 000000000000..9f44bdfbc4fd +{ + struct ssh_packet_args args; + struct ssh_packet *packet; ++ struct ssam_span buf; + struct msgbuf msgb; ++ int status; + -+ args.type = 0; -+ args.priority = SSH_PACKET_PRIORITY(ACK, 0); -+ args.ops = &ssh_ptl_ctrl_packet_ops; -+ -+ packet = ptl_alloc_ctrl_packet(ptl, &args, GFP_KERNEL); -+ if (!packet) { ++ status = ptl_alloc_ctrl_packet(ptl, &packet, &buf, GFP_KERNEL); ++ if (status) { + ptl_err(ptl, "ptl: failed to allocate ACK packet\n"); + return; + } + -+ msgb_init(&msgb, packet->data, packet->data_length); ++ args.type = 0; ++ args.priority = SSH_PACKET_PRIORITY(ACK, 0); ++ args.ops = &ssh_ptl_ctrl_packet_ops; ++ ssh_packet_init(packet, &args); ++ ++ msgb_init(&msgb, buf.ptr, buf.len); + msgb_push_ack(&msgb, seq); -+ packet->data_length = msgb_bytes_used(&msgb); ++ ssh_packet_set_data(packet, msgb.begin, msgb_bytes_used(&msgb)); + + ssh_ptl_submit(ptl, packet); + ssh_packet_put(packet); @@ -7554,31 +7710,34 @@ index 000000000000..9f44bdfbc4fd +{ + struct ssh_packet_args args; + struct ssh_packet *packet; ++ struct ssam_span buf; + struct msgbuf msgb; ++ int status; + -+ args.type = 0; -+ args.priority = SSH_PACKET_PRIORITY(NAK, 0); -+ args.ops = &ssh_ptl_ctrl_packet_ops; -+ -+ packet = ptl_alloc_ctrl_packet(ptl, &args, GFP_KERNEL); -+ if (!packet) { ++ status = ptl_alloc_ctrl_packet(ptl, &packet, &buf, GFP_KERNEL); ++ if (status) { + ptl_err(ptl, "ptl: failed to allocate NAK packet\n"); + return; + } + -+ msgb_init(&msgb, packet->data, packet->data_length); ++ args.type = 0; ++ args.priority = SSH_PACKET_PRIORITY(NAK, 0); ++ args.ops = &ssh_ptl_ctrl_packet_ops; ++ ssh_packet_init(packet, &args); ++ ++ msgb_init(&msgb, buf.ptr, buf.len); + msgb_push_nak(&msgb); -+ packet->data_length = msgb_bytes_used(&msgb); ++ ssh_packet_set_data(packet, msgb.begin, msgb_bytes_used(&msgb)); + + ssh_ptl_submit(ptl, packet); + ssh_packet_put(packet); +} + -+static size_t ssh_ptl_rx_eval(struct ssh_ptl *ptl, struct sshp_span *source) ++static size_t ssh_ptl_rx_eval(struct ssh_ptl *ptl, struct ssam_span *source) +{ + struct ssh_frame *frame; -+ struct sshp_span payload; -+ struct sshp_span aligned; ++ struct ssam_span payload; ++ struct ssam_span aligned; + bool syn_found; + int status; + @@ -7654,7 +7813,7 @@ index 000000000000..9f44bdfbc4fd + struct ssh_ptl *ptl = data; + + while (true) { -+ struct sshp_span span; ++ struct ssam_span span; + size_t offs = 0; + size_t n; + @@ -7723,7 +7882,7 @@ index 000000000000..9f44bdfbc4fd + int used; + + if (test_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state)) -+ return used; ++ return -ESHUTDOWN; + + used = kfifo_in(&ptl->rx.fifo, buf, n); + if (used) @@ -7761,65 +7920,6 @@ index 000000000000..9f44bdfbc4fd +}; + +/** -+ * ssh_ptl_flush - flush the packet transmission layer -+ * @ptl: packet transmission layer -+ * @timeout: timeout for the flush operation in jiffies -+ * -+ * Queue a special flush-packet and wait for its completion. This packet will -+ * be completed after all other currently queued and pending packets have been -+ * completed. Flushing guarantees that all previously submitted data packets -+ * have been fully completed before this call returns. Additionally, flushing -+ * blocks execution of all later submitted data packets until the flush has been -+ * completed. -+ * -+ * Control (i.e. ACK/NAK) packets that have been submitted after this call will -+ * be placed before the flush packet in the queue, as long as the flush-packet -+ * has not been chosen for processing yet. -+ * -+ * Flushing, even when no new data packets are submitted after this call, does -+ * not guarantee that no more packets are scheduled. For example, incoming -+ * messages can promt automated submission of ACK or NAK type packets. If this -+ * happens while the flush-packet is being processed (i.e. after it has been -+ * taken from the queue), such packets may still be queued after this function -+ * returns. -+ * -+ * Return: Zero on success, -ETIMEDOUT if the flush timed out and has been -+ * canceled as a result of the timeout, or -ESHUTDOWN if the packet transmission -+ * layer has been shut down before this call. May also return -EINTR if the -+ * packet transmission has been interrupted. -+ */ -+static int ssh_ptl_flush(struct ssh_ptl *ptl, unsigned long timeout) -+{ -+ struct ssh_flush_packet packet; -+ struct ssh_packet_args args; -+ int status; -+ -+ args.type = SSH_PACKET_TY_FLUSH | SSH_PACKET_TY_BLOCKING; -+ args.priority = SSH_PACKET_PRIORITY(FLUSH, 0); -+ args.ops = &ssh_flush_packet_ops; -+ -+ ssh_packet_init(&packet.base, &args); -+ init_completion(&packet.completion); -+ -+ status = ssh_ptl_submit(ptl, &packet.base); -+ if (status) -+ return status; -+ -+ ssh_packet_put(&packet.base); -+ -+ if (wait_for_completion_timeout(&packet.completion, timeout)) -+ return 0; -+ -+ ssh_ptl_cancel(&packet.base); -+ wait_for_completion(&packet.completion); -+ -+ WARN_ON(packet.status != 0 && packet.status != -ECANCELED -+ && packet.status != -ESHUTDOWN && packet.status != -EINTR); -+ -+ return packet.status == -ECANCELED ? -ETIMEDOUT : status; -+} -+ -+/** + * ssh_ptl_shutdown - shut down the packet transmission layer + * @ptl: packet transmission layer + * @@ -7955,10 +8055,10 @@ index 000000000000..9f44bdfbc4fd + + ptl->ops = *ops; + -+ // initialize SEQ blacklist with invalid sequence IDs -+ for (i = 0; i < ARRAY_SIZE(ptl->rx.blacklist.seqs); i++) -+ ptl->rx.blacklist.seqs[i] = 0xFFFF; -+ ptl->rx.blacklist.offset = 0; ++ // initialize list of recent/blocked SEQs with invalid sequence IDs ++ for (i = 0; i < ARRAY_SIZE(ptl->rx.blocked.seqs); i++) ++ ptl->rx.blocked.seqs[i] = 0xFFFF; ++ ptl->rx.blocked.offset = 0; + + status = kfifo_alloc(&ptl->rx.fifo, SSH_PTL_RX_FIFO_LEN, GFP_KERNEL); + if (status) @@ -7980,7 +8080,7 @@ index 000000000000..9f44bdfbc4fd + +/* -- Request transport layer (rtl). ---------------------------------------- */ + -+#define SSH_RTL_REQUEST_TIMEOUT ms_to_ktime(1000) ++#define SSH_RTL_REQUEST_TIMEOUT ms_to_ktime(3000) +#define SSH_RTL_REQUEST_TIMEOUT_RESOLUTION ms_to_ktime(max(2000 / HZ, 50)) + +#define SSH_RTL_MAX_PENDING 3 @@ -7992,7 +8092,7 @@ index 000000000000..9f44bdfbc4fd + +struct ssh_rtl_ops { + void (*handle_event)(struct ssh_rtl *rtl, const struct ssh_command *cmd, -+ const struct sshp_span *data); ++ const struct ssam_span *data); +}; + +struct ssh_rtl { @@ -8036,6 +8136,12 @@ index 000000000000..9f44bdfbc4fd +#define to_ssh_request(ptr, member) \ + container_of(ptr, struct ssh_request, member) + ++static inline struct ssh_rtl *ssh_request_rtl(struct ssh_request *rqst) ++{ ++ struct ssh_ptl *ptl = READ_ONCE(rqst->packet.ptl); ++ return likely(ptl) ? to_ssh_rtl(ptl, ptl) : NULL; ++} ++ + +/** + * ssh_rtl_should_drop_response - error injection hook to drop request responses @@ -8050,26 +8156,15 @@ index 000000000000..9f44bdfbc4fd +ALLOW_ERROR_INJECTION(ssh_rtl_should_drop_response, TRUE); + + -+static inline void ssh_request_get(struct ssh_request *rqst) -+{ -+ ssh_packet_get(&rqst->packet); -+} -+ -+static inline void ssh_request_put(struct ssh_request *rqst) -+{ -+ ssh_packet_put(&rqst->packet); -+} -+ -+ +static inline u16 ssh_request_get_rqid(struct ssh_request *rqst) +{ -+ return get_unaligned_le16(rqst->packet.data ++ return get_unaligned_le16(rqst->packet.data.ptr + + SSH_MSGOFFSET_COMMAND(rqid)); +} + +static inline u32 ssh_request_get_rqid_safe(struct ssh_request *rqst) +{ -+ if (!rqst->packet.data) ++ if (!rqst->packet.data.ptr) + return -1; + + return ssh_request_get_rqid(rqst); @@ -8078,15 +8173,16 @@ index 000000000000..9f44bdfbc4fd + +static void ssh_rtl_queue_remove(struct ssh_request *rqst) +{ ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + bool remove; + -+ spin_lock(&rqst->rtl->queue.lock); ++ spin_lock(&rtl->queue.lock); + + remove = test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state); + if (remove) + list_del(&rqst->node); + -+ spin_unlock(&rqst->rtl->queue.lock); ++ spin_unlock(&rtl->queue.lock); + + if (remove) + ssh_request_put(rqst); @@ -8094,17 +8190,18 @@ index 000000000000..9f44bdfbc4fd + +static void ssh_rtl_pending_remove(struct ssh_request *rqst) +{ ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + bool remove; + -+ spin_lock(&rqst->rtl->pending.lock); ++ spin_lock(&rtl->pending.lock); + + remove = test_and_clear_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state); + if (remove) { -+ atomic_dec(&rqst->rtl->pending.count); ++ atomic_dec(&rtl->pending.count); + list_del(&rqst->node); + } + -+ spin_unlock(&rqst->rtl->pending.lock); ++ spin_unlock(&rtl->pending.lock); + + if (remove) + ssh_request_put(rqst); @@ -8113,24 +8210,29 @@ index 000000000000..9f44bdfbc4fd + +static void ssh_rtl_complete_with_status(struct ssh_request *rqst, int status) +{ -+ struct ssh_rtl *rtl = READ_ONCE(rqst->rtl); ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + + trace_ssam_request_complete(rqst, status); + -+ // rqst->rtl may not be set if we're cancelling before submitting ++ // rtl/ptl may not be set if we're cancelling before submitting + rtl_dbg_cond(rtl, "rtl: completing request (rqid: 0x%04x," + " status: %d)\n", ssh_request_get_rqid_safe(rqst), status); + ++ if (status && status != -ECANCELED) ++ rtl_dbg_cond(rtl, "rtl: request error: %d\n", status); ++ + rqst->ops->complete(rqst, NULL, NULL, status); +} + +static void ssh_rtl_complete_with_rsp(struct ssh_request *rqst, + const struct ssh_command *cmd, -+ const struct sshp_span *data) ++ const struct ssam_span *data) +{ ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); ++ + trace_ssam_request_complete(rqst, 0); + -+ rtl_dbg(rqst->rtl, "rtl: completing request with response" ++ rtl_dbg(rtl, "rtl: completing request with response" + " (rqid: 0x%04x)\n", ssh_request_get_rqid(rqst)); + + rqst->ops->complete(rqst, cmd, data, 0); @@ -8139,10 +8241,12 @@ index 000000000000..9f44bdfbc4fd + +static bool ssh_rtl_tx_can_process(struct ssh_request *rqst) +{ -+ if (test_bit(SSH_REQUEST_TY_FLUSH_BIT, &rqst->state)) -+ return !atomic_read(&rqst->rtl->pending.count); ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + -+ return atomic_read(&rqst->rtl->pending.count) < SSH_RTL_MAX_PENDING; ++ if (test_bit(SSH_REQUEST_TY_FLUSH_BIT, &rqst->state)) ++ return !atomic_read(&rtl->pending.count); ++ ++ return atomic_read(&rtl->pending.count) < SSH_RTL_MAX_PENDING; +} + +static struct ssh_request *ssh_rtl_tx_next(struct ssh_rtl *rtl) @@ -8182,7 +8286,7 @@ index 000000000000..9f44bdfbc4fd + +static int ssh_rtl_tx_pending_push(struct ssh_request *rqst) +{ -+ struct ssh_rtl *rtl = rqst->rtl; ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + + spin_lock(&rtl->pending.lock); + @@ -8323,11 +8427,11 @@ index 000000000000..9f44bdfbc4fd + * is required to be changed in the code. + */ + if (test_bit(SSH_REQUEST_TY_HAS_RESPONSE_BIT, &rqst->state)) -+ if (!(rqst->packet.type & SSH_PACKET_TY_SEQUENCED)) ++ if (!test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &rqst->packet.state)) + return -EINVAL; + -+ // try to set rtl and check if this request has already been submitted -+ if (cmpxchg(&rqst->rtl, NULL, rtl) != NULL) ++ // try to set ptl and check if this request has already been submitted ++ if (cmpxchg(&rqst->packet.ptl, NULL, &rtl->ptl) != NULL) + return -EALREADY; + + spin_lock(&rtl->queue.lock); @@ -8372,7 +8476,7 @@ index 000000000000..9f44bdfbc4fd + +static void ssh_rtl_timeout_start(struct ssh_request *rqst) +{ -+ struct ssh_rtl *rtl = rqst->rtl; ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + ktime_t timestamp = ktime_get_coarse_boottime(); + ktime_t timeout = rtl->rtx_timeout.timeout; + @@ -8382,13 +8486,13 @@ index 000000000000..9f44bdfbc4fd + WRITE_ONCE(rqst->timestamp, timestamp); + smp_mb__after_atomic(); + -+ ssh_rtl_timeout_reaper_mod(rqst->rtl, timestamp, timestamp + timeout); ++ ssh_rtl_timeout_reaper_mod(rtl, timestamp, timestamp + timeout); +} + + +static void ssh_rtl_complete(struct ssh_rtl *rtl, + const struct ssh_command *command, -+ const struct sshp_span *command_data) ++ const struct ssam_span *command_data) +{ + struct ssh_request *r = NULL; + struct ssh_request *p, *n; @@ -8504,6 +8608,7 @@ index 000000000000..9f44bdfbc4fd + +static bool ssh_rtl_cancel_nonpending(struct ssh_request *r) +{ ++ struct ssh_rtl *rtl; + unsigned long state, fixed; + bool remove; + @@ -8512,19 +8617,19 @@ index 000000000000..9f44bdfbc4fd + * expecting the state to be zero (i.e. unsubmitted). Note that, if + * setting the state worked, we might still be adding the packet to the + * queue in a currently executing submit call. In that case, however, -+ * rqst->rtl must have been set previously, as locked is checked after -+ * setting rqst->rtl. Thus only if we successfully lock this request and -+ * rqst->rtl is NULL, we have successfully removed the request. ++ * ptl reference must have been set previously, as locked is checked ++ * after setting ptl. Thus only if we successfully lock this request and ++ * ptl is NULL, we have successfully removed the request. + * Otherwise we need to try and grab it from the queue. + * -+ * Note that if the CMPXCHG fails, we are guaranteed that rqst->rtl has ++ * Note that if the CMPXCHG fails, we are guaranteed that ptl has + * been set and is non-NULL, as states can only be nonzero after this + * has been set. Also note that we need to fetch the static (type) flags + * to ensure that they don't cause the cmpxchg to fail. + */ + fixed = READ_ONCE(r->state) & SSH_REQUEST_FLAGS_TY_MASK; + state = cmpxchg(&r->state, fixed, SSH_REQUEST_SF_LOCKED_BIT); -+ if (!state && !READ_ONCE(r->rtl)) { ++ if (!state && !READ_ONCE(r->packet.ptl)) { + if (test_and_set_bit(SSH_REQUEST_SF_COMPLETED_BIT, &r->state)) + return true; + @@ -8532,7 +8637,8 @@ index 000000000000..9f44bdfbc4fd + return true; + } + -+ spin_lock(&r->rtl->queue.lock); ++ rtl = ssh_request_rtl(r); ++ spin_lock(&rtl->queue.lock); + + /* + * Note: 1) Requests cannot be re-submitted. 2) If a request is queued, @@ -8543,14 +8649,14 @@ index 000000000000..9f44bdfbc4fd + + remove = test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state); + if (!remove) { -+ spin_unlock(&r->rtl->queue.lock); ++ spin_unlock(&rtl->queue.lock); + return false; + } + + set_bit(SSH_REQUEST_SF_LOCKED_BIT, &r->state); + list_del(&r->node); + -+ spin_unlock(&r->rtl->queue.lock); ++ spin_unlock(&rtl->queue.lock); + + ssh_request_put(r); // drop reference obtained from queue + @@ -8569,12 +8675,12 @@ index 000000000000..9f44bdfbc4fd + + /* + * Now that we have locked the packet, we have guaranteed that it can't -+ * be added to the system any more. If rqst->rtl is zero, the locked ++ * be added to the system any more. If rtl is zero, the locked + * check in ssh_rtl_submit has not been run and any submission, + * currently in progress or called later, won't add the packet. Thus we + * can directly complete it. + */ -+ if (!READ_ONCE(r->rtl)) { ++ if (!ssh_request_rtl(r)) { + if (test_and_set_bit(SSH_REQUEST_SF_COMPLETED_BIT, &r->state)) + return true; + @@ -8620,8 +8726,8 @@ index 000000000000..9f44bdfbc4fd + else + canceled = ssh_rtl_cancel_nonpending(rqst); + -+ // note: rqst->rtl may be NULL if request has not been submitted yet -+ rtl = READ_ONCE(rqst->rtl); ++ // note: rtl may be NULL if request has not been submitted yet ++ rtl = ssh_request_rtl(rqst); + if (canceled && rtl) + ssh_rtl_tx_schedule(rtl); + @@ -8650,7 +8756,7 @@ index 000000000000..9f44bdfbc4fd + ssh_rtl_pending_remove(r); + ssh_rtl_complete_with_status(r, status); + -+ ssh_rtl_tx_schedule(r->rtl); ++ ssh_rtl_tx_schedule(ssh_request_rtl(r)); + return; + } + @@ -8683,7 +8789,7 @@ index 000000000000..9f44bdfbc4fd + ssh_rtl_pending_remove(r); + ssh_rtl_complete_with_status(r, 0); + -+ ssh_rtl_tx_schedule(r->rtl); ++ ssh_rtl_tx_schedule(ssh_request_rtl(r)); +} + + @@ -8775,7 +8881,7 @@ index 000000000000..9f44bdfbc4fd + + +static void ssh_rtl_rx_event(struct ssh_rtl *rtl, const struct ssh_command *cmd, -+ const struct sshp_span *data) ++ const struct ssam_span *data) +{ + trace_ssam_rx_event_received(cmd, data->len); + @@ -8785,12 +8891,12 @@ index 000000000000..9f44bdfbc4fd + rtl->ops.handle_event(rtl, cmd, data); +} + -+static void ssh_rtl_rx_command(struct ssh_ptl *p, const struct sshp_span *data) ++static void ssh_rtl_rx_command(struct ssh_ptl *p, const struct ssam_span *data) +{ + struct ssh_rtl *rtl = to_ssh_rtl(p, ptl); + struct device *dev = &p->serdev->dev; + struct ssh_command *command; -+ struct sshp_span command_data; ++ struct ssam_span command_data; + + if (sshp_parse_command(dev, data, &command, &command_data)) + return; @@ -8801,7 +8907,7 @@ index 000000000000..9f44bdfbc4fd + ssh_rtl_complete(rtl, command, &command_data); +} + -+static void ssh_rtl_rx_data(struct ssh_ptl *p, const struct sshp_span *data) ++static void ssh_rtl_rx_data(struct ssh_ptl *p, const struct ssam_span *data) +{ + switch (data->ptr[0]) { + case SSH_PLD_TYPE_CMD: @@ -8856,7 +8962,7 @@ index 000000000000..9f44bdfbc4fd +} + +static int ssh_rtl_init(struct ssh_rtl *rtl, struct serdev_device *serdev, -+ struct ssh_rtl_ops *ops) ++ const struct ssh_rtl_ops *ops) +{ + struct ssh_ptl_ops ptl_ops; + int status; @@ -8908,16 +9014,14 @@ index 000000000000..9f44bdfbc4fd +{ + struct ssh_packet_args packet_args; + -+ packet_args.type = SSH_PACKET_TY_BLOCKING; ++ packet_args.type = BIT(SSH_PACKET_TY_BLOCKING_BIT); + if (!(flags & SSAM_REQUEST_UNSEQUENCED)) -+ packet_args.type |= SSH_PACKET_TY_SEQUENCED; ++ packet_args.type |= BIT(SSH_PACKET_TY_SEQUENCED_BIT); + + packet_args.priority = SSH_PACKET_PRIORITY(DATA, 0); + packet_args.ops = &ssh_rtl_packet_ops; + + ssh_packet_init(&rqst->packet, &packet_args); -+ -+ rqst->rtl = NULL; + INIT_LIST_HEAD(&rqst->node); + + rqst->state = 0; @@ -8937,7 +9041,7 @@ index 000000000000..9f44bdfbc4fd + +static void ssh_rtl_flush_request_complete(struct ssh_request *r, + const struct ssh_command *cmd, -+ const struct sshp_span *data, ++ const struct ssam_span *data, + int status) +{ + struct ssh_flush_request *rqst; @@ -8994,7 +9098,7 @@ index 000000000000..9f44bdfbc4fd + int status; + + ssh_request_init(&rqst.base, init_flags, &ssh_rtl_flush_request_ops); -+ rqst.base.packet.type |= SSH_PACKET_TY_FLUSH; ++ rqst.base.packet.state |= BIT(SSH_PACKET_TY_FLUSH_BIT); + rqst.base.packet.priority = SSH_PACKET_PRIORITY(FLUSH, 0); + rqst.base.state |= BIT(SSH_REQUEST_TY_FLUSH_BIT); + @@ -9061,9 +9165,7 @@ index 000000000000..9f44bdfbc4fd + */ + + pending = atomic_read(&rtl->pending.count); -+ WARN_ON(pending); -+ -+ if (pending) { ++ if (WARN_ON(pending)) { + spin_lock(&rtl->pending.lock); + list_for_each_entry_safe(r, n, &rtl->pending.head, node) { + set_bit(SSH_REQUEST_SF_LOCKED_BIT, &r->state); @@ -9210,7 +9312,7 @@ index 000000000000..9f44bdfbc4fd +struct ssam_nf { + struct mutex lock; + struct rb_root refcount; -+ struct ssam_nf_head head[SURFACE_SAM_SSH_NUM_EVENTS]; ++ struct ssam_nf_head head[SSH_NUM_EVENTS]; +}; + + @@ -9221,7 +9323,7 @@ index 000000000000..9f44bdfbc4fd + struct ssam_nf_refcount_entry *entry; + struct ssam_nf_refcount_key key; + struct rb_node **link = &nf->refcount.rb_node; -+ struct rb_node *parent; ++ struct rb_node *parent = NULL; + int cmp; + + key.reg = reg; @@ -9321,65 +9423,11 @@ index 000000000000..9f44bdfbc4fd + } +} + -+static int ssam_nf_register(struct ssam_nf *nf, struct ssam_event_notifier *n) -+{ -+ u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); -+ struct ssam_nf_head *nf_head; -+ int rc, status; -+ -+ if (!ssh_rqid_is_event(rqid)) -+ return -EINVAL; -+ -+ nf_head = &nf->head[ssh_rqid_to_event(rqid)]; -+ -+ mutex_lock(&nf->lock); -+ -+ rc = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id); -+ if (rc < 0) { -+ mutex_lock(&nf->lock); -+ return rc; -+ } -+ -+ status = __ssam_nfblk_insert(nf_head, &n->base); -+ if (status) -+ ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); -+ -+ mutex_unlock(&nf->lock); -+ return status; -+} -+ -+static int ssam_nf_unregister(struct ssam_nf *nf, struct ssam_event_notifier *n) -+{ -+ u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); -+ struct ssam_nf_head *nf_head; -+ int status; -+ -+ if (!ssh_rqid_is_event(rqid)) -+ return -EINVAL; -+ -+ nf_head = &nf->head[ssh_rqid_to_event(rqid)]; -+ -+ mutex_lock(&nf->lock); -+ -+ status = __ssam_nfblk_remove(nf_head, &n->base); -+ if (status) { -+ mutex_unlock(&nf->lock); -+ return status; -+ } -+ -+ ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); -+ -+ mutex_unlock(&nf->lock); -+ synchronize_srcu(&nf_head->srcu); -+ -+ return 0; -+} -+ +static int ssam_nf_init(struct ssam_nf *nf) +{ + int i, status; + -+ for (i = 0; i < SURFACE_SAM_SSH_NUM_EVENTS; i++) { ++ for (i = 0; i < SSH_NUM_EVENTS; i++) { + status = ssam_nf_head_init(&nf->head[i]); + if (status) + break; @@ -9400,7 +9448,7 @@ index 000000000000..9f44bdfbc4fd +{ + int i; + -+ for (i = 0; i < SURFACE_SAM_SSH_NUM_EVENTS; i++) ++ for (i = 0; i < SSH_NUM_EVENTS; i++) + ssam_nf_head_destroy(&nf->head[i]); + + mutex_destroy(&nf->lock); @@ -9429,7 +9477,7 @@ index 000000000000..9f44bdfbc4fd +}; + +struct ssam_event_channel { -+ struct ssam_event_queue queue[SURFACE_SAM_SSH_NUM_EVENTS]; ++ struct ssam_event_queue queue[SSH_NUM_EVENTS]; +}; + +struct ssam_cplt { @@ -9437,7 +9485,7 @@ index 000000000000..9f44bdfbc4fd + struct workqueue_struct *wq; + + struct { -+ struct ssam_event_channel channel[SURFACE_SAM_SSH_NUM_CHANNELS]; ++ struct ssam_event_channel channel[SSH_NUM_CHANNELS]; + struct ssam_nf notif; + } event; +}; @@ -9586,32 +9634,304 @@ index 000000000000..9f44bdfbc4fd +} + + -+/* -- Top-Level Request Interface ------------------------------------------- */ ++/* -- Main SSAM device structures. ------------------------------------------ */ + -+struct ssam_response { ++enum ssam_controller_state { ++ SSAM_CONTROLLER_UNINITIALIZED, ++ SSAM_CONTROLLER_INITIALIZED, ++ SSAM_CONTROLLER_STARTED, ++ SSAM_CONTROLLER_STOPPED, ++ SSAM_CONTROLLER_SUSPENDED, ++}; ++ ++struct ssam_device_caps { ++ u32 notif_display:1; ++ u32 notif_d0exit:1; ++}; ++ ++struct ssam_controller { ++ enum ssam_controller_state state; ++ ++ struct ssh_rtl rtl; ++ struct ssam_cplt cplt; ++ ++ struct { ++ struct ssh_seq_counter seq; ++ struct ssh_rqid_counter rqid; ++ } counter; ++ ++ struct { ++ int num; ++ bool wakeup_enabled; ++ } irq; ++ ++ struct ssam_device_caps caps; ++}; ++ ++ ++#define ssam_dbg(ctrl, fmt, ...) rtl_dbg(&(ctrl)->rtl, fmt, ##__VA_ARGS__) ++#define ssam_info(ctrl, fmt, ...) rtl_info(&(ctrl)->rtl, fmt, ##__VA_ARGS__) ++#define ssam_warn(ctrl, fmt, ...) rtl_warn(&(ctrl)->rtl, fmt, ##__VA_ARGS__) ++#define ssam_err(ctrl, fmt, ...) rtl_err(&(ctrl)->rtl, fmt, ##__VA_ARGS__) ++ ++#define to_ssam_controller(ptr, member) \ ++ container_of(ptr, struct ssam_controller, member) ++ ++struct device *ssam_controller_device(struct ssam_controller *c) ++{ ++ return (c && c->rtl.ptl.serdev) ? &c->rtl.ptl.serdev->dev : NULL; ++} ++EXPORT_SYMBOL_GPL(ssam_controller_device); ++ ++ ++static void ssam_handle_event(struct ssh_rtl *rtl, ++ const struct ssh_command *cmd, ++ const struct ssam_span *data) ++{ ++ struct ssam_controller *ctrl = to_ssam_controller(rtl, rtl); ++ struct ssam_event_item *item; ++ ++ item = kzalloc(sizeof(struct ssam_event_item) + data->len, GFP_KERNEL); ++ if (!item) ++ return; ++ ++ item->rqid = get_unaligned_le16(&cmd->rqid); ++ item->event.target_category = cmd->tc; ++ item->event.command_id = cmd->cid; ++ item->event.instance_id = cmd->iid; ++ item->event.channel = cmd->chn_in; ++ item->event.length = data->len; ++ memcpy(&item->event.data[0], data->ptr, data->len); ++ ++ ssam_cplt_submit_event(&ctrl->cplt, item); ++} ++ ++static const struct ssh_rtl_ops ssam_rtl_ops = { ++ .handle_event = ssam_handle_event, ++}; ++ ++ ++#define SSAM_SSH_DSM_REVISION 0 ++#define SSAM_SSH_DSM_NOTIF_D0 8 ++static const guid_t SSAM_SSH_DSM_UUID = GUID_INIT(0xd5e383e1, 0xd892, 0x4a76, ++ 0x89, 0xfc, 0xf6, 0xaa, 0xae, 0x7e, 0xd5, 0xb5); ++ ++static int ssam_device_caps_load_from_acpi(acpi_handle handle, ++ struct ssam_device_caps *caps) ++{ ++ union acpi_object *obj; ++ u64 funcs = 0; ++ int i; ++ ++ // set defaults ++ caps->notif_display = true; ++ caps->notif_d0exit = false; ++ ++ if (!acpi_has_method(handle, "_DSM")) ++ return 0; ++ ++ // get function availability bitfield ++ obj = acpi_evaluate_dsm_typed(handle, &SSAM_SSH_DSM_UUID, 0, 0, NULL, ++ ACPI_TYPE_BUFFER); ++ if (!obj) ++ return -EFAULT; ++ ++ for (i = 0; i < obj->buffer.length && i < 8; i++) ++ funcs |= (((u64)obj->buffer.pointer[i]) << (i * 8)); ++ ++ ACPI_FREE(obj); ++ ++ // D0 exit/entry notification ++ if (funcs & BIT(SSAM_SSH_DSM_NOTIF_D0)) { ++ obj = acpi_evaluate_dsm_typed(handle, &SSAM_SSH_DSM_UUID, ++ SSAM_SSH_DSM_REVISION, SSAM_SSH_DSM_NOTIF_D0, ++ NULL, ACPI_TYPE_INTEGER); ++ if (!obj) ++ return -EFAULT; ++ ++ caps->notif_d0exit = !!obj->integer.value; ++ ACPI_FREE(obj); ++ } ++ ++ return 0; ++} ++ ++static int ssam_controller_init(struct ssam_controller *ctrl, ++ struct serdev_device *serdev) ++{ ++ acpi_handle handle = ACPI_HANDLE(&serdev->dev); + int status; -+ u16 capacity; -+ u16 length; -+ u8 *pointer; -+}; + -+struct ssam_request_sync { -+ struct ssh_request base; -+ struct completion comp; -+ struct ssam_response resp; -+}; ++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_UNINITIALIZED) { ++ dev_err(&serdev->dev, "embedded controller already initialized\n"); ++ return -EBUSY; ++ } ++ ++ status = ssam_device_caps_load_from_acpi(handle, &ctrl->caps); ++ if (status) ++ return status; ++ ++ dev_dbg(&serdev->dev, "device capabilities:\n"); ++ dev_dbg(&serdev->dev, " notif_display: %u\n", ctrl->caps.notif_display); ++ dev_dbg(&serdev->dev, " notif_d0exit: %u\n", ctrl->caps.notif_d0exit); ++ ++ ssh_seq_reset(&ctrl->counter.seq); ++ ssh_rqid_reset(&ctrl->counter.rqid); ++ ++ // initialize event/request completion system ++ status = ssam_cplt_init(&ctrl->cplt, &serdev->dev); ++ if (status) ++ return status; ++ ++ // initialize request and packet transmission layers ++ status = ssh_rtl_init(&ctrl->rtl, serdev, &ssam_rtl_ops); ++ if (status) { ++ ssam_cplt_flush(&ctrl->cplt); ++ ssam_cplt_destroy(&ctrl->cplt); ++ return status; ++ } ++ ++ // update state ++ smp_store_release(&ctrl->state, SSAM_CONTROLLER_INITIALIZED); ++ return 0; ++} ++ ++static int ssam_controller_start(struct ssam_controller *ctrl) ++{ ++ int status; ++ ++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_INITIALIZED) ++ return -EINVAL; ++ ++ status = ssh_rtl_tx_start(&ctrl->rtl); ++ if (status) ++ return status; ++ ++ status = ssh_rtl_rx_start(&ctrl->rtl); ++ if (status) { ++ ssh_rtl_tx_flush(&ctrl->rtl); ++ return status; ++ } ++ ++ smp_store_release(&ctrl->state, SSAM_CONTROLLER_STARTED); ++ return 0; ++} ++ ++static void ssam_controller_shutdown(struct ssam_controller *ctrl) ++{ ++ enum ssam_controller_state s = smp_load_acquire(&ctrl->state); ++ int status; ++ ++ if (s == SSAM_CONTROLLER_UNINITIALIZED || s == SSAM_CONTROLLER_STOPPED) ++ return; ++ ++ // try to flush pending events and requests while everything still works ++ status = ssh_rtl_flush(&ctrl->rtl, msecs_to_jiffies(5000)); ++ if (status) { ++ ssam_err(ctrl, "failed to flush request transmission layer: %d\n", ++ status); ++ } ++ ++ // flush out all currently completing requests and events ++ ssam_cplt_flush(&ctrl->cplt); ++ ++ // cancel rem. requests, ensure no new ones can be queued, stop threads ++ ssh_rtl_tx_flush(&ctrl->rtl); ++ ssh_rtl_shutdown(&ctrl->rtl); ++ ++ smp_store_release(&ctrl->state, SSAM_CONTROLLER_STOPPED); ++} ++ ++static void ssam_controller_destroy(struct ssam_controller *ctrl) ++{ ++ if (smp_load_acquire(&ctrl->state) == SSAM_CONTROLLER_UNINITIALIZED) ++ return; ++ ++ /* ++ * Ensure _all_ events are completed. New ones could still have been ++ * received after the previous flush in ssam_controller_shutdown, before ++ * the request transport layer has been shut down. At this point we can ++ * be sure that no new requests will be queued for completion after this ++ * call. ++ */ ++ ssam_cplt_flush(&ctrl->cplt); ++ ++ // actually free resources ++ ssam_cplt_destroy(&ctrl->cplt); ++ ssh_rtl_destroy(&ctrl->rtl); ++ ++ smp_store_release(&ctrl->state, SSAM_CONTROLLER_UNINITIALIZED); ++} ++ ++static inline int ssam_controller_suspend(struct ssam_controller *ctrl) ++{ ++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) ++ return -EINVAL; ++ ++ ssam_dbg(ctrl, "pm: suspending controller\n"); ++ smp_store_release(&ctrl->state, SSAM_CONTROLLER_SUSPENDED); ++ return 0; ++} ++ ++static inline int ssam_controller_resume(struct ssam_controller *ctrl) ++{ ++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_SUSPENDED) ++ return -EINVAL; ++ ++ ssam_dbg(ctrl, "pm: resuming controller\n"); ++ smp_store_release(&ctrl->state, SSAM_CONTROLLER_STARTED); ++ return 0; ++} ++ ++ ++static inline ++int ssam_controller_receive_buf(struct ssam_controller *ctrl, ++ const unsigned char *buf, size_t n) ++{ ++ return ssh_ptl_rx_rcvbuf(&ctrl->rtl.ptl, buf, n); ++} ++ ++static inline void ssam_controller_write_wakeup(struct ssam_controller *ctrl) ++{ ++ ssh_ptl_tx_wakeup(&ctrl->rtl.ptl, true); ++} ++ ++ ++/* -- Top-level request interface ------------------------------------------- */ ++ ++ssize_t ssam_request_write_data(struct ssam_span *buf, ++ struct ssam_controller *ctrl, ++ struct ssam_request *spec) ++{ ++ struct msgbuf msgb; ++ u16 rqid; ++ u8 seq; ++ ++ if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE) ++ return -EINVAL; ++ ++ msgb_init(&msgb, buf->ptr, buf->len); ++ seq = ssh_seq_next(&ctrl->counter.seq); ++ rqid = ssh_rqid_next(&ctrl->counter.rqid); ++ msgb_push_cmd(&msgb, seq, rqid, spec); ++ ++ return msgb_bytes_used(&msgb); ++} ++EXPORT_SYMBOL_GPL(ssam_request_write_data); + + +static void ssam_request_sync_complete(struct ssh_request *rqst, + const struct ssh_command *cmd, -+ const struct sshp_span *data, int status) ++ const struct ssam_span *data, int status) +{ ++ struct ssh_rtl *rtl = ssh_request_rtl(rqst); + struct ssam_request_sync *r; -+ struct ssh_rtl *rtl = READ_ONCE(rqst->rtl); + + r = container_of(rqst, struct ssam_request_sync, base); -+ r->resp.status = status; -+ r->resp.length = 0; ++ r->status = status; ++ ++ if (r->resp) ++ r->resp->length = 0; + + if (status) { + rtl_dbg_cond(rtl, "rsp: request failed: %d\n", status); @@ -9621,21 +9941,24 @@ index 000000000000..9f44bdfbc4fd + if (!data) // handle requests without a response + return; + -+ if (!r->resp.pointer && data->len) { -+ rtl_warn(rtl, "rsp: no response buffer provided, dropping data\n"); ++ if (!r->resp || !r->resp->pointer) { ++ if (data->len) { ++ rtl_warn(rtl, "rsp: no response buffer provided, " ++ "dropping data\n"); ++ } ++ return; ++ } ++ ++ if (data->len > r->resp->capacity) { ++ rtl_err(rtl, "rsp: response buffer too small, " ++ "capacity: %zu bytes, got: %zu bytes\n", ++ r->resp->capacity, data->len); ++ r->status = -ENOSPC; + return; + } + -+ if (data->len > r->resp.capacity) { -+ rtl_err(rtl, "rsp: response buffer too small," -+ " capacity: %u bytes, got: %zu bytes\n", -+ r->resp.capacity, data->len); -+ r->resp.status = -ENOSPC; -+ return; -+ } -+ -+ r->resp.length = data->len; -+ memcpy(r->resp.pointer, data->ptr, data->len); ++ r->resp->length = data->len; ++ memcpy(r->resp->pointer, data->ptr, data->len); +} + +static void ssam_request_sync_release(struct ssh_request *rqst) @@ -9648,100 +9971,170 @@ index 000000000000..9f44bdfbc4fd + .complete = ssam_request_sync_complete, +}; + -+static void ssam_request_sync_wait_complete(struct ssam_request_sync *rqst) ++ ++int ssam_request_sync_alloc(size_t payload_len, gfp_t flags, ++ struct ssam_request_sync **rqst, ++ struct ssam_span *buffer) +{ -+ wait_for_completion(&rqst->comp); -+} ++ size_t msglen = SSH_COMMAND_MESSAGE_LENGTH(payload_len); + ++ *rqst = kzalloc(sizeof(struct ssam_request_sync) + msglen, flags); ++ if (!*rqst) ++ return -ENOMEM; + -+/* -- TODO ------------------------------------------------------------------ */ -+ -+enum ssh_ec_state { -+ SSH_EC_UNINITIALIZED, -+ SSH_EC_INITIALIZED, -+ SSH_EC_SUSPENDED, -+}; -+ -+struct sam_ssh_ec { -+ struct serdev_device *serdev; -+ -+ struct ssh_rtl rtl; -+ struct ssam_cplt cplt; -+ -+ struct { -+ struct ssh_seq_counter seq; -+ struct ssh_rqid_counter rqid; -+ } counter; -+ -+ enum ssh_ec_state state; -+ -+ int irq; -+ bool irq_wakeup_enabled; -+}; -+ -+static struct sam_ssh_ec ssh_ec = { -+ .state = SSH_EC_UNINITIALIZED, -+ .serdev = NULL, -+}; -+ -+ -+/* -- TODO ------------------------------------------------------------------ */ -+ -+#define ssh_dbg(ec, fmt, ...) dev_dbg(&(ec)->serdev->dev, fmt, ##__VA_ARGS__) -+#define ssh_warn(ec, fmt, ...) dev_warn(&(ec)->serdev->dev, fmt, ##__VA_ARGS__) -+#define ssh_err(ec, fmt, ...) dev_err(&(ec)->serdev->dev, fmt, ##__VA_ARGS__) -+ -+ -+static inline struct sam_ssh_ec *surface_sam_ssh_acquire(void) -+{ -+ return &ssh_ec; -+} -+ -+static inline struct sam_ssh_ec *surface_sam_ssh_acquire_init(void) -+{ -+ struct sam_ssh_ec *ec = surface_sam_ssh_acquire(); -+ -+ if (smp_load_acquire(&ec->state) == SSH_EC_UNINITIALIZED) -+ return NULL; -+ -+ return ec; -+} -+ -+int surface_sam_ssh_consumer_register(struct device *consumer) -+{ -+ u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; -+ struct sam_ssh_ec *ec; -+ struct device_link *link; -+ -+ ec = surface_sam_ssh_acquire_init(); -+ if (!ec) -+ return -ENXIO; -+ -+ link = device_link_add(consumer, &ec->serdev->dev, flags); -+ if (!link) -+ return -EFAULT; ++ buffer->ptr = (u8 *)(*rqst + 1); ++ buffer->len = msglen; + + return 0; +} -+EXPORT_SYMBOL_GPL(surface_sam_ssh_consumer_register); ++EXPORT_SYMBOL_GPL(ssam_request_sync_alloc); ++ ++void ssam_request_sync_init(struct ssam_request_sync *rqst, ++ enum ssam_request_flags flags) ++{ ++ ssh_request_init(&rqst->base, flags, &ssam_request_sync_ops); ++ init_completion(&rqst->comp); ++ rqst->resp = NULL; ++ rqst->status = 0; ++} ++EXPORT_SYMBOL_GPL(ssam_request_sync_init); ++ ++int ssam_request_sync_submit(struct ssam_controller *ctrl, ++ struct ssam_request_sync *rqst) ++{ ++ enum ssam_controller_state state = smp_load_acquire(&ctrl->state); ++ int status; ++ ++ if (state == SSAM_CONTROLLER_SUSPENDED) { ++ ssam_warn(ctrl, "rqst: embedded controller is suspended\n"); ++ ssh_request_put(&rqst->base); ++ return -EPERM; ++ } ++ ++ if (state != SSAM_CONTROLLER_STARTED) { ++ ssam_warn(ctrl, "rqst: embedded controller is uninitialized\n"); ++ ssh_request_put(&rqst->base); ++ return -ENXIO; ++ } ++ ++ status = ssh_rtl_submit(&ctrl->rtl, &rqst->base); ++ ssh_request_put(&rqst->base); ++ ++ return status; ++} ++EXPORT_SYMBOL_GPL(ssam_request_sync_submit); ++ ++int ssam_request_sync(struct ssam_controller *ctrl, struct ssam_request *spec, ++ struct ssam_response *rsp) ++{ ++ struct ssam_request_sync *rqst; ++ struct ssam_span buf; ++ size_t len; ++ int status; ++ ++ // prevent overflow, allows us to skip checks later on ++ if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE) { ++ ssam_err(ctrl, "rqst: request payload too large\n"); ++ return -EINVAL; ++ } ++ ++ status = ssam_request_sync_alloc(spec->length, GFP_KERNEL, &rqst, &buf); ++ if (status) ++ return status; ++ ++ ssam_request_sync_init(rqst, spec->flags); ++ ssam_request_sync_set_resp(rqst, rsp); ++ ++ len = ssam_request_write_data(&buf, ctrl, spec); ++ ssam_request_sync_set_data(rqst, buf.ptr, len); ++ ++ status = ssam_request_sync_submit(ctrl, rqst); ++ if (!status) ++ status = ssam_request_sync_wait(rqst); ++ ++ kfree(rqst); ++ return status; ++} ++EXPORT_SYMBOL_GPL(ssam_request_sync); ++ ++int ssam_request_sync_with_buffer(struct ssam_controller *ctrl, ++ struct ssam_request *spec, ++ struct ssam_response *rsp, ++ struct ssam_span *buf) ++{ ++ struct ssam_request_sync rqst; ++ size_t len; ++ int status; ++ ++ // prevent overflow, allows us to skip checks later on ++ if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE) { ++ ssam_err(ctrl, "rqst: request payload too large\n"); ++ return -EINVAL; ++ } ++ ++ ssam_request_sync_init(&rqst, spec->flags); ++ ssam_request_sync_set_resp(&rqst, rsp); ++ ++ len = ssam_request_write_data(buf, ctrl, spec); ++ ssam_request_sync_set_data(&rqst, buf->ptr, len); ++ ++ status = ssam_request_sync_submit(ctrl, &rqst); ++ if (!status) ++ status = ssam_request_sync_wait(&rqst); ++ ++ return status; ++} ++EXPORT_SYMBOL_GPL(ssam_request_sync_with_buffer); + + -+static int __surface_sam_ssh_rqst(struct sam_ssh_ec *ec, -+ const struct surface_sam_ssh_rqst *rqst, -+ struct surface_sam_ssh_buf *result); ++/* -- Internal SAM requests. ------------------------------------------------ */ + -+static int surface_sam_ssh_event_enable(struct sam_ssh_ec *ec, -+ struct ssam_event_registry reg, -+ struct ssam_event_id id, -+ u8 flags) ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_get_firmware_version, __le32, { ++ .target_category = SSAM_SSH_TC_SAM, ++ .command_id = 0x13, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_off, u8, { ++ .target_category = SSAM_SSH_TC_SAM, ++ .command_id = 0x15, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_on, u8, { ++ .target_category = SSAM_SSH_TC_SAM, ++ .command_id = 0x16, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_exit, u8, { ++ .target_category = SSAM_SSH_TC_SAM, ++ .command_id = 0x33, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_entry, u8, { ++ .target_category = SSAM_SSH_TC_SAM, ++ .command_id = 0x34, ++ .instance_id = 0x00, ++ .channel = 0x01, ++}); ++ ++static int ssam_ssh_event_enable(struct ssam_controller *ctrl, ++ struct ssam_event_registry reg, ++ struct ssam_event_id id, u8 flags) +{ + struct ssh_notification_params params; -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; ++ struct ssam_request rqst; ++ struct ssam_response result; ++ int status; + + u16 rqid = ssh_tc_to_rqid(id.target_category); + u8 buf[1] = { 0x00 }; -+ int status; + + // only allow RQIDs that lie within event spectrum + if (!ssh_rqid_is_event(rqid)) @@ -9752,48 +10145,47 @@ index 000000000000..9f44bdfbc4fd + params.flags = flags; + put_unaligned_le16(rqid, ¶ms.request_id); + -+ rqst.tc = reg.target_category; -+ rqst.cid = reg.cid_enable; -+ rqst.iid = 0x00; -+ rqst.chn = reg.channel; -+ rqst.snc = 0x01; -+ rqst.cdl = sizeof(params); -+ rqst.pld = (u8 *)¶ms; ++ rqst.target_category = reg.target_category; ++ rqst.command_id = reg.cid_enable; ++ rqst.instance_id = 0x00; ++ rqst.channel = reg.channel; ++ rqst.flags = SSAM_REQUEST_HAS_RESPONSE; ++ rqst.length = sizeof(params); ++ rqst.payload = (u8 *)¶ms; + -+ result.cap = ARRAY_SIZE(buf); -+ result.len = 0; -+ result.data = buf; -+ -+ status = __surface_sam_ssh_rqst(ec, &rqst, &result); ++ result.capacity = ARRAY_SIZE(buf); ++ result.length = 0; ++ result.pointer = buf; + ++ status = ssam_request_sync_onstack(ctrl, &rqst, &result, sizeof(params)); + if (status) { -+ dev_err(&ec->serdev->dev, "failed to enable event source" -+ " (tc: 0x%02x, rqid: 0x%04x)\n", -+ id.target_category, rqid); ++ ssam_err(ctrl, "failed to enable event source " ++ "(tc: 0x%02x, iid: 0x%02x, reg: 0x%02x)\n", ++ id.target_category, id.instance, reg.target_category); + } + + if (buf[0] != 0x00) { -+ pr_warn(SSH_RQST_TAG_FULL -+ "unexpected result while enabling event source: " -+ "0x%02x\n", buf[0]); ++ ssam_warn(ctrl, "unexpected result while enabling event source: " ++ "0x%02x (tc: 0x%02x, iid: 0x%02x, reg: 0x%02x)\n", ++ buf[0], id.target_category, id.instance, ++ reg.target_category); + } + + return status; + +} + -+static int surface_sam_ssh_event_disable(struct sam_ssh_ec *ec, -+ struct ssam_event_registry reg, -+ struct ssam_event_id id, -+ u8 flags) ++static int ssam_ssh_event_disable(struct ssam_controller *ctrl, ++ struct ssam_event_registry reg, ++ struct ssam_event_id id, u8 flags) +{ + struct ssh_notification_params params; -+ struct surface_sam_ssh_rqst rqst; -+ struct surface_sam_ssh_buf result; ++ struct ssam_request rqst; ++ struct ssam_response result; ++ int status; + + u16 rqid = ssh_tc_to_rqid(id.target_category); + u8 buf[1] = { 0x00 }; -+ int status; + + // only allow RQIDs that lie within event spectrum + if (!ssh_rqid_is_event(rqid)) @@ -9804,54 +10196,168 @@ index 000000000000..9f44bdfbc4fd + params.flags = flags; + put_unaligned_le16(rqid, ¶ms.request_id); + -+ rqst.tc = reg.target_category; -+ rqst.cid = reg.cid_disable; -+ rqst.iid = 0x00; -+ rqst.chn = reg.channel; -+ rqst.snc = 0x01; -+ rqst.cdl = sizeof(params); -+ rqst.pld = (u8 *)¶ms; ++ rqst.target_category = reg.target_category; ++ rqst.command_id = reg.cid_disable; ++ rqst.instance_id = 0x00; ++ rqst.channel = reg.channel; ++ rqst.flags = SSAM_REQUEST_HAS_RESPONSE; ++ rqst.length = sizeof(params); ++ rqst.payload = (u8 *)¶ms; + -+ result.cap = ARRAY_SIZE(buf); -+ result.len = 0; -+ result.data = buf; -+ -+ status = __surface_sam_ssh_rqst(ec, &rqst, &result); ++ result.capacity = ARRAY_SIZE(buf); ++ result.length = 0; ++ result.pointer = buf; + ++ status = ssam_request_sync_onstack(ctrl, &rqst, &result, sizeof(params)); + if (status) { -+ dev_err(&ec->serdev->dev, "failed to disable event source" -+ " (tc: 0x%02x, rqid: 0x%04x)\n", -+ id.target_category, rqid); ++ ssam_err(ctrl, "failed to disable event source " ++ "(tc: 0x%02x, iid: 0x%02x, reg: 0x%02x)\n", ++ id.target_category, id.instance, reg.target_category); + } + + if (buf[0] != 0x00) { -+ dev_warn(&ec->serdev->dev, -+ "unexpected result while disabling event source: " -+ "0x%02x\n", buf[0]); ++ ssam_warn(ctrl, "unexpected result while disabling event source: " ++ "0x%02x (tc: 0x%02x, iid: 0x%02x, reg: 0x%02x)\n", ++ buf[0], id.target_category, id.instance, ++ reg.target_category); + } + + return status; +} + + -+int surface_sam_ssh_notifier_register(struct ssam_event_notifier *n) ++/* -- Wrappers for internal SAM requests. ----------------------------------- */ ++ ++static int ssam_log_firmware_version(struct ssam_controller *ctrl) +{ ++ __le32 __version; ++ u32 version, a, b, c; ++ int status; ++ ++ status = ssam_ssh_get_firmware_version(ctrl, &__version); ++ if (status) ++ return status; ++ ++ version = le32_to_cpu(__version); ++ a = (version >> 24) & 0xff; ++ b = ((version >> 8) & 0xffff); ++ c = version & 0xff; ++ ++ ssam_info(ctrl, "SAM controller version: %u.%u.%u\n", a, b, c); ++ return 0; ++} ++ ++static int ssam_ctrl_notif_display_off(struct ssam_controller *ctrl) ++{ ++ int status; ++ u8 response; ++ ++ if (!ctrl->caps.notif_display) ++ return 0; ++ ++ ssam_dbg(ctrl, "pm: notifying display off\n"); ++ ++ status = ssam_ssh_notif_display_off(ctrl, &response); ++ if (status) ++ return status; ++ ++ if (response != 0) { ++ ssam_err(ctrl, "unexpected response from display-off notification: " ++ "0x%02x\n", response); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int ssam_ctrl_notif_display_on(struct ssam_controller *ctrl) ++{ ++ int status; ++ u8 response; ++ ++ if (!ctrl->caps.notif_display) ++ return 0; ++ ++ ssam_dbg(ctrl, "pm: notifying display on\n"); ++ ++ status = ssam_ssh_notif_display_on(ctrl, &response); ++ if (status) ++ return status; ++ ++ if (response != 0) { ++ ssam_err(ctrl, "unexpected response from display-on notification: " ++ "0x%02x\n", response); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int ssam_ctrl_notif_d0_exit(struct ssam_controller *ctrl) ++{ ++ int status; ++ u8 response; ++ ++ if (!ctrl->caps.notif_d0exit) ++ return 0; ++ ++ ssam_dbg(ctrl, "pm: notifying D0 exit\n"); ++ ++ status = ssam_ssh_notif_d0_exit(ctrl, &response); ++ if (status) ++ return status; ++ ++ if (response != 0) { ++ ssam_err(ctrl, "unexpected response from D0-exit notification: " ++ "0x%02x\n", response); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl) ++{ ++ int status; ++ u8 response; ++ ++ if (!ctrl->caps.notif_d0exit) ++ return 0; ++ ++ ssam_dbg(ctrl, "pm: notifying D0 entry\n"); ++ ++ status = ssam_ssh_notif_d0_entry(ctrl, &response); ++ if (status) ++ return status; ++ ++ if (response != 0) { ++ ssam_err(ctrl, "unexpected response from D0-entry notification: " ++ "0x%02x\n", response); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++/* -- Top-level event registry interface. ----------------------------------- */ ++ ++int ssam_notifier_register(struct ssam_controller *ctrl, ++ struct ssam_event_notifier *n) ++{ ++ u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); + struct ssam_nf_head *nf_head; -+ struct sam_ssh_ec *ec; + struct ssam_nf *nf; -+ u16 event = ssh_tc_to_event(n->event.id.target_category); -+ u16 rqid = ssh_event_to_rqid(event); + int rc, status; + + if (!ssh_rqid_is_event(rqid)) + return -EINVAL; + -+ ec = surface_sam_ssh_acquire_init(); -+ if (!ec) ++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) + return -ENXIO; + -+ nf = &ec->cplt.event.notif; -+ nf_head = &nf->head[event]; ++ nf = &ctrl->cplt.event.notif; ++ nf_head = &nf->head[ssh_rqid_to_event(rqid)]; + + mutex_lock(&nf->lock); + @@ -9861,7 +10367,9 @@ index 000000000000..9f44bdfbc4fd + return rc; + } + -+ ssh_dbg(ec, "enabling event (tc: 0x%02x, rc: %d)\n", rqid, rc); ++ ssam_dbg(ctrl, "enabling event (reg: 0x%02x, tc: 0x%02x, iid: 0x%02x, " ++ "rc: %d)\n", n->event.reg.target_category, ++ n->event.id.target_category, n->event.id.instance, rc); + + status = __ssam_nfblk_insert(nf_head, &n->base); + if (status) { @@ -9871,38 +10379,39 @@ index 000000000000..9f44bdfbc4fd + } + + if (rc == 1) { -+ status = surface_sam_ssh_event_enable(ec, n->event.reg, n->event.id, n->event.flags); ++ status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id, ++ n->event.flags); + if (status) { + __ssam_nfblk_remove(nf_head, &n->base); + ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); + mutex_unlock(&nf->lock); ++ synchronize_srcu(&nf_head->srcu); + return status; + } + } + + mutex_unlock(&nf->lock); + return 0; -+} -+EXPORT_SYMBOL_GPL(surface_sam_ssh_notifier_register); + -+int surface_sam_ssh_notifier_unregister(struct ssam_event_notifier *n) ++} ++EXPORT_SYMBOL_GPL(ssam_notifier_register); ++ ++int ssam_notifier_unregister(struct ssam_controller *ctrl, ++ struct ssam_event_notifier *n) +{ ++ u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); + struct ssam_nf_head *nf_head; -+ struct sam_ssh_ec *ec; + struct ssam_nf *nf; -+ u16 event = ssh_tc_to_event(n->event.id.target_category); -+ u16 rqid = ssh_event_to_rqid(event); + int rc, status = 0; + + if (!ssh_rqid_is_event(rqid)) + return -EINVAL; + -+ ec = surface_sam_ssh_acquire_init(); -+ if (!ec) ++ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) + return -ENXIO; + -+ nf = &ec->cplt.event.notif; -+ nf_head = &nf->head[event]; ++ nf = &ctrl->cplt.event.notif; ++ nf_head = &nf->head[ssh_rqid_to_event(rqid)]; + + mutex_lock(&nf->lock); + @@ -9912,10 +10421,14 @@ index 000000000000..9f44bdfbc4fd + return rc; + } + -+ ssh_dbg(ec, "disabling event (tc: 0x%02x, rc: %d)\n", rqid, rc); ++ ssam_dbg(ctrl, "disabling event (reg: 0x%02x, tc: 0x%02x, iid: 0x%02x, " ++ "rc: %d)\n", n->event.reg.target_category, ++ n->event.id.target_category, n->event.id.instance, rc); + -+ if (rc == 0) -+ status = surface_sam_ssh_event_disable(ec, n->event.reg, n->event.id, n->event.flags); ++ if (rc == 0) { ++ status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id, ++ n->event.flags); ++ } + + __ssam_nfblk_remove(nf_head, &n->base); + mutex_unlock(&nf->lock); @@ -9923,270 +10436,57 @@ index 000000000000..9f44bdfbc4fd + + return status; +} -+EXPORT_SYMBOL_GPL(surface_sam_ssh_notifier_unregister); ++EXPORT_SYMBOL_GPL(ssam_notifier_unregister); + + -+static int __surface_sam_ssh_rqst(struct sam_ssh_ec *ec, -+ const struct surface_sam_ssh_rqst *rqst, -+ struct surface_sam_ssh_buf *result) -+{ -+ struct ssam_request_sync actual; -+ struct msgbuf msgb; -+ size_t msglen = SSH_COMMAND_MESSAGE_LENGTH(rqst->cdl); -+ unsigned flags = 0; -+ u16 rqid; -+ u8 seq; -+ int status; ++/* -- Wakeup IRQ. ----------------------------------------------------------- */ + -+ // prevent overflow -+ if (rqst->cdl > SSH_COMMAND_MAX_PAYLOAD_SIZE) { -+ ssh_err(ec, SSH_RQST_TAG "request payload too large\n"); -+ return -EINVAL; -+ } ++static const struct acpi_gpio_params gpio_ssam_wakeup_int = { 0, 0, false }; ++static const struct acpi_gpio_params gpio_ssam_wakeup = { 1, 0, false }; + -+ if (result && result->data && rqst->snc) -+ flags |= SSAM_REQUEST_HAS_RESPONSE; -+ -+ ssh_request_init(&actual.base, flags, &ssam_request_sync_ops); -+ init_completion(&actual.comp); -+ -+ actual.resp.pointer = NULL; -+ actual.resp.capacity = 0; -+ actual.resp.length = 0; -+ actual.resp.status = 0; -+ -+ if (result) { -+ actual.resp.pointer = result->data; -+ actual.resp.capacity = result->cap; -+ } -+ -+ // alloc and create message -+ status = msgb_alloc(&msgb, msglen, GFP_KERNEL); -+ if (status) -+ return status; -+ -+ seq = ssh_seq_next(&ec->counter.seq); -+ rqid = ssh_rqid_next(&ec->counter.rqid); -+ msgb_push_cmd(&msgb, seq, rqst, rqid); -+ -+ actual.base.packet.data = msgb.buffer; -+ actual.base.packet.data_length = msgb.ptr - msgb.buffer; -+ -+ status = ssh_rtl_submit(&ec->rtl, &actual.base); -+ if (status) { -+ msgb_free(&msgb); -+ return status; -+ } -+ -+ ssh_request_put(&actual.base); -+ ssam_request_sync_wait_complete(&actual); -+ msgb_free(&msgb); -+ -+ if (result) -+ result->len = actual.resp.length; -+ -+ return actual.resp.status; -+} -+ -+int surface_sam_ssh_rqst(const struct surface_sam_ssh_rqst *rqst, struct surface_sam_ssh_buf *result) -+{ -+ struct sam_ssh_ec *ec; -+ -+ ec = surface_sam_ssh_acquire_init(); -+ if (!ec) { -+ pr_warn(SSH_RQST_TAG_FULL "embedded controller is uninitialized\n"); -+ return -ENXIO; -+ } -+ -+ if (smp_load_acquire(&ec->state) == SSH_EC_SUSPENDED) { -+ ssh_warn(ec, SSH_RQST_TAG "embedded controller is suspended\n"); -+ return -EPERM; -+ } -+ -+ return __surface_sam_ssh_rqst(ec, rqst, result); -+} -+EXPORT_SYMBOL_GPL(surface_sam_ssh_rqst); -+ -+ -+/** -+ * surface_sam_ssh_ec_resume - Resume the EC if it is in a suspended mode. -+ * @ec: the EC to resume -+ * -+ * Moves the EC from a suspended state to a normal state. See the -+ * `surface_sam_ssh_ec_suspend` function what the specific differences of -+ * these states are. Multiple repeated calls to this function seem to be -+ * handled fine by the EC, after the first call, the state will remain -+ * "normal". -+ * -+ * Must be called with the EC initialized and its lock held. -+ */ -+static int surface_sam_ssh_ec_resume(struct sam_ssh_ec *ec) -+{ -+ u8 buf[1] = { 0x00 }; -+ int status; -+ -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x01, -+ .cid = 0x16, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surface_sam_ssh_buf result = { -+ result.cap = ARRAY_SIZE(buf), -+ result.len = 0, -+ result.data = buf, -+ }; -+ -+ ssh_dbg(ec, "pm: resuming system aggregator module\n"); -+ status = __surface_sam_ssh_rqst(ec, &rqst, &result); -+ if (status) -+ return status; -+ -+ /* -+ * The purpose of the return value of this request is unknown. Based on -+ * logging and experience, we expect it to be zero. No other value has -+ * been observed so far. -+ */ -+ if (buf[0] != 0x00) { -+ ssh_warn(ec, "unexpected result while trying to resume EC: " -+ "0x%02x\n", buf[0]); -+ } -+ -+ return 0; -+} -+ -+/** -+ * surface_sam_ssh_ec_suspend - Put the EC in a suspended mode: -+ * @ec: the EC to suspend -+ * -+ * Tells the EC to enter a suspended mode. In this mode, events are quiesced -+ * and the wake IRQ is armed (note that the wake IRQ does not fire if the EC -+ * has not been suspended via this request). On some devices, the keyboard -+ * backlight is turned off. Apart from this, the EC seems to continue to work -+ * as normal, meaning requests sent to it are acknowledged and seem to be -+ * correctly handled, including potential responses. Multiple repeated calls -+ * to this function seem to be handled fine by the EC, after the first call, -+ * the state will remain "suspended". -+ * -+ * Must be called with the EC initialized and its lock held. -+ */ -+static int surface_sam_ssh_ec_suspend(struct sam_ssh_ec *ec) -+{ -+ u8 buf[1] = { 0x00 }; -+ int status; -+ -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x01, -+ .cid = 0x15, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surface_sam_ssh_buf result = { -+ result.cap = ARRAY_SIZE(buf), -+ result.len = 0, -+ result.data = buf, -+ }; -+ -+ ssh_dbg(ec, "pm: suspending system aggregator module\n"); -+ status = __surface_sam_ssh_rqst(ec, &rqst, &result); -+ if (status) -+ return status; -+ -+ /* -+ * The purpose of the return value of this request is unknown. Based on -+ * logging and experience, we expect it to be zero. No other value has -+ * been observed so far. -+ */ -+ if (buf[0] != 0x00) { -+ ssh_warn(ec, "unexpected result while trying to suspend EC: " -+ "0x%02x\n", buf[0]); -+ } -+ -+ return 0; -+} -+ -+ -+static int surface_sam_ssh_get_controller_version(struct sam_ssh_ec *ec, u32 *version) -+{ -+ struct surface_sam_ssh_rqst rqst = { -+ .tc = 0x01, -+ .cid = 0x13, -+ .iid = 0x00, -+ .chn = 0x01, -+ .snc = 0x01, -+ .cdl = 0x00, -+ .pld = NULL, -+ }; -+ -+ struct surface_sam_ssh_buf result = { -+ result.cap = sizeof(*version), -+ result.len = 0, -+ result.data = (u8 *)version, -+ }; -+ -+ *version = 0; -+ return __surface_sam_ssh_rqst(ec, &rqst, &result); -+} -+ -+static int surface_sam_ssh_log_controller_version(struct sam_ssh_ec *ec) -+{ -+ u32 version, a, b, c; -+ int status; -+ -+ status = surface_sam_ssh_get_controller_version(ec, &version); -+ if (status) -+ return status; -+ -+ a = (version >> 24) & 0xff; -+ b = le16_to_cpu((version >> 8) & 0xffff); -+ c = version & 0xff; -+ -+ dev_info(&ec->serdev->dev, "SAM controller version: %u.%u.%u\n", -+ a, b, c); -+ return 0; -+} -+ -+ -+static const struct acpi_gpio_params gpio_ssh_wakeup_int = { 0, 0, false }; -+static const struct acpi_gpio_params gpio_ssh_wakeup = { 1, 0, false }; -+ -+static const struct acpi_gpio_mapping ssh_acpi_gpios[] = { -+ { "ssh_wakeup-int-gpio", &gpio_ssh_wakeup_int, 1 }, -+ { "ssh_wakeup-gpio", &gpio_ssh_wakeup, 1 }, ++static const struct acpi_gpio_mapping ssam_acpi_gpios[] = { ++ { "ssam_wakeup-int-gpio", &gpio_ssam_wakeup_int, 1 }, ++ { "ssam_wakeup-gpio", &gpio_ssam_wakeup, 1 }, + { }, +}; + -+static irqreturn_t ssh_wake_irq_handler(int irq, void *dev_id) ++static irqreturn_t ssam_irq_handle(int irq, void *dev_id) +{ -+ struct serdev_device *serdev = dev_id; ++ struct ssam_controller *ctrl = dev_id; + -+ dev_dbg(&serdev->dev, "pm: wake irq triggered\n"); ++ ssam_dbg(ctrl, "pm: wake irq triggered\n"); + ++ // Note: Proper wakeup detection is currently unimplemented. ++ // When the EC is in display-off or any other non-D0 state, it ++ // does not send events/notifications to the host. Instead it ++ // signals that there are events available via the wakeup IRQ. ++ // This driver is responsible for calling back to the EC to ++ // release these events one-by-one. ++ // ++ // This IRQ should not cause a full system resume by its own. ++ // Instead, events should be handled by their respective subsystem ++ // drivers, which in turn should signal whether a full system ++ // resume should be performed. ++ // + // TODO: Send GPIO callback command repeatedly to EC until callback + // returns 0x00. Return flag of callback is "has more events". + // Each time the command is sent, one event is "released". Once + // all events have been released (return = 0x00), the GPIO is -+ // re-armed. ++ // re-armed. Detect wakeup events during this process, go back to ++ // sleep if no wakeup event has been received. + + return IRQ_HANDLED; +} + -+static int ssh_setup_irq(struct serdev_device *serdev) ++static int ssam_irq_setup(struct ssam_controller *ctrl) +{ -+ const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING; ++ const int irqf = IRQF_SHARED | IRQF_ONESHOT; ++ struct device *dev = ssam_controller_device(ctrl); + struct gpio_desc *gpiod; + int irq; + int status; + -+ gpiod = gpiod_get(&serdev->dev, "ssh_wakeup-int", GPIOD_ASIS); ++ gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + @@ -10196,16 +10496,47 @@ index 000000000000..9f44bdfbc4fd + if (irq < 0) + return irq; + -+ status = request_threaded_irq(irq, NULL, ssh_wake_irq_handler, -+ irqf, "surface_sam_sh_wakeup", serdev); ++ status = request_threaded_irq(irq, NULL, ssam_irq_handle, irqf, ++ "surface_sam_wakeup", ctrl); + if (status) + return status; + -+ return irq; ++ ctrl->irq.num = irq; ++ return 0; ++} ++ ++static void ssam_irq_free(struct ssam_controller *ctrl) ++{ ++ free_irq(ctrl->irq.num, ctrl); ++ ctrl->irq.num = -1; +} + + -+static acpi_status ssh_setup_from_resource(struct acpi_resource *rsc, void *ctx) ++/* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */ ++ ++static int ssam_receive_buf(struct serdev_device *dev, const unsigned char *buf, ++ size_t n) ++{ ++ struct ssam_controller *ctrl = serdev_device_get_drvdata(dev); ++ return ssam_controller_receive_buf(ctrl, buf, n); ++} ++ ++static void ssam_write_wakeup(struct serdev_device *dev) ++{ ++ struct ssam_controller *ctrl = serdev_device_get_drvdata(dev); ++ ssam_controller_write_wakeup(ctrl); ++} ++ ++static const struct serdev_device_ops ssam_serdev_ops = { ++ .receive_buf = ssam_receive_buf, ++ .write_wakeup = ssam_write_wakeup, ++}; ++ ++ ++/* -- ACPI based device setup. ---------------------------------------------- */ ++ ++static acpi_status ssam_serdev_setup_via_acpi_crs(struct acpi_resource *rsc, ++ void *ctx) +{ + struct serdev_device *serdev = ctx; + struct acpi_resource_common_serialbus *serial; @@ -10226,7 +10557,7 @@ index 000000000000..9f44bdfbc4fd + serdev_device_set_baudrate(serdev, uart->default_baud_rate); + + // serdev currently only supports RTSCTS flow control -+ if (uart->flow_control & SSH_SUPPORTED_FLOW_CONTROL_MASK) { ++ if (uart->flow_control & (~((u8) ACPI_UART_FLOW_CONTROL_HW))) { + dev_warn(&serdev->dev, "setup: unsupported flow control" + " (value: 0x%02x)\n", uart->flow_control); + } @@ -10261,60 +10592,118 @@ index 000000000000..9f44bdfbc4fd + return AE_CTRL_TERMINATE; // we've found the resource and are done +} + ++static acpi_status ssam_serdev_setup_via_acpi(acpi_handle handle, ++ struct serdev_device *serdev) ++{ ++ return acpi_walk_resources(handle, METHOD_NAME__CRS, ++ ssam_serdev_setup_via_acpi_crs, serdev); ++} ++ ++ ++/* -- Power management. ----------------------------------------------------- */ ++ ++static void surface_sam_ssh_shutdown(struct device *dev) ++{ ++ struct ssam_controller *c = dev_get_drvdata(dev); ++ int status; ++ ++ /* ++ * Try to signal display-off and D0-exit, ignore any errors. ++ * ++ * Note: It has not been established yet if this is actually ++ * necessary/useful for shutdown. ++ */ ++ ++ status = ssam_ctrl_notif_display_off(c); ++ if (status) ++ ssam_err(c, "pm: display-off notification failed: %d\n", status); ++ ++ status = ssam_ctrl_notif_d0_exit(c); ++ if (status) ++ ssam_err(c, "pm: D0-exit notification failed: %d\n", status); ++} + +static int surface_sam_ssh_suspend(struct device *dev) +{ -+ struct sam_ssh_ec *ec; ++ struct ssam_controller *c = dev_get_drvdata(dev); + int status; + -+ dev_dbg(dev, "pm: suspending\n"); ++ /* ++ * Try to signal display-off and D0-exit, enable IRQ wakeup if ++ * specified. Abort on error. ++ * ++ * Note: Signalling display-off/display-on should normally be done from ++ * some sort of display state notifier. As that is not available, signal ++ * it here. ++ */ + -+ ec = surface_sam_ssh_acquire_init(); -+ if (ec) { -+ status = surface_sam_ssh_ec_suspend(ec); -+ if (status) -+ return status; -+ -+ if (device_may_wakeup(dev)) { -+ status = enable_irq_wake(ec->irq); -+ if (status) -+ return status; -+ -+ ec->irq_wakeup_enabled = true; -+ } else { -+ ec->irq_wakeup_enabled = false; -+ } -+ -+ smp_store_release(&ec->state, SSH_EC_SUSPENDED); ++ status = ssam_ctrl_notif_display_off(c); ++ if (status) { ++ ssam_err(c, "pm: display-off notification failed: %d\n", status); ++ return status; + } + ++ status = ssam_ctrl_notif_d0_exit(c); ++ if (status) { ++ ssam_err(c, "pm: D0-exit notification failed: %d\n", status); ++ goto err_notif; ++ } ++ ++ if (device_may_wakeup(dev)) { ++ status = enable_irq_wake(c->irq.num); ++ if (status) { ++ ssam_err(c, "failed to disable wake IRQ: %d\n", status); ++ goto err_irq; ++ } ++ ++ c->irq.wakeup_enabled = true; ++ } else { ++ c->irq.wakeup_enabled = false; ++ } ++ ++ WARN_ON(ssam_controller_suspend(c)); + return 0; ++ ++err_irq: ++ ssam_ctrl_notif_d0_entry(c); ++err_notif: ++ ssam_ctrl_notif_display_on(c); ++ return status; +} + +static int surface_sam_ssh_resume(struct device *dev) +{ -+ struct sam_ssh_ec *ec; ++ struct ssam_controller *c = dev_get_drvdata(dev); + int status; + -+ dev_dbg(dev, "pm: resuming\n"); ++ WARN_ON(ssam_controller_resume(c)); + -+ ec = surface_sam_ssh_acquire_init(); -+ if (ec) { -+ smp_store_release(&ec->state, SSH_EC_INITIALIZED); ++ /* ++ * Try to disable IRQ wakeup (if specified), signal display-on and ++ * D0-entry. In case of errors, log them and try to restore normal ++ * operation state as far as possible. ++ * ++ * Note: Signalling display-off/display-on should normally be done from ++ * some sort of display state notifier. As that is not available, signal ++ * it here. ++ */ + -+ if (ec->irq_wakeup_enabled) { -+ status = disable_irq_wake(ec->irq); -+ if (status) -+ return status; -+ -+ ec->irq_wakeup_enabled = false; -+ } -+ -+ status = surface_sam_ssh_ec_resume(ec); ++ if (c->irq.wakeup_enabled) { ++ status = disable_irq_wake(c->irq.num); + if (status) -+ return status; ++ ssam_err(c, "failed to disable wake IRQ: %d\n", status); ++ ++ c->irq.wakeup_enabled = false; + } + ++ status = ssam_ctrl_notif_d0_entry(c); ++ if (status) ++ ssam_err(c, "pm: display-on notification failed: %d\n", status); ++ ++ status = ssam_ctrl_notif_display_on(c); ++ if (status) ++ ssam_err(c, "pm: D0-entry notification failed: %d\n", status); ++ + return 0; +} + @@ -10322,307 +10711,171 @@ index 000000000000..9f44bdfbc4fd + surface_sam_ssh_resume); + + -+static void ssam_handle_event(struct ssh_rtl *rtl, -+ const struct ssh_command *cmd, -+ const struct sshp_span *data) -+{ -+ struct sam_ssh_ec *ec = container_of(rtl, struct sam_ssh_ec, rtl); -+ struct ssam_event_item *item; ++/* -- Device/driver setup. -------------------------------------------------- */ + -+ item = kzalloc(sizeof(struct ssam_event_item) + data->len, GFP_KERNEL); -+ if (!item) -+ return; -+ -+ item->rqid = get_unaligned_le16(&cmd->rqid); -+ item->event.target_category = cmd->tc; -+ item->event.command_id = cmd->cid; -+ item->event.instance_id = cmd->iid; -+ item->event.channel = cmd->chn_in; -+ item->event.length = data->len; -+ memcpy(&item->event.data[0], data->ptr, data->len); -+ -+ ssam_cplt_submit_event(&ec->cplt, item); -+} -+ -+static struct ssh_rtl_ops ssam_rtl_ops = { -+ .handle_event = ssam_handle_event, ++static struct ssam_controller ssam_controller = { ++ .state = SSAM_CONTROLLER_UNINITIALIZED, +}; ++static DEFINE_MUTEX(ssam_controller_lock); + -+ -+static int ssam_receive_buf(struct serdev_device *dev, const unsigned char *buf, size_t n) ++static int __ssam_client_link(struct ssam_controller *c, struct device *client) +{ -+ struct sam_ssh_ec *ec = serdev_device_get_drvdata(dev); -+ return ssh_ptl_rx_rcvbuf(&ec->rtl.ptl, buf, n); -+} ++ const u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; ++ struct device_link *link; ++ struct device *ctrldev; + -+static void ssam_write_wakeup(struct serdev_device *dev) -+{ -+ struct sam_ssh_ec *ec = serdev_device_get_drvdata(dev); -+ ssh_ptl_tx_wakeup(&ec->rtl.ptl, true); -+} ++ if (smp_load_acquire(&c->state) != SSAM_CONTROLLER_STARTED) ++ return -ENXIO; + -+struct serdev_device_ops ssam_serdev_ops = { -+ .receive_buf = ssam_receive_buf, -+ .write_wakeup = ssam_write_wakeup, -+}; ++ if ((ctrldev = ssam_controller_device(c)) == NULL) ++ return -ENXIO; + ++ if ((link = device_link_add(client, ctrldev, flags)) == NULL) ++ return -ENOMEM; + -+#ifdef CONFIG_SURFACE_SAM_SSH_DEBUG_DEVICE ++ /* ++ * Return -ENXIO if supplier driver is on its way to be removed. In this ++ * case, the controller won't be around for much longer and the device ++ * link is not going to save us any more, as unbinding is already in ++ * progress. ++ */ ++ if (link->status == DL_STATE_SUPPLIER_UNBIND) ++ return -ENXIO; + -+static char sam_ssh_debug_rqst_buf_sysfs[256] = { 0 }; -+static char sam_ssh_debug_rqst_buf_pld[255] = { 0 }; -+static char sam_ssh_debug_rqst_buf_res[255] = { 0 }; -+ -+struct sysfs_rqst { -+ u8 tc; -+ u8 cid; -+ u8 iid; -+ u8 chn; -+ u8 snc; -+ u8 cdl; -+ u8 pld[0]; -+} __packed; -+ -+static ssize_t rqst_read(struct file *f, struct kobject *kobj, struct bin_attribute *attr, -+ char *buf, loff_t offs, size_t count) -+{ -+ if (offs < 0 || count + offs > ARRAY_SIZE(sam_ssh_debug_rqst_buf_sysfs)) -+ return -EINVAL; -+ -+ memcpy(buf, sam_ssh_debug_rqst_buf_sysfs + offs, count); -+ return count; -+} -+ -+static ssize_t rqst_write(struct file *f, struct kobject *kobj, struct bin_attribute *attr, -+ char *buf, loff_t offs, size_t count) -+{ -+ struct sysfs_rqst *input; -+ struct surface_sam_ssh_rqst rqst = {}; -+ struct surface_sam_ssh_buf result = {}; -+ int status; -+ -+ // check basic write constriants -+ if (offs != 0 || count - sizeof(struct sysfs_rqst) > ARRAY_SIZE(sam_ssh_debug_rqst_buf_pld)) -+ return -EINVAL; -+ -+ if (count < sizeof(struct sysfs_rqst)) -+ return -EINVAL; -+ -+ input = (struct sysfs_rqst *)buf; -+ -+ // payload length should be consistent with data provided -+ if (input->cdl + sizeof(struct sysfs_rqst) != count) -+ return -EINVAL; -+ -+ rqst.tc = input->tc; -+ rqst.cid = input->cid; -+ rqst.iid = input->iid; -+ rqst.chn = input->chn; -+ rqst.snc = input->snc; -+ rqst.cdl = input->cdl; -+ rqst.pld = sam_ssh_debug_rqst_buf_pld; -+ memcpy(sam_ssh_debug_rqst_buf_pld, &input->pld[0], input->cdl); -+ -+ result.cap = ARRAY_SIZE(sam_ssh_debug_rqst_buf_res); -+ result.len = 0; -+ result.data = sam_ssh_debug_rqst_buf_res; -+ -+ status = surface_sam_ssh_rqst(&rqst, &result); -+ if (status) -+ return status; -+ -+ sam_ssh_debug_rqst_buf_sysfs[0] = result.len; -+ memcpy(sam_ssh_debug_rqst_buf_sysfs + 1, result.data, result.len); -+ memset(sam_ssh_debug_rqst_buf_sysfs + result.len + 1, 0, -+ ARRAY_SIZE(sam_ssh_debug_rqst_buf_sysfs) + 1 - result.len); -+ -+ return count; -+} -+ -+static const BIN_ATTR_RW(rqst, ARRAY_SIZE(sam_ssh_debug_rqst_buf_sysfs)); -+ -+static int surface_sam_ssh_sysfs_register(struct device *dev) -+{ -+ return sysfs_create_bin_file(&dev->kobj, &bin_attr_rqst); -+} -+ -+static void surface_sam_ssh_sysfs_unregister(struct device *dev) -+{ -+ sysfs_remove_bin_file(&dev->kobj, &bin_attr_rqst); -+} -+ -+#else /* CONFIG_SURFACE_SAM_SSH_DEBUG_DEVICE */ -+ -+static int surface_sam_ssh_sysfs_register(struct device *dev) -+{ + return 0; +} + -+static void surface_sam_ssh_sysfs_unregister(struct device *dev) ++int ssam_client_bind(struct device *client, struct ssam_controller **ctrl) +{ -+} ++ struct ssam_controller *c = &ssam_controller; ++ int status; + -+#endif /* CONFIG_SURFACE_SAM_SSH_DEBUG_DEVICE */ ++ mutex_lock(&ssam_controller_lock); ++ status = __ssam_client_link(c, client); ++ mutex_unlock(&ssam_controller_lock); ++ ++ *ctrl = status == 0 ? c : NULL; ++ return status; ++} ++EXPORT_SYMBOL_GPL(ssam_client_bind); + + +static int surface_sam_ssh_probe(struct serdev_device *serdev) +{ -+ struct sam_ssh_ec *ec; ++ struct ssam_controller *ctrl = &ssam_controller; + acpi_handle *ssh = ACPI_HANDLE(&serdev->dev); -+ int status, irq; ++ int status; + + if (gpiod_count(&serdev->dev, NULL) < 0) + return -ENODEV; + -+ status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssh_acpi_gpios); ++ status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios); + if (status) + return status; + -+ // setup IRQ -+ irq = ssh_setup_irq(serdev); -+ if (irq < 0) -+ return irq; -+ + // set up EC -+ ec = surface_sam_ssh_acquire(); -+ if (smp_load_acquire(&ec->state) != SSH_EC_UNINITIALIZED) { -+ dev_err(&serdev->dev, "embedded controller already initialized\n"); ++ mutex_lock(&ssam_controller_lock); + -+ status = -EBUSY; -+ goto err_ecinit; -+ } -+ -+ ec->serdev = serdev; -+ ec->irq = irq; -+ ssh_seq_reset(&ec->counter.seq); -+ ssh_rqid_reset(&ec->counter.rqid); -+ -+ // initialize event/request completion system -+ status = ssam_cplt_init(&ec->cplt, &serdev->dev); ++ // initialize controller ++ status = ssam_controller_init(ctrl, serdev); + if (status) -+ goto err_ecinit; -+ -+ // initialize request and packet transmission layers -+ status = ssh_rtl_init(&ec->rtl, serdev, &ssam_rtl_ops); -+ if (status) -+ goto err_rtl; -+ -+ serdev_device_set_drvdata(serdev, ec); ++ goto err_ctrl_init; + ++ // set up serdev device ++ serdev_device_set_drvdata(serdev, ctrl); + serdev_device_set_client_ops(serdev, &ssam_serdev_ops); + status = serdev_device_open(serdev); + if (status) -+ goto err_open; ++ goto err_devopen; + -+ status = acpi_walk_resources(ssh, METHOD_NAME__CRS, -+ ssh_setup_from_resource, serdev); ++ status = ssam_serdev_setup_via_acpi(ssh, serdev); + if (ACPI_FAILURE(status)) + goto err_devinit; + -+ status = ssh_rtl_tx_start(&ec->rtl); ++ // start controller ++ status = ssam_controller_start(ctrl); + if (status) + goto err_devinit; + -+ status = ssh_rtl_rx_start(&ec->rtl); ++ // initial SAM requests: log version, notify default/init power states ++ status = ssam_log_firmware_version(ctrl); + if (status) -+ goto err_devinit; ++ goto err_initrq; + -+ smp_store_release(&ec->state, SSH_EC_INITIALIZED); -+ -+ status = surface_sam_ssh_log_controller_version(ec); ++ status = ssam_ctrl_notif_d0_entry(ctrl); + if (status) -+ goto err_finalize; ++ goto err_initrq; + -+ status = surface_sam_ssh_ec_resume(ec); ++ status = ssam_ctrl_notif_display_on(ctrl); + if (status) -+ goto err_finalize; ++ goto err_initrq; + -+ status = surface_sam_ssh_sysfs_register(&serdev->dev); ++ // setup IRQ ++ status = ssam_irq_setup(ctrl); + if (status) -+ goto err_finalize; ++ goto err_initrq; + -+ // TODO: The EC can wake up the system via the associated GPIO interrupt in -+ // multiple situations. One of which is the remaining battery capacity -+ // falling below a certain threshold. Normally, we should use the -+ // device_init_wakeup function, however, the EC also seems to have other -+ // reasons for waking up the system and it seems that Windows has -+ // additional checks whether the system should be resumed. In short, this -+ // causes some spourious unwanted wake-ups. For now let's thus default -+ // power/wakeup to false. ++ mutex_unlock(&ssam_controller_lock); ++ ++ /* ++ * TODO: The EC can wake up the system via the associated GPIO interrupt ++ * in multiple situations. One of which is the remaining battery ++ * capacity falling below a certain threshold. Normally, we should ++ * use the device_init_wakeup function, however, the EC also seems ++ * to have other reasons for waking up the system and it seems ++ * that Windows has additional checks whether the system should be ++ * resumed. In short, this causes some spurious unwanted wake-ups. ++ * For now let's thus default power/wakeup to false. ++ */ + device_set_wakeup_capable(&serdev->dev, true); + acpi_walk_dep_device_list(ssh); + + return 0; + -+err_finalize: -+ smp_store_release(&ec->state, SSH_EC_UNINITIALIZED); -+ ssh_rtl_flush(&ec->rtl, msecs_to_jiffies(5000)); ++err_initrq: ++ ssam_controller_shutdown(ctrl); +err_devinit: + serdev_device_close(serdev); -+err_open: -+ ssh_rtl_shutdown(&ec->rtl); -+ ssh_rtl_destroy(&ec->rtl); -+err_rtl: -+ ssam_cplt_flush(&ec->cplt); -+ ssam_cplt_destroy(&ec->cplt); -+err_ecinit: -+ free_irq(irq, serdev); ++err_devopen: ++ ssam_controller_destroy(ctrl); ++err_ctrl_init: + serdev_device_set_drvdata(serdev, NULL); ++ mutex_unlock(&ssam_controller_lock); + return status; +} + +static void surface_sam_ssh_remove(struct serdev_device *serdev) +{ -+ struct sam_ssh_ec *ec; ++ struct ssam_controller *ctrl = serdev_device_get_drvdata(serdev); + int status; + -+ ec = surface_sam_ssh_acquire_init(); -+ if (!ec) -+ return; -+ -+ free_irq(ec->irq, serdev); -+ surface_sam_ssh_sysfs_unregister(&serdev->dev); ++ mutex_lock(&ssam_controller_lock); ++ ssam_irq_free(ctrl); + + // suspend EC and disable events -+ status = surface_sam_ssh_ec_suspend(ec); -+ if (status) -+ dev_err(&serdev->dev, "failed to suspend EC: %d\n", status); ++ status = ssam_ctrl_notif_display_off(ctrl); ++ if (status) { ++ dev_err(&serdev->dev, "display-off notification failed: %d\n", ++ status); ++ } + -+ // flush pending events and requests while everything still works -+ status = ssh_rtl_flush(&ec->rtl, msecs_to_jiffies(5000)); -+ if (status) -+ dev_err(&serdev->dev, "failed to flush request transmission layer: %d\n", status); ++ status = ssam_ctrl_notif_d0_exit(ctrl); ++ if (status) { ++ dev_err(&serdev->dev, "D0-exit notification failed: %d\n", ++ status); ++ } + -+ ssam_cplt_flush(&ec->cplt); -+ -+ // mark device as uninitialized -+ smp_store_release(&ec->state, SSH_EC_UNINITIALIZED); -+ -+ // cancel rem. requests, ensure no new ones can be queued, stop threads -+ ssh_rtl_tx_flush(&ec->rtl); -+ ssh_rtl_shutdown(&ec->rtl); ++ ssam_controller_shutdown(ctrl); + + // shut down actual transport -+ serdev_device_wait_until_sent(ec->serdev, 0); -+ serdev_device_close(ec->serdev); ++ serdev_device_wait_until_sent(serdev, 0); ++ serdev_device_close(serdev); + -+ /* -+ * Ensure _all_ events are completed. New ones could still have been -+ * received after the last flush, before the request transport layer -+ * has been shut down. At this point we can be sure that no requests -+ * will remain after this call. -+ */ -+ ssam_cplt_flush(&ec->cplt); -+ -+ // actually free resources -+ ssam_cplt_destroy(&ec->cplt); -+ ssh_rtl_destroy(&ec->rtl); -+ -+ ec->serdev = NULL; -+ ec->irq = -1; ++ ssam_controller_destroy(ctrl); + + device_set_wakeup_capable(&serdev->dev, false); + serdev_device_set_drvdata(serdev, NULL); ++ mutex_unlock(&ssam_controller_lock); +} + + @@ -10639,11 +10892,14 @@ index 000000000000..9f44bdfbc4fd + .name = "surface_sam_ssh", + .acpi_match_table = surface_sam_ssh_match, + .pm = &surface_sam_ssh_pm_ops, ++ .shutdown = surface_sam_ssh_shutdown, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + + ++/* -- Module setup. --------------------------------------------------------- */ ++ +static int __init surface_sam_ssh_init(void) +{ + return serdev_device_driver_register(&surface_sam_ssh); @@ -10672,10 +10928,10 @@ index 000000000000..9f44bdfbc4fd +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh.h b/drivers/platform/x86/surface_sam/surface_sam_ssh.h new file mode 100644 -index 000000000000..25a3ae85fee7 +index 0000000000000..ba57adb2a3c9d --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh.h -@@ -0,0 +1,488 @@ +@@ -0,0 +1,717 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Interface for Surface Serial Hub (SSH). @@ -10839,7 +11095,16 @@ index 000000000000..25a3ae85fee7 + (2ull * sizeof(u16) + sizeof(struct ssh_frame) \ + + offsetof(struct ssh_command, field)) + -+struct sshp_span { ++/** ++ * struct ssam_span - reference to a buffer region ++ * @ptr: pointer to the buffer region ++ * @len: length of the buffer region ++ * ++ * A reference to a (non-owned) buffer segment, consisting of pointer and ++ * length. Use of this struct indicates non-owned data, i.e. data of which the ++ * life-time is managed (i.e. it is allocated/freed) via another pointer. ++ */ ++struct ssam_span { + u8 *ptr; + size_t len; +}; @@ -10860,17 +11125,7 @@ index 000000000000..25a3ae85fee7 +#define ssh_packet_priority_get_try(p) ((p) & 0x0f) + + -+enum ssh_packet_type_flags { -+ SSH_PACKET_TY_FLUSH_BIT, -+ SSH_PACKET_TY_SEQUENCED_BIT, -+ SSH_PACKET_TY_BLOCKING_BIT, -+ -+ SSH_PACKET_TY_FLUSH = BIT(SSH_PACKET_TY_FLUSH_BIT), -+ SSH_PACKET_TY_SEQUENCED = BIT(SSH_PACKET_TY_SEQUENCED_BIT), -+ SSH_PACKET_TY_BLOCKING = BIT(SSH_PACKET_TY_BLOCKING_BIT), -+}; -+ -+enum ssh_packet_state_flags { ++enum ssh_packet_flags { + SSH_PACKET_SF_LOCKED_BIT, + SSH_PACKET_SF_QUEUED_BIT, + SSH_PACKET_SF_PENDING_BIT, @@ -10879,6 +11134,25 @@ index 000000000000..25a3ae85fee7 + SSH_PACKET_SF_ACKED_BIT, + SSH_PACKET_SF_CANCELED_BIT, + SSH_PACKET_SF_COMPLETED_BIT, ++ ++ SSH_PACKET_TY_FLUSH_BIT, ++ SSH_PACKET_TY_SEQUENCED_BIT, ++ SSH_PACKET_TY_BLOCKING_BIT, ++ ++ SSH_PACKET_FLAGS_SF_MASK = ++ BIT(SSH_PACKET_SF_LOCKED_BIT) ++ | BIT(SSH_PACKET_SF_QUEUED_BIT) ++ | BIT(SSH_PACKET_SF_PENDING_BIT) ++ | BIT(SSH_PACKET_SF_TRANSMITTING_BIT) ++ | BIT(SSH_PACKET_SF_TRANSMITTED_BIT) ++ | BIT(SSH_PACKET_SF_ACKED_BIT) ++ | BIT(SSH_PACKET_SF_CANCELED_BIT) ++ | BIT(SSH_PACKET_SF_COMPLETED_BIT), ++ ++ SSH_PACKET_FLAGS_TY_MASK = ++ BIT(SSH_PACKET_TY_FLUSH_BIT) ++ | BIT(SSH_PACKET_TY_SEQUENCED_BIT) ++ | BIT(SSH_PACKET_TY_BLOCKING_BIT), +}; + + @@ -10886,18 +11160,20 @@ index 000000000000..25a3ae85fee7 +struct ssh_packet; + +struct ssh_packet_ops { -+ void (*release)(struct ssh_packet *packet); -+ void (*complete)(struct ssh_packet *packet, int status); ++ void (*release)(struct ssh_packet *p); ++ void (*complete)(struct ssh_packet *p, int status); +}; + +struct ssh_packet { + struct ssh_ptl *ptl; + struct kref refcnt; + -+ u8 type; + u8 priority; -+ u16 data_length; -+ u8 *data; ++ ++ struct { ++ size_t len; ++ u8 *ptr; ++ } data; + + unsigned long state; + ktime_t timestamp; @@ -10909,6 +11185,16 @@ index 000000000000..25a3ae85fee7 +}; + + ++void ssh_packet_get(struct ssh_packet *p); ++void ssh_packet_put(struct ssh_packet *p); ++ ++static inline void ssh_packet_set_data(struct ssh_packet *p, u8 *ptr, size_t len) ++{ ++ p->data.ptr = ptr; ++ p->data.len = len; ++} ++ ++ +/* -- Request transport layer (rtl). ---------------------------------------- */ + +enum ssh_request_flags { @@ -10947,11 +11233,10 @@ index 000000000000..25a3ae85fee7 + void (*release)(struct ssh_request *rqst); + void (*complete)(struct ssh_request *rqst, + const struct ssh_command *cmd, -+ const struct sshp_span *data, int status); ++ const struct ssam_span *data, int status); +}; + +struct ssh_request { -+ struct ssh_rtl *rtl; + struct ssh_packet packet; + struct list_head node; + @@ -10962,6 +11247,22 @@ index 000000000000..25a3ae85fee7 +}; + + ++static inline void ssh_request_get(struct ssh_request *r) ++{ ++ ssh_packet_get(&r->packet); ++} ++ ++static inline void ssh_request_put(struct ssh_request *r) ++{ ++ ssh_packet_put(&r->packet); ++} ++ ++static inline void ssh_request_set_data(struct ssh_request *r, u8 *ptr, size_t len) ++{ ++ ssh_packet_set_data(&r->packet, ptr, len); ++} ++ ++ +/* -- Main data types and definitions --------------------------------------- */ + +enum ssam_ssh_tc { @@ -11000,6 +11301,8 @@ index 000000000000..25a3ae85fee7 + SSAM_SSH_TC_REG = 0x21, +}; + ++struct ssam_controller; ++ +/** + * struct ssam_event_flags - Flags for enabling/disabling SAM-over-SSH events + * @SSAM_EVENT_SEQUENCED: The event will be sent via a sequenced data frame. @@ -11017,6 +11320,242 @@ index 000000000000..25a3ae85fee7 + u8 data[0]; +}; + ++enum ssam_request_flags { ++ SSAM_REQUEST_HAS_RESPONSE = BIT(0), ++ SSAM_REQUEST_UNSEQUENCED = BIT(1), ++}; ++ ++struct ssam_request { ++ u8 target_category; ++ u8 command_id; ++ u8 instance_id; ++ u8 channel; ++ u16 flags; ++ u16 length; ++ const u8 *payload; ++}; ++ ++struct ssam_response { ++ size_t capacity; ++ size_t length; ++ u8 *pointer; ++}; ++ ++ ++int ssam_client_bind(struct device *client, struct ssam_controller **ctrl); ++ ++struct device *ssam_controller_device(struct ssam_controller *c); ++ ++ssize_t ssam_request_write_data(struct ssam_span *buf, ++ struct ssam_controller *ctrl, ++ struct ssam_request *spec); ++ ++ ++/* -- Synchronous request interface. ---------------------------------------- */ ++ ++struct ssam_request_sync { ++ struct ssh_request base; ++ struct completion comp; ++ struct ssam_response *resp; ++ int status; ++}; ++ ++int ssam_request_sync_alloc(size_t payload_len, gfp_t flags, ++ struct ssam_request_sync **rqst, ++ struct ssam_span *buffer); ++ ++void ssam_request_sync_init(struct ssam_request_sync *rqst, ++ enum ssam_request_flags flags); ++ ++static inline void ssam_request_sync_set_data(struct ssam_request_sync *rqst, ++ u8 *ptr, size_t len) ++{ ++ ssh_request_set_data(&rqst->base, ptr, len); ++} ++ ++static inline void ssam_request_sync_set_resp(struct ssam_request_sync *rqst, ++ struct ssam_response *resp) ++{ ++ rqst->resp = resp; ++} ++ ++int ssam_request_sync_submit(struct ssam_controller *ctrl, ++ struct ssam_request_sync *rqst); ++ ++static inline int ssam_request_sync_wait(struct ssam_request_sync *rqst) ++{ ++ wait_for_completion(&rqst->comp); ++ return rqst->status; ++} ++ ++int ssam_request_sync(struct ssam_controller *ctrl, struct ssam_request *spec, ++ struct ssam_response *rsp); ++ ++int ssam_request_sync_with_buffer(struct ssam_controller *ctrl, ++ struct ssam_request *spec, ++ struct ssam_response *rsp, ++ struct ssam_span *buf); ++ ++ ++#define ssam_request_sync_onstack(ctrl, rqst, rsp, payload_len) \ ++ ({ \ ++ u8 __data[SSH_COMMAND_MESSAGE_LENGTH(payload_len)]; \ ++ struct ssam_span __buf = { &__data[0], ARRAY_SIZE(__data) }; \ ++ int __status; \ ++ \ ++ /* ensure input does not overflow buffer */ \ ++ if ((rqst)->length <= payload_len) { \ ++ __status = ssam_request_sync_with_buffer( \ ++ ctrl, rqst, rsp, &__buf); \ ++ } else { \ ++ __status = -EINVAL; \ ++ } \ ++ \ ++ __status; \ ++ }) ++ ++ ++struct ssam_request_spec { ++ u8 target_category; ++ u8 command_id; ++ u8 instance_id; ++ u8 channel; ++ u8 flags; ++}; ++ ++struct ssam_request_spec_md { ++ u8 target_category; ++ u8 command_id; ++ u8 flags; ++}; ++ ++#define SSAM_DEFINE_SYNC_REQUEST_N(name, spec...) \ ++ int name(struct ssam_controller *ctrl) \ ++ { \ ++ struct ssam_request_spec s = (struct ssam_request_spec)spec; \ ++ struct ssam_request rqst; \ ++ \ ++ rqst.target_category = s.target_category; \ ++ rqst.command_id = s.command_id; \ ++ rqst.instance_id = s.instance_id; \ ++ rqst.channel = s.channel; \ ++ rqst.flags = s.flags; \ ++ rqst.length = 0; \ ++ rqst.payload = NULL; \ ++ \ ++ return ssam_request_sync_onstack(ctrl, &rqst, NULL, 0); \ ++ } ++ ++#define SSAM_DEFINE_SYNC_REQUEST_W(name, wtype, spec...) \ ++ int name(struct ssam_controller *ctrl, const wtype *in) \ ++ { \ ++ struct ssam_request_spec s = (struct ssam_request_spec)spec; \ ++ struct ssam_request rqst; \ ++ \ ++ rqst.target_category = s.target_category; \ ++ rqst.command_id = s.command_id; \ ++ rqst.instance_id = s.instance_id; \ ++ rqst.channel = s.channel; \ ++ rqst.flags = s.flags; \ ++ rqst.length = sizeof(wtype); \ ++ rqst.payload = (u8 *)in; \ ++ \ ++ return ssam_request_sync_onstack(ctrl, &rqst, NULL, \ ++ sizeof(wtype)); \ ++ } ++ ++#define SSAM_DEFINE_SYNC_REQUEST_R(name, rtype, spec...) \ ++ int name(struct ssam_controller *ctrl, rtype *out) \ ++ { \ ++ struct ssam_request_spec s = (struct ssam_request_spec)spec; \ ++ struct ssam_request rqst; \ ++ struct ssam_response rsp; \ ++ int status; \ ++ \ ++ rqst.target_category = s.target_category; \ ++ rqst.command_id = s.command_id; \ ++ rqst.instance_id = s.instance_id; \ ++ rqst.channel = s.channel; \ ++ rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE; \ ++ rqst.length = 0; \ ++ rqst.payload = NULL; \ ++ \ ++ rsp.capacity = sizeof(rtype); \ ++ rsp.length = 0; \ ++ rsp.pointer = (u8 *)out; \ ++ \ ++ status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, 0); \ ++ if (status) \ ++ return status; \ ++ \ ++ if (rsp.length != sizeof(rtype)) { \ ++ struct device *dev = ssam_controller_device(ctrl); \ ++ dev_err(dev, "rqst: invalid response length, expected %zu, got %zu" \ ++ " (tc: 0x%02x, cid: 0x%02x)", sizeof(rtype), \ ++ rsp.length, rqst.target_category, \ ++ rqst.command_id); \ ++ return -EIO; \ ++ } \ ++ \ ++ return 0; \ ++ } ++ ++#define SSAM_DEFINE_SYNC_REQUEST_MD_W(name, wtype, spec...) \ ++ int name(struct ssam_controller *ctrl, u8 chn, u8 iid, const wtype *in) \ ++ { \ ++ struct ssam_request_spec_md s \ ++ = (struct ssam_request_spec_md)spec; \ ++ struct ssam_request rqst; \ ++ \ ++ rqst.target_category = s.target_category; \ ++ rqst.command_id = s.command_id; \ ++ rqst.instance_id = iid; \ ++ rqst.channel = chn; \ ++ rqst.flags = s.flags; \ ++ rqst.length = sizeof(wtype); \ ++ rqst.payload = (u8 *)in; \ ++ \ ++ return ssam_request_sync_onstack(ctrl, &rqst, NULL, \ ++ sizeof(wtype)); \ ++ } ++ ++#define SSAM_DEFINE_SYNC_REQUEST_MD_R(name, rtype, spec...) \ ++ int name(struct ssam_controller *ctrl, u8 chn, u8 iid, rtype *out) \ ++ { \ ++ struct ssam_request_spec_md s \ ++ = (struct ssam_request_spec_md)spec; \ ++ struct ssam_request rqst; \ ++ struct ssam_response rsp; \ ++ int status; \ ++ \ ++ rqst.target_category = s.target_category; \ ++ rqst.command_id = s.command_id; \ ++ rqst.instance_id = iid; \ ++ rqst.channel = chn; \ ++ rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE; \ ++ rqst.length = 0; \ ++ rqst.payload = NULL; \ ++ \ ++ rsp.capacity = sizeof(rtype); \ ++ rsp.length = 0; \ ++ rsp.pointer = (u8 *)out; \ ++ \ ++ status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, 0); \ ++ if (status) \ ++ return status; \ ++ \ ++ if (rsp.length != sizeof(rtype)) { \ ++ struct device *dev = ssam_controller_device(ctrl); \ ++ dev_err(dev, "rqst: invalid response length, expected %zu, got %zu" \ ++ " (tc: 0x%02x, cid: 0x%02x)", sizeof(rtype), \ ++ rsp.length, rqst.target_category, \ ++ rqst.command_id); \ ++ return -EIO; \ ++ } \ ++ \ ++ return 0; \ ++ } ++ + +/* -- Event notifier/callbacks. --------------------------------------------- */ + @@ -11041,9 +11580,7 @@ index 000000000000..25a3ae85fee7 + +static inline u32 ssam_notifier_from_errno(int err) +{ -+ WARN_ON(err > 0); -+ -+ if (err >= 0) ++ if (WARN_ON(err > 0) || err == 0) + return 0; + else + return ((-err) << SSAM_NOTIF_STATE_SHIFT) | SSAM_NOTIF_STOP; @@ -11105,71 +11642,19 @@ index 000000000000..25a3ae85fee7 + } event; +}; + ++int ssam_notifier_register(struct ssam_controller *ctrl, ++ struct ssam_event_notifier *n); + -+/* -- TODO -------------------------------------------------------------------*/ -+ -+/* -+ * Maximum response payload size in bytes. -+ * Value based on ACPI (255 bytes minus header/status bytes). -+ */ -+#define SURFACE_SAM_SSH_MAX_RQST_RESPONSE (255 - 4) -+ -+/* -+ * The number of reserved event IDs, used for registering an SSH event -+ * handler. Valid event IDs are numbers below or equal to this value, with -+ * exception of zero, which is not an event ID. Thus, this is also the -+ * absolute maximum number of event handlers that can be registered. -+ */ -+#define SURFACE_SAM_SSH_NUM_EVENTS 0x22 -+ -+/* -+ * The number of communication channels used in the protocol. -+ */ -+#define SURFACE_SAM_SSH_NUM_CHANNELS 2 -+ -+ -+struct surface_sam_ssh_buf { -+ u8 cap; -+ u8 len; -+ u8 *data; -+}; -+ -+struct surface_sam_ssh_rqst { -+ u8 tc; // target category -+ u8 cid; // command ID -+ u8 iid; // instance ID -+ u8 chn; // channel -+ u8 snc; // expect response flag (bool: 0/1) -+ u16 cdl; // command data length (length of payload) -+ u8 *pld; // pointer to payload of length cdl -+}; -+ -+// TODO: remove rqid on external api -+struct surface_sam_ssh_event { -+ u16 rqid; // event type/source ID -+ u8 tc; // target category -+ u8 cid; // command ID -+ u8 iid; // instance ID -+ u8 chn; // channel -+ u8 len; // length of payload -+ u8 *pld; // payload of length len -+}; -+ -+ -+int surface_sam_ssh_consumer_register(struct device *consumer); -+ -+int surface_sam_ssh_notifier_register(struct ssam_event_notifier *n); -+int surface_sam_ssh_notifier_unregister(struct ssam_event_notifier *n); -+ -+int surface_sam_ssh_rqst(const struct surface_sam_ssh_rqst *rqst, struct surface_sam_ssh_buf *result); ++int ssam_notifier_unregister(struct ssam_controller *ctrl, ++ struct ssam_event_notifier *n); + +#endif /* _SURFACE_SAM_SSH_H */ diff --git a/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h new file mode 100644 -index 000000000000..801c60205128 +index 0000000000000..4755183fa423b --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_ssh_trace.h -@@ -0,0 +1,536 @@ +@@ -0,0 +1,532 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM surface_sam_ssh + @@ -11186,14 +11671,6 @@ index 000000000000..801c60205128 +TRACE_DEFINE_ENUM(SSH_FRAME_TYPE_ACK); +TRACE_DEFINE_ENUM(SSH_FRAME_TYPE_NAK); + -+TRACE_DEFINE_ENUM(SSH_PACKET_TY_FLUSH_BIT); -+TRACE_DEFINE_ENUM(SSH_PACKET_TY_SEQUENCED_BIT); -+TRACE_DEFINE_ENUM(SSH_PACKET_TY_BLOCKING_BIT); -+ -+TRACE_DEFINE_ENUM(SSH_PACKET_TY_FLUSH); -+TRACE_DEFINE_ENUM(SSH_PACKET_TY_SEQUENCED); -+TRACE_DEFINE_ENUM(SSH_PACKET_TY_BLOCKING); -+ +TRACE_DEFINE_ENUM(SSH_PACKET_SF_LOCKED_BIT); +TRACE_DEFINE_ENUM(SSH_PACKET_SF_QUEUED_BIT); +TRACE_DEFINE_ENUM(SSH_PACKET_SF_PENDING_BIT); @@ -11203,6 +11680,13 @@ index 000000000000..801c60205128 +TRACE_DEFINE_ENUM(SSH_PACKET_SF_CANCELED_BIT); +TRACE_DEFINE_ENUM(SSH_PACKET_SF_COMPLETED_BIT); + ++TRACE_DEFINE_ENUM(SSH_PACKET_TY_FLUSH_BIT); ++TRACE_DEFINE_ENUM(SSH_PACKET_TY_SEQUENCED_BIT); ++TRACE_DEFINE_ENUM(SSH_PACKET_TY_BLOCKING_BIT); ++ ++TRACE_DEFINE_ENUM(SSH_PACKET_FLAGS_SF_MASK); ++TRACE_DEFINE_ENUM(SSH_PACKET_FLAGS_TY_MASK); ++ +TRACE_DEFINE_ENUM(SSH_REQUEST_SF_LOCKED_BIT); +TRACE_DEFINE_ENUM(SSH_REQUEST_SF_QUEUED_BIT); +TRACE_DEFINE_ENUM(SSH_REQUEST_SF_PENDING_BIT); @@ -11211,6 +11695,7 @@ index 000000000000..801c60205128 +TRACE_DEFINE_ENUM(SSH_REQUEST_SF_RSPRCVD_BIT); +TRACE_DEFINE_ENUM(SSH_REQUEST_SF_CANCELED_BIT); +TRACE_DEFINE_ENUM(SSH_REQUEST_SF_COMPLETED_BIT); ++ +TRACE_DEFINE_ENUM(SSH_REQUEST_TY_FLUSH_BIT); +TRACE_DEFINE_ENUM(SSH_REQUEST_TY_HAS_RESPONSE_BIT); + @@ -11273,33 +11758,33 @@ index 000000000000..801c60205128 + +static inline u16 ssam_trace_get_packet_seq(const struct ssh_packet *p) +{ -+ if (!p->data || p->data_length < SSH_MESSAGE_LENGTH(0)) ++ if (!p->data.ptr || p->data.len < SSH_MESSAGE_LENGTH(0)) + return SSAM_SEQ_NOT_APPLICABLE; + -+ return p->data[SSH_MSGOFFSET_FRAME(seq)]; ++ return p->data.ptr[SSH_MSGOFFSET_FRAME(seq)]; +} + +static inline u32 ssam_trace_get_request_id(const struct ssh_packet *p) +{ -+ if (!p->data || p->data_length < SSH_COMMAND_MESSAGE_LENGTH(0)) ++ if (!p->data.ptr || p->data.len < SSH_COMMAND_MESSAGE_LENGTH(0)) + return SSAM_RQID_NOT_APPLICABLE; + -+ return get_unaligned_le16(&p->data[SSH_MSGOFFSET_COMMAND(rqid)]); ++ return get_unaligned_le16(&p->data.ptr[SSH_MSGOFFSET_COMMAND(rqid)]); +} + +static inline u32 ssam_trace_get_request_tc(const struct ssh_packet *p) +{ -+ if (!p->data || p->data_length < SSH_COMMAND_MESSAGE_LENGTH(0)) ++ if (!p->data.ptr || p->data.len < SSH_COMMAND_MESSAGE_LENGTH(0)) + return SSAM_SSH_TC_NOT_APPLICABLE; + -+ return get_unaligned_le16(&p->data[SSH_MSGOFFSET_COMMAND(tc)]); ++ return get_unaligned_le16(&p->data.ptr[SSH_MSGOFFSET_COMMAND(tc)]); +} + +#endif /* _SURFACE_SAM_SSH_TRACE_HELPERS */ + +#define ssam_trace_get_command_field_u8(packet, field) \ -+ ((!packet || packet->data_length < SSH_COMMAND_MESSAGE_LENGTH(0)) \ -+ ? 0 : p->data[SSH_MSGOFFSET_COMMAND(field)]) ++ ((!packet || packet->data.len < SSH_COMMAND_MESSAGE_LENGTH(0)) \ ++ ? 0 : p->data.ptr[SSH_MSGOFFSET_COMMAND(field)]) + +#define ssam_show_generic_u8_field(value) \ + __print_symbolic(value, \ @@ -11316,14 +11801,14 @@ index 000000000000..801c60205128 + ) + +#define ssam_show_packet_type(type) \ -+ __print_flags(type, "", \ -+ { SSH_PACKET_TY_FLUSH, "F" }, \ -+ { SSH_PACKET_TY_SEQUENCED, "S" }, \ -+ { SSH_PACKET_TY_BLOCKING, "B" } \ ++ __print_flags(flags & SSH_PACKET_FLAGS_TY_MASK, "", \ ++ { BIT(SSH_PACKET_TY_FLUSH_BIT), "F" }, \ ++ { BIT(SSH_PACKET_TY_SEQUENCED_BIT), "S" }, \ ++ { BIT(SSH_PACKET_TY_BLOCKING_BIT), "B" } \ + ) + +#define ssam_show_packet_state(state) \ -+ __print_flags(state, "", \ ++ __print_flags(flags & SSH_PACKET_FLAGS_SF_MASK, "", \ + { BIT(SSH_PACKET_SF_LOCKED_BIT), "L" }, \ + { BIT(SSH_PACKET_SF_QUEUED_BIT), "Q" }, \ + { BIT(SSH_PACKET_SF_PENDING_BIT), "P" }, \ @@ -11477,7 +11962,6 @@ index 000000000000..801c60205128 + + TP_STRUCT__entry( + __array(char, uid, SSAM_PTR_UID_LEN) -+ __field(u8, type) + __field(u8, priority) + __field(u16, length) + __field(unsigned long, state) @@ -11486,9 +11970,8 @@ index 000000000000..801c60205128 + + TP_fast_assign( + ssam_trace_ptr_uid(packet, __entry->uid); -+ __entry->type = packet->type; + __entry->priority = READ_ONCE(packet->priority); -+ __entry->length = packet->data_length; ++ __entry->length = packet->data.len; + __entry->state = READ_ONCE(packet->state); + __entry->seq = ssam_trace_get_packet_seq(packet); + ), @@ -11496,7 +11979,7 @@ index 000000000000..801c60205128 + TP_printk("uid=%s, seq=%s, ty=%s, pri=0x%02x, len=%u, sta=%s", + __entry->uid, + ssam_show_packet_seq(__entry->seq), -+ ssam_show_packet_type(__entry->type), ++ ssam_show_packet_type(__entry->state), + __entry->priority, + __entry->length, + ssam_show_packet_state(__entry->state) @@ -11517,7 +12000,6 @@ index 000000000000..801c60205128 + + TP_STRUCT__entry( + __array(char, uid, SSAM_PTR_UID_LEN) -+ __field(u8, type) + __field(u8, priority) + __field(u16, length) + __field(unsigned long, state) @@ -11527,9 +12009,8 @@ index 000000000000..801c60205128 + + TP_fast_assign( + ssam_trace_ptr_uid(packet, __entry->uid); -+ __entry->type = packet->type; + __entry->priority = READ_ONCE(packet->priority); -+ __entry->length = packet->data_length; ++ __entry->length = packet->data.len; + __entry->state = READ_ONCE(packet->state); + __entry->seq = ssam_trace_get_packet_seq(packet); + __entry->status = status; @@ -11538,7 +12019,7 @@ index 000000000000..801c60205128 + TP_printk("uid=%s, seq=%s, ty=%s, pri=0x%02x, len=%u, sta=%s, status=%d", + __entry->uid, + ssam_show_packet_seq(__entry->seq), -+ ssam_show_packet_type(__entry->type), ++ ssam_show_packet_type(__entry->state), + __entry->priority, + __entry->length, + ssam_show_packet_state(__entry->state), @@ -11708,10 +12189,10 @@ index 000000000000..801c60205128 +#include diff --git a/drivers/platform/x86/surface_sam/surface_sam_vhf.c b/drivers/platform/x86/surface_sam/surface_sam_vhf.c new file mode 100644 -index 000000000000..984035c55d63 +index 0000000000000..8455f952c2724 --- /dev/null +++ b/drivers/platform/x86/surface_sam/surface_sam_vhf.c -@@ -0,0 +1,261 @@ +@@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Virtual HID Framework (VHF) driver for input events via SAM. @@ -11735,8 +12216,11 @@ index 000000000000..984035c55d63 + +struct vhf_drvdata { + struct platform_device *dev; -+ struct hid_device *hid; ++ struct ssam_controller *ctrl; ++ + struct ssam_event_notifier notif; ++ ++ struct hid_device *hid; +}; + + @@ -11891,12 +12375,13 @@ index 000000000000..984035c55d63 + +static int surface_sam_vhf_probe(struct platform_device *pdev) +{ ++ struct ssam_controller *ctrl; + struct vhf_drvdata *drvdata; + struct hid_device *hid; + int status; + + // add device link to EC -+ status = surface_sam_ssh_consumer_register(&pdev->dev); ++ status = ssam_client_bind(&pdev->dev, &ctrl); + if (status) + return status == -ENXIO ? -EPROBE_DEFER : status; + @@ -11915,6 +12400,7 @@ index 000000000000..984035c55d63 + goto err_add_hid; + + drvdata->dev = pdev; ++ drvdata->ctrl = ctrl; + drvdata->hid = hid; + + drvdata->notif.base.priority = 1; @@ -11926,7 +12412,7 @@ index 000000000000..984035c55d63 + + platform_set_drvdata(pdev, drvdata); + -+ status = surface_sam_ssh_notifier_register(&drvdata->notif); ++ status = ssam_notifier_register(ctrl, &drvdata->notif); + if (status) + goto err_add_hid; + @@ -11944,7 +12430,7 @@ index 000000000000..984035c55d63 +{ + struct vhf_drvdata *drvdata = platform_get_drvdata(pdev); + -+ surface_sam_ssh_notifier_unregister(&drvdata->notif); ++ ssam_notifier_unregister(drvdata->ctrl, &drvdata->notif); + hid_destroy_device(drvdata->hid); + kfree(drvdata); + @@ -11974,7 +12460,7 @@ index 000000000000..984035c55d63 +MODULE_DESCRIPTION("Virtual HID Framework Driver for 5th Generation Surface Devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c -index a9719858c950..ce5309d00280 100644 +index a9719858c950b..ce5309d002805 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -552,16 +552,97 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl) @@ -12129,5 +12615,5 @@ index a9719858c950..ce5309d00280 100644 if (!ctrl->serdev) return -ENODEV; -- -2.27.0 +2.28.0 diff --git a/patches/5.4/0004-surface-sam-over-hid.patch b/patches/5.4/0004-surface-sam-over-hid.patch new file mode 100644 index 000000000..25508b2eb --- /dev/null +++ b/patches/5.4/0004-surface-sam-over-hid.patch @@ -0,0 +1,65 @@ +From 14bc7782c703218156fe5fd6b8df97a5f25f8d3e Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Sat, 25 Jul 2020 17:19:53 +0200 +Subject: [PATCH 4/6] surface-sam-over-hid + +--- + drivers/i2c/i2c-core-acpi.c | 35 +++++++++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + +diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c +index ce70b5288472c..5df647c4d9a51 100644 +--- a/drivers/i2c/i2c-core-acpi.c ++++ b/drivers/i2c/i2c-core-acpi.c +@@ -582,6 +582,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, + return (ret == 1) ? 0 : -EIO; + } + ++static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client, ++ u8 *data, u8 data_len) ++{ ++ struct i2c_msg msgs[1]; ++ int ret = AE_OK; ++ ++ msgs[0].addr = client->addr; ++ msgs[0].flags = client->flags; ++ msgs[0].len = data_len + 1; ++ msgs[0].buf = data; ++ ++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ ++ if (ret < 0) { ++ dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); ++ return ret; ++ } ++ ++ /* 1 transfer must have completed successfully */ ++ return (ret == 1) ? 0 : -EIO; ++} ++ + static acpi_status + i2c_acpi_space_handler(u32 function, acpi_physical_address command, + u32 bits, u64 *value64, +@@ -683,6 +705,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, + } + break; + ++ case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES: ++ if (action == ACPI_READ) { ++ dev_warn(&adapter->dev, ++ "protocol 0x%02x not supported for client 0x%02x\n", ++ accessor_type, client->addr); ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } else { ++ status = acpi_gsb_i2c_write_raw_bytes(client, ++ gsb->data, info->access_length); ++ } ++ break; ++ + default: + dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", + accessor_type, client->addr); +-- +2.28.0 + diff --git a/patches/5.4/0004-wifi.patch b/patches/5.4/0005-wifi.patch similarity index 97% rename from patches/5.4/0004-wifi.patch rename to patches/5.4/0005-wifi.patch index efab89bc3..f19fb97e9 100644 --- a/patches/5.4/0004-wifi.patch +++ b/patches/5.4/0005-wifi.patch @@ -1,7 +1,7 @@ -From 81f59a0eb1eb61ad4fca8352c414985c1f334115 Mon Sep 17 00:00:00 2001 +From 25533d6a5d98372ae1a7e04c7fb0fd35bd4d19f6 Mon Sep 17 00:00:00 2001 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> Date: Thu, 20 Feb 2020 16:51:11 +0900 -Subject: [PATCH 4/5] wifi +Subject: [PATCH 5/6] wifi --- .../net/wireless/marvell/mwifiex/cfg80211.c | 26 ++++++ @@ -10,7 +10,7 @@ Subject: [PATCH 4/5] wifi 3 files changed, 84 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c -index 9e6dc289ec3e..00b4bc446989 100644 +index 9e6dc289ec3e8..00b4bc4469892 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -25,6 +25,11 @@ @@ -54,7 +54,7 @@ index 9e6dc289ec3e..00b4bc446989 100644 } diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c -index fc1706d0647d..b51c5e357142 100644 +index fc1706d0647d7..b51c5e3571426 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -146,38 +146,45 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) @@ -188,7 +188,7 @@ index fc1706d0647d..b51c5e357142 100644 pdev->vendor, pdev->device, pdev->revision); diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c -index 4ed10cf82f9a..410bef3d6a6e 100644 +index 4ed10cf82f9a4..410bef3d6a6eb 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -2254,7 +2254,6 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, @@ -251,5 +251,5 @@ index 4ed10cf82f9a..410bef3d6a6e 100644 if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Send cmd to FW to enable/disable 11D function */ -- -2.27.0 +2.28.0 diff --git a/patches/5.4/0005-ipts.patch b/patches/5.4/0006-ipts.patch similarity index 97% rename from patches/5.4/0005-ipts.patch rename to patches/5.4/0006-ipts.patch index d2b43fff9..d9e6e419d 100644 --- a/patches/5.4/0005-ipts.patch +++ b/patches/5.4/0006-ipts.patch @@ -1,7 +1,7 @@ -From e7b66a10da721768f9472502bedd9583685e2e5e Mon Sep 17 00:00:00 2001 +From 2a57236358e1eaf2bdb4f3f6a247d1a8e25afd8b Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Mon, 27 Jan 2020 21:16:20 +0100 -Subject: [PATCH 5/5] ipts +Subject: [PATCH 6/6] ipts --- drivers/input/touchscreen/Kconfig | 2 + @@ -76,7 +76,7 @@ Subject: [PATCH 5/5] ipts create mode 100644 drivers/input/touchscreen/ipts/stylus.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig -index 46ad9090493b..c476a153b2f8 100644 +index 46ad9090493bb..c476a153b2f80 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1314,4 +1314,6 @@ config TOUCHSCREEN_IQS5XX @@ -87,7 +87,7 @@ index 46ad9090493b..c476a153b2f8 100644 + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile -index 94c6162409b3..864f0e092ab6 100644 +index 94c6162409b37..864f0e092ab67 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o @@ -100,7 +100,7 @@ index 94c6162409b3..864f0e092ab6 100644 obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o diff --git a/drivers/input/touchscreen/ipts/Kconfig b/drivers/input/touchscreen/ipts/Kconfig new file mode 100644 -index 000000000000..d3c530dafa94 +index 0000000000000..d3c530dafa948 --- /dev/null +++ b/drivers/input/touchscreen/ipts/Kconfig @@ -0,0 +1,16 @@ @@ -122,7 +122,7 @@ index 000000000000..d3c530dafa94 + module will be called ipts. diff --git a/drivers/input/touchscreen/ipts/Makefile b/drivers/input/touchscreen/ipts/Makefile new file mode 100644 -index 000000000000..0f7c904e7317 +index 0000000000000..0f7c904e73171 --- /dev/null +++ b/drivers/input/touchscreen/ipts/Makefile @@ -0,0 +1,17 @@ @@ -145,7 +145,7 @@ index 000000000000..0f7c904e7317 +ipts-objs += stylus.o diff --git a/drivers/input/touchscreen/ipts/context.h b/drivers/input/touchscreen/ipts/context.h new file mode 100644 -index 000000000000..ab26552579a5 +index 0000000000000..ab26552579a5c --- /dev/null +++ b/drivers/input/touchscreen/ipts/context.h @@ -0,0 +1,60 @@ @@ -211,7 +211,7 @@ index 000000000000..ab26552579a5 +#endif /* _IPTS_CONTEXT_H_ */ diff --git a/drivers/input/touchscreen/ipts/control.c b/drivers/input/touchscreen/ipts/control.c new file mode 100644 -index 000000000000..9179eca66558 +index 0000000000000..9179eca665585 --- /dev/null +++ b/drivers/input/touchscreen/ipts/control.c @@ -0,0 +1,94 @@ @@ -311,7 +311,7 @@ index 000000000000..9179eca66558 +} diff --git a/drivers/input/touchscreen/ipts/control.h b/drivers/input/touchscreen/ipts/control.h new file mode 100644 -index 000000000000..e57609c85d62 +index 0000000000000..e57609c85d62a --- /dev/null +++ b/drivers/input/touchscreen/ipts/control.h @@ -0,0 +1,18 @@ @@ -335,7 +335,7 @@ index 000000000000..e57609c85d62 +#endif /* _IPTS_CONTROL_H_ */ diff --git a/drivers/input/touchscreen/ipts/data.c b/drivers/input/touchscreen/ipts/data.c new file mode 100644 -index 000000000000..568bf04f7ea6 +index 0000000000000..568bf04f7ea6e --- /dev/null +++ b/drivers/input/touchscreen/ipts/data.c @@ -0,0 +1,107 @@ @@ -448,7 +448,7 @@ index 000000000000..568bf04f7ea6 +} diff --git a/drivers/input/touchscreen/ipts/data.h b/drivers/input/touchscreen/ipts/data.h new file mode 100644 -index 000000000000..fa72c1be0945 +index 0000000000000..fa72c1be09451 --- /dev/null +++ b/drivers/input/touchscreen/ipts/data.h @@ -0,0 +1,12 @@ @@ -466,7 +466,7 @@ index 000000000000..fa72c1be0945 +#endif /* _IPTS_DATA_H_ */ diff --git a/drivers/input/touchscreen/ipts/hid.c b/drivers/input/touchscreen/ipts/hid.c new file mode 100644 -index 000000000000..2642990b8c42 +index 0000000000000..2642990b8c420 --- /dev/null +++ b/drivers/input/touchscreen/ipts/hid.c @@ -0,0 +1,38 @@ @@ -510,7 +510,7 @@ index 000000000000..2642990b8c42 +} diff --git a/drivers/input/touchscreen/ipts/hid.h b/drivers/input/touchscreen/ipts/hid.h new file mode 100644 -index 000000000000..e6cf38fce454 +index 0000000000000..e6cf38fce4541 --- /dev/null +++ b/drivers/input/touchscreen/ipts/hid.h @@ -0,0 +1,13 @@ @@ -529,7 +529,7 @@ index 000000000000..e6cf38fce454 +#endif /* _IPTS_HID_H_ */ diff --git a/drivers/input/touchscreen/ipts/init.c b/drivers/input/touchscreen/ipts/init.c new file mode 100644 -index 000000000000..fb70d55542af +index 0000000000000..fb70d55542af7 --- /dev/null +++ b/drivers/input/touchscreen/ipts/init.c @@ -0,0 +1,93 @@ @@ -628,7 +628,7 @@ index 000000000000..fb70d55542af +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ipts/math.c b/drivers/input/touchscreen/ipts/math.c new file mode 100644 -index 000000000000..df956e5447e0 +index 0000000000000..df956e5447e03 --- /dev/null +++ b/drivers/input/touchscreen/ipts/math.c @@ -0,0 +1,103 @@ @@ -737,7 +737,7 @@ index 000000000000..df956e5447e0 +} diff --git a/drivers/input/touchscreen/ipts/math.h b/drivers/input/touchscreen/ipts/math.h new file mode 100644 -index 000000000000..8e831074ab60 +index 0000000000000..8e831074ab60b --- /dev/null +++ b/drivers/input/touchscreen/ipts/math.h @@ -0,0 +1,21 @@ @@ -764,7 +764,7 @@ index 000000000000..8e831074ab60 +#endif /* _IPTS_MATH_H_ */ diff --git a/drivers/input/touchscreen/ipts/params.c b/drivers/input/touchscreen/ipts/params.c new file mode 100644 -index 000000000000..6aa3f5cf1d76 +index 0000000000000..6aa3f5cf1d762 --- /dev/null +++ b/drivers/input/touchscreen/ipts/params.c @@ -0,0 +1,27 @@ @@ -797,7 +797,7 @@ index 000000000000..6aa3f5cf1d76 +); diff --git a/drivers/input/touchscreen/ipts/params.h b/drivers/input/touchscreen/ipts/params.h new file mode 100644 -index 000000000000..1f992a3bc21b +index 0000000000000..1f992a3bc21b9 --- /dev/null +++ b/drivers/input/touchscreen/ipts/params.h @@ -0,0 +1,15 @@ @@ -818,7 +818,7 @@ index 000000000000..1f992a3bc21b +#endif /* _IPTS_PARAMS_H_ */ diff --git a/drivers/input/touchscreen/ipts/payload.c b/drivers/input/touchscreen/ipts/payload.c new file mode 100644 -index 000000000000..3572ddc0f2fb +index 0000000000000..3572ddc0f2fb0 --- /dev/null +++ b/drivers/input/touchscreen/ipts/payload.c @@ -0,0 +1,52 @@ @@ -876,7 +876,7 @@ index 000000000000..3572ddc0f2fb +} diff --git a/drivers/input/touchscreen/ipts/payload.h b/drivers/input/touchscreen/ipts/payload.h new file mode 100644 -index 000000000000..6603714bb6fd +index 0000000000000..6603714bb6fd0 --- /dev/null +++ b/drivers/input/touchscreen/ipts/payload.h @@ -0,0 +1,14 @@ @@ -896,7 +896,7 @@ index 000000000000..6603714bb6fd +#endif /* _IPTS_PAYLOAD_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/commands.h b/drivers/input/touchscreen/ipts/protocol/commands.h new file mode 100644 -index 000000000000..2533dfb13584 +index 0000000000000..2533dfb13584a --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/commands.h @@ -0,0 +1,61 @@ @@ -963,7 +963,7 @@ index 000000000000..2533dfb13584 +#endif /* _IPTS_PROTOCOL_COMMANDS_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/data.h b/drivers/input/touchscreen/ipts/protocol/data.h new file mode 100644 -index 000000000000..148e0545b2e4 +index 0000000000000..148e0545b2e4e --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/data.h @@ -0,0 +1,30 @@ @@ -999,7 +999,7 @@ index 000000000000..148e0545b2e4 +#endif /* _IPTS_PROTOCOL_DATA_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/events.h b/drivers/input/touchscreen/ipts/protocol/events.h new file mode 100644 -index 000000000000..f8b771f90bd2 +index 0000000000000..f8b771f90bd2b --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/events.h @@ -0,0 +1,29 @@ @@ -1034,7 +1034,7 @@ index 000000000000..f8b771f90bd2 +#endif /* _IPTS_PROTOCOL_EVENTS_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/feedback.h b/drivers/input/touchscreen/ipts/protocol/feedback.h new file mode 100644 -index 000000000000..8b3d8b689ee8 +index 0000000000000..8b3d8b689ee83 --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/feedback.h @@ -0,0 +1,30 @@ @@ -1070,7 +1070,7 @@ index 000000000000..8b3d8b689ee8 +#endif /* _IPTS_PROTOCOL_FEEDBACK_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/payload.h b/drivers/input/touchscreen/ipts/protocol/payload.h new file mode 100644 -index 000000000000..f46da4ea81f2 +index 0000000000000..f46da4ea81f25 --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/payload.h @@ -0,0 +1,47 @@ @@ -1123,7 +1123,7 @@ index 000000000000..f46da4ea81f2 +#endif /* _IPTS_PROTOCOL_PAYLOAD_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/responses.h b/drivers/input/touchscreen/ipts/protocol/responses.h new file mode 100644 -index 000000000000..27153d82a5d6 +index 0000000000000..27153d82a5d67 --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/responses.h @@ -0,0 +1,62 @@ @@ -1191,7 +1191,7 @@ index 000000000000..27153d82a5d6 +#endif /* _IPTS_PROTOCOL_RESPONSES_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/singletouch.h b/drivers/input/touchscreen/ipts/protocol/singletouch.h new file mode 100644 -index 000000000000..bf9912ee2af4 +index 0000000000000..bf9912ee2af4c --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/singletouch.h @@ -0,0 +1,17 @@ @@ -1214,7 +1214,7 @@ index 000000000000..bf9912ee2af4 +#endif /* _IPTS_PROTOCOL_SINGLETOUCH_H_ */ diff --git a/drivers/input/touchscreen/ipts/protocol/stylus.h b/drivers/input/touchscreen/ipts/protocol/stylus.h new file mode 100644 -index 000000000000..950850b365df +index 0000000000000..950850b365dfb --- /dev/null +++ b/drivers/input/touchscreen/ipts/protocol/stylus.h @@ -0,0 +1,52 @@ @@ -1272,7 +1272,7 @@ index 000000000000..950850b365df +#endif /* _IPTS_PAYLOAD_STYLUS_H_ */ diff --git a/drivers/input/touchscreen/ipts/receiver.c b/drivers/input/touchscreen/ipts/receiver.c new file mode 100644 -index 000000000000..ab283994c3e5 +index 0000000000000..ab283994c3e5f --- /dev/null +++ b/drivers/input/touchscreen/ipts/receiver.c @@ -0,0 +1,265 @@ @@ -1543,7 +1543,7 @@ index 000000000000..ab283994c3e5 +} diff --git a/drivers/input/touchscreen/ipts/receiver.h b/drivers/input/touchscreen/ipts/receiver.h new file mode 100644 -index 000000000000..4d413a0abd4c +index 0000000000000..4d413a0abd4c5 --- /dev/null +++ b/drivers/input/touchscreen/ipts/receiver.h @@ -0,0 +1,8 @@ @@ -1557,7 +1557,7 @@ index 000000000000..4d413a0abd4c +#endif /* _IPTS_RECEIVER_H_ */ diff --git a/drivers/input/touchscreen/ipts/resources.c b/drivers/input/touchscreen/ipts/resources.c new file mode 100644 -index 000000000000..704db9fdd3fd +index 0000000000000..704db9fdd3fd4 --- /dev/null +++ b/drivers/input/touchscreen/ipts/resources.c @@ -0,0 +1,131 @@ @@ -1694,7 +1694,7 @@ index 000000000000..704db9fdd3fd +} diff --git a/drivers/input/touchscreen/ipts/resources.h b/drivers/input/touchscreen/ipts/resources.h new file mode 100644 -index 000000000000..cf9807b0dbe6 +index 0000000000000..cf9807b0dbe62 --- /dev/null +++ b/drivers/input/touchscreen/ipts/resources.h @@ -0,0 +1,11 @@ @@ -1711,7 +1711,7 @@ index 000000000000..cf9807b0dbe6 +#endif /* _IPTS_RESOURCES_H_ */ diff --git a/drivers/input/touchscreen/ipts/singletouch.c b/drivers/input/touchscreen/ipts/singletouch.c new file mode 100644 -index 000000000000..ed70444f649c +index 0000000000000..ed70444f649c4 --- /dev/null +++ b/drivers/input/touchscreen/ipts/singletouch.c @@ -0,0 +1,64 @@ @@ -1781,7 +1781,7 @@ index 000000000000..ed70444f649c +} diff --git a/drivers/input/touchscreen/ipts/singletouch.h b/drivers/input/touchscreen/ipts/singletouch.h new file mode 100644 -index 000000000000..53207497a462 +index 0000000000000..53207497a4628 --- /dev/null +++ b/drivers/input/touchscreen/ipts/singletouch.h @@ -0,0 +1,14 @@ @@ -1801,7 +1801,7 @@ index 000000000000..53207497a462 +#endif /* _IPTS_SINGLETOUCH_H_ */ diff --git a/drivers/input/touchscreen/ipts/stylus.c b/drivers/input/touchscreen/ipts/stylus.c new file mode 100644 -index 000000000000..987fa756fec3 +index 0000000000000..987fa756fec33 --- /dev/null +++ b/drivers/input/touchscreen/ipts/stylus.c @@ -0,0 +1,179 @@ @@ -1986,7 +1986,7 @@ index 000000000000..987fa756fec3 +} diff --git a/drivers/input/touchscreen/ipts/stylus.h b/drivers/input/touchscreen/ipts/stylus.h new file mode 100644 -index 000000000000..5b93add1eac2 +index 0000000000000..5b93add1eac2d --- /dev/null +++ b/drivers/input/touchscreen/ipts/stylus.h @@ -0,0 +1,14 @@ @@ -2005,7 +2005,7 @@ index 000000000000..5b93add1eac2 + +#endif /* _IPTS_STYLUS_H_ */ diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h -index e56dc4754064..a55c61c89238 100644 +index e56dc47540646..a55c61c89238a 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -59,6 +59,7 @@ @@ -2025,7 +2025,7 @@ index e56dc4754064..a55c61c89238 100644 #define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c -index 75ab2ffbf235..78790904d77c 100644 +index 75ab2ffbf235f..78790904d77cb 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -77,6 +77,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { @@ -2045,7 +2045,7 @@ index 75ab2ffbf235..78790904d77c 100644 {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH12_CFG)}, diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h -index 9a61c28ed3ae..47fc20975245 100644 +index 9a61c28ed3ae4..47fc20975245d 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -271,6 +271,7 @@ struct input_mask { @@ -2057,5 +2057,5 @@ index 9a61c28ed3ae..47fc20975245 100644 /* * MT_TOOL types -- -2.27.0 +2.28.0