From b6bee9963f9ac83156e2a2a24553c45782f3a07b Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 23 Aug 2020 22:20:35 +0200 Subject: [PATCH] Update v4.19 patches Changes: SAM: - Fix false wanring when plugging in AC adapter. - Fix includes for error injection. - Other bug fixes and code improvements. Links: - kernel: https://github.com/linux-surface/kernel/commit/ce875e1f82b1f5a3d7b77b8c79b96e4edd212587 - SAM: https://github.com/linux-surface/surface-aggregator-module/commit/4ca8b315e038965a56bcd899deda09ce90d13fec --- patches/4.19/0001-surface3-power.patch | 2 +- .../0002-surface3-touchscreen-dma-fix.patch | 2 +- patches/4.19/0003-surface3-oemb.patch | 2 +- patches/4.19/0004-surface-buttons.patch | 2 +- patches/4.19/0005-suspend.patch | 10 +- patches/4.19/0006-ipts.patch | 2 +- patches/4.19/0007-wifi.patch | 2 +- patches/4.19/0008-surface-sam.patch | 1823 +++++++++++++---- patches/4.19/0009-surface-sam-over-hid.patch | 2 +- patches/4.19/0010-surface-gpe.patch | 2 +- pkg/arch/kernel-lts/PKGBUILD | 20 +- 11 files changed, 1427 insertions(+), 442 deletions(-) diff --git a/patches/4.19/0001-surface3-power.patch b/patches/4.19/0001-surface3-power.patch index 083318b76..52ea28e7b 100644 --- a/patches/4.19/0001-surface3-power.patch +++ b/patches/4.19/0001-surface3-power.patch @@ -1,4 +1,4 @@ -From 76c7ae03b16a0e7d36eac430ec7215c36f79c9c9 Mon Sep 17 00:00:00 2001 +From be6f99a501266c1d3c7f4b85cb1520b14ba08325 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 18:00:43 +0200 Subject: [PATCH 01/10] surface3-power diff --git a/patches/4.19/0002-surface3-touchscreen-dma-fix.patch b/patches/4.19/0002-surface3-touchscreen-dma-fix.patch index c7a42873a..1210c364c 100644 --- a/patches/4.19/0002-surface3-touchscreen-dma-fix.patch +++ b/patches/4.19/0002-surface3-touchscreen-dma-fix.patch @@ -1,4 +1,4 @@ -From c6e0ef58982bb6be1928ba874e031ac433d9cdcc Mon Sep 17 00:00:00 2001 +From 3f5f3beb4919dbf3d581e00ddd4b9f507deabca9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 5 Jul 2020 14:56:20 +0300 Subject: [PATCH 02/10] surface3-touchscreen-dma-fix diff --git a/patches/4.19/0003-surface3-oemb.patch b/patches/4.19/0003-surface3-oemb.patch index 6aa0e249a..329bd5901 100644 --- a/patches/4.19/0003-surface3-oemb.patch +++ b/patches/4.19/0003-surface3-oemb.patch @@ -1,4 +1,4 @@ -From afbf6d8351de940f35369169ab9a8380f764428a Mon Sep 17 00:00:00 2001 +From 5f57ddb9f57f073b5864d4fb3b9df2ac040502cb Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Tue, 18 Sep 2018 11:01:37 +0800 Subject: [PATCH 03/10] surface3-oemb diff --git a/patches/4.19/0004-surface-buttons.patch b/patches/4.19/0004-surface-buttons.patch index 946c22b18..9603233c2 100644 --- a/patches/4.19/0004-surface-buttons.patch +++ b/patches/4.19/0004-surface-buttons.patch @@ -1,4 +1,4 @@ -From 2fc20021f9dcaaefbb8cf62c6e08297dbb881a5d Mon Sep 17 00:00:00 2001 +From e40608c4efca809574bf4deeb4d153ab3ee4688f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Jul 2019 17:51:37 +0200 Subject: [PATCH 04/10] surface-buttons diff --git a/patches/4.19/0005-suspend.patch b/patches/4.19/0005-suspend.patch index 0deb6cc4b..e764240f9 100644 --- a/patches/4.19/0005-suspend.patch +++ b/patches/4.19/0005-suspend.patch @@ -1,4 +1,4 @@ -From 4b6fec80690909e7a8ab733ebd56224510865677 Mon Sep 17 00:00:00 2001 +From 5f3edadc09a3ef67d394cbc5373f06229c993834 Mon Sep 17 00:00:00 2001 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> Date: Sat, 28 Sep 2019 17:48:21 +0200 Subject: [PATCH 05/10] suspend @@ -266,10 +266,10 @@ index 3c68a5b35ec1b..0cc7bea4eb707 100644 .err_handler = &nvme_err_handler, }; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c -index 6e50f84733b75..b03884b6bc6f8 100644 +index 279f9f0197b01..cb474338f39de 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c -@@ -1170,6 +1170,26 @@ static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) +@@ -1171,6 +1171,26 @@ static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) module_param_call(policy, pcie_aspm_set_policy, pcie_aspm_get_policy, NULL, 0644); @@ -297,10 +297,10 @@ index 6e50f84733b75..b03884b6bc6f8 100644 static ssize_t link_state_show(struct device *dev, struct device_attribute *attr, diff --git a/include/linux/pci.h b/include/linux/pci.h -index b1f297f4b7b0b..94ab2fc800d30 100644 +index 2517492dd1855..d96343cd5c998 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h -@@ -1486,8 +1486,10 @@ extern bool pcie_ports_native; +@@ -1489,8 +1489,10 @@ extern bool pcie_ports_native; #ifdef CONFIG_PCIEASPM bool pcie_aspm_support_enabled(void); diff --git a/patches/4.19/0006-ipts.patch b/patches/4.19/0006-ipts.patch index ddf3c5957..4c9659256 100644 --- a/patches/4.19/0006-ipts.patch +++ b/patches/4.19/0006-ipts.patch @@ -1,4 +1,4 @@ -From 4874eb199e3a0f1421fe34e1dbb75fc5030b6c34 Mon Sep 17 00:00:00 2001 +From fa535cf2153e2c817ca334f3b8a035a73ab25787 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 17:58:17 +0200 Subject: [PATCH 06/10] ipts diff --git a/patches/4.19/0007-wifi.patch b/patches/4.19/0007-wifi.patch index 84f035f73..7719a449a 100644 --- a/patches/4.19/0007-wifi.patch +++ b/patches/4.19/0007-wifi.patch @@ -1,4 +1,4 @@ -From 68e5be59b042a57c6b50d980817298078e14f573 Mon Sep 17 00:00:00 2001 +From 586dd640656e93b154045fd351d311858988b8bb 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 07/10] wifi diff --git a/patches/4.19/0008-surface-sam.patch b/patches/4.19/0008-surface-sam.patch index 5e596f02f..4b9dc8757 100644 --- a/patches/4.19/0008-surface-sam.patch +++ b/patches/4.19/0008-surface-sam.patch @@ -1,4 +1,4 @@ -From a4b7ecf94d88bff17296438e13ddf67b4f54dc93 Mon Sep 17 00:00:00 2001 +From 508c821af34eda7641947f8867c19cdda7c5df22 Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:15:40 +0200 Subject: [PATCH 08/10] surface-sam @@ -10,38 +10,38 @@ Subject: [PATCH 08/10] surface-sam drivers/misc/Makefile | 1 + drivers/misc/surface_sam/Kconfig | 46 + drivers/misc/surface_sam/Makefile | 14 + - drivers/misc/surface_sam/bus.c | 276 +++ + drivers/misc/surface_sam/bus.c | 394 +++ drivers/misc/surface_sam/bus.h | 14 + - drivers/misc/surface_sam/clients/Kconfig | 121 ++ + drivers/misc/surface_sam/clients/Kconfig | 121 + drivers/misc/surface_sam/clients/Makefile | 11 + .../surface_sam/clients/surface_sam_debugfs.c | 270 +++ - .../clients/surface_sam_device_hub.c | 582 ++++++ - .../surface_sam/clients/surface_sam_dtx.c | 582 ++++++ - .../surface_sam/clients/surface_sam_hps.c | 1287 ++++++++++++ - .../surface_sam/clients/surface_sam_san.c | 930 +++++++++ + .../clients/surface_sam_device_hub.c | 582 +++++ + .../surface_sam/clients/surface_sam_dtx.c | 582 +++++ + .../surface_sam/clients/surface_sam_hps.c | 1287 ++++++++++ + .../surface_sam/clients/surface_sam_san.c | 930 +++++++ .../surface_sam/clients/surface_sam_san.h | 30 + .../clients/surface_sam_sid_perfmode.c | 194 ++ - .../clients/surface_sam_sid_power.c | 1112 +++++++++++ + .../clients/surface_sam_sid_power.c | 1112 +++++++++ .../surface_sam/clients/surface_sam_sid_vhf.c | 430 ++++ .../surface_sam/clients/surface_sam_vhf.c | 266 +++ - drivers/misc/surface_sam/controller.c | 1475 ++++++++++++++ - drivers/misc/surface_sam/controller.h | 170 ++ - drivers/misc/surface_sam/core.c | 554 ++++++ - drivers/misc/surface_sam/ssam_trace.h | 588 ++++++ - drivers/misc/surface_sam/ssh_msgb.h | 132 ++ - drivers/misc/surface_sam/ssh_packet_layer.c | 1753 +++++++++++++++++ - drivers/misc/surface_sam/ssh_packet_layer.h | 125 ++ - drivers/misc/surface_sam/ssh_parser.c | 132 ++ + drivers/misc/surface_sam/controller.c | 2126 +++++++++++++++++ + drivers/misc/surface_sam/controller.h | 266 +++ + drivers/misc/surface_sam/core.c | 557 +++++ + drivers/misc/surface_sam/ssam_trace.h | 588 +++++ + drivers/misc/surface_sam/ssh_msgb.h | 132 + + drivers/misc/surface_sam/ssh_packet_layer.c | 1780 ++++++++++++++ + drivers/misc/surface_sam/ssh_packet_layer.h | 125 + + drivers/misc/surface_sam/ssh_parser.c | 132 + drivers/misc/surface_sam/ssh_parser.h | 83 + drivers/misc/surface_sam/ssh_protocol.h | 65 + - drivers/misc/surface_sam/ssh_request_layer.c | 1074 ++++++++++ - drivers/misc/surface_sam/ssh_request_layer.h | 91 + + drivers/misc/surface_sam/ssh_request_layer.c | 1099 +++++++++ + drivers/misc/surface_sam/ssh_request_layer.h | 93 + drivers/tty/serdev/core.c | 111 +- include/linux/mod_devicetable.h | 17 + - include/linux/surface_aggregator_module.h | 892 +++++++++ + include/linux/surface_aggregator_module.h | 954 ++++++++ scripts/mod/devicetable-offsets.c | 7 + - scripts/mod/file2alias.c | 21 + - 37 files changed, 13456 insertions(+), 29 deletions(-) + scripts/mod/file2alias.c | 22 + + 37 files changed, 14441 insertions(+), 29 deletions(-) create mode 100644 drivers/misc/surface_sam/Kconfig create mode 100644 drivers/misc/surface_sam/Makefile create mode 100644 drivers/misc/surface_sam/bus.c @@ -231,10 +231,10 @@ index 0000000000000..0a07dd2297874 +surface_sam_ssh-objs += bus.o diff --git a/drivers/misc/surface_sam/bus.c b/drivers/misc/surface_sam/bus.c new file mode 100644 -index 0000000000000..e1c4c8acc762d +index 0000000000000..4080b984c05e8 --- /dev/null +++ b/drivers/misc/surface_sam/bus.c -@@ -0,0 +1,276 @@ +@@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -250,7 +250,7 @@ index 0000000000000..e1c4c8acc762d + struct ssam_device *sdev = to_ssam_device(dev); + + return snprintf(buf, PAGE_SIZE - 1, "ssam:c%02Xt%02Xi%02xf%02X\n", -+ sdev->uid.category, sdev->uid.channel, ++ sdev->uid.category, sdev->uid.target, + sdev->uid.instance, sdev->uid.function); +} +static DEVICE_ATTR_RO(modalias); @@ -266,7 +266,7 @@ index 0000000000000..e1c4c8acc762d + struct ssam_device *sdev = to_ssam_device(dev); + + return add_uevent_var(env, "MODALIAS=ssam:c%02Xt%02Xi%02xf%02X", -+ sdev->uid.category, sdev->uid.channel, ++ sdev->uid.category, sdev->uid.target, + sdev->uid.instance, sdev->uid.function); +} + @@ -287,6 +287,14 @@ index 0000000000000..e1c4c8acc762d +EXPORT_SYMBOL_GPL(ssam_device_type); + + ++/** ++ * ssam_device_alloc - Allocate and initialize a SSAM client device. ++ * @ctrl: The controller under which the device should be added. ++ * @uid: The UID of the device to be added. ++ * ++ * This function only creates a new client device. It still has to be added ++ * via ssam_device_add(). Refer to that function for more details. ++ */ +struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, + struct ssam_device_uid uid) +{ @@ -304,16 +312,38 @@ index 0000000000000..e1c4c8acc762d + sdev->uid = uid; + + dev_set_name(&sdev->dev, "%02x:%02x:%02x:%02x", -+ sdev->uid.category, sdev->uid.channel, sdev->uid.instance, ++ sdev->uid.category, sdev->uid.target, sdev->uid.instance, + sdev->uid.function); + + return sdev; +} +EXPORT_SYMBOL_GPL(ssam_device_alloc); + ++/** ++ * ssam_device_add - Add a SSAM client device. ++ * @sdev: The SSAM client device to be added. ++ * ++ * Added client devices must be guaranteed to always have a valid and active ++ * controller. Thus, this function will fail with ``-ENXIO`` if the controller ++ * of the device has not been initialized yet, has been suspended, or has been ++ * shut down. ++ * ++ * The caller of this function should ensure that the corresponding call to ++ * ssam_device_remove is issued before the controller is shut down. If the ++ * added device is a direct child of the controller device (default), it will ++ * be automatically removed when the controller is shut down. ++ * ++ * By default, the controller device will become the parent of the newly ++ * created client device. The parent may be changed before ssam_device_add is ++ * called, but care must be taken that a) the correct suspend/resume ordering ++ * is guaranteed and b) the client device does not oultive the controller, ++ * i.e. that the device is removed before the controller is being shut down. ++ * In case these guarantees have to be manually enforced, please refer to the ++ * ssam_client_link() and ssam_client_bind() functions, which are intended to ++ * set up device-links for this purpose. ++ */ +int ssam_device_add(struct ssam_device *sdev) +{ -+ enum ssam_controller_state state; + int status; + + /* @@ -335,8 +365,7 @@ index 0000000000000..e1c4c8acc762d + */ + ssam_controller_statelock(sdev->ctrl); + -+ state = smp_load_acquire(&sdev->ctrl->state); -+ if (state != SSAM_CONTROLLER_STARTED) { ++ if (READ_ONCE(sdev->ctrl->state) != SSAM_CONTROLLER_STARTED) { + ssam_controller_stateunlock(sdev->ctrl); + return -ENXIO; + } @@ -348,6 +377,12 @@ index 0000000000000..e1c4c8acc762d +} +EXPORT_SYMBOL_GPL(ssam_device_add); + ++/** ++ * ssam_device_remove - Remove a SSAM client device. ++ * @sdev: The device to remove. ++ * ++ * Removes and unregisters the provided SSAM client device. ++ */ +void ssam_device_remove(struct ssam_device *sdev) +{ + device_unregister(&sdev->dev); @@ -355,13 +390,22 @@ index 0000000000000..e1c4c8acc762d +EXPORT_SYMBOL_GPL(ssam_device_remove); + + ++/** ++ * ssam_device_id_compatible - Check if a device ID matches a UID. ++ * @id: The device ID as potential match. ++ * @uid: The device UID matching against. ++ * ++ * Check if the given ID is a match for the given UID, i.e. if a device with ++ * the provided UID is compatible to the given ID following the match rules ++ * described in its &ssam_device_id.match_flags member. ++ */ +static inline bool ssam_device_id_compatible(const struct ssam_device_id *id, + struct ssam_device_uid uid) +{ + if (id->category != uid.category) + return false; + -+ if ((id->match_flags & SSAM_MATCH_CHANNEL) && id->channel != uid.channel) ++ if ((id->match_flags & SSAM_MATCH_TARGET) && id->target != uid.target) + return false; + + if ((id->match_flags & SSAM_MATCH_INSTANCE) && id->instance != uid.instance) @@ -373,14 +417,31 @@ index 0000000000000..e1c4c8acc762d + return true; +} + ++/** ++ * ssam_device_id_is_null - Check if a device ID is null. ++ * @id: The device ID to check. ++ * ++ * Check if a given device ID is null, i.e. all zeros. Used to check for the ++ * end of ``MODULE_DEVICE_TABLE(ssam, ...)`` or similar lists. ++ */ +static inline bool ssam_device_id_is_null(const struct ssam_device_id *id) +{ -+ return id->category == 0 -+ && id->channel == 0 ++ return id->match_flags == 0 ++ && id->category == 0 ++ && id->target == 0 + && id->instance == 0 -+ && id->function == 0; ++ && id->function == 0 ++ && id->driver_data == 0; +} + ++/** ++ * ssam_device_id_match - Find the matching ID table entry for the given UID. ++ * @table: The table to search in. ++ * @uid: The UID to matched against the individual table entries. ++ * ++ * Find the first match for the provided device UID in the provided ID table ++ * and return it. Returns NULL if no match could be found. ++ */ +const struct ssam_device_id *ssam_device_id_match( + const struct ssam_device_id *table, + const struct ssam_device_uid uid) @@ -395,6 +456,19 @@ index 0000000000000..e1c4c8acc762d +} +EXPORT_SYMBOL_GPL(ssam_device_id_match); + ++/** ++ * ssam_device_get_match - Find and return the ID matching the device in the ++ * ID table of the bound driver. ++ * @dev: The device for which to get the matching ID table entry. ++ * ++ * Find the fist match for the UID of the device in the ID table of the ++ * currently bound driver and return it. Returns NULL if the device does not ++ * have a driver bound to it, the driver does not have match_table (i.e. it is ++ * NULL), or there is no match in the driver's match_table. ++ * ++ * This function essentially calls ssam_device_id_match() with the ID table of ++ * the bound device driver and the UID of the device. ++ */ +const struct ssam_device_id *ssam_device_get_match( + const struct ssam_device *dev) +{ @@ -411,6 +485,20 @@ index 0000000000000..e1c4c8acc762d +} +EXPORT_SYMBOL_GPL(ssam_device_get_match); + ++/** ++ * ssam_device_get_match_data - Find the ID matching the device in hte ++ * ID table of the bound driver and return its ``driver_data`` member. ++ * @dev: The device for which to get the match data. ++ * ++ * Find the fist match for the UID of the device in the ID table of the ++ * corresponding driver and return its driver_data. Returns NULL if the device ++ * does not have a driver bound to it, the driver does not have match_table ++ * (i.e. it is NULL), there is no match in the driver's match_table, or the ++ * match does not have any driver_data. ++ * ++ * This function essentially calls ssam_device_get_match() and, if any match ++ * could be found, returns its &ssam_device_id.driver_data member. ++ */ +const void *ssam_device_get_match_data(const struct ssam_device *dev) +{ + const struct ssam_device_id *id; @@ -461,6 +549,14 @@ index 0000000000000..e1c4c8acc762d +EXPORT_SYMBOL_GPL(ssam_bus_type); + + ++/** ++ * __ssam_device_driver_register - Register a SSAM device driver. ++ * @sdrv: The driver to register. ++ * @owner: The module owning the provided driver. ++ * ++ * Please refer to the ssam_device_driver_register() macro for the normal way ++ * to register a driver from inside its owning module. ++ */ +int __ssam_device_driver_register(struct ssam_device_driver *sdrv, + struct module *owner) +{ @@ -468,12 +564,16 @@ index 0000000000000..e1c4c8acc762d + sdrv->driver.bus = &ssam_bus_type; + + /* force drivers to async probe so I/O is possible in probe */ -+ sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; ++ sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; + + return driver_register(&sdrv->driver); +} +EXPORT_SYMBOL_GPL(__ssam_device_driver_register); + ++/** ++ * ssam_device_driver_unregister - Unregister a SSAM device driver. ++ * @sdrv: The driver to unregister. ++ */ +void ssam_device_driver_unregister(struct ssam_device_driver *sdrv) +{ + driver_unregister(&sdrv->driver); @@ -491,9 +591,20 @@ index 0000000000000..e1c4c8acc762d + return 0; +} + -+/* -+ * Controller lock should be held during this call and subsequent -+ * de-initialization. ++/** ++ * ssam_controller_remove_clients - Remove SSAM client devices registered as ++ * direct children under the given controller. ++ * @ctrl: The controller to remove all direct clients for. ++ * ++ * Remove all SSAM client devices registered as direct children under the ++ * given controller. Note that this only accounts for direct children ot the ++ * controller device. This does not take care of any client devices where the ++ * parent device has been manually set before calling ssam_device_add. Refer ++ * to ssam_device_add()/ssam_device_remove() for more details on those cases. ++ * ++ * To avoid new devices being added in parallel to this call, the main ++ * controller lock (not statelock) must be held during this (and if ++ * necessary, any subsequent de-initialization) call. + */ +void ssam_controller_remove_clients(struct ssam_controller *ctrl) +{ @@ -503,12 +614,19 @@ index 0000000000000..e1c4c8acc762d +} + + ++/** ++ * ssam_bus_register - Register and set-up the SSAM client device bus. ++ */ +int ssam_bus_register(void) +{ + return bus_register(&ssam_bus_type); +} + -+void ssam_bus_unregister(void) { ++/** ++ * ssam_bus_unregister - Unregister the SSAM client device bus. ++ */ ++void ssam_bus_unregister(void) ++{ + return bus_unregister(&ssam_bus_type); +} diff --git a/drivers/misc/surface_sam/bus.h b/drivers/misc/surface_sam/bus.h @@ -677,7 +795,7 @@ index 0000000000000..1db9db2f86252 +obj-$(CONFIG_SURFACE_SAM_DEVICE_HUB) += surface_sam_device_hub.o diff --git a/drivers/misc/surface_sam/clients/surface_sam_debugfs.c b/drivers/misc/surface_sam/clients/surface_sam_debugfs.c new file mode 100644 -index 0000000000000..cc6c31de73e8b +index 0000000000000..15fd8d8d34898 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_debugfs.c @@ -0,0 +1,270 @@ @@ -699,9 +817,9 @@ index 0000000000000..cc6c31de73e8b + +struct ssam_dbgdev_request { + __u8 target_category; ++ __u8 target_id; + __u8 command_id; + __u8 instance_id; -+ __u8 channel; + __u16 flags; + __s16 status; + @@ -753,9 +871,9 @@ index 0000000000000..cc6c31de73e8b + + // setup basic request fields + spec.target_category = rqst.target_category; ++ spec.target_id = rqst.target_id; + 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; + @@ -953,7 +1071,7 @@ index 0000000000000..cc6c31de73e8b +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_device_hub.c b/drivers/misc/surface_sam/clients/surface_sam_device_hub.c new file mode 100644 -index 0000000000000..182b3f4128fe3 +index 0000000000000..bd903b86b96f4 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_device_hub.c @@ -0,0 +1,582 @@ @@ -1200,9 +1318,9 @@ index 0000000000000..182b3f4128fe3 + +static SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .command_id = 0x0d, + .instance_id = 0x00, -+ .channel = 0x01, +}); + +#define SSAM_BAS_OPMODE_TABLET 0x00 @@ -1541,7 +1659,7 @@ index 0000000000000..182b3f4128fe3 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_dtx.c b/drivers/misc/surface_sam/clients/surface_sam_dtx.c new file mode 100644 -index 0000000000000..df95124e166e2 +index 0000000000000..106543112b206 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_dtx.c @@ -0,0 +1,582 @@ @@ -1640,37 +1758,37 @@ index 0000000000000..df95124e166e2 + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .command_id = 0x06, + .instance_id = 0x00, -+ .channel = 0x01, +}); + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .command_id = 0x07, + .instance_id = 0x00, -+ .channel = 0x01, +}); + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_request, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .command_id = 0x08, + .instance_id = 0x00, -+ .channel = 0x01, +}); + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_open, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .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, ++ .target_id = 0x01, + .command_id = 0x0d, + .instance_id = 0x00, -+ .channel = 0x01, +}); + + @@ -2129,7 +2247,7 @@ index 0000000000000..df95124e166e2 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_hps.c b/drivers/misc/surface_sam/clients/surface_sam_hps.c new file mode 100644 -index 0000000000000..cee2356054ae9 +index 0000000000000..a47a5eb7391a1 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_hps.c @@ -0,0 +1,1287 @@ @@ -2314,16 +2432,16 @@ index 0000000000000..cee2356054ae9 + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .command_id = 0x06, + .instance_id = 0x00, -+ .channel = 0x01, +}); + +static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, { + .target_category = SSAM_SSH_TC_BAS, ++ .target_id = 0x01, + .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) { @@ -2936,7 +3054,7 @@ index 0000000000000..cee2356054ae9 + * dGPU. Specifically, this function is called from the RQSG handler of + * SAN, invoked by the ACPI _ON method of the dGPU root port. This means + * that this function is run inside `pci_set_power_state(rp, ...)` -+ * syncrhonously and thus returns before the `pci_set_power_state` call ++ * synchronously and thus returns before the `pci_set_power_state` call + * does. + * + * `pci_set_power_state` may either be called by us or when the PCI @@ -3422,7 +3540,7 @@ index 0000000000000..cee2356054ae9 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_san.c b/drivers/misc/surface_sam/clients/surface_sam_san.c new file mode 100644 -index 0000000000000..14e634f6e3c3c +index 0000000000000..99a5401d7d58a --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_san.c @@ -0,0 +1,930 @@ @@ -4015,9 +4133,9 @@ index 0000000000000..14e634f6e3c3c + return AE_OK; + + rqst.target_category = gsb_rqst->tc; ++ rqst.target_id = gsb_rqst->tid; + 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]; @@ -4394,7 +4512,7 @@ index 0000000000000..3408dde964b3c +#endif /* _SURFACE_SAM_SAN_H */ diff --git a/drivers/misc/surface_sam/clients/surface_sam_sid_perfmode.c b/drivers/misc/surface_sam/clients/surface_sam_sid_perfmode.c new file mode 100644 -index 0000000000000..4b5c8b4f9e0f1 +index 0000000000000..24907e15c47ae --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_sid_perfmode.c @@ -0,0 +1,194 @@ @@ -4444,16 +4562,16 @@ index 0000000000000..4b5c8b4f9e0f1 + +static SSAM_DEFINE_SYNC_REQUEST_R(ssam_tmp_perf_mode_get, struct ssam_perf_info, { + .target_category = SSAM_SSH_TC_TMP, ++ .target_id = 0x01, + .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, ++ .target_id = 0x01, + .command_id = 0x03, + .instance_id = 0x00, -+ .channel = 0x01, +}); + +static int ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, u32 mode) @@ -4594,7 +4712,7 @@ index 0000000000000..4b5c8b4f9e0f1 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_sid_power.c b/drivers/misc/surface_sam/clients/surface_sam_sid_power.c new file mode 100644 -index 0000000000000..2e5b2c181030f +index 0000000000000..d6559a251fa8b --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_sid_power.c @@ -0,0 +1,1112 @@ @@ -5052,7 +5170,7 @@ index 0000000000000..2e5b2c181030f + if (status > 0) + power_supply_changed(ac->psy); + -+ return status; ++ return status >= 0 ? 0 : status; +} + +static u32 spwr_notify_bat(struct ssam_notifier_block *nb, @@ -5063,10 +5181,10 @@ index 0000000000000..2e5b2c181030f + + bat = container_of(nb, struct spwr_battery_device, notif.base); + -+ dev_dbg(&bat->sdev->dev, "power event (cid = 0x%02x, iid = %d, chn = %d)\n", -+ event->command_id, event->instance_id, event->channel); ++ dev_dbg(&bat->sdev->dev, "power event (cid = 0x%02x, iid = %d, tid = %d)\n", ++ event->command_id, event->instance_id, event->target_id); + -+ // handled here, needs to be handled for all channels/instances ++ // handled here, needs to be handled for all targets/instances + if (event->command_id == SAM_EVENT_PWR_CID_ADAPTER) { + status = spwr_notify_adapter_bat(bat); + return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; @@ -5099,16 +5217,16 @@ index 0000000000000..2e5b2c181030f + + ac = container_of(nb, struct spwr_ac_device, notif.base); + -+ dev_dbg(&ac->sdev->dev, "power event (cid = 0x%02x, iid = %d, chn = %d)\n", -+ event->command_id, event->instance_id, event->channel); ++ dev_dbg(&ac->sdev->dev, "power event (cid = 0x%02x, iid = %d, tid = %d)\n", ++ event->command_id, event->instance_id, event->target_id); + + if (event->target_category != ac->sdev->uid.category) + return 0; + + /* -+ * Allow events of all channels/instances here. Global adapter status -+ * seems to be handled via channel=1 and instance=1, but events are -+ * reported on all channels/instances in use. ++ * Allow events of all targets/instances here. Global adapter status ++ * seems to be handled via target=1 and instance=1, but events are ++ * reported on all targets/instances in use. + * + * While it should be enough to just listen on 1/1, listen everywhere to + * make sure we don't miss anything. @@ -5712,7 +5830,7 @@ index 0000000000000..2e5b2c181030f +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_sid_vhf.c b/drivers/misc/surface_sam/clients/surface_sam_sid_vhf.c new file mode 100644 -index 0000000000000..444c9f883f25a +index 0000000000000..84e40305a268b --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_sid_vhf.c @@ -0,0 +1,430 @@ @@ -5831,9 +5949,9 @@ index 0000000000000..444c9f883f25a + data.rqst.end = 0; + + rqst.target_category = sdev->uid.category; ++ rqst.target_id = sdev->uid.target; + rqst.command_id = 0x04; + rqst.instance_id = sdev->uid.instance; -+ rqst.channel = sdev->uid.channel; + rqst.flags = SSAM_REQUEST_HAS_RESPONSE; + rqst.length = sizeof(struct surface_sam_sid_vhf_meta_rqst); + rqst.payload = (u8 *)&data.rqst; @@ -5865,9 +5983,9 @@ index 0000000000000..444c9f883f25a + data.rqst.end = 0; + + rqst.target_category = sdev->uid.category; ++ rqst.target_id = sdev->uid.target;; + rqst.command_id = 0x04; + rqst.instance_id = sdev->uid.instance; -+ rqst.channel = sdev->uid.channel;; + rqst.flags = SSAM_REQUEST_HAS_RESPONSE; + rqst.length = sizeof(struct surface_sam_sid_vhf_meta_rqst); + rqst.payload = (u8 *)&data.rqst; @@ -5975,7 +6093,7 @@ index 0000000000000..444c9f883f25a + } + + rqst.target_category = vhf->sdev->uid.category; -+ rqst.channel = vhf->sdev->uid.channel; ++ rqst.target_id = vhf->sdev->uid.target; + rqst.instance_id = vhf->sdev->uid.instance; + rqst.command_id = cid; + rqst.flags = reqtype == HID_REQ_GET_REPORT ? SSAM_REQUEST_HAS_RESPONSE : 0; @@ -6125,7 +6243,7 @@ index 0000000000000..444c9f883f25a + +static const struct ssam_device_id surface_sam_sid_vhf_match[] = { + { -+ SSAM_DEVICE(HID, SSAM_ANY_CHN, SSAM_ANY_IID, 0x00), ++ SSAM_DEVICE(HID, SSAM_ANY_TID, SSAM_ANY_IID, 0x00), + .driver_data = (unsigned long)&sid_vhf_default_props + }, + { }, @@ -6420,10 +6538,10 @@ index 0000000000000..95e730a86fb0c +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/controller.c b/drivers/misc/surface_sam/controller.c new file mode 100644 -index 0000000000000..9672518fe0a80 +index 0000000000000..32a599a8acac7 --- /dev/null +++ b/drivers/misc/surface_sam/controller.c -@@ -0,0 +1,1475 @@ +@@ -0,0 +1,2126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -6455,11 +6573,19 @@ index 0000000000000..9672518fe0a80 + +/* -- Safe counters. -------------------------------------------------------- */ + ++/** ++ * ssh_seq_reset - Reset/initialize sequence ID counter. ++ * @c: The counter to reset. ++ */ +static inline void ssh_seq_reset(struct ssh_seq_counter *c) +{ + WRITE_ONCE(c->value, 0); +} + ++/** ++ * ssh_seq_next - Get next sequence ID. ++ * @c: The counter providing the sequence IDs. ++ */ +static inline u8 ssh_seq_next(struct ssh_seq_counter *c) +{ + u8 old = READ_ONCE(c->value); @@ -6474,11 +6600,19 @@ index 0000000000000..9672518fe0a80 + return old; +} + ++/** ++ * ssh_rqid_reset - Reset/initialize request ID counter. ++ * @c: The counter to reset. ++ */ +static inline void ssh_rqid_reset(struct ssh_rqid_counter *c) +{ + WRITE_ONCE(c->value, 0); +} + ++/** ++ * ssh_rqid_next - Get next request ID. ++ * @c: The counter providing the request IDs. ++ */ +static inline u16 ssh_rqid_next(struct ssh_rqid_counter *c) +{ + u16 old = READ_ONCE(c->value); @@ -6498,13 +6632,28 @@ index 0000000000000..9672518fe0a80 +/* + * The notifier system is based on linux/notifier.h, specifically the SRCU + * implementation. The difference to that is, that some bits of the notifier -+ * call return value can be tracked accross multiple calls. This is done so that ++ * call return value can be tracked across multiple calls. This is done so that + * handling of events can be tracked and a warning can be issued in case an + * event goes unhandled. The idea of that waring is that it should help discover + * and identify new/currently unimplemented features. + */ + -+int ssam_nfblk_call_chain(struct ssam_nf_head *nh, struct ssam_event *event) ++/** ++ * ssam_nfblk_call_chain - Call event notifier callbacks of the given chain. ++ * @nh: The notifier head for which the notifier callbacks should be called. ++ * @event: The event data provided to the callbacks. ++ * ++ * Call all registered notifier callbacks in order of their priority until ++ * either no notifier is left or a notifier returns a value with the ++ * %SSAM_NOTIF_STOP bit set. Note that this bit is automatically set via ++ * ssam_notifier_from_errno() on any non-zero error value. ++ * ++ * Returns the notifier status value, which contains the notifier status bits ++ * (%SSAM_NOTIF_HANDLED and %SSAM_NOTIF_STOP) as well as a potential error ++ * value returned from the last executed notifier callback. Use ++ * ssam_notifier_to_errno() to convert this value to the original error value. ++ */ ++static int ssam_nfblk_call_chain(struct ssam_nf_head *nh, struct ssam_event *event) +{ + struct ssam_notifier_block *nb, *next_nb; + int ret = 0, idx; @@ -6526,11 +6675,16 @@ index 0000000000000..9672518fe0a80 + return ret; +} + -+/* ++/** ++ * __ssam_nfblk_insert - Insert a new notifier block into the given notifier ++ * list. ++ * @nh: The notifier head into which the block should be inserted. ++ * @nb: The notifier block to add. ++ * + * Note: This function must be synchronized by the caller with respect to other + * insert and/or remove calls. + */ -+int __ssam_nfblk_insert(struct ssam_nf_head *nh, struct ssam_notifier_block *nb) ++static int __ssam_nfblk_insert(struct ssam_nf_head *nh, struct ssam_notifier_block *nb) +{ + struct ssam_notifier_block **link = &nh->head; + @@ -6552,14 +6706,18 @@ index 0000000000000..9672518fe0a80 + return 0; +} + -+/* ++/** ++ * __ssam_nfblk_remove - Remove a notifier block from the given notifier list. ++ * @nh: The notifier head from which the block should be removed. ++ * @nb: The notifier block to remove. ++ * + * Note: This function must be synchronized by the caller with respect to other + * insert and/or remove calls. On success, the caller _must_ ensure SRCU + * synchronization by calling `synchronize_srcu(&nh->srcu)` after leaving the + * critical section, to ensure that the removed notifier block is not in use any + * more. + */ -+int __ssam_nfblk_remove(struct ssam_nf_head *nh, struct ssam_notifier_block *nb) ++static int __ssam_nfblk_remove(struct ssam_nf_head *nh, struct ssam_notifier_block *nb) +{ + struct ssam_notifier_block **link = &nh->head; + @@ -6575,6 +6733,10 @@ index 0000000000000..9672518fe0a80 + return -ENOENT; +} + ++/** ++ * ssam_nf_head_init - Initialize the given notifier head. ++ * @nh: The notifier head to initialize. ++ */ +static int ssam_nf_head_init(struct ssam_nf_head *nh) +{ + int status; @@ -6587,6 +6749,10 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_nf_head_destroy - Deinitialize the given notifier head. ++ * @nh: The notifier head to deinitialize. ++ */ +static void ssam_nf_head_destroy(struct ssam_nf_head *nh) +{ + cleanup_srcu_struct(&nh->srcu); @@ -6595,11 +6761,23 @@ index 0000000000000..9672518fe0a80 + +/* -- Event/notification registry. ------------------------------------------ */ + ++/** ++ * ssam_nf_refcount_key - Key used for event activation reference counting. ++ * @reg: The registry via which the event is enabled/disabled. ++ * @id: The ID uniquely describing the event. ++ */ +struct ssam_nf_refcount_key { + struct ssam_event_registry reg; + struct ssam_event_id id; +}; + ++/** ++ * ssam_nf_refcount_entry - RB-tree entry for referecnce counting event ++ * activations. ++ * @node: The node of this entry in the rb-tree. ++ * @key: The key of the event. ++ * @refcount: The reference-count of the event. ++ */ +struct ssam_nf_refcount_entry { + struct rb_node node; + struct ssam_nf_refcount_key key; @@ -6607,6 +6785,13 @@ index 0000000000000..9672518fe0a80 +}; + + ++/** ++ * ssam_nf_refcount_inc - Increment reference-/activation-count of the given ++ * event. ++ * @nf: The notifier system reference. ++ * @reg: The registry used to enable/disable the event. ++ * @id: The event ID. ++ */ +static int ssam_nf_refcount_inc(struct ssam_nf *nf, + struct ssam_event_registry reg, + struct ssam_event_id id) @@ -6625,15 +6810,14 @@ index 0000000000000..9672518fe0a80 + parent = *link; + + cmp = memcmp(&key, &entry->key, sizeof(key)); -+ if (cmp < 0) { ++ if (cmp < 0) + link = &(*link)->rb_left; -+ } else if (cmp > 0) { ++ else if (cmp > 0) + link = &(*link)->rb_right; -+ } else if (entry->refcount < INT_MAX) { ++ else if (entry->refcount < INT_MAX) + return ++entry->refcount; -+ } else { ++ else + return -ENOSPC; -+ } + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); @@ -6649,6 +6833,13 @@ index 0000000000000..9672518fe0a80 + return entry->refcount; +} + ++/** ++ * ssam_nf_refcount_dec - Decrement reference-/activation-count of the given ++ * event. ++ * @nf: The notifier system reference. ++ * @reg: The registry used to enable/disable the event. ++ * @id: The event ID. ++ */ +static int ssam_nf_refcount_dec(struct ssam_nf *nf, + struct ssam_event_registry reg, + struct ssam_event_id id) @@ -6684,11 +6875,35 @@ index 0000000000000..9672518fe0a80 + return -ENOENT; +} + ++/** ++ * ssam_nf_refcount_empty - Test if the notification system has any ++ * enabled/active events. ++ * @nf: The notification system. ++ */ +static bool ssam_nf_refcount_empty(struct ssam_nf *nf) +{ + return RB_EMPTY_ROOT(&nf->refcount); +} + ++/** ++ * ssam_nf_call - Call notification callbacks for the provided event. ++ * @nf: The notifier system ++ * @dev: The associated device, only used for logging. ++ * @rqid: The request ID of the event. ++ * @event: The event provided to the callbacks. ++ * ++ * Executa registered callbacks in order of their priority until either no ++ * callback is left or a callback returned a value with the %SSAM_NOTIF_STOP ++ * bit set. Note that this bit is set automatically when converting non.zero ++ * error values via ssam_notifier_from_errno() to notifier values. ++ * ++ * Also note that any callback that could handle an event should return a value ++ * with bit %SSAM_NOTIF_HANDLED set, indicating that the event does not go ++ * unhandled/ignored. In case no registered callback could handle an event, ++ * this function will emit a warning. ++ * ++ * In case a callback failed, this function will emit an error message. ++ */ +static void ssam_nf_call(struct ssam_nf *nf, struct device *dev, u16 rqid, + struct ssam_event *event) +{ @@ -6706,19 +6921,23 @@ index 0000000000000..9672518fe0a80 + + if (status < 0) { + dev_err(dev, "event: error handling event: %d " -+ "(tc: 0x%02x, cid: 0x%02x, iid: 0x%02x, chn: 0x%02x)\n", -+ status, event->target_category, event->command_id, -+ event->instance_id, event->channel); ++ "(tc: 0x%02x, tid: 0x%02x, cid: 0x%02x, iid: 0x%02x)\n", ++ status, event->target_category, event->target_id, ++ event->command_id, event->instance_id); + } + + if (!(nf_ret & SSAM_NOTIF_HANDLED)) { + dev_warn(dev, "event: unhandled event (rqid: 0x%02x, " -+ "tc: 0x%02x, cid: 0x%02x, iid: 0x%02x, chn: 0x%02x)\n", -+ rqid, event->target_category, event->command_id, -+ event->instance_id, event->channel); ++ "tc: 0x%02x, tid: 0x%02x, cid: 0x%02x, iid: 0x%02x)\n", ++ rqid, event->target_category, event->target_id, ++ event->command_id, event->instance_id); + } +} + ++/** ++ * ssam_nf_init - Initialize the notifier system. ++ * @nf: The notifier system to initialize. ++ */ +static int ssam_nf_init(struct ssam_nf *nf) +{ + int i, status; @@ -6740,6 +6959,10 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_nf_destroy - Deinitialize the notifier system. ++ * @nf: The notifier system to deinitialize. ++ */ +static void ssam_nf_destroy(struct ssam_nf *nf) +{ + int i; @@ -6756,7 +6979,8 @@ index 0000000000000..9672518fe0a80 +#define SSAM_CPLT_WQ_NAME "ssam_cpltq" + +/** -+ * Maximum payload length for cached `ssam_event_item`s. ++ * SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN - Maximum payload length for a cached ++ * &struct ssam_event_item. + * + * This length has been chosen to be accomodate standard touchpad and keyboard + * input events. Events with larger payloads will be allocated separately. @@ -6765,6 +6989,9 @@ index 0000000000000..9672518fe0a80 + +static struct kmem_cache *ssam_event_item_cache; + ++/** ++ * ssam_event_item_cache_init - Initialize the event item cache. ++ */ +int ssam_event_item_cache_init(void) +{ + const unsigned int size = sizeof(struct ssam_event_item) @@ -6780,6 +7007,9 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_event_item_cache_destroy - Deinitialize the event item cache. ++ */ +void ssam_event_item_cache_destroy(void) +{ + kmem_cache_destroy(ssam_event_item_cache); @@ -6796,12 +7026,25 @@ index 0000000000000..9672518fe0a80 + kfree(item); +} + ++/** ++ * ssam_event_item_free - Free the provided event item. ++ * @item: The event item to free. ++ */ +static inline void ssam_event_item_free(struct ssam_event_item *item) +{ + trace_ssam_event_item_free(item); + item->ops.free(item); +} + ++/** ++ * ssam_event_item_alloc - Allocate an event item with the given payload size. ++ * @len: The event payload length. ++ * @flags: The flags used for allocation. ++ * ++ * Allocate an event item with the given payload size, preferring allocation ++ * from the event item cache if the payload is small enough (i.e. smaller than ++ * %SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN). ++ */ +static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags) +{ + struct ssam_event_item *item; @@ -6814,6 +7057,7 @@ index 0000000000000..9672518fe0a80 + item->ops.free = __ssam_event_item_free_cached; + } else { + const size_t n = sizeof(struct ssam_event_item) + len; ++ + item = kzalloc(n, GFP_KERNEL); + if (!item) + return NULL; @@ -6828,6 +7072,11 @@ index 0000000000000..9672518fe0a80 +} + + ++/** ++ * ssam_event_queue_push - Push an event item to the event queue. ++ * @q: The event queue. ++ * @item: The item to add. ++ */ +static void ssam_event_queue_push(struct ssam_event_queue *q, + struct ssam_event_item *item) +{ @@ -6836,6 +7085,13 @@ index 0000000000000..9672518fe0a80 + spin_unlock(&q->lock); +} + ++/** ++ * ssam_event_queue_pop - Pop the next event item from the event queue. ++ * @q: The event queue. ++ * ++ * Returns and removes the next event item from the queue. Returns NULL If ++ * there is no event item left. ++ */ +static struct ssam_event_item *ssam_event_queue_pop(struct ssam_event_queue *q) +{ + struct ssam_event_item *item; @@ -6849,6 +7105,10 @@ index 0000000000000..9672518fe0a80 + return item; +} + ++/** ++ * ssam_event_queue_is_empty - Check if the event queue is empty. ++ * @q: The event queue. ++ */ +static bool ssam_event_queue_is_empty(struct ssam_event_queue *q) +{ + bool empty; @@ -6860,38 +7120,62 @@ index 0000000000000..9672518fe0a80 + return empty; +} + ++/** ++ * ssam_cplt_get_event_queue - Get the event queue for the given parameters. ++ * @cplt: The completion system on which to look for the queue. ++ * @tid: The target ID of the queue. ++ * @rqid: The request ID representing the event ID for which to get the queue. ++ * ++ * Returns the event queue corresponding to the event type described by the ++ * given parameters. If the request ID does not represent an event, this ++ * function returns NULL. If the target ID is not supported, this function ++ * will fall back to the default target ID (tid=1). ++ */ +static struct ssam_event_queue *ssam_cplt_get_event_queue( -+ struct ssam_cplt *cplt, u8 channel, u16 rqid) ++ struct ssam_cplt *cplt, u8 tid, u16 rqid) +{ + u16 event = ssh_rqid_to_event(rqid); -+ u16 chidx = ssh_channel_to_index(channel); ++ u16 tidx = ssh_tid_to_index(tid); + + if (!ssh_rqid_is_event(rqid)) { -+ dev_err(cplt->dev, "event: unsupported rqid: 0x%04x\n", rqid); ++ dev_err(cplt->dev, "event: unsupported rquest ID: 0x%04x\n", rqid); + return NULL; + } + -+ if (!ssh_channel_is_valid(channel)) { -+ dev_warn(cplt->dev, "event: unsupported channel: %u\n", -+ channel); -+ chidx = 0; ++ if (!ssh_tid_is_valid(tid)) { ++ dev_warn(cplt->dev, "event: unsupported target ID: %u\n", tid); ++ tidx = 0; + } + -+ return &cplt->event.channel[chidx].queue[event]; ++ return &cplt->event.target[tidx].queue[event]; +} + ++/** ++ * ssam_cplt_submit - Submit a work item to the compeltion system workqueue. ++ * @cplt: The completion system. ++ * @work: The work item to submit. ++ */ +static inline bool ssam_cplt_submit(struct ssam_cplt *cplt, + struct work_struct *work) +{ + return queue_work(cplt->wq, work); +} + ++/** ++ * ssam_cplt_submit_event - Submit an event to the completion system. ++ * @cplt: The completion system. ++ * @item: The event item to submit. ++ * ++ * Submits the event to the completion system by queuing it on the event item ++ * queue and queuing the respective event queue work item on the completion ++ * workqueue, which will eventually complete the event. ++ */ +static int ssam_cplt_submit_event(struct ssam_cplt *cplt, + struct ssam_event_item *item) +{ + struct ssam_event_queue *evq; + -+ evq = ssam_cplt_get_event_queue(cplt, item->event.channel, item->rqid); ++ evq = ssam_cplt_get_event_queue(cplt, item->event.target_id, item->rqid); + if (!evq) + return -EINVAL; + @@ -6900,6 +7184,23 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_cplt_flush - Flush the completion system. ++ * @cplt: The completion system. ++ * ++ * Flush the completion system by waiting until all currently submitted work ++ * items have been completed. ++ * ++ * Note: This function does not guarantee that all events will have been ++ * handled once this call terminates. In case of a larger number of ++ * to-be-completed events, the event queue work function may re-schedule its ++ * work item, which this flush operation will ignore. ++ * ++ * This operation is only intended to, during normal operation prior to ++ * shutdown, try to complete most events and requests to get them out of the ++ * system while the system is still fully operational. It does not aim to ++ * provide any guraantee that all of them have been handled. ++ */ +static void ssam_cplt_flush(struct ssam_cplt *cplt) +{ + flush_workqueue(cplt->wq); @@ -6917,6 +7218,7 @@ index 0000000000000..9672518fe0a80 + nf = &queue->cplt->event.notif; + dev = queue->cplt->dev; + ++ // limit number of processed events to avoid livelocking + for (i = 0; i < 10; i++) { + item = ssam_event_queue_pop(queue); + if (item == NULL) @@ -6930,6 +7232,11 @@ index 0000000000000..9672518fe0a80 + ssam_cplt_submit(queue->cplt, &queue->work); +} + ++/** ++ * ssam_event_queue_init - Initialize an event queue. ++ * @cplt: The completion system on which the queue resides. ++ * @evq: The event queue to initialize. ++ */ +static void ssam_event_queue_init(struct ssam_cplt *cplt, + struct ssam_event_queue *evq) +{ @@ -6939,9 +7246,14 @@ index 0000000000000..9672518fe0a80 + INIT_WORK(&evq->work, ssam_event_queue_work_fn); +} + ++/** ++ * ssam_cplt_init - Initialize completion system. ++ * @cplt: The completion system to initialize. ++ * @dev: The device used for logging. ++ */ +static int ssam_cplt_init(struct ssam_cplt *cplt, struct device *dev) +{ -+ struct ssam_event_channel *channel; ++ struct ssam_event_target *target; + int status, c, i; + + cplt->dev = dev; @@ -6950,11 +7262,11 @@ index 0000000000000..9672518fe0a80 + if (!cplt->wq) + return -ENOMEM; + -+ for (c = 0; c < ARRAY_SIZE(cplt->event.channel); c++) { -+ channel = &cplt->event.channel[c]; ++ for (c = 0; c < ARRAY_SIZE(cplt->event.target); c++) { ++ target = &cplt->event.target[c]; + -+ for (i = 0; i < ARRAY_SIZE(channel->queue); i++) -+ ssam_event_queue_init(cplt, &channel->queue[i]); ++ for (i = 0; i < ARRAY_SIZE(target->queue); i++) ++ ssam_event_queue_init(cplt, &target->queue[i]); + } + + status = ssam_nf_init(&cplt->event.notif); @@ -6964,6 +7276,13 @@ index 0000000000000..9672518fe0a80 + return status; +} + ++/** ++ * ssam_cplt_destroy - Deinitialize the completion system. ++ * @cplt: The completion system to deinitialize. ++ * ++ * Deinitialize the given completion system and ensure that all pending, i.e. ++ * yet-to-be-completed, event items and requests have been handled. ++ */ +static void ssam_cplt_destroy(struct ssam_cplt *cplt) +{ + /* @@ -6979,6 +7298,11 @@ index 0000000000000..9672518fe0a80 + +/* -- Main SSAM device structures. ------------------------------------------ */ + ++/** ++ * ssam_controller_device - Return the &struct device associated with this ++ * controller. ++ * @c: The controller for which to get the device. ++ */ +struct device *ssam_controller_device(struct ssam_controller *c) +{ + return ssh_rtl_get_device(&c->rtl); @@ -6993,6 +7317,12 @@ index 0000000000000..9672518fe0a80 + kfree(ctrl); +} + ++/** ++ * ssam_controller_get - Increment reference count of controller. ++ * @c: The controller. ++ * ++ * Returns the controller, i.e. ``c``. ++ */ +struct ssam_controller *ssam_controller_get(struct ssam_controller *c) +{ + kref_get(&c->kref); @@ -7000,6 +7330,10 @@ index 0000000000000..9672518fe0a80 +} +EXPORT_SYMBOL_GPL(ssam_controller_get); + ++/** ++ * ssam_controller_put - Decrement reference count of controller. ++ * @c: The controller. ++ */ +void ssam_controller_put(struct ssam_controller *c) +{ + kref_put(&c->kref, __ssam_controller_release); @@ -7007,23 +7341,62 @@ index 0000000000000..9672518fe0a80 +EXPORT_SYMBOL_GPL(ssam_controller_put); + + ++/** ++ * ssam_controller_statelock - Lock the controller against state transitions. ++ * @c: The controller to lock. ++ * ++ * Lock the controller against state transitions. Holding this lock guarantees ++ * that the controller will not transition between states, i.e. if the ++ * controller is in state "started", when this lock has been acquired, it will ++ * remain in this state at least until the lock has been released. ++ * ++ * Multiple clients may concurrently hold this lock. In other words: The ++ * ``statelock`` functions represent the read-lock part of a r/w-semaphore. ++ * Actions causing state transitions of the controller must be executed while ++ * holding the write-part of this r/w-semaphore (see ssam_controller_lock() ++ * and ssam_controller_unlock() for that). ++ * ++ * See ssam_controller_stateunlock() for the corresponding unlock function. ++ */ +void ssam_controller_statelock(struct ssam_controller *c) +{ + down_read(&c->lock); +} +EXPORT_SYMBOL_GPL(ssam_controller_statelock); + ++/** ++ * ssam_controller_stateunlock - Unlock controller state transitions. ++ * @c: The controller to unlock. ++ * ++ * See ssam_controller_statelock() for the corresponding lock function. ++ */ +void ssam_controller_stateunlock(struct ssam_controller *c) +{ + up_read(&c->lock); +} +EXPORT_SYMBOL_GPL(ssam_controller_stateunlock); + ++/** ++ * ssam_controller_lock - Acquire the main controller lock. ++ * @c: The controller to lock. ++ * ++ * This lock must be held for any state transitions, including transition to ++ * suspend/resumed states and during shutdown. See ssam_controller_statelock() ++ * for more details on controller locking. ++ * ++ * See ssam_controller_unlock() for the corresponding unlock function. ++ */ +void ssam_controller_lock(struct ssam_controller *c) +{ + down_write(&c->lock); +} + ++/* ++ * ssam_controller_unlock - Release the main controller lock. ++ * @c: The controller to unlock. ++ * ++ * See ssam_controller_lock() for the corresponding lock function. ++ */ +void ssam_controller_unlock(struct ssam_controller *c) +{ + up_write(&c->lock); @@ -7043,9 +7416,9 @@ index 0000000000000..9672518fe0a80 + + item->rqid = get_unaligned_le16(&cmd->rqid); + item->event.target_category = cmd->tc; ++ item->event.target_id = cmd->tid_in; + item->event.command_id = cmd->cid; + item->event.instance_id = cmd->iid; -+ item->event.channel = cmd->chn_in; + memcpy(&item->event.data[0], data->ptr, data->len); + + WARN_ON(ssam_cplt_submit_event(&ctrl->cplt, item)); @@ -7065,6 +7438,15 @@ index 0000000000000..9672518fe0a80 +static const guid_t SSAM_SSH_DSM_UUID = GUID_INIT(0xd5e383e1, 0xd892, 0x4a76, + 0x89, 0xfc, 0xf6, 0xaa, 0xae, 0x7e, 0xd5, 0xb5); + ++/** ++ * ssam_device_caps_load_from_acpi - Load controller capabilities from _DSM. ++ * @handle: The handle of the ACPI controller/SSH device. ++ * @caps: Where to store the capabilities in. ++ * ++ * Initializes the given controller capabilities with default values, then ++ * checks and, if the respective _DSM functions are available, loads the ++ * actual capabilities from the _DSM. ++ */ +static int ssam_device_caps_load_from_acpi(acpi_handle handle, + struct ssam_device_caps *caps) +{ @@ -7105,6 +7487,19 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_controller_init - Initialize SSAM controller. ++ * @ctrl: The controller to initialize. ++ * @serdev: The serial device representing the underlying data transport. ++ * ++ * Initializes the given controller. Does neither start receiver nor ++ * transmitter threads. After this call, the controller has to be hooked up to ++ * the serdev core separately via &struct serdev_device_ops, relaying calls to ++ * ssam_controller_receive_buf() and ssam_controller_write_wakeup(). Once the ++ * controller has been hooked up, transmitter and receiver threads may be ++ * started via ssam_controller_start(). These setup steps need to be completed ++ * before controller can be used for requests. ++ */ +int ssam_controller_init(struct ssam_controller *ctrl, + struct serdev_device *serdev) +{ @@ -7138,15 +7533,24 @@ index 0000000000000..9672518fe0a80 + } + + // update state -+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_INITIALIZED); ++ WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_INITIALIZED); + return 0; +} + ++/** ++ * ssam_controller_start - Start the receiver and transmitter threads of the ++ * controller. ++ * @ctrl: The controller. ++ * ++ * Note: When this function is called, the controller shouldbe properly hooked ++ * up to the serdev core via &struct serdev_device_ops. Please refert to ++ * ssam_controller_init() for more details on controller initialization. ++ */ +int ssam_controller_start(struct ssam_controller *ctrl) +{ + int status; + -+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_INITIALIZED) ++ if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_INITIALIZED) + return -EINVAL; + + status = ssh_rtl_tx_start(&ctrl->rtl); @@ -7159,13 +7563,32 @@ index 0000000000000..9672518fe0a80 + return status; + } + -+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_STARTED); ++ WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED); + return 0; +} + ++/** ++ * ssam_controller_shutdown - Shut down the controller. ++ * @ctrl: The controller. ++ * ++ * Shuts down the controller by flushing all pending requests and stopping the ++ * transmitter and receiver threads. All requests submitted after this call ++ * will fail with -ESHUTDOWN. While it is discouraged to do so, this function ++ * is safe to use in parallel with ongoing request submission. ++ * ++ * In the course of this shutdown procedure, all currently registered ++ * notifiers will be unregistered. It is, however, strongly recommended to not ++ * rely on this behavior, and instead the party registring the notifier should ++ * unregister it before the controller gets shut down, e.g. via the SSAM bus ++ * which guarantees client devices to be removed before a shutdown. ++ * ++ * Note that events may still be pending after this call, but due to the ++ * notifiers being unregistered, the will be dropped when the controller is ++ * subsequently being destroyed via ssam_controller_destroy(). ++ */ +void ssam_controller_shutdown(struct ssam_controller *ctrl) +{ -+ enum ssam_controller_state s = smp_load_acquire(&ctrl->state); ++ enum ssam_controller_state s = READ_ONCE(ctrl->state); + int status; + + if (s == SSAM_CONTROLLER_UNINITIALIZED || s == SSAM_CONTROLLER_STOPPED) @@ -7198,15 +7621,25 @@ index 0000000000000..9672518fe0a80 + ssh_rtl_tx_flush(&ctrl->rtl); + ssh_rtl_shutdown(&ctrl->rtl); + -+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_STOPPED); ++ WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STOPPED); + ctrl->rtl.ptl.serdev = NULL; +} + ++/** ++ * ssam_controller_destroy - Destroy the controller and free its resources. ++ * @ctrl: The controller. ++ * ++ * Ensures that all resources associated with the controller get freed. This ++ * function should only be called after the controller has been stopped via ++ * ssam_controller_shutdown(). ++ */ +void ssam_controller_destroy(struct ssam_controller *ctrl) +{ -+ if (smp_load_acquire(&ctrl->state) == SSAM_CONTROLLER_UNINITIALIZED) ++ if (READ_ONCE(ctrl->state) == SSAM_CONTROLLER_UNINITIALIZED) + return; + ++ WARN_ON(ctrl->state != SSAM_CONTROLLER_STOPPED); ++ + /* + * Note: New events could still have been received after the previous + * flush in ssam_controller_shutdown, before the request transport layer @@ -7219,36 +7652,60 @@ index 0000000000000..9672518fe0a80 + ssam_cplt_destroy(&ctrl->cplt); + ssh_rtl_destroy(&ctrl->rtl); + -+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_UNINITIALIZED); ++ WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_UNINITIALIZED); +} + ++/** ++ * ssam_controller_suspend - Suspend the controller. ++ * @ctrl: The controller to suspend. ++ * ++ * Marks the controller as suspended. Note that display-off and D0-exit ++ * notifications have to be sent manually before transitioning the controller ++ * into the suspended state via this function. ++ * ++ * See ssam_controller_resume() for the corresponding resume function. ++ * ++ * Returns ``-EINVAL`` if the controller is currently not in the "started" ++ * state. ++ */ +int ssam_controller_suspend(struct ssam_controller *ctrl) +{ + ssam_controller_lock(ctrl); + -+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_STARTED) { ++ if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_STARTED) { + ssam_controller_unlock(ctrl); + return -EINVAL; + } + + ssam_dbg(ctrl, "pm: suspending controller\n"); -+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_SUSPENDED); ++ WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_SUSPENDED); + + ssam_controller_unlock(ctrl); + return 0; +} + ++/** ++ * ssam_controller_resume - Resume the controller from suspend. ++ * @ctrl: The controller to resume. ++ * ++ * Resume the controller from the suspended state it was put into via ++ * ssam_controller_suspend(). This function does not issue display-on and ++ * D0-entry notifications. If required, those have to be sent manually after ++ * this call. ++ * ++ * Returns ``-EINVAL`` if the controller is currently not suspended. ++ */ +int ssam_controller_resume(struct ssam_controller *ctrl) +{ + ssam_controller_lock(ctrl); + -+ if (smp_load_acquire(&ctrl->state) != SSAM_CONTROLLER_SUSPENDED) { ++ if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_SUSPENDED) { + ssam_controller_unlock(ctrl); + return -EINVAL; + } + + ssam_dbg(ctrl, "pm: resuming controller\n"); -+ smp_store_release(&ctrl->state, SSAM_CONTROLLER_STARTED); ++ WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED); + + ssam_controller_unlock(ctrl); + return 0; @@ -7257,6 +7714,23 @@ index 0000000000000..9672518fe0a80 + +/* -- Top-level request interface ------------------------------------------- */ + ++/** ++ * ssam_request_write_data - Construct and write SAM request message to buffer. ++ * @buf: The buffer to write the data to. ++ * @ctrl: The controller via which the request will be sent. ++ * @spec: The request data/specification. ++ * ++ * Constructs a SAM/SSH request message and writes it to the provided buffer. ++ * The request and transport counters, specifically RQID and SEQ, will be set ++ * in this call. These counters are obtained from the controller. It is thus ++ * only valid to send the resulting message via the controller specified here. ++ * ++ * Returns the number of bytes used in the buffer on success. Returns -EINVAL ++ * if the payload length provided in the request specification is too large ++ * (larger than %SSH_COMMAND_MAX_PAYLOAD_SIZE) or if the provided buffer is ++ * too small. For calculation of the required buffer size, refer to the ++ * SSH_COMMAND_MESSAGE_LENGTH() macro. ++ */ +ssize_t ssam_request_write_data(struct ssam_span *buf, + struct ssam_controller *ctrl, + struct ssam_request *spec) @@ -7268,6 +7742,9 @@ index 0000000000000..9672518fe0a80 + if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE) + return -EINVAL; + ++ if (SSH_COMMAND_MESSAGE_LENGTH(spec->length) > buf->len) ++ return -EINVAL; ++ + msgb_init(&msgb, buf->ptr, buf->len); + seq = ssh_seq_next(&ctrl->counter.seq); + rqid = ssh_rqid_next(&ctrl->counter.rqid); @@ -7300,17 +7777,14 @@ index 0000000000000..9672518fe0a80 + return; + + if (!r->resp || !r->resp->pointer) { -+ if (data->len) { -+ rtl_warn(rtl, "rsp: no response buffer provided, " -+ "dropping data\n"); -+ } ++ 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); ++ rtl_err(rtl, "rsp: response buffer too small, capacity: %zu bytes," ++ " got: %zu bytes\n", r->resp->capacity, data->len); + r->status = -ENOSPC; + return; + } @@ -7330,6 +7804,23 @@ index 0000000000000..9672518fe0a80 +}; + + ++/** ++ * ssam_request_sync_alloc - Allocate a synchronous request. ++ * @payload_len: The length of the request payload. ++ * @flags: Flags used for allocation. ++ * @rqst: Where to store the pointer to the allocated request. ++ * @buffer: Where to store the buffer descriptor for the message buffer of ++ * the request. ++ * ++ * Allocates a synchronous request with corresponding message buffer. The ++ * request still needs to be initialized ssam_request_sync_init() before ++ * it can be submitted, and the message buffer data must still be set to the ++ * returned buffer via ssam_request_sync_set_data() after it has been filled, ++ * if need be with adjusted message length. ++ * ++ * After use, the request and its corresponding message buffer should be freed ++ * via ssam_request_sync_free(). The buffer must not be freed separately. ++ */ +int ssam_request_sync_alloc(size_t payload_len, gfp_t flags, + struct ssam_request_sync **rqst, + struct ssam_span *buffer) @@ -7347,6 +7838,37 @@ index 0000000000000..9672518fe0a80 +} +EXPORT_SYMBOL_GPL(ssam_request_sync_alloc); + ++/** ++ * ssam_request_sync_free - Free a synchronous request. ++ * @rqst: The request to free. ++ * ++ * Free a synchronous request and its corresponding buffer allocated with ++ * ssam_request_sync_alloc(). Do not use for requests allocated on the stack ++ * or via any other function. ++ * ++ * Warning: The caller must ensure that the request is not in use any more. ++ * I.e. the caller must ensure that it has the only reference to the request ++ * and the request is not currently pending. This means that the caller has ++ * either never submitted the request, request submission has failed, or the ++ * caller has waited until the submitted request has been completed via ++ * ssam_request_sync_wait(). ++ */ ++void ssam_request_sync_free(struct ssam_request_sync *rqst) ++{ ++ kfree(rqst); ++} ++EXPORT_SYMBOL_GPL(ssam_request_sync_free); ++ ++/** ++ * ssam_request_sync_init - Initialize a synchronous request struct. ++ * @rqst: The request to initialize. ++ * @flags: The request flags. ++ * ++ * Initializes the given request struct. Does not initialize the request ++ * message data. This has to be done explicitly after this call via ++ * ssam_request_sync_set_data() and the actual message data has to be written ++ * via ssam_request_write_data(). ++ */ +void ssam_request_sync_init(struct ssam_request_sync *rqst, + enum ssam_request_flags flags) +{ @@ -7357,14 +7879,31 @@ index 0000000000000..9672518fe0a80 +} +EXPORT_SYMBOL_GPL(ssam_request_sync_init); + ++/** ++ * ssam_request_sync_submit - Submit a synchronous request. ++ * @ctrl: The controller with which to submit the request. ++ * @rqst: The request to submit. ++ * ++ * Submit a synchronous request. The request has to be initialized and ++ * properly set up, including response buffer (may be NULL if no response is ++ * expected) and command message data. This function does not wait for the ++ * request to be completed. ++ * ++ * If this function succeeds, ssam_request_sync_wait() must be used to ensure ++ * that the request has been completed before the response data can be ++ * accessed and/or the request can be freed. On failure, the request may ++ * immediately be freed. ++ * ++ * This function may only be used if the controller is active, i.e. has been ++ * initialized and not suspended. ++ */ +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; + + /* -+ * This is only a superficial checks. In general, the caller needs to ++ * This is only a superficial check. In general, the caller needs to + * ensure that the controller is initialized and is not (and does not + * get) suspended during use, i.e. until the request has been completed + * (if _absolutely_ necessary, by use of ssam_controller_statelock/ @@ -7377,7 +7916,7 @@ index 0000000000000..9672518fe0a80 + * is safe with regards to this), but it is generally discouraged to do + * so. + */ -+ if (WARN_ON(state != SSAM_CONTROLLER_STARTED)) { ++ if (WARN_ON(READ_ONCE(ctrl->state) != SSAM_CONTROLLER_STARTED)) { + ssh_request_put(&rqst->base); + return -ENXIO; + } @@ -7389,12 +7928,25 @@ index 0000000000000..9672518fe0a80 +} +EXPORT_SYMBOL_GPL(ssam_request_sync_submit); + ++/** ++ * ssam_request_sync - Execute a synchronous request. ++ * @ctrl: The controller via which the request will be submitted. ++ * @spec: The request specification and payload. ++ * @rsp: The response buffer. ++ * ++ * Allocates a synchronous request with its message data buffer on the heap ++ * via ssam_request_sync_alloc(), fully intializes it via the provided request ++ * specification, submits it, and finally waits for its completion before ++ * freeing it and returning its status. ++ * ++ * Returns the status of the request or any failure during setup. ++ */ +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; ++ ssize_t len; + int status; + + // prevent overflow, allows us to skip checks later on @@ -7411,24 +7963,48 @@ index 0000000000000..9672518fe0a80 + ssam_request_sync_set_resp(rqst, rsp); + + len = ssam_request_write_data(&buf, ctrl, spec); ++ if (len < 0) ++ return len; ++ + 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); ++ ssam_request_sync_free(rqst); + return status; +} +EXPORT_SYMBOL_GPL(ssam_request_sync); + ++/** ++ * ssam_request_sync_with_buffer - Execute a synchronous request with the ++ * provided buffer as backend for the message buffer. ++ * @ctrl: The controller via which the request will be submitted. ++ * @spec: The request specification and payload. ++ * @rsp: The response buffer. ++ * @buf: The buffer for the request message data. ++ * ++ * Allocates a synchronous request struct on the stack, fully initializes it ++ * using the provided buffer as message data buffer, submits it, and then ++ * waits for its completion before returning its staus. The ++ * SSH_COMMAND_MESSAGE_LENGTH() macro can be used to compute the required ++ * message buffer size. ++ * ++ * This function does essentially the same as ssam_request_sync(), but instead ++ * of dynamically allocating the request and message data buffer, it uses the ++ * provided message data buffer and stores the (small) request struct on the ++ * heap. ++ * ++ * Returns the status of the request or any failure during setup. ++ */ +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; ++ ssize_t len; + int status; + + // prevent overflow, allows us to skip checks later on @@ -7441,6 +8017,9 @@ index 0000000000000..9672518fe0a80 + ssam_request_sync_set_resp(&rqst, rsp); + + len = ssam_request_write_data(buf, ctrl, spec); ++ if (len < 0) ++ return len; ++ + ssam_request_sync_set_data(&rqst, buf->ptr, len); + + status = ssam_request_sync_submit(ctrl, &rqst); @@ -7456,39 +8035,54 @@ index 0000000000000..9672518fe0a80 + +static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_get_firmware_version, __le32, { + .target_category = SSAM_SSH_TC_SAM, ++ .target_id = 0x01, + .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, ++ .target_id = 0x01, + .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, ++ .target_id = 0x01, + .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, ++ .target_id = 0x01, + .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, ++ .target_id = 0x01, + .command_id = 0x34, + .instance_id = 0x00, -+ .channel = 0x01, +}); + ++/** ++ * ssam_ssh_event_enable - Enable SSH event. ++ * @ctrl: The controller for which to enable the event. ++ * @reg: The event registry describing what request to use for enabling and ++ * disabling the event. ++ * @id: The event identifier. ++ * @flags: The event flags. ++ * ++ * This is a wrapper for the raw SAM request to enable an event, thus it does ++ * not handle referecnce counting for enable/disable of events. If an event ++ * has already been enabled, the EC will ignore this request. ++ * ++ * Returns the status of the executed SAM request or -EPROTO if the request ++ * response indicates a failure. ++ */ +static int ssam_ssh_event_enable(struct ssam_controller *ctrl, + struct ssam_event_registry reg, + struct ssam_event_id id, u8 flags) @@ -7511,9 +8105,9 @@ index 0000000000000..9672518fe0a80 + put_unaligned_le16(rqid, ¶ms.request_id); + + rqst.target_category = reg.target_category; ++ rqst.target_id = reg.target_id; + 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; @@ -7524,22 +8118,38 @@ index 0000000000000..9672518fe0a80 + + status = ssam_request_sync_onstack(ctrl, &rqst, &result, sizeof(params)); + if (status) { -+ 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); ++ 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) { -+ 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); ++ ssam_err(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 -EPROTO; + } + + return status; + +} + ++/** ++ * ssam_ssh_event_disable - Disable SSH event. ++ * @ctrl: The controller for which to disable the event. ++ * @reg: The event registry describing what request to use for enabling and ++ * disabling the event (must be same as used when enabling the event). ++ * @id: The event identifier. ++ * @flags: The event flags (likely ignored for disabling of events). ++ * ++ * This is a wrapper for the raw SAM request to disable an event, thus it does ++ * not handle reference counting for enable/disable of events. If an event has ++ * already been disabled, the EC will ignore this request. ++ * ++ * Returns the status of the executed SAM request or -EPROTO if the request ++ * response indicates a failure. ++ */ +static int ssam_ssh_event_disable(struct ssam_controller *ctrl, + struct ssam_event_registry reg, + struct ssam_event_id id, u8 flags) @@ -7562,9 +8172,9 @@ index 0000000000000..9672518fe0a80 + put_unaligned_le16(rqid, ¶ms.request_id); + + rqst.target_category = reg.target_category; ++ rqst.target_id = reg.target_id; + 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; @@ -7575,16 +8185,17 @@ index 0000000000000..9672518fe0a80 + + status = ssam_request_sync_onstack(ctrl, &rqst, &result, sizeof(params)); + if (status) { -+ 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); ++ 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) { -+ 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); ++ ssam_err(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 -EPROTO; + } + + return status; @@ -7593,6 +8204,10 @@ index 0000000000000..9672518fe0a80 + +/* -- Wrappers for internal SAM requests. ----------------------------------- */ + ++/** ++ * ssam_log_firmware_version - Log SAM/EC firmware version to kernel log. ++ * @ctrl: The controller. ++ */ +int ssam_log_firmware_version(struct ssam_controller *ctrl) +{ + __le32 __version; @@ -7612,6 +8227,33 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_ctrl_notif_display_off - Notify EC that the display has been turned ++ * off. ++ * @ctrl: The controller. ++ * ++ * Notify the EC that the display has been turned off and the driver may enter ++ * a lower-power state. This will prevent events from being sent directly. ++ * Rather, the EC signals an event by pulling the wakeup GPIO high for as long ++ * as there are pending events. The events then need to be manually released, ++ * one by one, via the GPIO callback request. All pending events accumulated ++ * during this state can also be released by issuing the display-on ++ * notification, e.g. via ssam_ctrl_notif_display_on(), which will also reset ++ * the GPIO. ++ * ++ * On some devices, specifically ones with an integrated keyboard, the keyboard ++ * backlight will be turned off by this call. ++ * ++ * This function will only send the display-off notification command if ++ * display noticications are supported by the EC. Currently all known devices ++ * support these notification. ++ * ++ * Use ssam_ctrl_notif_display_on() to reverse the effects of this function. ++ * ++ * Returns the status of the executed SAM command, zero on success or if no ++ * request has been executed, or -EPROTO if an unexpected response has been ++ * received. ++ */ +int ssam_ctrl_notif_display_off(struct ssam_controller *ctrl) +{ + int status; @@ -7627,14 +8269,33 @@ index 0000000000000..9672518fe0a80 + return status; + + if (response != 0) { -+ ssam_err(ctrl, "unexpected response from display-off notification: " -+ "0x%02x\n", response); -+ return -EIO; ++ ssam_err(ctrl, "unexpected response from display-off notification: 0x%02x\n", ++ response); ++ return -EPROTO; + } + + return 0; +} + ++/** ++ * ssam_ctrl_notif_display_on - Notify EC that the display has been turned on. ++ * @ctrl: The controller. ++ * ++ * Notify the EC that the display has been turned back on and the driver has ++ * exited its lower-power state. This notification is the counterpart to the ++ * display-off notification sent via ssam_ctrl_notif_display_off() and will ++ * reverse its effects, including resetting events to their default behavior. ++ * ++ * This function will only send the display-on notification command if display ++ * noticications are supported by the EC. Currently all known devices support ++ * these notification. ++ * ++ * See ssam_ctrl_notif_display_off() for more details. ++ * ++ * Returns the status of the executed SAM command, zero on success or if no ++ * request has been executed, or -EPROTO if an unexpected response has been ++ * received. ++ */ +int ssam_ctrl_notif_display_on(struct ssam_controller *ctrl) +{ + int status; @@ -7650,14 +8311,33 @@ index 0000000000000..9672518fe0a80 + return status; + + if (response != 0) { -+ ssam_err(ctrl, "unexpected response from display-on notification: " -+ "0x%02x\n", response); -+ return -EIO; ++ ssam_err(ctrl, "unexpected response from display-on notification: 0x%02x\n", ++ response); ++ return -EPROTO; + } + + return 0; +} + ++/** ++ * ssam_ctrl_notif_d0_exit - Notify EC that the driver/device exits the D0 ++ * power state. ++ * @ctrl: The controller ++ * ++ * Notifies the EC that the driver prepares to exit the D0 power state in ++ * favor of a lower-power state. Exact effects of this function related to the ++ * EC are currently unknown. ++ * ++ * This function will only send the D0-exit notification command if D0-state ++ * noticications are supported by the EC. Only newer Surface generations ++ * support these notifications. ++ * ++ * Use ssam_ctrl_notif_d0_entry() to reverse the effects of this function. ++ * ++ * Returns the status of the executed SAM command, zero on success or if no ++ * request has been executed, or -EPROTO if an unexpected response has been ++ * received. ++ */ +int ssam_ctrl_notif_d0_exit(struct ssam_controller *ctrl) +{ + int status; @@ -7673,14 +8353,33 @@ index 0000000000000..9672518fe0a80 + return status; + + if (response != 0) { -+ ssam_err(ctrl, "unexpected response from D0-exit notification: " -+ "0x%02x\n", response); -+ return -EIO; ++ ssam_err(ctrl, "unexpected response from D0-exit notification:" ++ " 0x%02x\n", response); ++ return -EPROTO; + } + + return 0; +} + ++/** ++ * ssam_ctrl_notif_d0_entry - Notify EC that the driver/device enters the D0 ++ * power state. ++ * @ctrl: The controller ++ * ++ * Notifies the EC that the driver has exited a lower-power state and entered ++ * the D0 power state. Exact effects of this function related to the EC are ++ * currently unknown. ++ * ++ * This function will only send the D0-entry notification command if D0-state ++ * noticications are supported by the EC. Only newer Surface generations ++ * support these notifications. ++ * ++ * See ssam_ctrl_notif_d0_exit() for more details. ++ * ++ * Returns the status of the executed SAM command, zero on success or if no ++ * request has been executed, or -EPROTO if an unexpected response has been ++ * received. ++ */ +int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl) +{ + int status; @@ -7696,9 +8395,9 @@ index 0000000000000..9672518fe0a80 + return status; + + if (response != 0) { -+ ssam_err(ctrl, "unexpected response from D0-entry notification: " -+ "0x%02x\n", response); -+ return -EIO; ++ ssam_err(ctrl, "unexpected response from D0-entry notification:" ++ " 0x%02x\n", response); ++ return -EPROTO; + } + + return 0; @@ -7707,6 +8406,15 @@ index 0000000000000..9672518fe0a80 + +/* -- Top-level event registry interface. ----------------------------------- */ + ++/** ++ * ssam_notifier_register - Register an event notifier. ++ * @ctrl: The controller to register the notifier on. ++ * @n: The event notifier to register. ++ * ++ * Register an event notifier and increment the usage counter of the ++ * associated SAM event. If the event was previously not enabled, it will be ++ * enabled during this call. ++ */ +int ssam_notifier_register(struct ssam_controller *ctrl, + struct ssam_event_notifier *n) +{ @@ -7729,8 +8437,8 @@ index 0000000000000..9672518fe0a80 + return rc; + } + -+ ssam_dbg(ctrl, "enabling event (reg: 0x%02x, tc: 0x%02x, iid: 0x%02x, " -+ "rc: %d)\n", n->event.reg.target_category, ++ 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); @@ -7758,6 +8466,15 @@ index 0000000000000..9672518fe0a80 +} +EXPORT_SYMBOL_GPL(ssam_notifier_register); + ++/** ++ * ssam_notifier_unregister - Unregister an event notifier. ++ * @ctrl: The controller the notifier has been registered on. ++ * @n: The event notifier to unregister. ++ * ++ * Unregister an event notifier and decrement the usage counter of the ++ * associated SAM event. If the usage counter reaches zero, the event will be ++ * disabled. ++ */ +int ssam_notifier_unregister(struct ssam_controller *ctrl, + struct ssam_event_notifier *n) +{ @@ -7780,8 +8497,8 @@ index 0000000000000..9672518fe0a80 + return rc; + } + -+ ssam_dbg(ctrl, "disabling event (reg: 0x%02x, tc: 0x%02x, iid: 0x%02x, " -+ "rc: %d)\n", n->event.reg.target_category, ++ 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) { @@ -7797,6 +8514,13 @@ index 0000000000000..9672518fe0a80 +} +EXPORT_SYMBOL_GPL(ssam_notifier_unregister); + ++/** ++ * ssam_notifier_empty - Check if there are any registered notifiers. ++ * @ctrl: The controller to check on. ++ * ++ * Return true if there are currently no notifiers registered on the ++ * controller, false otherwise. ++ */ +static bool ssam_notifier_empty(struct ssam_controller *ctrl) +{ + struct ssam_nf *nf = &ctrl->cplt.event.notif; @@ -7809,6 +8533,15 @@ index 0000000000000..9672518fe0a80 + return result; +} + ++/** ++ * ssam_notifier_unregister_all - Unregister all currently registered ++ * notifiers. ++ * @ctrl: The controller to unregister the notifiers on. ++ * ++ * Unregisters all currently registered notifiers. This function is used to ++ * ensure that all notifiers will be unregistered and assocaited ++ * entries/resources freed when the controller is being shut down. ++ */ +static void ssam_notifier_unregister_all(struct ssam_controller *ctrl) +{ + struct ssam_nf *nf = &ctrl->cplt.event.notif; @@ -7833,28 +8566,58 @@ index 0000000000000..9672518fe0a80 + + 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. Detect wakeup events during this process, go back to -+ // sleep if no wakeup event has been received. ++ /* ++ * 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. Detect wakeup events during this process, go back to ++ * sleep if no wakeup event has been received. ++ */ + + return IRQ_HANDLED; +} + ++/** ++ * ssam_irq_setup - Set up SAM EC wakeup-GPIO interrupt. ++ * @ctrl: The controller for which the IRQ should be set up. ++ * ++ * Set up an IRQ for the wakeup-GPIO pin of the SAM EC. This IRQ can be used ++ * to wake the device from a low power state. ++ * ++ * Note that this IRQ can only be triggered while the EC is in the display-off ++ * state. In this state, events are not sent to the host in the usual way. ++ * Instead the wakeup-GPIO gets pulled to "high" as long as there are pending ++ * events and these events need to be released one-by-one via the GPIO ++ * callback request, either until there are no events left and the GPIO is ++ * reset, or all at once by transitioning the EC out of the display-off state, ++ * which will also clear the GPIO. ++ * ++ * Not all events, however, should trigger a full system wakeup. Instead the ++ * driver should, if necessary, inspect and forward each event to the ++ * corresponding subsystem, which in turn should decide if the system needs to ++ * be woken up. This logic has not been implemented yet, thus wakeup by this ++ * IRQ should be disabled by default to avoid spurious wake-ups, caused, for ++ * example, by the remaining battery percentage changing. Refer to comments in ++ * this function and comments in the corresponding IRQ handler for more ++ * details on how this should be implemented. ++ * ++ * See also ssam_ctrl_notif_display_off() and ssam_ctrl_notif_display_off() ++ * for functions to transition the EC into and out of the display-off state as ++ * well as more details on it. ++ */ +int ssam_irq_setup(struct ssam_controller *ctrl) +{ + struct device *dev = ssam_controller_device(ctrl); @@ -7894,6 +8657,12 @@ index 0000000000000..9672518fe0a80 + return 0; +} + ++/** ++ * ssam_irq_free - Free SAM EC wakeup-GPIO interrupt. ++ * @ctrl: The controller for which the IRQ should be freed. ++ * ++ * Free the wakeup-GPIO IRQ previously set-up via ssam_irq_setup(). ++ */ +void ssam_irq_free(struct ssam_controller *ctrl) +{ + free_irq(ctrl->irq.num, ctrl); @@ -7901,10 +8670,10 @@ index 0000000000000..9672518fe0a80 +} diff --git a/drivers/misc/surface_sam/controller.h b/drivers/misc/surface_sam/controller.h new file mode 100644 -index 0000000000000..9212243b526d4 +index 0000000000000..051dfeff41ad7 --- /dev/null +++ b/drivers/misc/surface_sam/controller.h -@@ -0,0 +1,170 @@ +@@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _SSAM_CONTROLLER_H @@ -7929,10 +8698,18 @@ index 0000000000000..9212243b526d4 + +/* -- Safe counters. -------------------------------------------------------- */ + ++/** ++ * struct ssh_seq_counter - Safe counter for SSH sequence IDs. ++ * @value: The current counter value. ++ */ +struct ssh_seq_counter { + u8 value; +}; + ++/** ++ * struct ssh_rqid_counter - Safe counter for SSH request IDs. ++ * @value: The current counter value. ++ */ +struct ssh_rqid_counter { + u16 value; +}; @@ -7940,11 +8717,26 @@ index 0000000000000..9212243b526d4 + +/* -- Event/notification system. -------------------------------------------- */ + ++/** ++ * struct ssam_nf_head - Notifier head for SSAM events. ++ * @srcu: The SRCU struct for synchronization. ++ * @head: Head-pointer for the single-linked list of notifier blocks registered ++ * under this head. ++ */ +struct ssam_nf_head { + struct srcu_struct srcu; + struct ssam_notifier_block __rcu *head; +}; + ++/** ++ * struct ssam_nf - Notifier callback- and activation-registry for SSAM events. ++ * @lock: Lock guarding (de-)registration of notifier blocks. Note: This ++ * lock does not need to be held for notifier calls, only ++ * registration and deregistration. ++ * @refcount: The root of the RB-tree used for reference-counting enabled ++ * events/notifications. ++ * @head: The list of notifier heads for event/notifiaction callbacks. ++ */ +struct ssam_nf { + struct mutex lock; + struct rb_root refcount; @@ -7955,20 +8747,32 @@ index 0000000000000..9212243b526d4 +/* -- Event/async request completion system. -------------------------------- */ + +struct ssam_cplt; -+struct ssam_event_item; -+ -+struct ssam_event_item_ops { -+ void (*free)(struct ssam_event_item *); -+}; + ++/** ++ * struct ssam_event_item - Struct for event queuing and completion. ++ * @node: The node in the queue. ++ * @rqid: The request ID of the event. ++ * @ops.free: Callback for freeing this event item. ++ * @event: Actual event data. ++ */ +struct ssam_event_item { + struct list_head node; + u16 rqid; + -+ struct ssam_event_item_ops ops; ++ struct { ++ void (*free)(struct ssam_event_item *event); ++ } ops; ++ + struct ssam_event event; // must be last +}; + ++/** ++ * struct ssam_event_queue - Queue for completing received events. ++ * @cplt: Reference to the completion system on which this queue is active. ++ * @lock: The lock for any operation on the queue. ++ * @head: The list-head of the queue. ++ * @work: The &struct work_struct performing completion work for this queue. ++ */ +struct ssam_event_queue { + struct ssam_cplt *cplt; + @@ -7977,16 +8781,29 @@ index 0000000000000..9212243b526d4 + struct work_struct work; +}; + -+struct ssam_event_channel { ++/** ++ * struct ssam_event_target - Set of queues for a single SSH target ID. ++ * @queue: The array of queues, one queue per event ID. ++ */ ++struct ssam_event_target { + struct ssam_event_queue queue[SSH_NUM_EVENTS]; +}; + ++/** ++ * struct ssam_cplt - SSAM event/async request completion system. ++ * @dev: The device with which this system is associated. Only used ++ * for logging. ++ * @wq: The &struct workqueue_struct on which all completion work ++ * items are queued. ++ * @event.target: Array of &struct ssam_event_target, one for each target. ++ * @event.notif: Notifier callbacks and event activation reference counting. ++ */ +struct ssam_cplt { + struct device *dev; + struct workqueue_struct *wq; + + struct { -+ struct ssam_event_channel channel[SSH_NUM_CHANNELS]; ++ struct ssam_event_target target[SSH_NUM_TARGETS]; + struct ssam_nf notif; + } event; +}; @@ -7994,6 +8811,19 @@ index 0000000000000..9212243b526d4 + +/* -- Main SSAM device structures. ------------------------------------------ */ + ++/** ++ * enum ssam_controller_state - State values for &struct ssam_controller. ++ * @SSAM_CONTROLLER_UNINITIALIZED: ++ * The controller has not been initialized yet or has been de-initialized. ++ * @SSAM_CONTROLLER_INITIALIZED: ++ * The controller is initialized, but has not been started yet. ++ * @SSAM_CONTROLLER_STARTED: ++ * The controller has been started and is ready to use. ++ * @SSAM_CONTROLLER_STOPPED: ++ * The controller has been stopped. ++ * @SSAM_CONTROLLER_SUSPENDED: ++ * The controller has been suspended. ++ */ +enum ssam_controller_state { + SSAM_CONTROLLER_UNINITIALIZED, + SSAM_CONTROLLER_INITIALIZED, @@ -8002,11 +8832,29 @@ index 0000000000000..9212243b526d4 + SSAM_CONTROLLER_SUSPENDED, +}; + ++/** ++ * struct ssam_device_caps - Controller device capabilities. ++ * @notif_display: The controller supports display-on/-off notifications. ++ * @notif_d0exit: The controller supports D0-entry/D0-exit notifications ++ */ +struct ssam_device_caps { + u32 notif_display:1; + u32 notif_d0exit:1; +}; + ++/** ++ * struct ssam_controller - SSAM controller device. ++ * @kref: Reference count of the controller. ++ * @lock: Main lock for the controller, used to guard state changes. ++ * @state: Controller state. ++ * @rtl: Request transport layer for SSH I/O. ++ * @cplt: Completion system for SSH/SSAM events and asynchronous requests. ++ * @counter.seq: Sequence ID counter. ++ * @counter.rqid: Request ID counter. ++ * @irq.num: The wakeup IRQ number. ++ * @irq.wakeup_enabled: Whether wakeup by IRQ is enabled during suspend. ++ * @caps: The controller device capabilities. ++ */ +struct ssam_controller { + struct kref kref; + @@ -8038,6 +8886,18 @@ index 0000000000000..9212243b526d4 +#define ssam_err(ctrl, fmt, ...) rtl_err(&(ctrl)->rtl, fmt, ##__VA_ARGS__) + + ++/** ++ * ssam_controller_receive_buf - Provide input-data to the controller. ++ * @ctrl: The controller. ++ * @buf: The input buffer. ++ * @n: The number of bytes in the input buffer. ++ * ++ * Provide input data to be evaluated by the controller, which has been ++ * received via the lower-level transport. ++ * ++ * Returns the number of bytes consumed, or, if the packet transition ++ * layer of the controller has been shut down, -ESHUTDOWN. ++ */ +static inline +int ssam_controller_receive_buf(struct ssam_controller *ctrl, + const unsigned char *buf, size_t n) @@ -8045,6 +8905,11 @@ index 0000000000000..9212243b526d4 + return ssh_ptl_rx_rcvbuf(&ctrl->rtl.ptl, buf, n); +} + ++/** ++ * ssam_controller_write_wakeup - Notify the controller that the underlying ++ * device has space avaliable for data to be written. ++ * @ctrl: The controller. ++ */ +static inline void ssam_controller_write_wakeup(struct ssam_controller *ctrl) +{ + ssh_ptl_tx_wakeup(&ctrl->rtl.ptl, true); @@ -8077,10 +8942,10 @@ index 0000000000000..9212243b526d4 +#endif /* _SSAM_CONTROLLER_H */ diff --git a/drivers/misc/surface_sam/core.c b/drivers/misc/surface_sam/core.c new file mode 100644 -index 0000000000000..f8685e5d2bf36 +index 0000000000000..8d5f37d32ef90 --- /dev/null +++ b/drivers/misc/surface_sam/core.c -@@ -0,0 +1,554 @@ +@@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System @@ -8110,14 +8975,15 @@ index 0000000000000..f8685e5d2bf36 +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); ++ struct ssam_controller *ctrl; ++ ++ 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); ++ ssam_controller_write_wakeup(serdev_device_get_drvdata(dev)); +} + +static const struct serdev_device_ops ssam_serdev_ops = { @@ -8151,8 +9017,8 @@ index 0000000000000..f8685e5d2bf36 + + // serdev currently only supports RTSCTS flow control + 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); ++ dev_warn(&serdev->dev, "setup: unsupported flow control (value: 0x%02x)\n", ++ uart->flow_control); + } + + // set RTSCTS flow control @@ -8171,14 +9037,14 @@ index 0000000000000..f8685e5d2bf36 + status = serdev_device_set_parity(serdev, SERDEV_PARITY_ODD); + break; + default: -+ dev_warn(&serdev->dev, "setup: unsupported parity" -+ " (value: 0x%02x)\n", uart->parity); ++ dev_warn(&serdev->dev, "setup: unsupported parity (value: 0x%02x)\n", ++ uart->parity); + break; + } + + if (status) { -+ dev_err(&serdev->dev, "setup: failed to set parity" -+ " (value: 0x%02x)\n", uart->parity); ++ dev_err(&serdev->dev, "setup: failed to set parity (value: 0x%02x)\n", ++ uart->parity); + return status; + } + @@ -8306,7 +9172,7 @@ index 0000000000000..f8685e5d2bf36 + +/* -- Static controller reference. ------------------------------------------ */ + -+static struct ssam_controller *__ssam_controller = NULL; ++static struct ssam_controller *__ssam_controller; +static DEFINE_SPINLOCK(__ssam_controller_lock); + +struct ssam_controller *ssam_get_controller(void) @@ -8356,13 +9222,15 @@ index 0000000000000..f8685e5d2bf36 + struct device_link *link; + struct device *ctrldev; + -+ if (smp_load_acquire(&c->state) != SSAM_CONTROLLER_STARTED) ++ if (READ_ONCE(c->state) != SSAM_CONTROLLER_STARTED) + return -ENXIO; + -+ if ((ctrldev = ssam_controller_device(c)) == NULL) ++ ctrldev = ssam_controller_device(c); ++ if (!ctrldev) + return -ENXIO; + -+ if ((link = device_link_add(client, ctrldev, flags)) == NULL) ++ link = device_link_add(client, ctrldev, flags); ++ if (!link) + return -ENOMEM; + + /* @@ -8637,7 +9505,7 @@ index 0000000000000..f8685e5d2bf36 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/ssam_trace.h b/drivers/misc/surface_sam/ssam_trace.h new file mode 100644 -index 0000000000000..b2a4507b3c9a6 +index 0000000000000..36c2ef6ee7ac0 --- /dev/null +++ b/drivers/misc/surface_sam/ssam_trace.h @@ -0,0 +1,588 @@ @@ -8734,9 +9602,9 @@ index 0000000000000..b2a4507b3c9a6 +#ifndef _SURFACE_SAM_SSH_TRACE_HELPERS +#define _SURFACE_SAM_SSH_TRACE_HELPERS + -+static inline void ssam_trace_ptr_uid(const void *ptr, char* uid_str) ++static inline void ssam_trace_ptr_uid(const void *ptr, char *uid_str) +{ -+ char buf[2 * sizeof(void*) + 1]; ++ char buf[2 * sizeof(void *) + 1]; + + snprintf(buf, ARRAY_SIZE(buf), "%p", ptr); + memcpy(uid_str, &buf[ARRAY_SIZE(buf) - SSAM_PTR_UID_LEN], @@ -8775,16 +9643,16 @@ index 0000000000000..b2a4507b3c9a6 + +#define ssam_show_generic_u8_field(value) \ + __print_symbolic(value, \ -+ { SSAM_U8_FIELD_NOT_APPLICABLE, "N/A" } \ ++ { SSAM_U8_FIELD_NOT_APPLICABLE, "N/A" } \ + ) + + +#define ssam_show_frame_type(ty) \ + __print_symbolic(ty, \ -+ { SSH_FRAME_TYPE_DATA_SEQ, "DSEQ" }, \ -+ { SSH_FRAME_TYPE_DATA_NSQ, "DNSQ" }, \ -+ { SSH_FRAME_TYPE_ACK, "ACK" }, \ -+ { SSH_FRAME_TYPE_NAK, "NAK" } \ ++ { SSH_FRAME_TYPE_DATA_SEQ, "DSEQ" }, \ ++ { SSH_FRAME_TYPE_DATA_NSQ, "DNSQ" }, \ ++ { SSH_FRAME_TYPE_ACK, "ACK" }, \ ++ { SSH_FRAME_TYPE_NAK, "NAK" } \ + ) + +#define ssam_show_packet_type(type) \ @@ -8796,19 +9664,19 @@ index 0000000000000..b2a4507b3c9a6 + +#define ssam_show_packet_state(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" }, \ -+ { BIT(SSH_PACKET_SF_TRANSMITTING_BIT), "S" }, \ -+ { BIT(SSH_PACKET_SF_TRANSMITTED_BIT), "T" }, \ -+ { BIT(SSH_PACKET_SF_ACKED_BIT), "A" }, \ -+ { BIT(SSH_PACKET_SF_CANCELED_BIT), "C" }, \ -+ { BIT(SSH_PACKET_SF_COMPLETED_BIT), "F" } \ ++ { BIT(SSH_PACKET_SF_LOCKED_BIT), "L" }, \ ++ { BIT(SSH_PACKET_SF_QUEUED_BIT), "Q" }, \ ++ { BIT(SSH_PACKET_SF_PENDING_BIT), "P" }, \ ++ { BIT(SSH_PACKET_SF_TRANSMITTING_BIT), "S" }, \ ++ { BIT(SSH_PACKET_SF_TRANSMITTED_BIT), "T" }, \ ++ { BIT(SSH_PACKET_SF_ACKED_BIT), "A" }, \ ++ { BIT(SSH_PACKET_SF_CANCELED_BIT), "C" }, \ ++ { BIT(SSH_PACKET_SF_COMPLETED_BIT), "F" } \ + ) + +#define ssam_show_packet_seq(seq) \ + __print_symbolic(seq, \ -+ { SSAM_SEQ_NOT_APPLICABLE, "N/A" } \ ++ { SSAM_SEQ_NOT_APPLICABLE, "N/A" } \ + ) + + @@ -8820,57 +9688,57 @@ index 0000000000000..b2a4507b3c9a6 + +#define ssam_show_request_state(flags) \ + __print_flags(flags & SSH_REQUEST_FLAGS_SF_MASK, "", \ -+ { BIT(SSH_REQUEST_SF_LOCKED_BIT), "L" }, \ -+ { BIT(SSH_REQUEST_SF_QUEUED_BIT), "Q" }, \ -+ { BIT(SSH_REQUEST_SF_PENDING_BIT), "P" }, \ ++ { BIT(SSH_REQUEST_SF_LOCKED_BIT), "L" }, \ ++ { BIT(SSH_REQUEST_SF_QUEUED_BIT), "Q" }, \ ++ { BIT(SSH_REQUEST_SF_PENDING_BIT), "P" }, \ + { BIT(SSH_REQUEST_SF_TRANSMITTING_BIT), "S" }, \ -+ { BIT(SSH_REQUEST_SF_TRANSMITTED_BIT), "T" }, \ -+ { BIT(SSH_REQUEST_SF_RSPRCVD_BIT), "A" }, \ -+ { BIT(SSH_REQUEST_SF_CANCELED_BIT), "C" }, \ -+ { BIT(SSH_REQUEST_SF_COMPLETED_BIT), "F" } \ ++ { BIT(SSH_REQUEST_SF_TRANSMITTED_BIT), "T" }, \ ++ { BIT(SSH_REQUEST_SF_RSPRCVD_BIT), "A" }, \ ++ { BIT(SSH_REQUEST_SF_CANCELED_BIT), "C" }, \ ++ { BIT(SSH_REQUEST_SF_COMPLETED_BIT), "F" } \ + ) + +#define ssam_show_request_id(rqid) \ + __print_symbolic(rqid, \ -+ { SSAM_RQID_NOT_APPLICABLE, "N/A" } \ ++ { SSAM_RQID_NOT_APPLICABLE, "N/A" } \ + ) + +#define ssam_show_ssh_tc(rqid) \ + __print_symbolic(rqid, \ -+ { SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \ -+ { SSAM_SSH_TC_SAM, "SAM" }, \ -+ { SSAM_SSH_TC_BAT, "BAT" }, \ -+ { SSAM_SSH_TC_TMP, "TMP" }, \ -+ { SSAM_SSH_TC_PMC, "PMC" }, \ -+ { SSAM_SSH_TC_FAN, "FAN" }, \ -+ { SSAM_SSH_TC_PoM, "PoM" }, \ -+ { SSAM_SSH_TC_DBG, "DBG" }, \ -+ { SSAM_SSH_TC_KBD, "KBD" }, \ -+ { SSAM_SSH_TC_FWU, "FWU" }, \ -+ { SSAM_SSH_TC_UNI, "UNI" }, \ -+ { SSAM_SSH_TC_LPC, "LPC" }, \ -+ { SSAM_SSH_TC_TCL, "TCL" }, \ -+ { SSAM_SSH_TC_SFL, "SFL" }, \ -+ { SSAM_SSH_TC_KIP, "KIP" }, \ -+ { SSAM_SSH_TC_EXT, "EXT" }, \ -+ { SSAM_SSH_TC_BLD, "BLD" }, \ -+ { SSAM_SSH_TC_BAS, "BAS" }, \ -+ { SSAM_SSH_TC_SEN, "SEN" }, \ -+ { SSAM_SSH_TC_SRQ, "SRQ" }, \ -+ { SSAM_SSH_TC_MCU, "MCU" }, \ -+ { SSAM_SSH_TC_HID, "HID" }, \ -+ { SSAM_SSH_TC_TCH, "TCH" }, \ -+ { SSAM_SSH_TC_BKL, "BKL" }, \ -+ { SSAM_SSH_TC_TAM, "TAM" }, \ -+ { SSAM_SSH_TC_ACC, "ACC" }, \ -+ { SSAM_SSH_TC_UFI, "UFI" }, \ -+ { SSAM_SSH_TC_USC, "USC" }, \ -+ { SSAM_SSH_TC_PEN, "PEN" }, \ -+ { SSAM_SSH_TC_VID, "VID" }, \ -+ { SSAM_SSH_TC_AUD, "AUD" }, \ -+ { SSAM_SSH_TC_SMC, "SMC" }, \ -+ { SSAM_SSH_TC_KPD, "KPD" }, \ -+ { SSAM_SSH_TC_REG, "REG" } \ ++ { SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \ ++ { SSAM_SSH_TC_SAM, "SAM" }, \ ++ { SSAM_SSH_TC_BAT, "BAT" }, \ ++ { SSAM_SSH_TC_TMP, "TMP" }, \ ++ { SSAM_SSH_TC_PMC, "PMC" }, \ ++ { SSAM_SSH_TC_FAN, "FAN" }, \ ++ { SSAM_SSH_TC_PoM, "PoM" }, \ ++ { SSAM_SSH_TC_DBG, "DBG" }, \ ++ { SSAM_SSH_TC_KBD, "KBD" }, \ ++ { SSAM_SSH_TC_FWU, "FWU" }, \ ++ { SSAM_SSH_TC_UNI, "UNI" }, \ ++ { SSAM_SSH_TC_LPC, "LPC" }, \ ++ { SSAM_SSH_TC_TCL, "TCL" }, \ ++ { SSAM_SSH_TC_SFL, "SFL" }, \ ++ { SSAM_SSH_TC_KIP, "KIP" }, \ ++ { SSAM_SSH_TC_EXT, "EXT" }, \ ++ { SSAM_SSH_TC_BLD, "BLD" }, \ ++ { SSAM_SSH_TC_BAS, "BAS" }, \ ++ { SSAM_SSH_TC_SEN, "SEN" }, \ ++ { SSAM_SSH_TC_SRQ, "SRQ" }, \ ++ { SSAM_SSH_TC_MCU, "MCU" }, \ ++ { SSAM_SSH_TC_HID, "HID" }, \ ++ { SSAM_SSH_TC_TCH, "TCH" }, \ ++ { SSAM_SSH_TC_BKL, "BKL" }, \ ++ { SSAM_SSH_TC_TAM, "TAM" }, \ ++ { SSAM_SSH_TC_ACC, "ACC" }, \ ++ { SSAM_SSH_TC_UFI, "UFI" }, \ ++ { SSAM_SSH_TC_USC, "USC" }, \ ++ { SSAM_SSH_TC_PEN, "PEN" }, \ ++ { SSAM_SSH_TC_VID, "VID" }, \ ++ { SSAM_SSH_TC_AUD, "AUD" }, \ ++ { SSAM_SSH_TC_SMC, "SMC" }, \ ++ { SSAM_SSH_TC_KPD, "KPD" }, \ ++ { SSAM_SSH_TC_REG, "REG" } \ + ) + + @@ -9162,7 +10030,7 @@ index 0000000000000..b2a4507b3c9a6 + + +DECLARE_EVENT_CLASS(ssam_generic_uint_class, -+ TP_PROTO(const char* property, unsigned int value), ++ TP_PROTO(const char *property, unsigned int value), + + TP_ARGS(property, value), + @@ -9181,7 +10049,7 @@ index 0000000000000..b2a4507b3c9a6 + +#define DEFINE_SSAM_GENERIC_UINT_EVENT(name) \ + DEFINE_EVENT(ssam_generic_uint_class, ssam_##name, \ -+ TP_PROTO(const char* property, unsigned int value), \ ++ TP_PROTO(const char *property, unsigned int value), \ + TP_ARGS(property, value) \ + ) + @@ -9231,7 +10099,7 @@ index 0000000000000..b2a4507b3c9a6 +#include diff --git a/drivers/misc/surface_sam/ssh_msgb.h b/drivers/misc/surface_sam/ssh_msgb.h new file mode 100644 -index 0000000000000..69b13dbdf939a +index 0000000000000..d433be547e31c --- /dev/null +++ b/drivers/misc/surface_sam/ssh_msgb.h @@ -0,0 +1,132 @@ @@ -9351,8 +10219,8 @@ index 0000000000000..69b13dbdf939a + + cmd->type = SSH_PLD_TYPE_CMD; + cmd->tc = rqst->target_category; -+ cmd->chn_out = rqst->channel; -+ cmd->chn_in = 0x00; ++ cmd->tid_out = rqst->target_id; ++ cmd->tid_in = 0x00; + cmd->iid = rqst->instance_id; + put_unaligned_le16(rqid, &cmd->rqid); + cmd->cid = rqst->command_id; @@ -9369,15 +10237,15 @@ index 0000000000000..69b13dbdf939a +#endif /* _SSAM_SSH_MSGB_H */ diff --git a/drivers/misc/surface_sam/ssh_packet_layer.c b/drivers/misc/surface_sam/ssh_packet_layer.c new file mode 100644 -index 0000000000000..d48297817cec1 +index 0000000000000..a25d9785ff20c --- /dev/null +++ b/drivers/misc/surface_sam/ssh_packet_layer.c -@@ -0,0 +1,1753 @@ +@@ -0,0 +1,1780 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include -+#include ++#include +#include +#include +#include @@ -9953,6 +10821,11 @@ index 0000000000000..d48297817cec1 + return; + + WRITE_ONCE(packet->timestamp, timestamp); ++ /* ++ * Ensure timestamp is set before starting the reaper. Paired with ++ * implicit barrier following check on ssh_packet_get_expiration in ++ * ssh_ptl_timeout_reap. ++ */ + smp_mb__after_atomic(); + + ssh_ptl_timeout_reaper_mod(packet->ptl, timestamp, timestamp + timeout); @@ -10182,11 +11055,8 @@ index 0000000000000..d48297817cec1 + + list_del(&p->queue_node); + -+ /* -+ * Ensure that the "queued" bit gets cleared after setting the -+ * "transmitting" bit to guaranteee non-zero flags. -+ */ + set_bit(SSH_PACKET_SF_TRANSMITTING_BIT, &p->state); ++ // ensure that state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state); + @@ -10231,11 +11101,9 @@ index 0000000000000..d48297817cec1 + + ptl_dbg(ptl, "ptl: successfully transmitted packet %p\n", packet); + -+ /* -+ * Transition to state to "transmitted". Ensure that the flags never get -+ * zero with barrier. -+ */ ++ // transition state to "transmitted" + set_bit(SSH_PACKET_SF_TRANSMITTED_BIT, &packet->state); ++ // ensure that state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_PACKET_SF_TRANSMITTING_BIT, &packet->state); + @@ -10254,11 +11122,9 @@ index 0000000000000..d48297817cec1 + +static void ssh_ptl_tx_compl_error(struct ssh_packet *packet, int status) +{ -+ /* -+ * Transmission failure: Lock the packet and try to complete it. Ensure -+ * that the flags never get zero with barrier. -+ */ ++ // transmission failure: lock the packet and try to complete it + set_bit(SSH_PACKET_SF_LOCKED_BIT, &packet->state); ++ // ensure that state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_PACKET_SF_TRANSMITTING_BIT, &packet->state); + @@ -10358,6 +11224,12 @@ index 0000000000000..d48297817cec1 + + if (force || atomic_read(&ptl->pending.count) < SSH_PTL_MAX_PENDING) { + WRITE_ONCE(ptl->tx.thread_signal, true); ++ /* ++ * Ensure that the signal is set before we wake the transmitter ++ * thread to prevent lost updates: If the signal is not set, ++ * when the thread checks it in ssh_ptl_tx_threadfn_wait, it ++ * may go back to sleep. ++ */ + smp_mb__after_atomic(); + wake_up(&ptl->tx.thread_wq); + } @@ -10401,8 +11273,8 @@ index 0000000000000..d48297817cec1 + continue; + + /* -+ * In case we receive an ACK while handling a transmission error -+ * completion. The packet will be removed shortly. ++ * In case we receive an ACK while handling a transmission ++ * error completion. The packet will be removed shortly. + */ + if (unlikely(test_bit(SSH_PACKET_SF_LOCKED_BIT, &p->state))) { + packet = ERR_PTR(-EPERM); @@ -10410,10 +11282,11 @@ index 0000000000000..d48297817cec1 + } + + /* -+ * Mark packet as ACKed and remove it from pending. Ensure that -+ * the flags never get zero with barrier. ++ * Mark the packet as ACKed and remove it from pending by ++ * removing its node and decrementing the pending counter. + */ + set_bit(SSH_PACKET_SF_ACKED_BIT, &p->state); ++ // ensure that state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state); + @@ -10447,8 +11320,7 @@ index 0000000000000..d48297817cec1 + * The packet has not been found in the set of pending + * packets. + */ -+ ptl_warn(ptl, "ptl: received ACK for non-pending" -+ " packet\n"); ++ ptl_warn(ptl, "ptl: received ACK for non-pending packet\n"); + } else { + /* + * The packet is pending, but we are not allowed to take @@ -10480,8 +11352,7 @@ index 0000000000000..d48297817cec1 + } + + if (unlikely(!test_bit(SSH_PACKET_SF_TRANSMITTED_BIT, &p->state))) { -+ ptl_err(ptl, "ptl: received ACK before packet had been fully" -+ " transmitted\n"); ++ ptl_err(ptl, "ptl: received ACK before packet had been fully transmitted\n"); + status = -EREMOTEIO; + } + @@ -10525,6 +11396,9 @@ index 0000000000000..d48297817cec1 + return 0; +} + ++/* ++ * This function must be called with pending lock held. ++ */ +static void __ssh_ptl_resubmit(struct ssh_packet *packet) +{ + struct list_head *head; @@ -10542,8 +11416,13 @@ index 0000000000000..d48297817cec1 + // find first node with lower priority + head = __ssh_ptl_queue_find_entrypoint(packet); + ++ /* ++ * Reset the timestamp. This must be called and executed before the ++ * pending lock is released. The lock release should be a sufficient ++ * barrier for this operation, thus there is no need to manually add ++ * one here. ++ */ + WRITE_ONCE(packet->timestamp, KTIME_MAX); -+ smp_mb__after_atomic(); + + // add packet + list_add_tail(&ssh_packet_get(packet)->queue_node, head); @@ -10654,6 +11533,12 @@ index 0000000000000..d48297817cec1 + * packets to avoid lost-update type problems. + */ + WRITE_ONCE(ptl->rtx_timeout.expires, KTIME_MAX); ++ /* ++ * Ensure that the reaper is marked as deactivated before we continue ++ * checking packets to prevent lost-update problems when a packet is ++ * added to the pending set and ssh_ptl_timeout_reaper_mod is called ++ * during execution of the part below. ++ */ + smp_mb__after_atomic(); + + spin_lock(&ptl->pending.lock); @@ -10980,7 +11865,7 @@ index 0000000000000..d48297817cec1 + * + * Shuts down the packet transmission layer, removing and canceling all queued + * and pending packets. Packets canceled by this operation will be completed -+ * with -ESHUTDOWN as status. ++ * with -ESHUTDOWN as status. Receiver and transmitter threads will be stopped. + * + * As a result of this function, the transmission layer will be marked as shut + * down. Submission of packets after the transmission layer has been shut down @@ -10995,6 +11880,14 @@ index 0000000000000..d48297817cec1 + + // ensure that no new packets (including ACK/NAK) can be submitted + set_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state); ++ /* ++ * Ensure that the layer gets marked as shut-down before actually ++ * stopping it. In combination with the check in ssh_ptl_queue_push, ++ * this guarantees that no new packets can be added and all already ++ * queued packets are properly cancelled. In combination with the check ++ * in ssh_ptl_rx_rcvbuf, this guarantees that received data is properly ++ * cut off. ++ */ + smp_mb__after_atomic(); + + status = ssh_ptl_rx_stop(ptl); @@ -11031,6 +11924,7 @@ index 0000000000000..d48297817cec1 + spin_lock(&ptl->queue.lock); + list_for_each_entry_safe(p, n, &ptl->queue.head, queue_node) { + set_bit(SSH_PACKET_SF_LOCKED_BIT, &p->state); ++ // ensure that state does not get zero + smp_mb__before_atomic(); + clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state); + @@ -11043,6 +11937,7 @@ index 0000000000000..d48297817cec1 + spin_lock(&ptl->pending.lock); + list_for_each_entry_safe(p, n, &ptl->pending.head, pending_node) { + set_bit(SSH_PACKET_SF_LOCKED_BIT, &p->state); ++ // ensure that state does not get zero + smp_mb__before_atomic(); + clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state); + @@ -11128,7 +12023,7 @@ index 0000000000000..d48297817cec1 +} diff --git a/drivers/misc/surface_sam/ssh_packet_layer.h b/drivers/misc/surface_sam/ssh_packet_layer.h new file mode 100644 -index 0000000000000..e819243eb3fe4 +index 0000000000000..74e5adba888ea --- /dev/null +++ b/drivers/misc/surface_sam/ssh_packet_layer.h @@ -0,0 +1,125 @@ @@ -11218,7 +12113,7 @@ index 0000000000000..e819243eb3fe4 + do { \ + if ((p)) \ + func((p), fmt, ##__VA_ARGS__); \ -+ } while (0); ++ } while (0) + +#define ptl_dbg(p, fmt, ...) dev_dbg(&(p)->serdev->dev, fmt, ##__VA_ARGS__) +#define ptl_info(p, fmt, ...) dev_info(&(p)->serdev->dev, fmt, ##__VA_ARGS__) @@ -11259,7 +12154,7 @@ index 0000000000000..e819243eb3fe4 +#endif /* _SSAM_SSH_PACKET_LAYER_H */ diff --git a/drivers/misc/surface_sam/ssh_parser.c b/drivers/misc/surface_sam/ssh_parser.c new file mode 100644 -index 0000000000000..365e26b57838c +index 0000000000000..6958e80f36736 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_parser.c @@ -0,0 +1,132 @@ @@ -11300,11 +12195,11 @@ index 0000000000000..365e26b57838c + rem->ptr = src->ptr + src->len - 1; + rem->len = 1; + return false; -+ } else { -+ rem->ptr = src->ptr + src->len; -+ rem->len = 0; -+ return false; + } ++ ++ rem->ptr = src->ptr + src->len; ++ rem->len = 0; ++ return false; +} + +int sshp_parse_frame(const struct device *dev, const struct ssam_span *source, @@ -11390,8 +12285,8 @@ index 0000000000000..365e26b57838c + command_data->ptr = source->ptr + sizeof(struct ssh_command); + command_data->len = source->len - sizeof(struct ssh_command); + -+ dev_dbg(dev, "rx: parser: valid command found (tc: 0x%02x," -+ " cid: 0x%02x)\n", (*command)->tc, (*command)->cid); ++ dev_dbg(dev, "rx: parser: valid command found (tc: 0x%02x, cid: 0x%02x)\n", ++ (*command)->tc, (*command)->cid); + + return 0; +} @@ -11486,7 +12381,7 @@ index 0000000000000..27a684de4503b +#endif /* _SURFACE_SAM_SSH_PARSER_h */ diff --git a/drivers/misc/surface_sam/ssh_protocol.h b/drivers/misc/surface_sam/ssh_protocol.h new file mode 100644 -index 0000000000000..21bcbc71f4441 +index 0000000000000..e2122cd6128fa --- /dev/null +++ b/drivers/misc/surface_sam/ssh_protocol.h @@ -0,0 +1,65 @@ @@ -11509,12 +12404,12 @@ index 0000000000000..21bcbc71f4441 +#define SSH_NUM_EVENTS 34 + +/* -+ * The number of communication channels used in the protocol. ++ * The number of communication targets used in the protocol. + */ -+#define SSH_NUM_CHANNELS 2 ++#define SSH_NUM_TARGETS 2 + +/** -+ * SSH message syncrhonization (SYN) bytes. ++ * SSH message synchronization (SYN) bytes. + */ +#define SSH_MSG_SYN ((u16)0x55aa) + @@ -11544,28 +12439,28 @@ index 0000000000000..21bcbc71f4441 + return tc; +} + -+static inline u8 ssh_channel_to_index(u8 channel) ++static inline u8 ssh_tid_to_index(u8 tid) +{ -+ return channel - 1u; ++ return tid - 1u; +} + -+static inline bool ssh_channel_is_valid(u8 channel) ++static inline bool ssh_tid_is_valid(u8 tid) +{ -+ return ssh_channel_to_index(channel) < SSH_NUM_CHANNELS; ++ return ssh_tid_to_index(tid) < SSH_NUM_TARGETS; +} + +#endif /* _SSAM_SSH_PROTOCOL_H */ diff --git a/drivers/misc/surface_sam/ssh_request_layer.c b/drivers/misc/surface_sam/ssh_request_layer.c new file mode 100644 -index 0000000000000..419fbd0aa4c50 +index 0000000000000..4ecf286d1dcf2 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_request_layer.c -@@ -0,0 +1,1074 @@ +@@ -0,0 +1,1099 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include -+#include ++#include +#include +#include +#include @@ -11704,8 +12599,8 @@ index 0000000000000..419fbd0aa4c50 + trace_ssam_request_complete(rqst, status); + + // 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); ++ 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); @@ -11721,8 +12616,8 @@ index 0000000000000..419fbd0aa4c50 + + trace_ssam_request_complete(rqst, 0); + -+ rtl_dbg(rtl, "rtl: completing request with response" -+ " (rqid: 0x%04x)\n", ssh_request_get_rqid(rqst)); ++ rtl_dbg(rtl, "rtl: completing request with response (rqid: 0x%04x)\n", ++ ssh_request_get_rqid(rqst)); + + rqst->ops->complete(rqst, cmd, data, 0); +} @@ -11755,11 +12650,9 @@ index 0000000000000..419fbd0aa4c50 + break; + } + -+ /* -+ * Remove from queue and mark as transmitting. Ensure that the -+ * state does not get zero via memory barrier. -+ */ ++ // remove from queue and mark as transmitting + set_bit(SSH_REQUEST_SF_TRANSMITTING_BIT, &p->state); ++ // ensure state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &p->state); + @@ -11798,7 +12691,16 @@ index 0000000000000..419fbd0aa4c50 + * down. Complete it here. + */ + set_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state); -+ smp_mb__after_atomic(); ++ /* ++ * Note: A barrier is not required here, as there are only two ++ * references in the system at this point: The one that we have, ++ * and the other one that belongs to the pending set. Due to the ++ * request being marked as "transmitting", our process is the ++ * only one allowed to remove the pending node and change the ++ * state. Normally, the task would fall to the packet callback, ++ * but as this is a path where submission failed, this callback ++ * will never be executed. ++ */ + + ssh_rtl_pending_remove(rqst); + ssh_rtl_complete_with_status(rqst, -ESHUTDOWN); @@ -11936,6 +12838,11 @@ index 0000000000000..419fbd0aa4c50 + return; + + WRITE_ONCE(rqst->timestamp, timestamp); ++ /* ++ * Ensure timestamp is set before starting the reaper. Paired with ++ * implicit barrier following check on ssh_request_get_expiration in ++ * ssh_rtl_timeout_reap. ++ */ + smp_mb__after_atomic(); + + ssh_rtl_timeout_reaper_mod(rtl, timestamp, timestamp + timeout); @@ -11967,19 +12874,18 @@ index 0000000000000..419fbd0aa4c50 + spin_unlock(&rtl->pending.lock); + + trace_ssam_ei_rx_drop_response(p); -+ rtl_info(rtl, "request error injection: " -+ "dropping response for request %p\n", ++ rtl_info(rtl, "request error injection: dropping response for request %p\n", + &p->packet); + return; + } + + /* + * Mark as "response received" and "locked" as we're going to -+ * complete it. Ensure that the state doesn't get zero by -+ * employing a memory barrier. ++ * complete it. + */ + set_bit(SSH_REQUEST_SF_LOCKED_BIT, &p->state); + set_bit(SSH_REQUEST_SF_RSPRCVD_BIT, &p->state); ++ // ensure state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_REQUEST_SF_PENDING_BIT, &p->state); + @@ -11992,8 +12898,8 @@ index 0000000000000..419fbd0aa4c50 + spin_unlock(&rtl->pending.lock); + + if (!r) { -+ rtl_warn(rtl, "rtl: dropping unexpected command message" -+ " (rqid = 0x%04x)\n", rqid); ++ rtl_warn(rtl, "rtl: dropping unexpected command message (rqid = 0x%04x)\n", ++ rqid); + return; + } + @@ -12017,7 +12923,7 @@ index 0000000000000..419fbd0aa4c50 + * should never expect a response as ensured in ssh_rtl_submit. If this + * ever changes, one would have to test for + * -+ * (r->state & (transmitting | transmitted)) ++ * (r->state & (transmitting | transmitted)) + * + * on unsequenced packets to determine if they could have been + * transmitted. There are no synchronization guarantees as in the @@ -12025,8 +12931,8 @@ index 0000000000000..419fbd0aa4c50 + * run on the same thread. Thus an exact determination is impossible. + */ + if (!test_bit(SSH_REQUEST_SF_TRANSMITTED_BIT, &r->state)) { -+ rtl_err(rtl, "rtl: received response before ACK for request" -+ " (rqid = 0x%04x)\n", rqid); ++ rtl_err(rtl, "rtl: received response before ACK for request (rqid = 0x%04x)\n", ++ rqid); + + /* + * NB: Timeout has already been canceled, request already been @@ -12212,11 +13118,9 @@ index 0000000000000..419fbd0aa4c50 + return; + } + -+ /* -+ * Mark as transmitted, ensure that state doesn't get zero by inserting -+ * a memory barrier. -+ */ ++ // update state: mark as transmitted and clear transmitting + set_bit(SSH_REQUEST_SF_TRANSMITTED_BIT, &r->state); ++ // ensure state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_REQUEST_SF_TRANSMITTING_BIT, &r->state); + @@ -12271,6 +13175,12 @@ index 0000000000000..419fbd0aa4c50 + * requests to avoid lost-update type problems. + */ + WRITE_ONCE(rtl->rtx_timeout.expires, KTIME_MAX); ++ /* ++ * Ensure that the reaper is marked as deactivated before we continue ++ * checking requests to prevent lost-update problems when a request is ++ * added to the pending set and ssh_rtl_timeout_reaper_mod is called ++ * during execution of the part below. ++ */ + smp_mb__after_atomic(); + + spin_lock(&rtl->pending.lock); @@ -12367,8 +13277,8 @@ index 0000000000000..419fbd0aa4c50 + break; + + default: -+ ptl_err(p, "rtl: rx: unknown frame payload type" -+ " (type: 0x%02x)\n", data->ptr[0]); ++ ptl_err(p, "rtl: rx: unknown frame payload type (type: 0x%02x)\n", ++ data->ptr[0]); + break; + } +} @@ -12446,7 +13356,9 @@ index 0000000000000..419fbd0aa4c50 + +static void ssh_rtl_packet_release(struct ssh_packet *p) +{ -+ struct ssh_request *rqst = to_ssh_request(p, packet); ++ struct ssh_request *rqst; ++ ++ rqst = to_ssh_request(p, packet); + rqst->ops->release(rqst); +} + @@ -12539,7 +13451,7 @@ index 0000000000000..419fbd0aa4c50 + */ +int ssh_rtl_flush(struct ssh_rtl *rtl, unsigned long timeout) +{ -+ const unsigned init_flags = SSAM_REQUEST_UNSEQUENCED; ++ const unsigned int init_flags = SSAM_REQUEST_UNSEQUENCED; + struct ssh_flush_request rqst; + int status; + @@ -12576,12 +13488,19 @@ index 0000000000000..419fbd0aa4c50 + int pending; + + set_bit(SSH_RTL_SF_SHUTDOWN_BIT, &rtl->state); ++ /* ++ * Ensure that the layer gets marked as shut-down before actually ++ * stopping it. In combination with the check in ssh_rtl_sunmit, this ++ * guarantees that no new requests can be added and all already queued ++ * requests are properly cancelled. ++ */ + smp_mb__after_atomic(); + + // remove requests from queue + spin_lock(&rtl->queue.lock); + list_for_each_entry_safe(r, n, &rtl->queue.head, node) { + set_bit(SSH_REQUEST_SF_LOCKED_BIT, &r->state); ++ // ensure state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state); + @@ -12615,6 +13534,7 @@ index 0000000000000..419fbd0aa4c50 + 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); ++ // ensure state never gets zero + smp_mb__before_atomic(); + clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state); + @@ -12637,10 +13557,10 @@ index 0000000000000..419fbd0aa4c50 +} diff --git a/drivers/misc/surface_sam/ssh_request_layer.h b/drivers/misc/surface_sam/ssh_request_layer.h new file mode 100644 -index 0000000000000..adcbd32b06819 +index 0000000000000..ffbb43719f853 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_request_layer.h -@@ -0,0 +1,91 @@ +@@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _SSAM_SSH_REQUEST_LAYER_H @@ -12710,7 +13630,9 @@ index 0000000000000..adcbd32b06819 + +static inline struct ssh_rtl *ssh_request_rtl(struct ssh_request *rqst) +{ -+ struct ssh_ptl *ptl = READ_ONCE(rqst->packet.ptl); ++ struct ssh_ptl *ptl; ++ ++ ptl = READ_ONCE(rqst->packet.ptl); + return likely(ptl) ? to_ssh_rtl(ptl, ptl) : NULL; +} + @@ -12888,7 +13810,7 @@ index c66a04d24f1d3..1b18d12d217f8 100644 if (!ctrl->serdev) return -ENODEV; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h -index 610cdf8082f2e..69f4527315e71 100644 +index 610cdf8082f2e..d3b51c3488cba 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -768,4 +768,21 @@ struct typec_device_id { @@ -12897,7 +13819,7 @@ index 610cdf8082f2e..69f4527315e71 100644 +/* Surface System Aggregator Module */ + -+#define SSAM_MATCH_CHANNEL 0x1 ++#define SSAM_MATCH_TARGET 0x1 +#define SSAM_MATCH_INSTANCE 0x2 +#define SSAM_MATCH_FUNCTION 0x4 + @@ -12905,7 +13827,7 @@ index 610cdf8082f2e..69f4527315e71 100644 + __u8 match_flags; + + __u8 category; -+ __u8 channel; ++ __u8 target; + __u8 instance; + __u8 function; + @@ -12915,10 +13837,10 @@ index 610cdf8082f2e..69f4527315e71 100644 #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/surface_aggregator_module.h b/include/linux/surface_aggregator_module.h new file mode 100644 -index 0000000000000..81e54e5c8f9e9 +index 0000000000000..a86136382b385 --- /dev/null +++ b/include/linux/surface_aggregator_module.h -@@ -0,0 +1,892 @@ +@@ -0,0 +1,954 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Interface for Surface System Aggregator Module (SSAM) via Surface Serial @@ -12981,8 +13903,10 @@ index 0000000000000..81e54e5c8f9e9 +} __packed; + +/* -+ * Maximum SSH frame payload length in bytes. This is the physical maximum -+ * length of the protocol. Implementations may set a more constrained limit. ++ * SSH_FRAME_MAX_PAYLOAD_SIZE - Maximum SSH frame payload length in bytes. ++ * ++ * This is the physical maximum length of the protocol. Implementations may ++ * set a more constrained limit. + */ +#define SSH_FRAME_MAX_PAYLOAD_SIZE U16_MAX + @@ -13000,9 +13924,9 @@ index 0000000000000..81e54e5c8f9e9 + * @type: The type of the payload. See &enum ssh_payload_type. Should be + * SSH_PLD_TYPE_CMD for this struct. + * @tc: Command target category. -+ * @chn_out: Output channel. Should be zero if this an incoming (EC to host) ++ * @tid_out: Output target ID. Should be zero if this an incoming (EC to host) + * message. -+ * @chn_in: Input channel. Should be zero if this is an outgoing (hos to EC) ++ * @tid_in: Input target ID. Should be zero if this is an outgoing (hos to EC) + * message. + * @iid: Instance ID. + * @rqid: Request ID. Used to match requests with responses and differentiate @@ -13012,16 +13936,18 @@ index 0000000000000..81e54e5c8f9e9 +struct ssh_command { + u8 type; + u8 tc; -+ u8 chn_out; -+ u8 chn_in; ++ u8 tid_out; ++ u8 tid_in; + u8 iid; + __le16 rqid; + u8 cid; +} __packed; + -+/* -+ * Maximum SSH command payload length in bytes. This is the physical maximum -+ * length of the protocol. Implementations may set a more constrained limit. ++/** ++ * SSH_COMMAND_MAX_PAYLOAD_SIZE - Maximum SSH command payload length in bytes. ++ * ++ * This is the physical maximum length of the protocol. Implementations may ++ * set a more constrained limit. + */ +#define SSH_COMMAND_MAX_PAYLOAD_SIZE \ + (SSH_FRAME_MAX_PAYLOAD_SIZE - sizeof(struct ssh_command)) @@ -13044,36 +13970,51 @@ index 0000000000000..81e54e5c8f9e9 +} __packed; + +/** -+ * Base-length of a SSH message. This is the minimum number of bytes required -+ * to form a message. The actual message length is SSH_MSG_LEN_BASE plus the -+ * length of the frame payload. ++ * SSH_MSG_LEN_BASE - Base-length of a SSH message. ++ * ++ * This is the minimum number of bytes required to form a message. The actual ++ * message length is SSH_MSG_LEN_BASE plus the length of the frame payload. + */ +#define SSH_MSG_LEN_BASE (sizeof(struct ssh_frame) + 3ull * sizeof(u16)) + +/** -+ * Length of a SSH control message. ++ * SSH_MSG_LEN_CTRL - Length of a SSH control message. ++ * ++ * This is the length of a SSH control message, which is equal to a SSH ++ * message without any payload. + */ +#define SSH_MSG_LEN_CTRL SSH_MSG_LEN_BASE + +/** ++ * SSH_MESSAGE_LENGTH() - Comute lenght of SSH message. ++ * + * Length of a SSH message with payload of specified size. + */ +#define SSH_MESSAGE_LENGTH(payload_size) (SSH_MSG_LEN_BASE + payload_size) + +/** ++ * SSH_COMMAND_MESSAGE_LENGTH() - Compute length of SSH command message. ++ * + * Length of a SSH command message with command payload of specified size. + */ +#define SSH_COMMAND_MESSAGE_LENGTH(payload_size) \ + SSH_MESSAGE_LENGTH(sizeof(struct ssh_command) + payload_size) + +/** -+ * Offset of the specified struct ssh_frame field in the raw SSH message data. ++ * SSH_MSGOFFSET_FRAME() - Compute offset in SSH message to specified field in ++ * frame. ++ * ++ * Offset of the specified &struct ssh_frame field in the raw SSH message data. + */ +#define SSH_MSGOFFSET_FRAME(field) \ + (sizeof(u16) + offsetof(struct ssh_frame, field)) + +/** -+ * Offset of the specified struct ssh_command field in the raw SSH message data. ++ * SSH_MSGOFFSET_FRAME() - Compute offset in SSH message to specified field in ++ * command. ++ * ++ * Offset of the specified &struct ssh_command field in the raw SSH message ++ * data. + */ +#define SSH_MSGOFFSET_COMMAND(field) \ + (2ull * sizeof(u16) + sizeof(struct ssh_frame) \ @@ -13311,9 +14252,9 @@ index 0000000000000..81e54e5c8f9e9 + +struct ssam_event { + u8 target_category; ++ u8 target_id; + u8 command_id; + u8 instance_id; -+ u8 channel; + u16 length; + u8 data[0]; +}; @@ -13325,9 +14266,9 @@ index 0000000000000..81e54e5c8f9e9 + +struct ssam_request { + u8 target_category; ++ u8 target_id; + u8 command_id; + u8 instance_id; -+ u8 channel; + u16 flags; + u16 length; + const u8 *payload; @@ -13370,15 +14311,35 @@ index 0000000000000..81e54e5c8f9e9 + struct ssam_request_sync **rqst, + struct ssam_span *buffer); + ++void ssam_request_sync_free(struct ssam_request_sync *rqst); ++ +void ssam_request_sync_init(struct ssam_request_sync *rqst, + enum ssam_request_flags flags); + ++/** ++ * ssam_request_sync_set_data - Set message data of a synchronous request. ++ * @rqst: The request. ++ * @ptr: Pointer to the request message data. ++ * @len: Length of the request message data. ++ * ++ * Set the request message data of a synchronous request. The provided buffer ++ * needs to live until the request has been completed. ++ */ +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); +} + ++/** ++ * ssam_request_sync_set_resp - Set response buffer of a synchronous request. ++ * @rqst: The request. ++ * @rsp: The response buffer. ++ * ++ * Sets the response buffer ot a synchronous request. This buffer will store ++ * the response of the request after it has been completed. May be NULL if ++ * no response is expected. ++ */ +static inline void ssam_request_sync_set_resp(struct ssam_request_sync *rqst, + struct ssam_response *resp) +{ @@ -13388,6 +14349,19 @@ index 0000000000000..81e54e5c8f9e9 +int ssam_request_sync_submit(struct ssam_controller *ctrl, + struct ssam_request_sync *rqst); + ++/** ++ * ssam_request_sync_wait - Wait for completion of a synchronous request. ++ * @rqst: The request to wait for. ++ * ++ * Wait for completion and release of a synchronous request. After this ++ * function terminates, the request is guaranteed to have left the ++ * transmission system. After successful submission of a request, this ++ * function must be called before accessing the response of the request, ++ * freeing the request, or freeing any of the buffers associated with the ++ * request. ++ * ++ * Returns the status of the request. ++ */ +static inline int ssam_request_sync_wait(struct ssam_request_sync *rqst) +{ + wait_for_completion(&rqst->comp); @@ -13403,29 +14377,38 @@ index 0000000000000..81e54e5c8f9e9 + struct ssam_span *buf); + + ++/** ++ * ssam_request_sync_onstack - Execute a synchronous request on the stack. ++ * @ctrl: The controller via which the request is submitted. ++ * @rqst: The request specification. ++ * @rsp: The response buffer. ++ * @payload_len: The (maximum) request payload length. ++ * ++ * Allocates a synchronous request with specified payload length on the stack, ++ * fully intializes it via the provided request specification, submits it, and ++ * finally waits for its completion before returning its status. This helper ++ * macro essentially allocates the request message buffer on the stack and ++ * then calls ssam_request_sync_with_buffer(). ++ * ++ * Note: The ``payload_len`` parameter specifies the maximum payload length, ++ * used for buffer allocation. The actual payload length may be smaller. ++ * ++ * Returns the status of the request or any failure during setup. ++ */ +#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; \ ++ ssam_request_sync_with_buffer(ctrl, rqst, rsp, &__buf); \ + }) + + +struct ssam_request_spec { + u8 target_category; ++ u8 target_id; + u8 command_id; + u8 instance_id; -+ u8 channel; + u8 flags; +}; + @@ -13442,9 +14425,9 @@ index 0000000000000..81e54e5c8f9e9 + struct ssam_request rqst; \ + \ + rqst.target_category = s.target_category; \ ++ rqst.target_id = s.target_id; \ + 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; \ @@ -13459,9 +14442,9 @@ index 0000000000000..81e54e5c8f9e9 + struct ssam_request rqst; \ + \ + rqst.target_category = s.target_category; \ ++ rqst.target_id = s.target_id; \ + 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; \ @@ -13479,9 +14462,9 @@ index 0000000000000..81e54e5c8f9e9 + int status; \ + \ + rqst.target_category = s.target_category; \ ++ rqst.target_id = s.target_id; \ + 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; \ @@ -13496,9 +14479,9 @@ index 0000000000000..81e54e5c8f9e9 + \ + 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, \ ++ 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; \ + } \ @@ -13507,16 +14490,16 @@ index 0000000000000..81e54e5c8f9e9 + } + +#define SSAM_DEFINE_SYNC_REQUEST_MD_W(name, wtype, spec...) \ -+ int name(struct ssam_controller *ctrl, u8 chn, u8 iid, const wtype *in) \ ++ int name(struct ssam_controller *ctrl, u8 tid, 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.target_id = tid; \ + 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; \ @@ -13526,7 +14509,7 @@ index 0000000000000..81e54e5c8f9e9 + } + +#define SSAM_DEFINE_SYNC_REQUEST_MD_R(name, rtype, spec...) \ -+ int name(struct ssam_controller *ctrl, u8 chn, u8 iid, rtype *out) \ ++ int name(struct ssam_controller *ctrl, u8 tid, u8 iid, rtype *out) \ + { \ + struct ssam_request_spec_md s \ + = (struct ssam_request_spec_md)spec; \ @@ -13535,9 +14518,9 @@ index 0000000000000..81e54e5c8f9e9 + int status; \ + \ + rqst.target_category = s.target_category; \ ++ rqst.target_id = tid; \ + 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; \ @@ -13552,9 +14535,9 @@ index 0000000000000..81e54e5c8f9e9 + \ + 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, \ ++ 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; \ + } \ @@ -13602,7 +14585,7 @@ index 0000000000000..81e54e5c8f9e9 + +struct ssam_event_registry { + u8 target_category; -+ u8 channel; ++ u8 target_id; + u8 cid_enable; + u8 cid_disable; +}; @@ -13613,10 +14596,10 @@ index 0000000000000..81e54e5c8f9e9 +}; + + -+#define SSAM_EVENT_REGISTRY(tc, chn, cid_en, cid_dis) \ ++#define SSAM_EVENT_REGISTRY(tc, tid, cid_en, cid_dis) \ + ((struct ssam_event_registry) { \ + .target_category = (tc), \ -+ .channel = (chn), \ ++ .target_id = (tid), \ + .cid_enable = (cid_en), \ + .cid_disable = (cid_dis), \ + }) @@ -13659,34 +14642,35 @@ index 0000000000000..81e54e5c8f9e9 + +struct ssam_device_uid { + u8 category; -+ u8 channel; ++ u8 target; + u8 instance; + u8 function; +}; + -+#define SSAM_DUID(__cat, __chn, __iid, __fun) \ ++#define SSAM_DUID(__cat, __tid, __iid, __fun) \ + ((struct ssam_device_uid) { \ + .category = SSAM_SSH_TC_##__cat, \ -+ .channel = (__chn), \ ++ .target = (__tid), \ + .instance = (__iid), \ + .function = (__fun) \ + }) + +#define SSAM_DUID_NULL ((struct ssam_device_uid) { 0 }) + -+#define SSAM_ANY_CHN 0xffff ++#define SSAM_ANY_TID 0xffff +#define SSAM_ANY_IID 0xffff +#define SSAM_ANY_FUN 0xffff + -+#define SSAM_DEVICE(__cat, __chn, __iid, __fun) \ -+ .match_flags = (((__chn) != SSAM_ANY_CHN) ? SSAM_MATCH_CHANNEL : 0) \ ++#define SSAM_DEVICE(__cat, __tid, __iid, __fun) \ ++ .match_flags = (((__tid) != SSAM_ANY_TID) ? SSAM_MATCH_TARGET : 0) \ + | (((__iid) != SSAM_ANY_IID) ? SSAM_MATCH_INSTANCE : 0) \ + | (((__fun) != SSAM_ANY_FUN) ? SSAM_MATCH_FUNCTION : 0), \ + .category = SSAM_SSH_TC_##__cat, \ -+ .channel = ((__chn) != SSAM_ANY_CHN) ? (__chn) : 0, \ ++ .target = ((__tid) != SSAM_ANY_TID) ? (__tid) : 0, \ + .instance = ((__iid) != SSAM_ANY_IID) ? (__iid) : 0, \ + .function = ((__fun) != SSAM_ANY_FUN) ? (__fun) : 0 \ + ++ +static inline bool ssam_device_uid_equal(const struct ssam_device_uid u1, + const struct ssam_device_uid u2) +{ @@ -13711,8 +14695,8 @@ index 0000000000000..81e54e5c8f9e9 + + const struct ssam_device_id *match_table; + -+ int (*probe)(struct ssam_device *); -+ void (*remove)(struct ssam_device *); ++ int (*probe)(struct ssam_device *sdev); ++ void (*remove)(struct ssam_device *sdev); +}; + +extern struct bus_type ssam_bus_type; @@ -13789,7 +14773,7 @@ index 0000000000000..81e54e5c8f9e9 + SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_##name, wtype, spec) \ + int name(struct ssam_device *sdev, const wtype *in) \ + { \ -+ return __raw_##name(sdev->ctrl, sdev->uid.channel, \ ++ return __raw_##name(sdev->ctrl, sdev->uid.target, \ + sdev->uid.instance, in); \ + } + @@ -13797,7 +14781,7 @@ index 0000000000000..81e54e5c8f9e9 + SSAM_DEFINE_SYNC_REQUEST_MD_R(__raw_##name, rtype, spec) \ + int name(struct ssam_device *sdev, rtype *out) \ + { \ -+ return __raw_##name(sdev->ctrl, sdev->uid.channel, \ ++ return __raw_##name(sdev->ctrl, sdev->uid.target, \ + sdev->uid.instance, out); \ + } + @@ -13806,13 +14790,13 @@ index 0000000000000..81e54e5c8f9e9 + const struct ssam_event *event) +{ + return uid.category == event->target_category -+ && uid.channel == event->channel ++ && uid.target == event->target_id + && uid.instance == event->instance_id; +} + +#endif /* _SURFACE_AGGREGATOR_MODULE_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c -index 293004499b4db..13acbf55c6fd9 100644 +index 293004499b4db..a15dabffe273f 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -225,5 +225,12 @@ int main(void) @@ -13822,21 +14806,22 @@ index 293004499b4db..13acbf55c6fd9 100644 + DEVID(ssam_device_id); + DEVID_FIELD(ssam_device_id, match_flags); + DEVID_FIELD(ssam_device_id, category); -+ DEVID_FIELD(ssam_device_id, channel); ++ DEVID_FIELD(ssam_device_id, target); + DEVID_FIELD(ssam_device_id, instance); + DEVID_FIELD(ssam_device_id, function); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c -index 7f40b6aab689b..76e3b1d7db453 100644 +index 7f40b6aab689b..3ae63c7a8c03a 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c -@@ -1276,6 +1276,26 @@ static int do_typec_entry(const char *filename, void *symval, char *alias) +@@ -1276,6 +1276,27 @@ static int do_typec_entry(const char *filename, void *symval, char *alias) return 1; } -+/* Looks like: ssam:cNtNiNfN ++/* ++ * Looks like: ssam:cNtNiNfN + * + * N is exactly 2 digits, where each is an upper-case hex digit. + */ @@ -13844,12 +14829,12 @@ index 7f40b6aab689b..76e3b1d7db453 100644 +{ + DEF_FIELD(symval, ssam_device_id, match_flags); + DEF_FIELD(symval, ssam_device_id, category); -+ DEF_FIELD(symval, ssam_device_id, channel); ++ DEF_FIELD(symval, ssam_device_id, target); + DEF_FIELD(symval, ssam_device_id, instance); + DEF_FIELD(symval, ssam_device_id, function); + + sprintf(alias, "ssam:c%02X", category); -+ ADD(alias, "t", match_flags & SSAM_MATCH_CHANNEL, channel); ++ ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target); + ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); + ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); + @@ -13859,7 +14844,7 @@ index 7f40b6aab689b..76e3b1d7db453 100644 /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -@@ -1346,6 +1366,7 @@ static const struct devtable devtable[] = { +@@ -1346,6 +1367,7 @@ static const struct devtable devtable[] = { {"fslmc", SIZE_fsl_mc_device_id, do_fsl_mc_entry}, {"tbsvc", SIZE_tb_service_id, do_tbsvc_entry}, {"typec", SIZE_typec_device_id, do_typec_entry}, diff --git a/patches/4.19/0009-surface-sam-over-hid.patch b/patches/4.19/0009-surface-sam-over-hid.patch index d2845a374..f192904ce 100644 --- a/patches/4.19/0009-surface-sam-over-hid.patch +++ b/patches/4.19/0009-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From 8f6f115599cd67333e37069ea4fd16f8367b1885 Mon Sep 17 00:00:00 2001 +From caa0f67a2cfa53f7a99159fb5d20039f2c8a9dfc Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH 09/10] surface-sam-over-hid diff --git a/patches/4.19/0010-surface-gpe.patch b/patches/4.19/0010-surface-gpe.patch index bdef8d2c5..2bc5066c2 100644 --- a/patches/4.19/0010-surface-gpe.patch +++ b/patches/4.19/0010-surface-gpe.patch @@ -1,4 +1,4 @@ -From d3155cfe94e1745f43bbaacc7ed37d178a13ceeb Mon Sep 17 00:00:00 2001 +From 8b39a8359e783b55f4e8a08c1d9b9838008681aa Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 16 Aug 2020 23:39:56 +0200 Subject: [PATCH 10/10] surface-gpe diff --git a/pkg/arch/kernel-lts/PKGBUILD b/pkg/arch/kernel-lts/PKGBUILD index 24022acec..eaefb4caf 100644 --- a/pkg/arch/kernel-lts/PKGBUILD +++ b/pkg/arch/kernel-lts/PKGBUILD @@ -41,16 +41,16 @@ sha256sums=('9c4ebf21fe949f80fbcfbbd6e7fe181040d325e89475e230ab53ef01f9d55605' '4e68572e7cc4c5368f0236e0792660ae8498373988625dca46e509399a7eaea6' 'a13581d3c6dc595206e4fe7fcf6b542e7a1bdbe96101f0f010fc5be49f99baf2' 'c04b7652fbc7dbca65bfd64e7123adcb6da9dae0ab33d85be58907b5b9de1c66' - '14595051a39d52d8b29478ff0736f7d0f199850e84fa64a1dcef362ebb23da26' - '3fa4663668f3daf1d72d4611f581070b4f3aa6378667a524577b238a93ee16c6' - '50c30e275196afa332239d0c40972abe88e3d63d1ccb714b72d7507e3c77d9ee' - 'ebf70e5bdee4c199672fa83ce141b97a8e7dbb3d690838ab189b504869946e14' - 'e0802caad90c6523ece61af6dc4065cf9ce80e7b0b6f8ead8054b7682e56f162' - '2f26f91d6bdbef70340374dc90d6f5faa93cb49378bc6c4c7b2ae79eec69dbb3' - '4b8fea7414e5746d9e9e8fe354bb7a0f899290735a8aadca235a332642b2b2e3' - '258d351b639ffc44140e8bd84ef8ee53fda95668c91fbd1e60fc122c08244637' - 'fe59d6f4e604e2318c319af7300f2cd9f8340cd3dd1aa2a165cd4d229f80e3c9' - 'ef3df0ea66aba7e2aae1575acf50046673f8bcebb6692ebb48713af3347987ae') + '084ae7ae947331a4b0fd8bcf8a34c5f3bf2f09eb1a20a89854a5653407dfcecd' + '8162b7aee344326571b4349e23b40103f0e11a0bfdbe46f11574ec3a74762eea' + '229ee719ab0563a39224a6809b41eb6a9d4f66db4ee77759d9a51ec91b0a196f' + '8594e7da746913a3daf991940a616825604ad988eb7dba1c6cbd6d062b05796d' + 'dd2d494618ae67dec33291dfda0ef18628e0ada8ff16505546ec46048b19637f' + 'b641953a0f5e679519d3ecddab842414ed74bdeb0f37540aa0d625939a2ed0e1' + '29b3e2e5752a355e8af0936685d34c48b93a7fc3bda91e85b023884e58b6b08d' + '9d6ed8c0fa487af359eb3595ecf321b9ccc34c42b2335d9749297b27efda701d' + '5bcaa733602a96898031aa6a5233bf2a9a4cbb6a38351df24a455e397b6e66c3' + 'b2491c99b832e1646078560e84e5472258e5532847ffe1ae5f65a8c1b124dddc') export KBUILD_BUILD_HOST=archlinux export KBUILD_BUILD_USER=$pkgbase