diff --git a/configs/surface-5.8.config b/configs/surface-5.8.config index 3edd56cb9..5aff6e130 100644 --- a/configs/surface-5.8.config +++ b/configs/surface-5.8.config @@ -32,3 +32,4 @@ CONFIG_SURFACE_3_BUTTON=m CONFIG_SURFACE_3_POWER_OPREGION=m CONFIG_SURFACE_PRO3_BUTTON=m CONFIG_SURFACE_GPE=m +CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m diff --git a/patches/5.8/0001-surface3-oemb.patch b/patches/5.8/0001-surface3-oemb.patch index b75f1bc33..f21fcc1ed 100644 --- a/patches/5.8/0001-surface3-oemb.patch +++ b/patches/5.8/0001-surface3-oemb.patch @@ -1,4 +1,4 @@ -From e56ab1f796646568a6f183bc64cbbbe615e3ec3d Mon Sep 17 00:00:00 2001 +From 7cd3be5c7f76a886083272c6dd562d7217dc0767 Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Tue, 18 Sep 2018 11:01:37 +0800 Subject: [PATCH 1/6] surface3-oemb diff --git a/patches/5.8/0002-wifi.patch b/patches/5.8/0002-wifi.patch index 35e5ce851..666574d6c 100644 --- a/patches/5.8/0002-wifi.patch +++ b/patches/5.8/0002-wifi.patch @@ -1,4 +1,4 @@ -From 4e625be82e0c20fd760e84c51c90b12129178e39 Mon Sep 17 00:00:00 2001 +From 2a3ed3370f323ca5026a37f9ad2dedb23fd378ca 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 2/6] wifi diff --git a/patches/5.8/0003-ipts.patch b/patches/5.8/0003-ipts.patch index f21a59041..63a0d4b4d 100644 --- a/patches/5.8/0003-ipts.patch +++ b/patches/5.8/0003-ipts.patch @@ -1,4 +1,4 @@ -From 617b52707ec79582c381c551cc77c53a3ee12c21 Mon Sep 17 00:00:00 2001 +From ba612c1a501963c3b941275a4d5f2b3b215b8a5b Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 Subject: [PATCH 3/6] ipts diff --git a/patches/5.8/0004-surface-sam.patch b/patches/5.8/0004-surface-sam.patch index e4d148743..cd5d7b281 100644 --- a/patches/5.8/0004-surface-sam.patch +++ b/patches/5.8/0004-surface-sam.patch @@ -1,47 +1,59 @@ -From 523a14538490af248e6ef8134077efc32297657d Mon Sep 17 00:00:00 2001 +From cf006a00cf91c80ebf40bb250ffa00da2258fa3b Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Mon, 17 Aug 2020 01:23:20 +0200 Subject: [PATCH 4/6] surface-sam --- Documentation/driver-api/index.rst | 1 + - Documentation/driver-api/ssam/index.rst | 69 + + Documentation/driver-api/ssam/client-api.rst | 19 + + Documentation/driver-api/ssam/client.rst | 381 +++ + Documentation/driver-api/ssam/index.rst | 20 + + .../driver-api/ssam/internal-api.rst | 70 + + Documentation/driver-api/ssam/internal.rst | 50 + + Documentation/driver-api/ssam/overview.rst | 76 + + Documentation/driver-api/ssam/ssh.rst | 297 ++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/surface_sam/Kconfig | 46 + drivers/misc/surface_sam/Makefile | 14 + - drivers/misc/surface_sam/bus.c | 394 +++ + drivers/misc/surface_sam/bus.c | 414 +++ drivers/misc/surface_sam/bus.h | 14 + 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 ++++ + .../clients/surface_sam_device_hub.c | 583 ++++ + .../surface_sam/clients/surface_sam_dtx.c | 583 ++++ .../surface_sam/clients/surface_sam_hps.c | 1287 +++++++++ - .../surface_sam/clients/surface_sam_san.c | 930 +++++++ + .../surface_sam/clients/surface_sam_san.c | 932 +++++++ .../surface_sam/clients/surface_sam_san.h | 30 + .../clients/surface_sam_sid_perfmode.c | 194 ++ - .../clients/surface_sam_sid_power.c | 1112 ++++++++ - .../surface_sam/clients/surface_sam_sid_vhf.c | 500 ++++ - .../surface_sam/clients/surface_sam_vhf.c | 336 +++ - drivers/misc/surface_sam/controller.c | 2384 +++++++++++++++++ + .../clients/surface_sam_sid_power.c | 1114 ++++++++ + .../surface_sam/clients/surface_sam_sid_vhf.c | 498 ++++ + .../surface_sam/clients/surface_sam_vhf.c | 334 +++ + drivers/misc/surface_sam/controller.c | 2441 +++++++++++++++++ drivers/misc/surface_sam/controller.h | 275 ++ - drivers/misc/surface_sam/core.c | 764 ++++++ + drivers/misc/surface_sam/core.c | 771 ++++++ drivers/misc/surface_sam/ssam_trace.h | 619 +++++ drivers/misc/surface_sam/ssh_msgb.h | 196 ++ - 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 | 215 ++ - drivers/misc/surface_sam/ssh_parser.h | 151 ++ - drivers/misc/surface_sam/ssh_protocol.h | 102 + - drivers/misc/surface_sam/ssh_request_layer.c | 1100 ++++++++ - drivers/misc/surface_sam/ssh_request_layer.h | 93 + + drivers/misc/surface_sam/ssh_packet_layer.c | 2001 ++++++++++++++ + drivers/misc/surface_sam/ssh_packet_layer.h | 173 ++ + drivers/misc/surface_sam/ssh_parser.c | 221 ++ + drivers/misc/surface_sam/ssh_parser.h | 151 + + drivers/misc/surface_sam/ssh_protocol.h | 103 + + drivers/misc/surface_sam/ssh_request_layer.c | 1246 +++++++++ + drivers/misc/surface_sam/ssh_request_layer.h | 136 + include/linux/mod_devicetable.h | 17 + - include/linux/surface_aggregator_module.h | 1006 +++++++ + include/linux/surface_aggregator_module.h | 1719 ++++++++++++ scripts/mod/devicetable-offsets.c | 7 + scripts/mod/file2alias.c | 22 + - 36 files changed, 15351 insertions(+) + 42 files changed, 17459 insertions(+) + create mode 100644 Documentation/driver-api/ssam/client-api.rst + create mode 100644 Documentation/driver-api/ssam/client.rst create mode 100644 Documentation/driver-api/ssam/index.rst + create mode 100644 Documentation/driver-api/ssam/internal-api.rst + create mode 100644 Documentation/driver-api/ssam/internal.rst + create mode 100644 Documentation/driver-api/ssam/overview.rst + create mode 100644 Documentation/driver-api/ssam/ssh.rst 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 @@ -73,61 +85,481 @@ Subject: [PATCH 4/6] surface-sam create mode 100644 include/linux/surface_aggregator_module.h diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst -index 6567187e76873..ef6bcf7f2f969 100644 +index 6567187e76873..93f663351ed1f 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -99,6 +99,7 @@ available subsections can be seen below. serial/index sm501 smsc_ece1099 -+ ssam/indx ++ ssam/index switchtec sync_file vfio-mediated-device -diff --git a/Documentation/driver-api/ssam/index.rst b/Documentation/driver-api/ssam/index.rst +diff --git a/Documentation/driver-api/ssam/client-api.rst b/Documentation/driver-api/ssam/client-api.rst new file mode 100644 -index 0000000000000..582ddaa91f2a6 +index 0000000000000..3d5fc6a4dc953 --- /dev/null -+++ b/Documentation/driver-api/ssam/index.rst -@@ -0,0 +1,69 @@ ++++ b/Documentation/driver-api/ssam/client-api.rst +@@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0 + -+Surface System Aggregator Module (SSAM) -+======================================= ++=============================== ++Client Driver API Documentation ++=============================== + -+The Surface System Aggregator Module ... -+ -+API -+--- -+ -+.. kernel-doc:: drivers/misc/surface_sam/bus.c -+ :export: -+ -+.. kernel-doc:: drivers/misc/surface_sam/controller.c -+ :export: -+ -+.. kernel-doc:: drivers/misc/surface_sam/core.c -+ :export: ++.. kernel-doc:: include/linux/surface_aggregator_module.h + +.. kernel-doc:: drivers/misc/surface_sam/ssh_packet_layer.c + :export: + -+.. kernel-doc:: include/linux/surface_aggregator_module.h -+ -+ -+Internal -+-------- -+ -+.. kernel-doc:: drivers/misc/surface_sam/bus.c -+ :internal: -+ -+.. kernel-doc:: drivers/misc/surface_sam/controller.h -+ :internal: -+ +.. kernel-doc:: drivers/misc/surface_sam/controller.c -+ :internal: ++ :export: + +.. kernel-doc:: drivers/misc/surface_sam/core.c ++ :export: ++ ++.. kernel-doc:: drivers/misc/surface_sam/bus.c ++ :export: +diff --git a/Documentation/driver-api/ssam/client.rst b/Documentation/driver-api/ssam/client.rst +new file mode 100644 +index 0000000000000..fa0aa032a9488 +--- /dev/null ++++ b/Documentation/driver-api/ssam/client.rst +@@ -0,0 +1,381 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++.. |ssam_controller| replace:: :c:type:`struct ssam_controller ` ++.. |ssam_device| replace:: :c:type:`struct ssam_device ` ++.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver ` ++.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind` ++.. |ssam_client_link| replace:: :c:func:`ssam_client_link` ++.. |ssam_get_controller| replace:: :c:func:`ssam_get_controller` ++.. |ssam_controller_get| replace:: :c:func:`ssam_controller_get` ++.. |ssam_controller_put| replace:: :c:func:`ssam_controller_put` ++.. |ssam_device_alloc| replace:: :c:func:`ssam_device_alloc` ++.. |ssam_device_add| replace:: :c:func:`ssam_device_add` ++.. |ssam_device_remove| replace:: :c:func:`ssam_device_remove` ++.. |ssam_device_driver_register| replace:: :c:func:`ssam_device_driver_register` ++.. |ssam_device_driver_unregister| replace:: :c:func:`ssam_device_driver_unregister` ++.. |module_ssam_device_driver| replace:: :c:func:`module_ssam_device_driver` ++.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE` ++.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register` ++.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister` ++.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync` ++.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask ` ++ ++ ++====================== ++Writing Client Drivers ++====================== ++ ++For the API documentation, refer to: ++ ++.. toctree:: ++ :maxdepth: 2 ++ ++ client-api ++ ++ ++Overview ++======== ++ ++Client drivers can be set up in two main ways, depending on how the ++corresponding device is made available to the system. We specifically ++differentiate between devices that are presented to the system via one of ++the conventional ways, e.g. as platform devices via ACPI, and devices that ++are non-discoverable and instead need to be explicitly provided by some ++other mechanism, as discussed further below. ++ ++ ++Non-SSAM Client Drivers ++======================= ++ ++All communication with the SAM EC is handled via the |ssam_controller| ++representing that EC to the kernel. Drivers targetting a non-SSAM device ++(and thus not being a |ssam_device_driver|) need to explicitly establish a ++connection/relation to that controller. This can be done via the ++|ssam_client_bind| function. Said function returns a reference to the SSAM ++controller, but, more importantly, also establishes a device link between ++client device and controller (this can also be done separate via ++|ssam_client_link|). It is important to do this, as it, first, guarantees ++that the returned controller is valid for use in the client driver for as ++long as this driver is bound to its device, i.e. that the driver gets ++un-bound before the controller ever becomes invalid, and, second, as it ++ensures correct suspend/resume ordering. This setup should be done in the ++driver's probe function, and may be used to defer probing in case the SSAM ++subsystem is not ready yet, for example: ++ ++.. code-block:: c ++ ++ static int client_driver_probe(struct platform_device *pdev) ++ { ++ struct ssam_controller *ctrl; ++ int status; ++ ++ status = ssam_client_bind(&pdev->dev, &ctrl); ++ if (status) ++ return status == -ENXIO ? -EPROBE_DEFER : status; ++ ++ // ... ++ ++ return 0; ++ } ++ ++The controller may be separately obtained via |ssam_get_controller| and its ++lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|. ++Note that none of these functions, however, guarantee that the controller ++will not be shut down or suspended. These functions essentially only operate ++on the reference, i.e. only guarantee a bare minimum of accessability ++without any guarantees at all on practical operability. ++ ++ ++Adding SSAM Devices ++=================== ++ ++If a device does not already exist/is not already provided via conventional ++means, it should be provided as |ssam_device| via the SSAM client device ++hub. New devices can be added to this hub by entering their UID into the ++corresponding registry. SSAM devices can also be manually allocated via ++|ssam_device_alloc|, subsequently to which they have to be added via ++|ssam_device_add| and eventually removed via |ssam_device_remove|. By ++default, the parent of the device is set to the controller device provided ++for allocation, however this may be changed before the device is added. Note ++that, when changing the parent device, care must be taken to ensure that the ++controller lifetime and suspend/resume ordering guarantees, in the default ++setup provided through the parent-child relation, are preserved. If ++necessary, by use of |ssam_client_link| as is done for non-SSAM client ++drivers and described in more detail above. ++ ++A client device must always be removed by the party which added the ++respective device before the controller shuts down. Such removal can be ++guaranteed by linking the driver providing the SSAM device to the controller ++via |ssam_client_link|, causing it to unbind before the controller driver ++unbinds. Client devices registered with the controller as parent are ++automatically removed when the controller shuts down, but this should not be ++relied upon, especially as this does not extend to client devices with a ++different parent. ++ ++ ++SSAM Client Drivers ++=================== ++ ++SSAM client device drivers are, in essence, no different than other device ++driver types. They are represented via |ssam_device_driver| and bind to a ++|ssam_device| via its UID (:c:type:`struct ssam_device.uid `) ++member and the match table ++(:c:type:`struct ssam_device_driver.match_table `), ++which should be set when declaring the driver struct instance. Refer to the ++|SSAM_DEVICE| macro documentation for more details on how to define members ++of the driver's match table. ++ ++A driver instance can be registered via |ssam_device_driver_register| and ++unregistered via |ssam_device_driver_unregister|. For convenience, the ++|module_ssam_device_driver| macro may be used to define module init- and ++exit-functions registering the driver. ++ ++The controller associated with a SSAM client device can be found in its ++:c:type:`struct ssam_device.ctrl ` member. This reference is ++guaranteed to be valid for at least as long as the client driver is bound, ++but should also be valid for as long as the client device exists. Note, ++however, that access outside of the bound client driver must ensure that the ++controller device is not suspended while making any requests or ++(un)registering event notifiers (and thus should generally be avoided). This ++is guaranteed when the controller is accessed from inside the bound client ++driver. ++ ++ ++Making Synchronous Requests ++=========================== ++ ++Synchronous requests are (currently) the main form of host-initiated ++communication with the EC. There are a couple of ways to define and execute ++such requests, however, most of them boil down to something similar as shown ++in the example below. This example defines a write-read request, meaning ++that the caller provides an argument to the SAM EC and receives a response. ++The caller needs to know the (maximum) length of the response payload and ++provide a buffer for it. ++ ++Care must be taken to ensure that any command payload data passed to the SAM ++EC is provided in little-endian format and, similarly, any response payload ++data received from it is converted from little-endian to host endianness. ++ ++.. code-block:: c ++ ++ int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret) ++ { ++ struct ssam_request rqst; ++ struct ssam_response resp; ++ int status; ++ ++ /* Convert request argument to little-endian. */ ++ __le32 arg_le = cpu_to_le32(arg); ++ __le32 ret_le = cpu_to_le32(0); ++ ++ /* ++ * Initialize request specification. Replace this with your values. ++ * The rqst.payload field may be NULL if rqst.length is zero, ++ * indicating that the request does not have any argument. ++ * ++ * Note: The request parameters used here are not valid, i.e. ++ * they do not correspond to an actual SAM/EC request. ++ */ ++ rqst.target_category = SSAM_SSH_TC_SAM; ++ rqst.target_id = 0x01; ++ rqst.command_id = 0x02; ++ rqst.instance_id = 0x03; ++ rqst.flags = SSAM_REQUEST_HAS_RESPONSE; ++ rqst.length = sizeof(arg_le); ++ rqst.payload = (u8 *)&arg_le; ++ ++ /* Initialize request response. */ ++ resp.capacity = sizeof(ret_le); ++ resp.length = 0; ++ resp.pointer = (u8 *)&ret_le; ++ ++ /* ++ * Perform actual request. The response pointer may be null in case ++ * the request does not have any response. This must be consistent ++ * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification ++ * above. ++ */ ++ status = ssam_request_sync(ctrl, &rqst, &resp); ++ if (status) ++ return status; ++ ++ /* ++ * Alternatively use ++ * ++ * ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le)); ++ * ++ * to perform the request, allocating the message buffer directly ++ * on the stack as opposed to via kzalloc(. ++ */ ++ ++ /* ++ * Convert request response back to native format. Note that in the ++ * error case, this value is not touched. ++ */ ++ *ret = le32_to_cpu(ret_le); ++ ++ return status; ++ } ++ ++Note that |ssam_request_sync| in its essence is a wrapper over lower-level ++request primitives, which may also be used to perform requests. Refer to its ++implementation and documentation for more details. ++ ++An arguably more user-friendly way of defining such functions is by using ++one of the generator macros, for example via: ++ ++.. code-block:: c ++ ++ 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, ++ }); ++ ++This example defines a function ++ ++.. code-block:: c ++ ++ int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg); ++ ++executing the specified request, with the controller passed in when calling ++said function. In this example, the argument is provided via the ``arg`` ++pointer. Note that the generated function allocates the message buffer on ++the stack. Thus, if the argument provided via the request is large, these ++kinds of macros should be avoided. Also note that, in contrast to the ++previous non-macro example, this function does not do any endianness ++conversion, which has to be handled by the caller. Apart from those ++differences the function generated by the macro is similar to the one ++provided in the non-macro example above. ++ ++The full list of such function-generating macros is ++ ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and ++ without argument. ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for equests with return value but no ++ argument. ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but ++ with argument. ++ ++Refer to their respecitve documentation for more details. For each one of ++these macros, a special variant is provided, which targets request types ++applicable to multiple instances of the same device type: ++ ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N` ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R` ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W` ++ ++The difference of those macros to the previously mentioned versions is, that ++the device target and instance IDs are not fixed for the generated function, ++but instead have to be provided by the caller of said function. ++ ++Additionally, variants for direct use with client devices, i.e. ++|ssam_device|, are also provided. These can, for example, be used as ++follows: ++ ++.. code-block:: c ++ ++ SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, { ++ .target_category = SSAM_SSH_TC_BAT, ++ .command_id = 0x01, ++ }); ++ ++This invocation of the macro defines a function ++ ++.. code-block:: c ++ ++ int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret); ++ ++executing the specified request, using the device IDs and controller given ++in the client device. The full list of such macros for client devices is: ++ ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N` ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R` ++- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W` ++ ++ ++Handling Events ++=============== ++ ++To receive events from the SAM EC, an event notifier must be registered for ++the desired event via |ssam_notifier_register|. The notifier must be ++unregistered via |ssam_notifier_unregister| once it is not required any ++more. ++ ++Event notifiers are registered by providing (at minimum) a callback to call ++in case an event has been received, the registry specifying how the event ++should be enabled, an event ID specifying for which target category and, ++optionally and depending on the registry used, for which instance ID events ++should be enabled, and finally, flags describing how the EC will send these ++events. Additionally, a priority for the respective notifier may be ++specified, which determines its order in relation to any other notifier ++registered for the same target category. ++ ++By default, event notifiers will receive all events for the specific target ++category, regardless of the instance ID specified when registering the ++notifier. The core may be instructed to only call a notifier if the target ++ID or instance ID (or both) of the event match the ones implied by the ++notifier IDs (in case of target ID, the target ID of the registry), by ++providing an event mask (see |ssam_event_mask|). ++ ++In general, the target ID of the registry is also the target ID of the ++enabled event (with the notable exception being keyboard input events on the ++Surface Laptop 1 and 2, which are enabled via a registry with target ID 1, ++but provide events with target ID 2). ++ ++A full example for registering an event notifier and handling received ++events is provided below: ++ ++.. code-block:: c ++ ++ u32 notifier_callback(struct ssam_event_notifier *nf, ++ const struct ssam_event *event) ++ { ++ int status = ... ++ ++ /* Handle the event here ... */ ++ ++ /* Convert return value and indicate that we handled the event. */ ++ return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; ++ } ++ ++ int setup_notifier(struct ssam_device *sdev, ++ struct ssam_event_notifier *nf) ++ { ++ /* Set priority wrt. other handlers of same target category. */ ++ nf->base.priority = 1; ++ ++ /* Set event/notifier callback. */ ++ nf->base.fn = notifier_callback; ++ ++ /* Specify event registry, i.e. how events get enabled/disabled. */ ++ nf->event.reg = SSAM_EVENT_REGISTRY_KIP; ++ ++ /* Specify which event to enable/disable */ ++ nf->event.id.target_category = sdev->uid.category; ++ nf->event.id.instance = sdev->uid.instance; ++ ++ /* ++ * Specify for which events the notifier callback gets executed. ++ * This essentially tells the core if it can skip notifiers that ++ * don't have target or instance IDs matching those of the event. ++ */ ++ nf->event.mask = SSAM_EVENT_MASK_STRICT; ++ ++ /* Specify event flags. */ ++ nf->event.flags = SSAM_EVENT_SEQUENCED; ++ ++ return ssam_notifier_register(sdev->ctrl, nf); ++ } ++ ++Multiple event notifiers can be registered for the same event. The event ++handler core takes care of enabling and disabling events when notifiers are ++registered and unregistered, by keeping track of how many notifiers for a ++specific event (combination of registry, event target category, and event ++instance ID) are currently registered. This means that a specific event will ++be enabled when the first notifier for it is being registered and disabled ++when the last notifier for it is being unregistered. Note that the event ++flags are therefore only used on the first registered notifier, however, one ++should take care that notifiers for a specific event are always registered ++with the same flag and it is considered a bug to do otherwise. +diff --git a/Documentation/driver-api/ssam/index.rst b/Documentation/driver-api/ssam/index.rst +new file mode 100644 +index 0000000000000..e1274d64db0d2 +--- /dev/null ++++ b/Documentation/driver-api/ssam/index.rst +@@ -0,0 +1,20 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++======================================= ++Surface System Aggregator Module (SSAM) ++======================================= ++ ++.. toctree:: ++ :maxdepth: 2 ++ ++ overview ++ ssh ++ client ++ internal ++ ++.. only:: subproject and html ++ ++ Indices ++ ======= ++ ++ * :ref:`genindex` +diff --git a/Documentation/driver-api/ssam/internal-api.rst b/Documentation/driver-api/ssam/internal-api.rst +new file mode 100644 +index 0000000000000..cd55b6b206e45 +--- /dev/null ++++ b/Documentation/driver-api/ssam/internal-api.rst +@@ -0,0 +1,70 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++========================== ++Internal API Documentation ++========================== ++ ++.. contents:: ++ :depth: 2 ++ ++ ++Packet Transport Layer ++====================== ++ ++.. kernel-doc:: drivers/misc/surface_sam/ssh_protocol.h ++ :internal: ++ ++.. kernel-doc:: drivers/misc/surface_sam/ssh_parser.h ++ :internal: ++ ++.. kernel-doc:: drivers/misc/surface_sam/ssh_parser.c + :internal: + +.. kernel-doc:: drivers/misc/surface_sam/ssh_msgb.h @@ -139,14 +571,9 @@ index 0000000000000..582ddaa91f2a6 +.. kernel-doc:: drivers/misc/surface_sam/ssh_packet_layer.c + :internal: + -+.. kernel-doc:: drivers/misc/surface_sam/ssh_parser.h -+ :internal: + -+.. kernel-doc:: drivers/misc/surface_sam/ssh_parser.c -+ :internal: -+ -+.. kernel-doc:: drivers/misc/surface_sam/ssh_protocol.h -+ :internal: ++Request Transport Layer ++======================= + +.. kernel-doc:: drivers/misc/surface_sam/ssh_request_layer.h + :internal: @@ -155,10 +582,475 @@ index 0000000000000..582ddaa91f2a6 + :internal: + + -+Internal trace helpers -+---------------------- ++Controller ++========== ++ ++.. kernel-doc:: drivers/misc/surface_sam/controller.h ++ :internal: ++ ++.. kernel-doc:: drivers/misc/surface_sam/controller.c ++ :internal: ++ ++ ++Client Device Bus ++================= ++ ++.. kernel-doc:: drivers/misc/surface_sam/bus.c ++ :internal: ++ ++ ++Core ++==== ++ ++.. kernel-doc:: drivers/misc/surface_sam/core.c ++ :internal: ++ ++ ++Trace Helpers ++============= + +.. kernel-doc:: drivers/misc/surface_sam/ssam_trace.h +diff --git a/Documentation/driver-api/ssam/internal.rst b/Documentation/driver-api/ssam/internal.rst +new file mode 100644 +index 0000000000000..0504247d87862 +--- /dev/null ++++ b/Documentation/driver-api/ssam/internal.rst +@@ -0,0 +1,50 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++===================== ++Core Driver Internals ++===================== ++ ++For the API documentation, refer to: ++ ++.. toctree:: ++ :maxdepth: 2 ++ ++ internal-api ++ ++ ++Overview ++======== ++ ++The SSAM core implementation is structured in layers, somewhat following the ++SSH protocol structure: ++ ++Lower-level packet transport is implemented in the *packet transport layer ++(PTL)*, directly building on top of the serial device (serdev) ++infrastructure of the kernel. As the name indicates, this layer deals with ++the packet transport logic and handles things like packet validation, packet ++acknowledgement (ACKing), packet (retransmission) timeouts, and relaying ++packet payloads to higher-level layers. ++ ++Above this sits the *request transport layer (RTL)*. This layer is centered ++around command-type packet payloads, i.e. requests (sent from host to EC), ++responses of the EC to those requests, and events (sent from EC to host). ++It, specifically, distinguishes events from request responses, matches ++responses to their corresponding requests, and implements request timeouts. ++ ++The *controller* layer is building on top of this and essentially decides ++how request responses and, especially, events are dealt with. It provides an ++event notifier system, handles event activation/deactivation, provides a ++workqueue for event and asynchronous request completion, and also manages ++the message counters required for building command messages (``SEQ``, ++``RQID``). This layer basically provides a fundamental interface to the SAM ++EC for use in other kernel drivers. ++ ++While the controller layer already provides an interface for other kernel ++drivers, the client *bus* extends this interface to provide support for ++native SSAM devices, i.e. devices that are not defined in ACPI and not ++implemented as platform devices, via :c:type:`struct ssam_device ` ++and :c:type:`struct ssam_device_driver `. This aims to ++simplify management of client devices and client drivers. ++ ++Refer to :doc:`client` for documentation regarding the client device/driver ++API and interface options for other kernel drivers. +diff --git a/Documentation/driver-api/ssam/overview.rst b/Documentation/driver-api/ssam/overview.rst +new file mode 100644 +index 0000000000000..7b7a6d9e8e22b +--- /dev/null ++++ b/Documentation/driver-api/ssam/overview.rst +@@ -0,0 +1,76 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++======== ++Overview ++======== ++ ++The Surface/System Aggregator Module (SAM, SSAM) is an (arguably *the*) ++embedded controller (EC) on Microsoft Surface devices. It has been originally ++introduced on 4th generation devices (Surface Pro 4, Surface Book 1), but ++its responsibilities and feature-set have since been expanded significantly ++with the following generations. ++ ++ ++Features and Integration ++======================== ++ ++Not much is currently known about SAM on 4th generation devices (Surface Pro ++4, Surface Book 1), due to the use of a different communication interface ++between host and EC (as detailed below). On 5th (Surface Pro 2017, Surface ++Book 2, Surface Laptop 1) and later generation devices, SAM is responsible ++for providing battery information (both current status and static values, ++such as maximum capacity etc.), as well as an assortment of temperature ++sensors (e.g. skin temperature) and cooling/performance-mode setting to the ++host. On the Surface Book 2, specifically, it additionally provides an ++interface for properly handling clipboard detachment (i.e. separating the ++display part from the keyboard part of the device), on the Surface Laptop 1 ++and 2 it is required for keyboard HID input. This HID subsystem has been ++restructured for 7th generation devices and on those, specifically Surface ++Laptop 3 and Surface Book 3, is responsible for all major HID input (i.e. ++keyboard and touchpad). ++ ++While the features have not changed much on a coarse level since the 5th ++generation, internal interfaces have undergone some rather large changes. On ++5th and 6th generation devices, both battery and temperature information is ++exposed to ACPI via a shim driver (referred to as Surface ACPI Notify, or ++SAN), translating ACPI generic serial bus write-/read-accesses to SAM ++requests. On 7th generation devices, this additional layer is gone and these ++devices require a driver hooking directly into the SAM interface. Equally, ++on newer generations, less devices are declared in ACPI, making them a bit ++harder to discover and requiring us to hard-code a sort of device registry. ++Due to this, a SSAM bus and subsystem with client devices ++(:c:type:`struct ssam_device `) has been implemented. ++ ++ ++Communication ++============= ++ ++The type of communication interface between host and EC depends on the ++generation of the Surface device. On 4th generation devices, host and EC ++communicate via HID, specifically using a HID-over-I2C device, whereas on ++5th and later generations, communication takes place via a USART serial ++device. In accordance to the drivers found on other operating systems, we ++refer to the serial device and its driver as Surface Serial Hub (SSH) and ++when needed to differentiate between both types of SAM as SAM-over-SSH, in ++contrast to SAM-over-HID for the former variant. ++ ++Currently, this subsystem only supports SAM-over-SSH. The SSH communication ++interface is described in more detail below. The HID interface has not been ++reverse engineered yet and it is, at the moment, unclear how many (and ++which) concepts of the SSH interface detailed below can be transferred to ++it. ++ ++Surface Serial Hub ++------------------ ++ ++As already elaborated above, the Surface Serial Hub (SSH) is the ++communication interface for SAM on 5th- and all later-generation Surface ++devices. On the highest level, communication can be sparated into two main ++types: Requests, messages sent from host to EC that may trigger a direct ++response from the EC (explicitly associated with the request), and events ++(sometimes also referred to as notifications), sent from EC to host without ++being a direct response to a previous request. We may also refer to requests ++without response as commands. In general, events need to be enabled via one ++of multiple dedicated commands before they are sent by the EC. ++ ++See :doc:`ssh` for a more technical protocol documentation. +diff --git a/Documentation/driver-api/ssam/ssh.rst b/Documentation/driver-api/ssam/ssh.rst +new file mode 100644 +index 0000000000000..2a8a10e219ac5 +--- /dev/null ++++ b/Documentation/driver-api/ssam/ssh.rst +@@ -0,0 +1,297 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++.. |u8| replace:: :c:type:`u8 ` ++.. |u16| replace:: :c:type:`u16 ` ++.. |TYPE| replace:: ``TYPE`` ++.. |LEN| replace:: ``LEN`` ++.. |SEQ| replace:: ``SEQ`` ++.. |SYN| replace:: ``SYN`` ++.. |NAK| replace:: ``NAK`` ++.. |ACK| replace:: ``ACK`` ++.. |DATA| replace:: ``DATA`` ++.. |DATA_SEQ| replace:: ``DATA_SEQ`` ++.. |DATA_NSQ| replace:: ``DATA_NSQ`` ++.. |TC| replace:: ``TC`` ++.. |TID| replace:: ``TID`` ++.. |IID| replace:: ``IID`` ++.. |RQID| replace:: ``RQID`` ++.. |CID| replace:: ``CID`` ++ ++=========================== ++Surface Serial Hub Protocol ++=========================== ++ ++The Surface Serial Hub (SSH) is the central communication interface for the ++embedded Surface Aggregator Module controller (SAM or EC) on newer Surface ++generations. We will refer to this protocol and interface as SAM-over-SSH, ++as opposed to SAM-over-HID for the older generations. ++ ++On Surface devices with SAM-over-SSH, SAM is connected to the host via UART ++and defined in ACPI as device with ID ``MSHW0084``. On these devices, ++significant functionality is provided via SAM, including access to battery ++and power information and events, thermal read-outs and events, and many ++more. For Surface Laptops, keyboard input is handled via HID directed ++through SAM, on the Surface Laptop 3 and Surface Book 3 this also includes ++touchpad input. ++ ++Note that the standard disclaimer for this subsystem also applies to this ++document: All of this has been reverse-engineered and may thus be erroneous ++and/or incomplete. ++ ++All CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``. ++All multi-byte values are little-endian, there is no implicit padding between ++values. ++ ++ ++SSH Packet Protocol: Definitions ++================================ ++ ++The fundamental communication unit of the SSH protocol is a frame ++(:c:type:`struct ssh_frame `). A frame consists of the following ++fields, packed together and in order: ++ ++ +--------+-------+--------------------------------------------------+ ++ | Field | Type | Description | ++ +========+=======+==================================================+ ++ | |TYPE| | |u8| | Type identifier of the frame. | ++ +--------+-------+--------------------------------------------------+ ++ | |LEN| | |u16| | Length of the payload associated with the frame. | ++ +--------+-------+--------------------------------------------------+ ++ | |SEQ| | |u8| | Sequence ID (see explanation below). | ++ +--------+-------+--------------------------------------------------+ ++ ++Each frame structure is followed by a CRC over this structure. The CRC over ++the frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly ++after the frame structure and before the payload. The payload is followed by ++its own CRC (over all payload bytes). If the payload is not present (i.e. ++the frame has ``LEN=0``), the CRC of the payload is still present and will ++evaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it ++equals the number of bytes inbetween the CRC of the frame and the CRC of the ++payload. ++ ++Additionally, the following fixed two-byte sequences are used: ++ ++ +-------+------------------+------------------------+ ++ | Name | Value | Description | ++ +=======+==================+========================+ ++ | |SYN| | ``[0xAA, 0x55]`` | Synchronization bytes. | ++ +-------+------------------+------------------------+ ++ ++A message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and ++CRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes, ++followed finally, regardless if the payload is present, the payload CRC. The ++messages corresponding to an exchange are, in part, identified by having the ++same sequence ID (|SEQ|), stored inside the frame (more on this in the next ++section). The sequence ID is a wrapping counter. ++ ++A frame can have the following types ++(:c:type:`enum ssh_frame_type `): ++ ++ +------------+----------+----------------------------------------------------+ ++ | Name | Value | Short Description | ++ +============+==========+====================================================+ ++ | |NAK| | ``0x04`` | Sent on error in previously received message. | ++ +------------+----------+----------------------------------------------------+ ++ | |ACK| | ``0x40`` | Sent to acknowledge receival of |DATA| frame. | ++ +------------+----------+----------------------------------------------------+ ++ | |DATA_SEQ| | ``0x80`` | Sent to transfer data. Sequenced. | ++ +------------+----------+----------------------------------------------------+ ++ | |DATA_NSQ| | ``0x00`` | Same as |DATA_SEQ|, but does not need to be ACKed. | ++ +------------+----------+----------------------------------------------------+ ++ ++Both |NAK|- and |ACK|-type frames are used to control flow of messages and ++thus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the ++other hand must carry a payload. The flow sequence and interaction of ++different frame types will be described in more depth in the next section. ++ ++ ++SSH Packet Protocol: Flow Sequence ++================================== ++ ++Each exchange begins with |SYN|, followed by a |DATA_SEQ|- or ++|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In ++case of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a ++|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of ++the frame by responding with a message containing an |ACK|-type frame with ++the same sequence ID of the |DATA| frame. In other words, the sequence ID of ++the |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an ++error, e.g. an invalid CRC, the receiving party responds with a message ++containing an |NAK|-type frame. As the sequence ID of the previous data ++frame, for which an error is indicated via the |NAK| frame, cannot be relied ++upon, the sequence ID of the |NAK| frame should not be used and is set to ++zero. After receival of an |NAK| frame, the sending party should re-send all ++outstanding (non-ACKed) messages. ++ ++Sequence IDs are not synchronized between the two parties, meaning that they ++are managed independently for each party. Identifying the messages ++corresponding to a single exchange thus relies on the sequence ID as well as ++the type of the message, and the context. Specifically, the sequence ID is ++used to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not ++``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames. ++ ++An example exchange might look like this: ++ ++:: ++ ++ tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) ----------------------------- ++ rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) -- ++ ++where both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)`` ++indicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame, ++``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the ++previous payload. In case of an error, the exchange would look like this: ++ ++:: ++ ++ tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) ----------------------------- ++ rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) -- ++ ++upon which the sender should re-send the message. ``FRAME(N)`` indicates an ++|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed ++to zero. For |DATA_NSQ|-type frames, both exchanges are the same: ++ ++:: ++ ++ tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ---------------------- ++ rx: ------------------------------------------------------------------- ++ ++Here, an error can be detected, but not corrected or indicated to the ++sending party. These exchanges are symmetric, i.e. switching rx and tx ++results again in a valid exchange. Currently, no longer exchanges are known. ++ ++ ++Commands: Requests, Responses, and Events ++========================================= ++ ++Commands are sent as payload inside a data frame. Currently, this is the ++only known payload type of |DATA| frames, with a payload-type value of ++``0x80`` (:c:type:`SSH_PLD_TYPE_CMD `). ++ ++The command-type payload (:c:type:`struct ssh_command `) ++consists of an eight-byte command structure, followed by optional and ++variable length command data. The length of this optional data is derived ++from the frame payload length given in the corresponding frame, i.e. it is ++``frame.len - sizeof(struct ssh_command)``. The command struct contains the ++following fields, packed together and in order: ++ ++ +-------------+-------+----------------------------------------------------+ ++ | Field | Type | Description | ++ +=============+=======+====================================================+ ++ | |TYPE| | |u8| | Type of the payload. For commands always ``0x80``. | ++ +-------------+-------+----------------------------------------------------+ ++ | |TC| | |u8| | Target category. | ++ +-------------+-------+----------------------------------------------------+ ++ | |TID| (out) | |u8| | Target ID for outgoing (host to EC) commands. | ++ +-------------+-------+----------------------------------------------------+ ++ | |TID| (in) | |u8| | Target ID for incoming (EC to host) commands. | ++ +-------------+-------+----------------------------------------------------+ ++ | |IID| | |u8| | Instance ID. | ++ +-------------+-------+----------------------------------------------------+ ++ | |RQID| | |u16| | Request ID. | ++ +-------------+-------+----------------------------------------------------+ ++ | |CID| | |u8| | Command ID. | ++ +-------------+-------+----------------------------------------------------+ ++ ++The command struct and data, in general, does not contain any failure ++detection mechanism (e.g. CRCs), this is solely done on the frame level. ++ ++Command-type payloads are used by the host to send commands and requests to ++the EC as well as by the EC to send responses and events back to the host. ++We differentiate between requests (sent by the host), responses (sent by the ++EC in response to a request), and events (sent by the EC without a ++preceeding request). ++ ++Commands and events are uniquely identified by their target category ++(``TC``) and command ID (``CID``). The target category specifies a general ++category for the command (e.g. system in general, vs. battery and ac, vs. ++temperature, and so on), while the command ID specifies the command inside ++that category. Only the combination of |TC| + |CID| is unique. Additionally, ++commands have an instance ID (``IID``), which is used to differentiate ++between different sub-devices. For example ``TC=3`` ``CID=1`` is a ++request to get the temperature on a thermal sensor, where |IID| specifies ++the respective sensor. If the instance ID is not used, it should be set to ++zero. If instance IDs are used, they, in general, start with a value of one, ++whereas zero may be used for instance independent queries, if applicable. A ++response to a request should have the same target category, command ID, and ++instance ID as the corresponding request. ++ ++Responses are matched to their corresponding request via the request ID ++(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence ++ID on the frames. Note that the sequence ID of the frames for a ++request-response pair does not match. Only the request ID has to match. ++Frame-protocol wise these are two separate exchanges, and may even be ++separated, e.g. by an event being sent after the request but before the ++response. Not all commands produce a response, and this is not detectable by ++|TC| + |CID|. It is the responsibility of the issuing party to wait for a ++response (or signal this to the communication framework, as is done in ++SAN/ACPI via the ``SNC`` flag). ++ ++Events are identified by unique and reserved request IDs. These IDs should ++not be used by the host when sending a new request. They are used on the ++host to, first, detect events and, second, match them with a registered ++event handler. Request IDs for events are chosen by the host and directed to ++the EC when setting up and enabling an event source (via the ++enable-event-source request). The EC then uses the specified request ID for ++events sent from the respective source. Note that an event should still be ++identified by its target category, command ID, and, if applicable, instance ++ID, as a single event source can send multiple different event types. In ++general, however, a single target category should map to a single reserved ++event request ID. ++ ++Furthermore, requests, responses, and events have an associated target ID ++(``TID``). This target ID is split into output (host to EC) and input (EC to ++host) fields, with the respecting other field (e.g. output field on incoming ++messages) set to zero. Two ``TID`` values are known: Primary (0x01) and ++secondary (0x02). In general, the response to a request should have the same ++``TID`` value, however, the field (output vs. input) should be used in ++accordance to the direction in which the response is sent (i.e. on the input ++field, as responses are generally sent from the EC to the host). ++ ++Note that, even though requests and events should be uniquely identifiable ++by target category and command ID alone, the EC may require specific ++priority and instance ID values to accept a command. A command that is ++accepted for ``TID=1``, for example, may not be accepted for ``TID=2`` ++and vice versa. ++ ++ ++Limitations and Observations ++============================ ++ ++The protocol can, in theory, handle up to ``U8_MAX`` frames in parallel, ++with up to ``U16_MAX`` pending requests (neglecting request IDs reserved for ++events). In practice, however, this is more limited. From our testing ++(altough via a python and thus a user-space program), it seems that the EC ++can handle up to four requests (mostly) reliably in parallel at a certain ++time. With five or more requests in parallel, consistent discarding of ++commands (ACKed frame but no command response) has been observed. For five ++simultaneous commands, this reproducibly resulted in one command being ++dropped and four commands being handled. ++ ++However, it has also been noted that, even with three requests in parallel, ++occasional frame drops happen. Apart from this, with a limit of three ++pending requests, no dropped commands (i.e. command being dropped but frame ++carrying command being ACKed) have been observed. In any case, frames (and ++possibly also commands) should be re-sent by the host if a certain timeout ++is exceeded. This is done by the EC for frames with a timeout of one second, ++up to two re-tries (i.e. three transmissions in total). The limit of ++re-tries also applies to received NAKs, and, in a worst case scenario, can ++lead to entire messages being dropped. ++ ++While this also seems to work fine for pending data frames as long as no ++transmission failures occur, implementation and handling of these seems to ++depend on the assumption that there is only one non-acknowledged data frame. ++In particular, the detection of repeated frames relies on the last sequence ++number. This means that, if a frame that has been successfully received by ++the EC is sent again, e.g. due to the host not receiving an |ACK|, the EC ++will only detect this if it has the sequence ID of the last frame received ++by the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1`` ++followed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0`` ++frame as such, and thus execute the command in this frame each time it has ++been received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and ++then repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of ++the first one and ignore it, thus executing the contained command only once. ++ ++In conclusion, this suggests a limit of at most one pending un-ACKed frame ++(per party, effectively leading to synchronous communication regarding ++frames) and at most three pending commands. The limit to synchronous frame ++transfers seems to be consistent with behavior observed on Windows. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index be901ffc66fee..a5a3ee2ea2e37 100644 --- a/drivers/misc/Kconfig @@ -253,10 +1145,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..e2b1dbad3f190 +index 0000000000000..6089e7b74f462 --- /dev/null +++ b/drivers/misc/surface_sam/bus.c -@@ -0,0 +1,394 @@ +@@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -314,8 +1206,13 @@ index 0000000000000..e2b1dbad3f190 + * @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. ++ * Allocates and initializes a new client device. The parent of the device ++ * will be set to the controller device and the name will be set based on the ++ * UID. Note that the device still has to be added via ssam_device_add(). ++ * Refer to that function for more details. ++ * ++ * Return: Returns the newly allocated and initialized SSAM client device, or ++ * %NULL if it could not be allocated. + */ +struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, + struct ssam_device_uid uid) @@ -351,7 +1248,7 @@ index 0000000000000..e2b1dbad3f190 + * 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 ++ * 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. + * @@ -363,6 +1260,8 @@ index 0000000000000..e2b1dbad3f190 + * 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. ++ * ++ * Return: Returns zero on success, a negative error code on failure. + */ +int ssam_device_add(struct ssam_device *sdev) +{ @@ -382,8 +1281,8 @@ index 0000000000000..e2b1dbad3f190 + * + * Note that for this to work, the controller has to be a parent device. + * If it is not a direct parent, care has to be taken that the device is -+ * removed via ssam_device_remove, as device_unregister does not remove -+ * child devices recursively. ++ * removed via ssam_device_remove(), as device_unregister does not ++ * remove child devices recursively. + */ + ssam_controller_statelock(sdev->ctrl); + @@ -420,6 +1319,9 @@ index 0000000000000..e2b1dbad3f190 + * 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. ++ * ++ * Return: Returns %true iff the given UID is compatible to the match rule ++ * described by the given ID, %false otherwise. + */ +static inline bool ssam_device_id_compatible(const struct ssam_device_id *id, + struct ssam_device_uid uid) @@ -445,6 +1347,9 @@ index 0000000000000..e2b1dbad3f190 + * + * 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. ++ * ++ * Return: Returns %true if the given ID represents a null ID, %false ++ * otherwise. + */ +static inline bool ssam_device_id_is_null(const struct ssam_device_id *id) +{ @@ -490,6 +1395,9 @@ index 0000000000000..e2b1dbad3f190 + * + * This function essentially calls ssam_device_id_match() with the ID table of + * the bound device driver and the UID of the device. ++ * ++ * Return: Returns the first match for the UID of the device in the device ++ * driver's match table, or %NULL if no such match could be found. + */ +const struct ssam_device_id *ssam_device_get_match( + const struct ssam_device *dev) @@ -519,7 +1427,11 @@ index 0000000000000..e2b1dbad3f190 + * 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. ++ * could be found, returns its ``struct ssam_device_id.driver_data`` member. ++ * ++ * Return: Returns the driver data associated with the first match for the UID ++ * of the device in the device driver's match table, or %NULL if no such match ++ * could be found. + */ +const void *ssam_device_get_match_data(const struct ssam_device *dev) +{ @@ -547,9 +1459,8 @@ index 0000000000000..e2b1dbad3f190 + +static int ssam_bus_probe(struct device *dev) +{ -+ struct ssam_device_driver *sdrv = to_ssam_device_driver(dev->driver); -+ -+ return sdrv->probe(to_ssam_device(dev)); ++ return to_ssam_device_driver(dev->driver) ++ ->probe(to_ssam_device(dev)); +} + +static int ssam_bus_remove(struct device *dev) @@ -572,7 +1483,7 @@ index 0000000000000..e2b1dbad3f190 + + +/** -+ * __ssam_device_driver_register() - Register a SSAM device driver. ++ * __ssam_device_driver_register() - Register a SSAM client device driver. + * @sdrv: The driver to register. + * @owner: The module owning the provided driver. + * @@ -626,12 +1537,13 @@ index 0000000000000..e2b1dbad3f190 + * + * 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. ++ * necessary, any subsequent deinitialization) call. + */ +void ssam_controller_remove_clients(struct ssam_controller *ctrl) +{ -+ struct device *dev = ssam_controller_device(ctrl); ++ struct device *dev; + ++ dev = ssam_controller_device(ctrl); + device_for_each_child_reverse(dev, NULL, ssam_remove_device); +} + @@ -1093,10 +2005,10 @@ index 0000000000000..9b7ffbe610b10 +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..bd903b86b96f4 +index 0000000000000..7b56d5e6ca1e5 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_device_hub.c -@@ -0,0 +1,582 @@ +@@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Device Registry. @@ -1426,14 +2338,14 @@ index 0000000000000..bd903b86b96f4 + return status; +} + -+static u32 ssam_base_hub_notif(struct ssam_notifier_block *nb, ++static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, + const struct ssam_event *event) +{ + struct ssam_base_hub *hub; + struct ssam_device *sdev; + enum ssam_base_hub_state new; + -+ hub = container_of(nb, struct ssam_base_hub, notif.base); ++ hub = container_of(nf, struct ssam_base_hub, notif); + sdev = hub->sdev; + + if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) @@ -1503,6 +2415,7 @@ index 0000000000000..bd903b86b96f4 + hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; + hub->notif.event.id.target_category = SSAM_SSH_TC_BAS, + hub->notif.event.id.instance = 0, ++ hub->notif.event.mask = SSAM_EVENT_MASK_NONE; + hub->notif.event.flags = SSAM_EVENT_SEQUENCED; + + status = ssam_notifier_register(sdev->ctrl, &hub->notif); @@ -1681,10 +2594,10 @@ index 0000000000000..bd903b86b96f4 +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..106543112b206 +index 0000000000000..ff73f54328069 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_dtx.c -@@ -0,0 +1,582 @@ +@@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Detachment system (DTX) driver for Microsoft Surface Book 2. @@ -2080,9 +2993,9 @@ index 0000000000000..106543112b206 + surface_dtx_update_opmpde(ddev); +} + -+static u32 surface_dtx_notification(struct ssam_notifier_block *nb, const struct ssam_event *in_event) ++static u32 surface_dtx_notification(struct ssam_event_notifier *nf, const struct ssam_event *in_event) +{ -+ struct surface_dtx_dev *ddev = container_of(nb, struct surface_dtx_dev, notif.base); ++ struct surface_dtx_dev *ddev = container_of(nf, struct surface_dtx_dev, notif); + struct surface_dtx_event event; + unsigned long delay; + @@ -2197,6 +3110,7 @@ index 0000000000000..106543112b206 + ddev->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; + ddev->notif.event.id.target_category = SSAM_SSH_TC_BAS; + ddev->notif.event.id.instance = 0; ++ ddev->notif.event.mask = SSAM_EVENT_MASK_NONE; + ddev->notif.event.flags = SSAM_EVENT_SEQUENCED; + + status = ssam_notifier_register(ctrl, &ddev->notif); @@ -3562,10 +4476,10 @@ index 0000000000000..a47a5eb7391a1 +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..99a5401d7d58a +index 0000000000000..5b08a1068bb94 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_san.c -@@ -0,0 +1,930 @@ +@@ -0,0 +1,932 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface ACPI Notify (SAN) and ACPI integration driver for SAM. @@ -3980,9 +4894,9 @@ index 0000000000000..99a5401d7d58a +} + + -+static u32 san_evt_power_nb(struct ssam_notifier_block *nb, const struct ssam_event *event) ++static u32 san_evt_power_nf(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ -+ struct san_data *d = to_san_data(nb, nf_bat.base); ++ struct san_data *d = to_san_data(nf, nf_bat); + struct san_event_work *work; + unsigned long delay = san_evt_power_delay(event->command_id); + @@ -4033,9 +4947,9 @@ index 0000000000000..99a5401d7d58a + return true; +} + -+static u32 san_evt_thermal_nb(struct ssam_notifier_block *nb, const struct ssam_event *event) ++static u32 san_evt_thermal_nf(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ -+ if (san_evt_thermal(event, to_san_data(nb, nf_tmp.base)->dev)) ++ if (san_evt_thermal(event, to_san_data(nf, nf_tmp)->dev)) + return SSAM_NOTIF_HANDLED; + else + return 0; @@ -4260,17 +5174,19 @@ index 0000000000000..99a5401d7d58a + int status; + + d->nf_bat.base.priority = 1; -+ d->nf_bat.base.fn = san_evt_power_nb; ++ d->nf_bat.base.fn = san_evt_power_nf; + d->nf_bat.event.reg = SSAM_EVENT_REGISTRY_SAM; + d->nf_bat.event.id.target_category = SSAM_SSH_TC_BAT; + d->nf_bat.event.id.instance = 0; ++ d->nf_bat.event.mask = SSAM_EVENT_MASK_TARGET; + d->nf_bat.event.flags = SSAM_EVENT_SEQUENCED; + + d->nf_tmp.base.priority = 1; -+ d->nf_tmp.base.fn = san_evt_thermal_nb; ++ d->nf_tmp.base.fn = san_evt_thermal_nf; + d->nf_tmp.event.reg = SSAM_EVENT_REGISTRY_SAM; + d->nf_tmp.event.id.target_category = SSAM_SSH_TC_TMP; + d->nf_tmp.event.id.instance = 0; ++ d->nf_tmp.event.mask = SSAM_EVENT_MASK_TARGET; + d->nf_tmp.event.flags = SSAM_EVENT_SEQUENCED; + + status = ssam_notifier_register(d->ctrl, &d->nf_bat); @@ -4734,10 +5650,10 @@ index 0000000000000..24907e15c47ae +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..d6559a251fa8b +index 0000000000000..da8dd1e914b70 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_sid_power.c -@@ -0,0 +1,1112 @@ +@@ -0,0 +1,1114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface SID Battery/AC Driver. @@ -5195,13 +6111,13 @@ index 0000000000000..d6559a251fa8b + return status >= 0 ? 0 : status; +} + -+static u32 spwr_notify_bat(struct ssam_notifier_block *nb, ++static u32 spwr_notify_bat(struct ssam_event_notifier *nf, + const struct ssam_event *event) +{ + struct spwr_battery_device *bat; + int status; + -+ bat = container_of(nb, struct spwr_battery_device, notif.base); ++ bat = container_of(nf, struct spwr_battery_device, notif); + + dev_dbg(&bat->sdev->dev, "power event (cid = 0x%02x, iid = %d, tid = %d)\n", + event->command_id, event->instance_id, event->target_id); @@ -5212,7 +6128,10 @@ index 0000000000000..d6559a251fa8b + return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; + } + -+ if (!ssam_event_matches_device(bat->sdev->uid, event)) ++ if (bat->sdev->uid.target != event->target_id) ++ return 0; ++ ++ if (bat->sdev->uid.instance != event->instance_id) + return 0; + + switch (event->command_id) { @@ -5231,20 +6150,17 @@ index 0000000000000..d6559a251fa8b + return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; +} + -+static u32 spwr_notify_ac(struct ssam_notifier_block *nb, ++static u32 spwr_notify_ac(struct ssam_event_notifier *nf, + const struct ssam_event *event) +{ + struct spwr_ac_device *ac; + int status; + -+ ac = container_of(nb, struct spwr_ac_device, notif.base); ++ ac = container_of(nf, struct spwr_ac_device, notif); + + 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 targets/instances here. Global adapter status + * seems to be handled via target=1 and instance=1, but events are @@ -5564,6 +6480,7 @@ index 0000000000000..d6559a251fa8b + ac->notif.event.reg = registry; + ac->notif.event.id.target_category = sdev->uid.category; + ac->notif.event.id.instance = 0; ++ ac->notif.event.mask = SSAM_EVENT_MASK_NONE; + ac->notif.event.flags = SSAM_EVENT_SEQUENCED; + + status = ssam_notifier_register(sdev->ctrl, &ac->notif); @@ -5651,6 +6568,7 @@ index 0000000000000..d6559a251fa8b + bat->notif.event.reg = registry; + bat->notif.event.id.target_category = sdev->uid.category; + bat->notif.event.id.instance = 0; ++ bat->notif.event.mask = SSAM_EVENT_MASK_NONE; + bat->notif.event.flags = SSAM_EVENT_SEQUENCED; + + status = ssam_notifier_register(sdev->ctrl, &bat->notif); @@ -5739,7 +6657,7 @@ index 0000000000000..d6559a251fa8b + +static const struct spwr_psy_properties spwr_psy_props_bat2_sb3 = { + .name = "BAT2", -+ .registry = SSAM_EVENT_REGISTRY_REG, ++ .registry = SSAM_EVENT_REGISTRY_KIP, +}; + +static const struct ssam_device_id surface_sam_sid_battery_match[] = { @@ -5852,10 +6770,10 @@ index 0000000000000..d6559a251fa8b +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..baf8b53e7f990 +index 0000000000000..e995c03f4b733 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_sid_vhf.c -@@ -0,0 +1,500 @@ +@@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microsofs Surface HID (VHF) driver for HID input events via SAM. @@ -6171,14 +7089,11 @@ index 0000000000000..baf8b53e7f990 + return hid; +} + -+static u32 sid_vhf_event_handler(struct ssam_notifier_block *nb, const struct ssam_event *event) ++static u32 sid_vhf_event_handler(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ -+ struct sid_vhf *vhf = container_of(nb, struct sid_vhf, notif.base); ++ struct sid_vhf *vhf = container_of(nf, struct sid_vhf, notif); + int status; + -+ if (!ssam_event_matches_device(vhf->sdev->uid, event)) -+ return 0; -+ + if (event->command_id != 0x00 && event->command_id != 0x03 && event->command_id != 0x04) + return 0; + @@ -6293,6 +7208,7 @@ index 0000000000000..baf8b53e7f990 + vhf->notif.event.reg = p->registry; + vhf->notif.event.id.target_category = sdev->uid.category; + vhf->notif.event.id.instance = sdev->uid.instance; ++ vhf->notif.event.mask = SSAM_EVENT_MASK_STRICT; + vhf->notif.event.flags = 0; + + ssam_device_set_drvdata(sdev, vhf); @@ -6358,10 +7274,10 @@ index 0000000000000..baf8b53e7f990 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_sam/clients/surface_sam_vhf.c b/drivers/misc/surface_sam/clients/surface_sam_vhf.c new file mode 100644 -index 0000000000000..3b7f08f7d028d +index 0000000000000..8f24e7a7bfea9 --- /dev/null +++ b/drivers/misc/surface_sam/clients/surface_sam_vhf.c -@@ -0,0 +1,336 @@ +@@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Virtual HID Framework (VHF) driver for input events via SAM. @@ -6526,14 +7442,11 @@ index 0000000000000..3b7f08f7d028d + return hid; +} + -+static u32 vhf_event_handler(struct ssam_notifier_block *nb, const struct ssam_event *event) ++static u32 vhf_event_handler(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ -+ struct vhf_drvdata *drvdata = container_of(nb, struct vhf_drvdata, notif.base); ++ struct vhf_drvdata *drvdata = container_of(nf, struct vhf_drvdata, notif); + int status; + -+ if (event->target_category != 0x08) -+ return 0; -+ + if (event->command_id == 0x03 || event->command_id == 0x04) { + status = hid_input_report(drvdata->hid, HID_INPUT_REPORT, (u8 *)&event->data[0], event->length, 1); + return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED; @@ -6646,6 +7559,7 @@ index 0000000000000..3b7f08f7d028d + drvdata->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; + drvdata->notif.event.id.target_category = SSAM_SSH_TC_KBD; + drvdata->notif.event.id.instance = 0; ++ drvdata->notif.event.mask = SSAM_EVENT_MASK_NONE; + drvdata->notif.event.flags = 0; + + platform_set_drvdata(pdev, drvdata); @@ -6700,10 +7614,10 @@ index 0000000000000..3b7f08f7d028d +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..5cbb54a2d54f2 +index 0000000000000..c279159e3aabc --- /dev/null +++ b/drivers/misc/surface_sam/controller.c -@@ -0,0 +1,2384 @@ +@@ -0,0 +1,2441 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -6747,6 +7661,8 @@ index 0000000000000..5cbb54a2d54f2 +/** + * ssh_seq_next() - Get next sequence ID. + * @c: The counter providing the sequence IDs. ++ * ++ * Return: Retunrs the next sequence ID of the counter. + */ +static inline u8 ssh_seq_next(struct ssh_seq_counter *c) +{ @@ -6774,6 +7690,9 @@ index 0000000000000..5cbb54a2d54f2 +/** + * ssh_rqid_next() - Get next request ID. + * @c: The counter providing the request IDs. ++ * ++ * Return: Returns the next request ID of the counter, skipping any reserved ++ * request IDs. + */ +static inline u16 ssh_rqid_next(struct ssh_rqid_counter *c) +{ @@ -6800,6 +7719,30 @@ index 0000000000000..5cbb54a2d54f2 + * and identify new/currently unimplemented features. + */ + ++ ++/** ++ * ssam_event_matches_notifier() - Test if an event matches a notifier; ++ * @notif: The event notifier to test against. ++ * @event: The event to test. ++ * ++ * Return: Returns %true iff the given event matches the given notifier ++ * according to the rules set in the notifier's event mask, %false otherwise. ++ */ ++static bool ssam_event_matches_notifier( ++ const struct ssam_event_notifier *notif, ++ const struct ssam_event *event) ++{ ++ bool match = notif->event.id.target_category == event->target_category; ++ ++ if (notif->event.mask & SSAM_EVENT_MASK_TARGET) ++ match &= notif->event.reg.target_id == event->target_id; ++ ++ if (notif->event.mask & SSAM_EVENT_MASK_INSTANCE) ++ match &= notif->event.id.instance == event->instance_id; ++ ++ return match; ++} ++ +/** + * ssam_nfblk_call_chain() - Call event notifier callbacks of the given chain. + * @nh: The notifier head for which the notifier callbacks should be called. @@ -6810,25 +7753,30 @@ index 0000000000000..5cbb54a2d54f2 + * %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. ++ * Return: 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; ++ struct ssam_event_notifier *nf; + int ret = 0, idx; + + idx = srcu_read_lock(&nh->srcu); + + nb = rcu_dereference_raw(nh->head); + while (nb) { ++ nf = container_of(nb, struct ssam_event_notifier, base); + next_nb = rcu_dereference_raw(nb->next); + -+ ret = (ret & SSAM_NOTIF_STATE_MASK) | nb->fn(nb, event); -+ if (ret & SSAM_NOTIF_STOP) -+ break; ++ if (ssam_event_matches_notifier(nf, event)) { ++ ret = (ret & SSAM_NOTIF_STATE_MASK) | nb->fn(nf, event); ++ if (ret & SSAM_NOTIF_STOP) ++ break; ++ } + + nb = next_nb; + } @@ -6879,9 +7827,9 @@ index 0000000000000..5cbb54a2d54f2 + * Note: This function must be synchronized by the caller with respect to + * insert and/or remove calls. + * -+ * Returns a pointer to the pointer pointing to the given notifier block from -+ * the previous node in the list, or %NULL if the given notifier block is not -+ * contained in the notifier list. ++ * Return: Returns a pointer to the link (i.e. pointer pointing) to the given ++ * notifier block, from the previous node in the list, or %NULL if the given ++ * notifier block is not contained in the notifier list. + */ +static struct ssam_notifier_block **__ssam_nfblk_find_link( + struct ssam_nf_head *nh, struct ssam_notifier_block *nb) @@ -6903,11 +7851,11 @@ index 0000000000000..5cbb54a2d54f2 + * list. + * @link: The link to be erased. + * -+ * Note: This function must be synchronized by the caller with respect to other -+ * insert and/or remove/erase/find calls. 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. ++ * Note: This function must be synchronized by the caller with respect to ++ * other insert and/or remove/erase/find calls. The caller _must_ ensure SRCU ++ * synchronization by calling synchronize_srcu() with ``nh->srcu`` after ++ * leaving the critical section, to ensure that the removed notifier block is ++ * not in use any more. + */ +static void __ssam_nfblk_erase(struct ssam_notifier_block **link) +{ @@ -7204,7 +8152,7 @@ index 0000000000000..5cbb54a2d54f2 + +#define SSAM_CPLT_WQ_NAME "ssam_cpltq" + -+/** ++/* + * SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN - Maximum payload length for a cached + * &struct ssam_event_item. + * @@ -7269,7 +8217,11 @@ index 0000000000000..5cbb54a2d54f2 + * + * 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). ++ * %SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN). Sets the item operations and payload ++ * length values. The item free callback (``ops.free``) should not be ++ * overwritten after this call. ++ * ++ * Return: Returns the newly allocated event item. + */ +static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags) +{ @@ -7352,10 +8304,10 @@ index 0000000000000..5cbb54a2d54f2 + * @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). ++ * Return: 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 tid, u16 rqid) @@ -7395,6 +8347,9 @@ index 0000000000000..5cbb54a2d54f2 + * 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. ++ * ++ * Return: Returns zero on success, %-EINVAL if there is no event queue that ++ * can handle the given event item. + */ +static int ssam_cplt_submit_event(struct ssam_cplt *cplt, + struct ssam_event_item *item) @@ -7525,9 +8480,12 @@ index 0000000000000..5cbb54a2d54f2 +/* -- Main SSAM device structures. ------------------------------------------ */ + +/** -+ * ssam_controller_device() - Return the &struct device associated with this ++ * ssam_controller_device() - Get the &struct device associated with this + * controller. + * @c: The controller for which to get the device. ++ * ++ * Return: Returns the &struct device associated with this controller, ++ * providing its lower-level transport. + */ +struct device *ssam_controller_device(struct ssam_controller *c) +{ @@ -7665,16 +8623,19 @@ index 0000000000000..5cbb54a2d54f2 + 0x89, 0xfc, 0xf6, 0xaa, 0xae, 0x7e, 0xd5, 0xb5); + +/** -+ * ssam_device_caps_load_from_acpi() - Load controller capabilities from _DSM. ++ * ssam_controller_caps_load_from_acpi() - Load controller capabilities from ++ * ACPI _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. ++ * ++ * Return: Returns zero on success, %-EFAULT on failure. + */ -+static int ssam_device_caps_load_from_acpi(acpi_handle handle, -+ struct ssam_device_caps *caps) ++static int ssam_controller_caps_load_from_acpi( ++ acpi_handle handle, struct ssam_controller_caps *caps) +{ + union acpi_object *obj; + u64 funcs = 0; @@ -7735,7 +8696,7 @@ index 0000000000000..5cbb54a2d54f2 + init_rwsem(&ctrl->lock); + kref_init(&ctrl->kref); + -+ status = ssam_device_caps_load_from_acpi(handle, &ctrl->caps); ++ status = ssam_controller_caps_load_from_acpi(handle, &ctrl->caps); + if (status) + return status; + @@ -7751,7 +8712,7 @@ index 0000000000000..5cbb54a2d54f2 + if (status) + return status; + -+ // initialize request and packet transmission layers ++ // initialize request and packet transport layers + status = ssh_rtl_init(&ctrl->rtl, serdev, &ssam_rtl_ops); + if (status) { + ssam_cplt_destroy(&ctrl->cplt); @@ -7779,16 +8740,10 @@ index 0000000000000..5cbb54a2d54f2 + if (READ_ONCE(ctrl->state) != SSAM_CONTROLLER_INITIALIZED) + return -EINVAL; + -+ status = ssh_rtl_tx_start(&ctrl->rtl); ++ status = ssh_rtl_start(&ctrl->rtl); + if (status) + return status; + -+ status = ssh_rtl_rx_start(&ctrl->rtl); -+ if (status) { -+ ssh_rtl_tx_flush(&ctrl->rtl); -+ return status; -+ } -+ + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STARTED); + return 0; +} @@ -7823,7 +8778,7 @@ index 0000000000000..5cbb54a2d54f2 + // try to flush pending events and requests while everything still works + status = ssh_rtl_flush(&ctrl->rtl, msecs_to_jiffies(5000)); + if (status) { -+ ssam_err(ctrl, "failed to flush request transmission layer: %d\n", ++ ssam_err(ctrl, "failed to flush request transport layer: %d\n", + status); + } + @@ -7844,7 +8799,6 @@ index 0000000000000..5cbb54a2d54f2 + ssam_notifier_unregister_all(ctrl); + + // cancel rem. requests, ensure no new ones can be queued, stop threads -+ ssh_rtl_tx_flush(&ctrl->rtl); + ssh_rtl_shutdown(&ctrl->rtl); + + WRITE_ONCE(ctrl->state, SSAM_CONTROLLER_STOPPED); @@ -7953,7 +8907,7 @@ index 0000000000000..5cbb54a2d54f2 + * only valid to send the resulting message via the controller specified here. + * + * For calculation of the required buffer size, refer to the -+ * ``SSH_COMMAND_MESSAGE_LENGTH()`` macro. ++ * SSH_COMMAND_MESSAGE_LENGTH() macro. + * + * Return: Returns the number of bytes used in the buffer on success. Returns + * %-EINVAL if the payload length provided in the request specification is too @@ -8049,6 +9003,9 @@ index 0000000000000..5cbb54a2d54f2 + * + * After use, the request and its corresponding message buffer should be freed + * via ssam_request_sync_free(). The buffer must not be freed separately. ++ * ++ * Return: Returns zero on success, %-ENOMEM if the request could not be ++ * allocated. + */ +int ssam_request_sync_alloc(size_t payload_len, gfp_t flags, + struct ssam_request_sync **rqst, @@ -8168,7 +9125,7 @@ index 0000000000000..5cbb54a2d54f2 + * 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. ++ * Return: 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) @@ -8178,12 +9135,6 @@ index 0000000000000..5cbb54a2d54f2 + ssize_t len; + int status; + -+ // prevent overflow, allows us to skip checks later on -+ if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE) { -+ ssam_err(ctrl, "rqst: request payload too large\n"); -+ return -EINVAL; -+ } -+ + status = ssam_request_sync_alloc(spec->length, GFP_KERNEL, &rqst, &buf); + if (status) + return status; @@ -8192,8 +9143,10 @@ index 0000000000000..5cbb54a2d54f2 + ssam_request_sync_set_resp(rqst, rsp); + + len = ssam_request_write_data(&buf, ctrl, spec); -+ if (len < 0) ++ if (len < 0) { ++ ssam_request_sync_free(rqst); + return len; ++ } + + ssam_request_sync_set_data(rqst, buf.ptr, len); + @@ -8217,7 +9170,7 @@ index 0000000000000..5cbb54a2d54f2 + * 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 ++ * 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 @@ -8225,7 +9178,7 @@ index 0000000000000..5cbb54a2d54f2 + * provided message data buffer and stores the (small) request struct on the + * heap. + * -+ * Returns the status of the request or any failure during setup. ++ * Return: 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, @@ -8236,12 +9189,6 @@ index 0000000000000..5cbb54a2d54f2 + ssize_t len; + int status; + -+ // prevent overflow, allows us to skip checks later on -+ if (spec->length > SSH_COMMAND_MAX_PAYLOAD_SIZE) { -+ ssam_err(ctrl, "rqst: request payload too large\n"); -+ return -EINVAL; -+ } -+ + ssam_request_sync_init(&rqst, spec->flags); + ssam_request_sync_set_resp(&rqst, rsp); + @@ -8298,6 +9245,25 @@ index 0000000000000..5cbb54a2d54f2 +}); + +/** ++ * struct ssh_notification_params - Command payload to enable/disable SSH ++ * notifications. ++ * @target_category: The target category for which notifications should be ++ * enabled/disabled. ++ * @flags: Flags determining how notifications are being sent. ++ * @request_id: The request ID that is used to send these notifications. ++ * @instance_id: The specific instance in the given target category for ++ * which notifications should be enabled. ++ */ ++struct ssh_notification_params { ++ u8 target_category; ++ u8 flags; ++ __le16 request_id; ++ u8 instance_id; ++} __packed; ++ ++static_assert(sizeof(struct ssh_notification_params) == 5); ++ ++/** + * 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 @@ -8309,8 +9275,9 @@ index 0000000000000..5cbb54a2d54f2 + * not handle referecnce counting for enable/disable of events. If an event + * has already been enabled, the EC will ignore this request. + * -+ * Return: Returns the status of the executed SAM request or %-EPROTO if the -+ * request response indicates a failure. ++ * Return: Returns the status of the executed SAM request (zero on success and ++ * negative on direct failure) or %-EPROTO if the request response indicates a ++ * failure. + */ +static int ssam_ssh_event_enable(struct ssam_controller *ctrl, + struct ssam_event_registry reg, @@ -8376,8 +9343,9 @@ index 0000000000000..5cbb54a2d54f2 + * not handle reference counting for enable/disable of events. If an event has + * already been disabled, the EC will ignore this request. + * -+ * Return: Returns the status of the executed SAM request or %-EPROTO if the -+ * request response indicates a failure. ++ * Return: Returns the status of the executed SAM request (zero on success and ++ * negative on direct failure) or %-EPROTO if the request response indicates a ++ * failure. + */ +static int ssam_ssh_event_disable(struct ssam_controller *ctrl, + struct ssam_event_registry reg, @@ -8436,6 +9404,9 @@ index 0000000000000..5cbb54a2d54f2 +/** + * ssam_log_firmware_version() - Log SAM/EC firmware version to kernel log. + * @ctrl: The controller. ++ * ++ * Return: Returns zero on success or the status of the executed SAM request ++ * if that request failed. + */ +int ssam_log_firmware_version(struct ssam_controller *ctrl) +{ @@ -8479,9 +9450,9 @@ index 0000000000000..5cbb54a2d54f2 + * + * Use ssam_ctrl_notif_display_on() to reverse the effects of this function. + * -+ * Return: 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. ++ * Return: Returns zero on success or if no request has been executed, the ++ * status of the executed SAM request if that request failed, or %-EPROTO if ++ * an unexpected response has been received. + */ +int ssam_ctrl_notif_display_off(struct ssam_controller *ctrl) +{ @@ -8521,9 +9492,9 @@ index 0000000000000..5cbb54a2d54f2 + * + * See ssam_ctrl_notif_display_off() for more details. + * -+ * Return: 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. ++ * Return: Returns zero on success or if no request has been executed, the ++ * status of the executed SAM request if that request failed, or %-EPROTO if ++ * an unexpected response has been received. + */ +int ssam_ctrl_notif_display_on(struct ssam_controller *ctrl) +{ @@ -8563,9 +9534,9 @@ index 0000000000000..5cbb54a2d54f2 + * + * Use ssam_ctrl_notif_d0_entry() to reverse the effects of this function. + * -+ * Return: 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. ++ * Return: Returns zero on success or if no request has been executed, the ++ * status of the executed SAM request if that request failed, or %-EPROTO if ++ * an unexpected response has been received. + */ +int ssam_ctrl_notif_d0_exit(struct ssam_controller *ctrl) +{ @@ -8605,9 +9576,9 @@ index 0000000000000..5cbb54a2d54f2 + * + * See ssam_ctrl_notif_d0_exit() for more details. + * -+ * Return: 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. ++ * Return: Returns zero on success or if no request has been executed, the ++ * status of the executed SAM request if that request failed, or %-EPROTO if ++ * an unexpected response has been received. + */ +int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl) +{ @@ -8648,8 +9619,8 @@ index 0000000000000..5cbb54a2d54f2 + * %INT_MAX notifiers for the event ID/type associated with the notifier block + * registered, %-ENOMEM if the corresponding event entry could not be + * allocated. If this is the first time that a notifier block is registered -+ * for the specific associated event, returns the status of the event enable -+ * EC command. ++ * for the specific associated event, returns the status of the event-enable ++ * EC-command. + */ +int ssam_notifier_register(struct ssam_controller *ctrl, + struct ssam_event_notifier *n) @@ -8727,7 +9698,7 @@ index 0000000000000..5cbb54a2d54f2 + * Return: Returns zero on success, %-ENOENT if the given notifier block has + * not been registered on the controller. If the given notifier block was the + * last one associated with its specific event, returns the status of the -+ * event disable EC command. ++ * event-disable EC-command. + */ +int ssam_notifier_unregister(struct ssam_controller *ctrl, + struct ssam_event_notifier *n) @@ -8803,8 +9774,8 @@ index 0000000000000..5cbb54a2d54f2 + * notifiers are going to be added after this call and before the corresponding + * call to ssam_notifier_restore_registered(). + * -+ * Returns zero on success. In case of failure returns the error code returned -+ * by the failed EC command to disable an event. ++ * Return: Returns zero on success. In case of failure returns the error code ++ * returned by the failed EC command to disable an event. + */ +int ssam_notifier_disable_registered(struct ssam_controller *ctrl) +{ @@ -8872,8 +9843,8 @@ index 0000000000000..5cbb54a2d54f2 + * 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. ++ * Return: Returns %true if there are currently no notifiers registered on the ++ * controller, %false otherwise. + */ +static bool ssam_notifier_empty(struct ssam_controller *ctrl) +{ @@ -9090,7 +10061,7 @@ index 0000000000000..5cbb54a2d54f2 +} diff --git a/drivers/misc/surface_sam/controller.h b/drivers/misc/surface_sam/controller.h new file mode 100644 -index 0000000000000..7f58e2a4b51b2 +index 0000000000000..2c328e21df68d --- /dev/null +++ b/drivers/misc/surface_sam/controller.h @@ -0,0 +1,275 @@ @@ -9236,7 +10207,7 @@ index 0000000000000..7f58e2a4b51b2 +/** + * 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. ++ * The controller has not been initialized yet or has been deinitialized. + * @SSAM_CONTROLLER_INITIALIZED: + * The controller is initialized, but has not been started yet. + * @SSAM_CONTROLLER_STARTED: @@ -9255,11 +10226,11 @@ index 0000000000000..7f58e2a4b51b2 +}; + +/** -+ * struct ssam_device_caps - Controller device capabilities. ++ * struct ssam_controller_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 { ++struct ssam_controller_caps { + u32 notif_display:1; + u32 notif_d0exit:1; +}; @@ -9298,7 +10269,7 @@ index 0000000000000..7f58e2a4b51b2 + bool wakeup_enabled; + } irq; + -+ struct ssam_device_caps caps; ++ struct ssam_controller_caps caps; +}; + +#define to_ssam_controller(ptr, member) \ @@ -9319,8 +10290,8 @@ index 0000000000000..7f58e2a4b51b2 + * 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. ++ * Return: Returns the number of bytes consumed, or, if the packet transport ++ * layer of the controller has been shut down, %-ESHUTDOWN. + */ +static inline +int ssam_controller_receive_buf(struct ssam_controller *ctrl, @@ -9336,7 +10307,7 @@ index 0000000000000..7f58e2a4b51b2 + */ +static inline void ssam_controller_write_wakeup(struct ssam_controller *ctrl) +{ -+ ssh_ptl_tx_wakeup(&ctrl->rtl.ptl, true); ++ ssh_ptl_tx_wakeup(&ctrl->rtl.ptl); +} + + @@ -9371,10 +10342,10 @@ index 0000000000000..7f58e2a4b51b2 +#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..db1019c90b114 +index 0000000000000..5164d13e52f8d --- /dev/null +++ b/drivers/misc/surface_sam/core.c -@@ -0,0 +1,764 @@ +@@ -0,0 +1,771 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface Serial Hub (SSH) driver for communication with the Surface/System @@ -9524,7 +10495,7 @@ index 0000000000000..db1019c90b114 + * link is not going to save us any more, as unbinding is already in + * progress. + */ -+ if (link->status == DL_STATE_SUPPLIER_UNBIND) { ++ if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) { + ssam_controller_stateunlock(c); + return -ENXIO; + } @@ -9693,12 +10664,19 @@ index 0000000000000..db1019c90b114 + int status; + + /* -+ * Try to signal display-off and D0-exit, ignore any errors. ++ * Try to disable notifiers, signal display-off and D0-exit, ignore any ++ * errors. + * + * Note: It has not been established yet if this is actually + * necessary/useful for shutdown. + */ + ++ status = ssam_notifier_disable_registered(c); ++ if (status) { ++ ssam_err(c, "pm: failed to disable notifiers for shutdown: %d\n", ++ status); ++ } ++ + status = ssam_ctrl_notif_display_off(c); + if (status) + ssam_err(c, "pm: display-off notification failed: %d\n", status); @@ -10141,7 +11119,7 @@ index 0000000000000..db1019c90b114 +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..7aa05c81ad427 +index 0000000000000..ad1ce1cdd474f --- /dev/null +++ b/drivers/misc/surface_sam/ssam_trace.h @@ -0,0 +1,619 @@ @@ -10290,9 +11268,9 @@ index 0000000000000..7aa05c81ad427 + * ssam_trace_get_request_tc() - Read the packet's request target category. + * @p: The packet. + * -+ * Returns the packet's request target category (TC) field if the packet -+ * represents a request with command data, or %SSAM_TC_NOT_APPLICABLE if not -+ * (e.g. flush request, control packet). ++ * Return: Returns the packet's request target category (TC) field if the ++ * packet represents a request with command data, or %SSAM_TC_NOT_APPLICABLE ++ * if not (e.g. flush request, control packet). + */ +static inline u32 ssam_trace_get_request_tc(const struct ssh_packet *p) +{ @@ -10766,7 +11744,7 @@ index 0000000000000..7aa05c81ad427 +#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..ae8c3886722f6 +index 0000000000000..4b37c5577897c --- /dev/null +++ b/drivers/misc/surface_sam/ssh_msgb.h @@ -0,0 +1,196 @@ @@ -10836,7 +11814,7 @@ index 0000000000000..ae8c3886722f6 + +/** + * msgb_push_syn() - Push SSH SYN bytes to the buffer. -+ * @msgb: The message buffer. ++ * @msgb: The message buffer. + */ +static inline void msgb_push_syn(struct msgbuf *msgb) +{ @@ -10968,10 +11946,10 @@ index 0000000000000..ae8c3886722f6 +#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..1b7d59ec4f433 +index 0000000000000..21a8f7e4473b4 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_packet_layer.c -@@ -0,0 +1,1780 @@ +@@ -0,0 +1,2001 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -11017,12 +11995,12 @@ index 0000000000000..1b7d59ec4f433 + * + * >> Data Structures, Packet Ownership, General Overview << + * -+ * The code below employs two main data structures: The packet queue, containing -+ * all packets scheduled for transmission, and the set of pending packets, -+ * containing all packets awaiting an ACK. ++ * The code below employs two main data structures: The packet queue, ++ * containing all packets scheduled for transmission, and the set of pending ++ * packets, containing all packets awaiting an ACK. + * -+ * Shared ownership of a packet is controlled via reference counting. Inside the -+ * transmission system are a total of five packet owners: ++ * Shared ownership of a packet is controlled via reference counting. Inside ++ * the transport system are a total of five packet owners: + * + * - the packet queue, + * - the pending set, @@ -11033,40 +12011,53 @@ index 0000000000000..1b7d59ec4f433 + * Normal operation is as follows: The initial reference of the packet is + * obtained by submitting the packet and queueing it. The receiver thread + * takes packets from the queue. By doing this, it does not increment the -+ * refcount but takes over the reference (removing it from the queue). -+ * If the packet is sequenced (i.e. needs to be ACKed by the client), the -+ * transmitter thread sets-up the timeout and adds the packet to the pending set -+ * before starting to transmit it. As the timeout is handled by a reaper task, -+ * no additional reference for it is needed. After the transmit is done, the -+ * reference hold by the transmitter thread is dropped. If the packet is ++ * refcount but takes over the reference (removing it from the queue). If the ++ * packet is sequenced (i.e. needs to be ACKed by the client), the transmitter ++ * thread sets-up the timeout and adds the packet to the pending set before ++ * starting to transmit it. As the timeout is handled by a reaper task, no ++ * additional reference for it is needed. After the transmit is done, the ++ * reference held by the transmitter thread is dropped. If the packet is + * unsequenced (i.e. does not need an ACK), the packet is completed by the + * transmitter thread before dropping that reference. + * -+ * On receial of an ACK, the receiver thread removes and obtains the refernce to -+ * the packet from the pending set. On succes, the receiver thread will then ++ * On receival of an ACK, the receiver thread removes and obtains the ++ * reference to the packet from the pending set. The receiver thread will then + * complete the packet and drop its reference. + * -+ * On error, the completion callback is immediately run by on thread on which -+ * the error was detected. ++ * On receival of a NAK, the receiver thread re-submits all currently pending ++ * packets. + * -+ * To ensure that a packet eventually leaves the system it is marked as "locked" -+ * directly before it is going to be completed or when it is canceled. Marking a -+ * packet as "locked" has the effect that passing and creating new references -+ * of the packet will be blocked. This means that the packet cannot be added -+ * to the queue, the pending set, and the timeout, or be picked up by the -+ * transmitter thread or receiver thread. To remove a packet from the system it -+ * has to be marked as locked and subsequently all references from the data -+ * structures (queue, pending) have to be removed. References held by threads -+ * will eventually be dropped automatically as their execution progresses. ++ * Packet timeouts are detected by the timeout reaper. This is a task, ++ * scheduled depending on the earliest packet timeout expiration date, ++ * checking all currently pending packets if their timeout has expired. If the ++ * timeout of a packet has expired, it is re-submitted and the number of tries ++ * of this packet is incremented. If this number reaches its limit, the packet ++ * will be completed with a failure. ++ * ++ * On transmission failure (such as repeated packet timeouts), the completion ++ * callback is immediately run by on thread on which the error was detected. ++ * ++ * To ensure that a packet eventually leaves the system it is marked as ++ * "locked" directly before it is going to be completed or when it is ++ * canceled. Marking a packet as "locked" has the effect that passing and ++ * creating new references of the packet is disallowed. This means that the ++ * packet cannot be added to the queue, the pending set, and the timeout, or ++ * be picked up by the transmitter thread or receiver thread. To remove a ++ * packet from the system it has to be marked as locked and subsequently all ++ * references from the data structures (queue, pending) have to be removed. ++ * References held by threads will eventually be dropped automatically as ++ * their execution progresses. + * + * Note that the packet completion callback is, in case of success and for a -+ * sequenced packet, guaranteed to run on the receiver thread, thus providing a -+ * way to reliably identify responses to the packet. The packet completion -+ * callback is only run once and it does not indicate that the packet has fully -+ * left the system. In case of re-submission (and with somewhat unlikely -+ * timing), it may be possible that the packet is being re-transmitted while the -+ * completion callback runs. Completion will occur both on success and internal -+ * error, as well as when the packet is canceled. ++ * sequenced packet, guaranteed to run on the receiver thread, thus providing ++ * a way to reliably identify responses to the packet. The packet completion ++ * callback is only run once and it does not indicate that the packet has ++ * fully left the system (for this, one should rely on the release method, ++ * triggered when the reference count of the packet reaches zero). In case of ++ * re-submission (and with somewhat unlikely timing), it may be possible that ++ * the packet is being re-transmitted while the completion callback runs. ++ * Completion will occur both on success and internal error, as well as when ++ * the packet is canceled. + * + * >> Flags << + * @@ -11081,19 +12072,19 @@ index 0000000000000..1b7d59ec4f433 + * ACKed, transmitted, ...). + * + * - completed -+ * Indicates if the packet completion has been run or is about to be run. This -+ * flag is used to ensure that the packet completion callback is only run -+ * once. ++ * Indicates if the packet completion callback has been executed or is about ++ * to be executed. This flag is used to ensure that the packet completion ++ * callback is only run once. + * + * - queued + * Indicates if a packet is present in the submission queue or not. This flag -+ * must only be modified with the queue lock held, and must be coherent ++ * must only be modified with the queue lock held, and must be coherent to the + * presence of the packet in the queue. + * + * - pending + * Indicates if a packet is present in the set of pending packets or not. + * This flag must only be modified with the pending lock held, and must be -+ * coherent presence of the packet in the pending set. ++ * coherent to the presence of the packet in the pending set. + * + * - transmitting + * Indicates if the packet is currently transmitting. In case of @@ -11109,7 +12100,7 @@ index 0000000000000..1b7d59ec4f433 + * Indicates if the packet has been acknowledged by the client. There are no + * other guarantees given. For example, the packet may still be canceled + * and/or the completion may be triggered an error even though this bit is -+ * set. Rely on the status provided by completion instead. ++ * set. Rely on the status provided to the completion callback instead. + * + * - canceled + * Indicates if the packet has been canceled from the outside. There are no @@ -11118,38 +12109,54 @@ index 0000000000000..1b7d59ec4f433 + * + * >> General Notes << + * -+ * To avoid deadlocks, if both queue and pending locks are required, the pending -+ * lock must be acquired before the queue lock. ++ * To avoid deadlocks, if both queue and pending locks are required, the ++ * pending lock must be acquired before the queue lock. + */ + +/* -+ * Maximum number transmission attempts per sequenced packet in case of -+ * time-outs. Must be smaller than 16. ++ * SSH_PTL_MAX_PACKET_TRIES - Maximum transmission attempts for packet. ++ * ++ * Maximum number of transmission attempts per sequenced packet in case of ++ * time-outs. Must be smaller than 16. If the packet times out after this ++ * amount of tries, the packet will be completed with %-ETIMEDOUT as status ++ * code. + */ -+#define SSH_PTL_MAX_PACKET_TRIES 3 ++#define SSH_PTL_MAX_PACKET_TRIES 3 + +/* ++ * SSH_PTL_PACKET_TIMEOUT - Packet timeout. ++ * + * Timeout as ktime_t delta for ACKs. If we have not received an ACK in this + * time-frame after starting transmission, the packet will be re-submitted. + */ +#define SSH_PTL_PACKET_TIMEOUT ms_to_ktime(1000) + +/* -+ * Maximum time resolution for timeouts. Should be larger than one jiffy to -+ * avoid direct re-scheduling of reaper work_struct. ++ * SSH_PTL_PACKET_TIMEOUT_RESOLUTION - Packet timeout granularity. ++ * ++ * Time-resolution for timeouts. Should be larger than one jiffy to avoid ++ * direct re-scheduling of reaper work_struct. + */ +#define SSH_PTL_PACKET_TIMEOUT_RESOLUTION ms_to_ktime(max(2000 / HZ, 50)) + +/* ++ * SSH_PTL_MAX_PENDING - Maximum number of pending packets. ++ * + * Maximum number of sequenced packets concurrently waiting for an ACK. + * Packets marked as blocking will not be transmitted while this limit is + * reached. + */ -+#define SSH_PTL_MAX_PENDING 1 ++#define SSH_PTL_MAX_PENDING 1 + -+#define SSH_PTL_RX_BUF_LEN 4096 ++/* ++ * SSH_PTL_RX_BUF_LEN - Evaluation-buffer size in bytes. ++ */ ++#define SSH_PTL_RX_BUF_LEN 4096 + -+#define SSH_PTL_RX_FIFO_LEN 4096 ++/* ++ * SSH_PTL_RX_FIFO_LEN - Fifo input-buffer size in bytes. ++ */ ++#define SSH_PTL_RX_FIFO_LEN 4096 + + +#ifdef CONFIG_SURFACE_SAM_SSH_ERROR_INJECTION @@ -11319,8 +12326,8 @@ index 0000000000000..1b7d59ec4f433 + if (unlikely(status)) { + trace_ssam_ei_tx_fail_write(packet, status); + ptl_info(packet->ptl, -+ "packet error injection: simulating transmit error %d, packet %p\n", -+ status, packet); ++ "packet error injection: simulating transmit error %d," ++ " packet %p\n", status, packet); + + return status; + } @@ -11436,7 +12443,7 @@ index 0000000000000..1b7d59ec4f433 + +static void __ssh_ptl_packet_release(struct kref *kref) +{ -+ struct ssh_packet *p = to_ssh_packet(kref, refcnt); ++ struct ssh_packet *p = container_of(kref, struct ssh_packet, refcnt); + + trace_ssam_packet_release(p); + @@ -11444,6 +12451,32 @@ index 0000000000000..1b7d59ec4f433 + p->ops->release(p); +} + ++/** ++ * ssh_packet_get() - Increment reference count of packet. ++ * @packet: The packet to increment the reference count of. ++ * ++ * Increments the reference count of the given packet. See ssh_packet_put() ++ * for the counter-part of this function. ++ * ++ * Return: Returns the packet provided as input. ++ */ ++struct ssh_packet *ssh_packet_get(struct ssh_packet *packet) ++{ ++ kref_get(&packet->refcnt); ++ return packet; ++} ++EXPORT_SYMBOL_GPL(ssh_packet_get); ++ ++/** ++ * ssh_packet_put() - Decrement reference count of packet. ++ * @packet: The packet to decrement the reference count of. ++ * ++ * If the reference count reaches zero, the ``release`` callback specified in ++ * the packet's &struct ssh_packet_ops, i.e. ``packet->ops->release``, will be ++ * called. ++ * ++ * See ssh_packet_get() for the counter-part of this function. ++ */ +void ssh_packet_put(struct ssh_packet *packet) +{ + kref_put(&packet->refcnt, __ssh_ptl_packet_release); @@ -11456,8 +12489,21 @@ index 0000000000000..1b7d59ec4f433 +} + + -+void ssh_packet_init(struct ssh_packet *packet, -+ const struct ssh_packet_args *args) ++/** ++ * ssh_packet_init() - Initialize SSH packet. ++ * @packet: The packet to initialize. ++ * @type: Type-flags of the packet. ++ * @priority: Priority of the packet. See SSH_PACKET_PRIORITY() for details. ++ * @ops: Packet operations. ++ * ++ * Initializes the given SSH packet. Sets the transmission buffer pointer to ++ * %NULL and the transmission buffer length to zero. For data-type packets, ++ * this buffer has to be set separately via ssh_packet_set_data() before ++ * submission, and must contain a valid SSH message, i.e. frame with optional ++ * payload of any type. ++ */ ++void ssh_packet_init(struct ssh_packet *packet, unsigned long type, ++ u8 priority, const struct ssh_packet_ops *ops) +{ + kref_init(&packet->refcnt); + @@ -11465,19 +12511,22 @@ index 0000000000000..1b7d59ec4f433 + INIT_LIST_HEAD(&packet->queue_node); + INIT_LIST_HEAD(&packet->pending_node); + -+ packet->state = args->type & SSH_PACKET_FLAGS_TY_MASK; -+ packet->priority = args->priority; ++ packet->state = type & SSH_PACKET_FLAGS_TY_MASK; ++ packet->priority = priority; + packet->timestamp = KTIME_MAX; + + packet->data.ptr = NULL; + packet->data.len = 0; + -+ packet->ops = args->ops; ++ packet->ops = ops; +} + + +static struct kmem_cache *ssh_ctrl_packet_cache; + ++/** ++ * ssh_ctrl_packet_cache_init() - Initialize the control packet cache. ++ */ +int ssh_ctrl_packet_cache_init(void) +{ + const unsigned int size = sizeof(struct ssh_packet) + SSH_MSG_LEN_CTRL; @@ -11492,12 +12541,30 @@ index 0000000000000..1b7d59ec4f433 + return 0; +} + ++/** ++ * ssh_ctrl_packet_cache_destroy() - Deinitialize the control packet cache. ++ */ +void ssh_ctrl_packet_cache_destroy(void) +{ + kmem_cache_destroy(ssh_ctrl_packet_cache); + ssh_ctrl_packet_cache = NULL; +} + ++/** ++ * ssh_ctrl_packet_alloc() - Allocate packet from control packet cache. ++ * @packet: Where the pointer to the newly allocated packet should be stored. ++ * @buffer: The buffer corresponding to this packet. ++ * @flags: Flags used for allocation. ++ * ++ * Allocates a packet and corresponding transport buffer from the control ++ * packet cache. Sets the packet's buffer reference to the allocated buffer. ++ * The packet must be freed via ssh_ctrl_packet_free(), which will also free ++ * the corresponding buffer. The corresponding buffer must not be freed ++ * separately. Intended to be used with %ssh_ptl_ctrl_packet_ops as packet ++ * operations. ++ * ++ * Return: Returns zero on success, %-ENOMEM if the allocation failed. ++ */ +static int ssh_ctrl_packet_alloc(struct ssh_packet **packet, + struct ssam_span *buffer, gfp_t flags) +{ @@ -11512,6 +12579,10 @@ index 0000000000000..1b7d59ec4f433 + return 0; +} + ++/** ++ * ssh_ctrl_packet_free() - Free packet allocated from control packet cache. ++ * @p: The packet to free. ++ */ +static void ssh_ctrl_packet_free(struct ssh_packet *p) +{ + trace_ssam_ctrl_packet_free(p); @@ -11529,15 +12600,20 @@ index 0000000000000..1b7d59ec4f433 +{ + unsigned long delta = msecs_to_jiffies(ktime_ms_delta(expires, now)); + ktime_t aexp = ktime_add(expires, SSH_PTL_PACKET_TIMEOUT_RESOLUTION); -+ ktime_t old; ++ ktime_t old_exp, old_act; + + // re-adjust / schedule reaper if it is above resolution delta -+ old = READ_ONCE(ptl->rtx_timeout.expires); -+ while (ktime_before(aexp, old)) -+ old = cmpxchg64(&ptl->rtx_timeout.expires, old, expires); ++ old_act = READ_ONCE(ptl->rtx_timeout.expires); ++ if (ktime_after(aexp, old_act)) ++ return; ++ ++ do { ++ old_exp = old_act; ++ old_act = cmpxchg64(&ptl->rtx_timeout.expires, old_exp, expires); ++ } while (old_exp != old_act && ktime_before(aexp, old_act)); + + // if we updated the reaper expiration, modify work timeout -+ if (old == expires) ++ if (old_exp == old_act && old_act != expires) + mod_delayed_work(system_wq, &ptl->rtx_timeout.reaper, delta); +} + @@ -11553,8 +12629,8 @@ index 0000000000000..1b7d59ec4f433 + 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. ++ * implicit barrier following check on ssh_packet_get_expiration() in ++ * ssh_ptl_timeout_reap(). + */ + smp_mb__after_atomic(); + @@ -11562,6 +12638,16 @@ index 0000000000000..1b7d59ec4f433 +} + + ++static void ssh_packet_next_try(struct ssh_packet *p) ++{ ++ u8 priority = READ_ONCE(p->priority); ++ u8 base = ssh_packet_priority_get_base(priority); ++ u8 try = ssh_packet_priority_get_try(priority); ++ ++ WRITE_ONCE(p->priority, __SSH_PACKET_PRIORITY(base, try + 1)); ++} ++ ++/* must be called with queue lock held */ +static struct list_head *__ssh_ptl_queue_find_entrypoint(struct ssh_packet *p) +{ + struct list_head *head; @@ -11580,7 +12666,7 @@ index 0000000000000..1b7d59ec4f433 + * from back to front. + */ + -+ if (priority > SSH_PACKET_PRIORITY_DATA) { ++ if (priority > SSH_PACKET_PRIORITY(DATA, 0)) { + list_for_each(head, &p->ptl->queue.head) { + p = list_entry(head, struct ssh_packet, queue_node); + @@ -11602,53 +12688,56 @@ index 0000000000000..1b7d59ec4f433 + return head; +} + -+static int ssh_ptl_queue_push(struct ssh_packet *packet) ++/* must be called with queue lock held */ ++static int __ssh_ptl_queue_push(struct ssh_packet *packet) +{ + struct ssh_ptl *ptl = packet->ptl; + struct list_head *head; + -+ spin_lock(&ptl->queue.lock); + -+ if (test_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state)) { -+ spin_unlock(&ptl->queue.lock); ++ if (test_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state)) + return -ESHUTDOWN; -+ } + + // avoid further transitions when cancelling/completing -+ if (test_bit(SSH_PACKET_SF_LOCKED_BIT, &packet->state)) { -+ spin_unlock(&ptl->queue.lock); ++ if (test_bit(SSH_PACKET_SF_LOCKED_BIT, &packet->state)) + return -EINVAL; -+ } + + // if this packet has already been queued, do not add it -+ if (test_and_set_bit(SSH_PACKET_SF_QUEUED_BIT, &packet->state)) { -+ spin_unlock(&ptl->queue.lock); ++ if (test_and_set_bit(SSH_PACKET_SF_QUEUED_BIT, &packet->state)) + return -EALREADY; -+ } + + head = __ssh_ptl_queue_find_entrypoint(packet); + + list_add_tail(&ssh_packet_get(packet)->queue_node, &ptl->queue.head); -+ -+ spin_unlock(&ptl->queue.lock); + return 0; +} + ++static int ssh_ptl_queue_push(struct ssh_packet *packet) ++{ ++ int status; ++ ++ spin_lock(&packet->ptl->queue.lock); ++ status = __ssh_ptl_queue_push(packet); ++ spin_unlock(&packet->ptl->queue.lock); ++ ++ return status; ++} ++ +static void ssh_ptl_queue_remove(struct ssh_packet *packet) +{ + struct ssh_ptl *ptl = packet->ptl; -+ bool remove; + + spin_lock(&ptl->queue.lock); + -+ remove = test_and_clear_bit(SSH_PACKET_SF_QUEUED_BIT, &packet->state); -+ if (remove) -+ list_del(&packet->queue_node); ++ if (!test_and_clear_bit(SSH_PACKET_SF_QUEUED_BIT, &packet->state)) { ++ spin_unlock(&ptl->queue.lock); ++ return; ++ } ++ ++ list_del(&packet->queue_node); + + spin_unlock(&ptl->queue.lock); -+ -+ if (remove) -+ ssh_packet_put(packet); ++ ssh_packet_put(packet); +} + + @@ -11679,32 +12768,30 @@ index 0000000000000..1b7d59ec4f433 +static void ssh_ptl_pending_remove(struct ssh_packet *packet) +{ + struct ssh_ptl *ptl = packet->ptl; -+ bool remove; + + spin_lock(&ptl->pending.lock); + -+ remove = test_and_clear_bit(SSH_PACKET_SF_PENDING_BIT, &packet->state); -+ if (remove) { -+ list_del(&packet->pending_node); -+ atomic_dec(&ptl->pending.count); ++ if (!test_and_clear_bit(SSH_PACKET_SF_PENDING_BIT, &packet->state)) { ++ spin_unlock(&ptl->pending.lock); ++ return; + } + ++ list_del(&packet->pending_node); ++ atomic_dec(&ptl->pending.count); ++ + spin_unlock(&ptl->pending.lock); + -+ if (remove) -+ ssh_packet_put(packet); ++ ssh_packet_put(packet); +} + + ++/* warning: does not check/set "completed" bit */ +static void __ssh_ptl_complete(struct ssh_packet *p, int status) +{ + struct ssh_ptl *ptl = READ_ONCE(p->ptl); + + trace_ssam_packet_complete(p, status); -+ -+ ptl_dbg_cond(ptl, "ptl: completing packet %p\n", p); -+ if (status && status != -ECANCELED) -+ ptl_dbg_cond(ptl, "ptl: packet error: %d\n", status); ++ ptl_dbg_cond(ptl, "ptl: completing packet %p (status: %d)\n", p, status); + + if (p->ops->complete) + p->ops->complete(p, status); @@ -11718,7 +12805,7 @@ index 0000000000000..1b7d59ec4f433 + * packet to the structures it's going to be removed from. + * + * The set_bit call does not need explicit memory barriers as the -+ * implicit barrier of the test_and_set_bit call below ensure that the ++ * implicit barrier of the test_and_set_bit() call below ensure that the + * flag is visible before we actually attempt to remove the packet. + */ + @@ -11820,7 +12907,7 @@ index 0000000000000..1b7d59ec4f433 + * the only place where we update the priority in-flight. As this runs + * only on the tx-thread, this read-modify-write procedure is safe. + */ -+ WRITE_ONCE(p->priority, READ_ONCE(p->priority) + 1); ++ ssh_packet_next_try(p); + + return p; +} @@ -11892,7 +12979,7 @@ index 0000000000000..1b7d59ec4f433 + ptl->tx.packet = ssh_ptl_tx_next(ptl); + ptl->tx.offset = 0; + -+ // if no packet is available, we are done ++ // if no packet can be processed, we are done + if (IS_ERR(ptl->tx.packet)) { + ssh_ptl_tx_threadfn_wait(ptl); + continue; @@ -11907,7 +12994,7 @@ index 0000000000000..1b7d59ec4f433 + if (ptl->tx.offset == 0 && !drop) + ssh_ptl_tx_inject_invalid_data(ptl->tx.packet); + -+ // flush-packets don't have any data ++ // note: flush-packets don't have any data + if (likely(ptl->tx.packet->data.ptr && !drop)) { + buf = ptl->tx.packet->data.ptr + ptl->tx.offset; + len = ptl->tx.packet->data.len - ptl->tx.offset; @@ -11947,34 +13034,52 @@ index 0000000000000..1b7d59ec4f433 + return 0; +} + -+void ssh_ptl_tx_wakeup(struct ssh_ptl *ptl, bool force) ++/** ++ * ssh_ptl_tx_wakeup() - Wake up packet transmitter thread. ++ * @ptl: The packet transport layer. ++ * ++ * Wakes up the packet transmitter thread. If the packet transport layer has ++ * been shut down, calls to this function will be ignored. ++ */ ++void ssh_ptl_tx_wakeup(struct ssh_ptl *ptl) +{ + if (test_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state)) + return; + -+ 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); -+ } ++ 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); +} + ++/** ++ * ssh_ptl_tx_start() - Start packet transmitter thread. ++ * @ptl: The packet transport layer. ++ * ++ * Return: Returns zero on success, a negative error code on failure. ++ */ +int ssh_ptl_tx_start(struct ssh_ptl *ptl) +{ -+ ptl->tx.thread = kthread_run(ssh_ptl_tx_threadfn, ptl, "surface-sh-tx"); ++ ptl->tx.thread = kthread_run(ssh_ptl_tx_threadfn, ptl, ++ "ssam_serial_hub-tx"); + if (IS_ERR(ptl->tx.thread)) + return PTR_ERR(ptl->tx.thread); + + return 0; +} + -+static int ssh_ptl_tx_stop(struct ssh_ptl *ptl) ++/** ++ * ssh_ptl_tx_stop() - Stop packet transmitter thread. ++ * @ptl: The packet transport layer. ++ * ++ * Return: Returns zero on success, a negative error code on failure. ++ */ ++int ssh_ptl_tx_stop(struct ssh_ptl *ptl) +{ + int status = 0; + @@ -12056,6 +13161,7 @@ index 0000000000000..1b7d59ec4f433 + * The packet is pending, but we are not allowed to take + * it because it has been locked. + */ ++ WARN_ON(PTR_ERR(p) != -EPERM); + } + return; + } @@ -12089,10 +13195,24 @@ index 0000000000000..1b7d59ec4f433 + ssh_ptl_remove_and_complete(p, status); + ssh_packet_put(p); + -+ ssh_ptl_tx_wakeup(ptl, false); ++ if (atomic_read(&ptl->pending.count) < SSH_PTL_MAX_PENDING) ++ ssh_ptl_tx_wakeup(ptl); +} + + ++/** ++ * ssh_ptl_submit() - Submit a packet to the transport layer. ++ * @ptl: The packet transport layer to to submit to. ++ * @p: The packet to submit. ++ * ++ * Submits a new packet to the transport layer, queuing it to be sent. This ++ * function should not be used for re-submission. ++ * ++ * Return: Returns zero on success, %-EINVAL if a packet field is invalid or ++ * the packet has been canceled prior to submission, %-EALREADY if the packet ++ * has already been submitted, or %-ESHUTDOWN if the packet transport layer ++ * has been shut down. ++ */ +int ssh_ptl_submit(struct ssh_ptl *ptl, struct ssh_packet *p) +{ + struct ssh_ptl *ptl_old; @@ -12115,37 +13235,40 @@ index 0000000000000..1b7d59ec4f433 + ptl_old = READ_ONCE(p->ptl); + if (ptl_old == NULL) + WRITE_ONCE(p->ptl, ptl); -+ else if (ptl_old != ptl) -+ return -EALREADY; ++ else if (WARN_ON(ptl_old != ptl)) ++ return -EALREADY; // submitted on different PTL + + status = ssh_ptl_queue_push(p); + if (status) + return status; + -+ ssh_ptl_tx_wakeup(ptl, !test_bit(SSH_PACKET_TY_BLOCKING_BIT, &p->state)); ++ if (!test_bit(SSH_PACKET_TY_BLOCKING_BIT, &p->state) ++ || (atomic_read(&ptl->pending.count) < SSH_PTL_MAX_PENDING)) ++ ssh_ptl_tx_wakeup(ptl); ++ + return 0; +} + -+/* -+ * This function must be called with pending lock held. -+ */ -+static void __ssh_ptl_resubmit(struct ssh_packet *packet) ++/* must be called with pending lock held */ ++static int __ssh_ptl_resubmit(struct ssh_packet *packet) +{ -+ struct list_head *head; ++ int status; + + trace_ssam_packet_resubmit(packet); + + spin_lock(&packet->ptl->queue.lock); + -+ // if this packet has already been queued, do not add it -+ if (test_and_set_bit(SSH_PACKET_SF_QUEUED_BIT, &packet->state)) { ++ status = __ssh_ptl_queue_push(packet); ++ if (status) { ++ /* ++ * An error here indicates that the packet has either already ++ * been queued, been locked, or the transport layer is being ++ * shut down. In all cases: Ignore the error. ++ */ + spin_unlock(&packet->ptl->queue.lock); -+ return; ++ return status; + } + -+ // 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 @@ -12154,10 +13277,8 @@ index 0000000000000..1b7d59ec4f433 + */ + WRITE_ONCE(packet->timestamp, KTIME_MAX); + -+ // add packet -+ list_add_tail(&ssh_packet_get(packet)->queue_node, head); -+ + spin_unlock(&packet->ptl->queue.lock); ++ return 0; +} + +static void ssh_ptl_resubmit_pending(struct ssh_ptl *ptl) @@ -12172,7 +13293,7 @@ index 0000000000000..1b7d59ec4f433 + * eventually canceled and completed by the timeout. Removing the packet + * here could lead to overly eager cancelation if the packet has not + * been re-transmitted yet but the tries-counter already updated (i.e -+ * ssh_ptl_tx_next removed the packet from the queue and updated the ++ * ssh_ptl_tx_next() removed the packet from the queue and updated the + * counter, but re-transmission for the last try has not actually + * started yet). + */ @@ -12190,15 +13311,38 @@ index 0000000000000..1b7d59ec4f433 + if (try >= SSH_PTL_MAX_PACKET_TRIES) + continue; + -+ resub = true; -+ __ssh_ptl_resubmit(p); ++ /* ++ * Submission fails if the packet has been locked, is already ++ * queued, or the layer is being shut down. No need to ++ * re-schedule tx-thread in those cases. ++ */ ++ if (!__ssh_ptl_resubmit(p)) ++ resub = true; + } + + spin_unlock(&ptl->pending.lock); + -+ ssh_ptl_tx_wakeup(ptl, resub); ++ if (resub) ++ ssh_ptl_tx_wakeup(ptl); +} + ++/** ++ * ssh_ptl_cancel() - Cancel a packet. ++ * @p: The packet to cancel. ++ * ++ * Cancels a packet. There are no guarantees on when completion and release ++ * callbacks will be called. This may occur during execution of this function ++ * or may occur at any point later. ++ * ++ * Note that it is not guaranteed that the packet will actually be cancelled ++ * if the packet is concurrently completed by another process. The only ++ * guarantee of this function is that the packet will be completed (with ++ * success, failure, or cancellation) and released from the transport layer in ++ * a reasonable time-frame. ++ * ++ * May be called before the packet has been submitted, in which case any later ++ * packet submission fails. ++ */ +void ssh_ptl_cancel(struct ssh_packet *p) +{ + if (test_and_set_bit(SSH_PACKET_SF_CANCELED_BIT, &p->state)) @@ -12229,7 +13373,10 @@ index 0000000000000..1b7d59ec4f433 + + if (READ_ONCE(p->ptl)) { + ssh_ptl_remove_and_complete(p, -ECANCELED); -+ ssh_ptl_tx_wakeup(p->ptl, false); ++ ++ if (atomic_read(&p->ptl->pending.count) < SSH_PTL_MAX_PENDING) ++ ssh_ptl_tx_wakeup(p->ptl); ++ + } else if (!test_and_set_bit(SSH_PACKET_SF_COMPLETED_BIT, &p->state)) { + __ssh_ptl_complete(p, -ECANCELED); + } @@ -12286,17 +13433,19 @@ index 0000000000000..1b7d59ec4f433 + continue; + } + -+ // avoid further transitions if locked -+ if (test_bit(SSH_PACKET_SF_LOCKED_BIT, &p->state)) -+ continue; -+ -+ trace_ssam_packet_timeout(p); -+ + // check if we still have some tries left + try = ssh_packet_priority_get_try(READ_ONCE(p->priority)); + if (likely(try < SSH_PTL_MAX_PACKET_TRIES)) { -+ resub = true; -+ __ssh_ptl_resubmit(p); ++ trace_ssam_packet_timeout(p); ++ ++ /* ++ * Submission fails if the packet has been locked, is ++ * already queued, or the layer is being shut down. ++ * No need to re-schedule tx-thread in those cases. ++ */ ++ if (!__ssh_ptl_resubmit(p)) ++ resub = true; ++ + continue; + } + @@ -12306,6 +13455,8 @@ index 0000000000000..1b7d59ec4f433 + if (test_and_set_bit(SSH_PACKET_SF_LOCKED_BIT, &p->state)) + continue; + ++ trace_ssam_packet_timeout(p); ++ + /* + * We have now marked the packet as locked. Thus it cannot be + * added to the pending list again after we've removed it here. @@ -12340,8 +13491,8 @@ index 0000000000000..1b7d59ec4f433 + if (next != KTIME_MAX) + ssh_ptl_timeout_reaper_mod(ptl, now, next); + -+ // force-wakeup to properly handle re-transmits if we've re-submitted -+ ssh_ptl_tx_wakeup(ptl, resub); ++ if (resub) ++ ssh_ptl_tx_wakeup(ptl); +} + + @@ -12378,7 +13529,6 @@ index 0000000000000..1b7d59ec4f433 + +static void ssh_ptl_send_ack(struct ssh_ptl *ptl, u8 seq) +{ -+ struct ssh_packet_args args; + struct ssh_packet *packet; + struct ssam_span buf; + struct msgbuf msgb; @@ -12390,10 +13540,8 @@ index 0000000000000..1b7d59ec4f433 + return; + } + -+ args.type = 0; -+ args.priority = SSH_PACKET_PRIORITY(ACK, 0); -+ args.ops = &ssh_ptl_ctrl_packet_ops; -+ ssh_packet_init(packet, &args); ++ ssh_packet_init(packet, 0, SSH_PACKET_PRIORITY(ACK, 0), ++ &ssh_ptl_ctrl_packet_ops); + + msgb_init(&msgb, buf.ptr, buf.len); + msgb_push_ack(&msgb, seq); @@ -12405,7 +13553,6 @@ index 0000000000000..1b7d59ec4f433 + +static void ssh_ptl_send_nak(struct ssh_ptl *ptl) +{ -+ struct ssh_packet_args args; + struct ssh_packet *packet; + struct ssam_span buf; + struct msgbuf msgb; @@ -12417,10 +13564,8 @@ index 0000000000000..1b7d59ec4f433 + return; + } + -+ args.type = 0; -+ args.priority = SSH_PACKET_PRIORITY(NAK, 0); -+ args.ops = &ssh_ptl_ctrl_packet_ops; -+ ssh_packet_init(packet, &args); ++ ssh_packet_init(packet, 0, SSH_PACKET_PRIORITY(NAK, 0), ++ &ssh_ptl_ctrl_packet_ops); + + msgb_init(&msgb, buf.ptr, buf.len); + msgb_push_nak(&msgb); @@ -12459,6 +13604,11 @@ index 0000000000000..1b7d59ec4f433 + * until direclty after the SYN. This causes the search for + * the next SYN, which is generally not placed directly after + * the last one. ++ * ++ * Open question: Should we send this in case of invalid ++ * payload CRCs if the frame-type is nonsequential (current ++ * implementation) or should we drop that frame without ++ * telling the EC? + */ + ssh_ptl_send_nak(ptl); + } @@ -12550,19 +13700,32 @@ index 0000000000000..1b7d59ec4f433 + wake_up(&ptl->rx.wq); +} + ++/** ++ * ssh_ptl_rx_start() - Start packet transport layer receiver thread. ++ * @ptl: The packet transport layer. ++ * ++ * Return: Returns zero on success, a negative error code on failure. ++ */ +int ssh_ptl_rx_start(struct ssh_ptl *ptl) +{ + if (ptl->rx.thread) + return 0; + -+ ptl->rx.thread = kthread_run(ssh_ptl_rx_threadfn, ptl, "surface-sh-rx"); ++ ptl->rx.thread = kthread_run(ssh_ptl_rx_threadfn, ptl, ++ "ssam_serial_hub-rx"); + if (IS_ERR(ptl->rx.thread)) + return PTR_ERR(ptl->rx.thread); + + return 0; +} + -+static int ssh_ptl_rx_stop(struct ssh_ptl *ptl) ++/** ++ * ssh_ptl_rx_stop() - Stop packet transport layer receiver thread. ++ * @ptl: The packet transport layer. ++ * ++ * Return: Returns zero on success, a negative error code on failure. ++ */ ++int ssh_ptl_rx_stop(struct ssh_ptl *ptl) +{ + int status = 0; + @@ -12574,6 +13737,20 @@ index 0000000000000..1b7d59ec4f433 + return status; +} + ++/** ++ * ssh_ptl_rx_rcvbuf() - Push data from lower-layer transport to the packet ++ * layer. ++ * @ptl: The packet transport layer. ++ * @buf: Pointer to the data to push to the layer. ++ * @n: Size of the data to push to the layer, in bytes. ++ * ++ * Pushes data from a lower-layer transport to the receiver fifo buffer of the ++ * packet layer and notifies the reveiver thread. Calls to this function are ++ * ignored once the packet layer has been shut down. ++ * ++ * Return: Returns the number of bytes transferred (positive or zero) on ++ * success. Returns %-ESHUTDOWN if the packet layer has been shut down. ++ */ +int ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n) +{ + int used; @@ -12590,16 +13767,16 @@ index 0000000000000..1b7d59ec4f433 + + +/** -+ * ssh_ptl_shutdown() - Shut down the packet transmission layer. -+ * @ptl: The packet transmission layer. ++ * ssh_ptl_shutdown() - Shut down the packet transport layer. ++ * @ptl: The packet transport layer. + * -+ * Shuts down the packet transmission layer, removing and canceling all queued ++ * Shuts down the packet transport layer, removing and canceling all queued + * and pending packets. Packets canceled by this operation will be completed + * 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 ++ * As a result of this function, the transport layer will be marked as shut ++ * down. Submission of packets after the transport layer has been shut down + * will fail with %-ESHUTDOWN. + */ +void ssh_ptl_shutdown(struct ssh_ptl *ptl) @@ -12613,11 +13790,11 @@ index 0000000000000..1b7d59ec4f433 + 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, ++ * 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. ++ * in ssh_ptl_rx_rcvbuf(), this guarantees that received data is ++ * properly cut off. + */ + smp_mb__after_atomic(); + @@ -12647,7 +13824,7 @@ index 0000000000000..1b7d59ec4f433 + * new list via other threads (e.g. canellation). + * + * Note 3: There may be overlap between complete_p and complete_q. -+ * This is handled via test_and_set_bit on the "completed" flag ++ * This is handled via test_and_set_bit() on the "completed" flag + * (also handles cancelation). + */ + @@ -12700,6 +13877,19 @@ index 0000000000000..1b7d59ec4f433 + */ +} + ++/** ++ * ssh_ptl_init() - Initialize packet transport layer. ++ * @ptl: The packet transport layer to initialize. ++ * @serdev: The underlying serial device, i.e. the lower-level transport. ++ * @ops: Packet layer operations. ++ * ++ * Initializes the given packet transport layer. Transmitter and receiver ++ * threads must be started separately via ssh_ptl_tx_start() and ++ * ssh_ptl_rx_start(), after the packet-layer has been initialized and the ++ * lower-level transport layer has been set up. ++ * ++ * Return: Returns zero on success and a nonzero error code on failure. ++ */ +int ssh_ptl_init(struct ssh_ptl *ptl, struct serdev_device *serdev, + struct ssh_ptl_ops *ops) +{ @@ -12747,6 +13937,15 @@ index 0000000000000..1b7d59ec4f433 + return status; +} + ++/** ++ * ssh_ptl_destroy() - Deinitialize packet transport layer. ++ * @ptl: The packet transport layer to deinitialize. ++ * ++ * Deinitializes the given packet transport layer and frees resources ++ * associated with it. If receiver and/or transmitter threads have been ++ * started, the layer must first be shut down via ssh_ptl_shutdown() before ++ * this function can be called. ++ */ +void ssh_ptl_destroy(struct ssh_ptl *ptl) +{ + kfifo_free(&ptl->rx.fifo); @@ -12754,10 +13953,10 @@ index 0000000000000..1b7d59ec4f433 +} 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..74e5adba888ea +index 0000000000000..2e2d961c5c046 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_packet_layer.h -@@ -0,0 +1,125 @@ +@@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _SSAM_SSH_PACKET_LAYER_H @@ -12780,14 +13979,59 @@ index 0000000000000..74e5adba888ea +#include "ssh_parser.h" + + ++/** ++ * enum ssh_ptl_state_flags - State-flags for &struct ssh_ptl. ++ * ++ * @SSH_PTL_SF_SHUTDOWN_BIT: ++ * Indicates that the packet transport layer has been shut down or is ++ * being shut down and should not accept any new packets/data. ++ */ +enum ssh_ptl_state_flags { + SSH_PTL_SF_SHUTDOWN_BIT, +}; + ++/** ++ * struct ssh_ptl_ops - Callback operations for packet transport layer. ++ * @data_received: Function called when a data-packet has been received. Both, ++ * the packet layer on which the packet has been received and ++ * the packet's payload data are provided to this function. ++ */ +struct ssh_ptl_ops { + void (*data_received)(struct ssh_ptl *p, const struct ssam_span *data); +}; + ++/** ++ * struct ssh_ptl - SSH packet transport layer. ++ * @serdev: Serial device providing the underlying data transport. ++ * @state: State(-flags) of the transport layer. ++ * @queue: Packet submission queue. ++ * @queue.lock: Lock for modifying the packet submission queue. ++ * @queue.head: List-head of the packet submission queue. ++ * @pending: Set/list of pending packets. ++ * @pending.lock: Lock for modifying the pending set. ++ * @pending.head: List-head of the pending set/list. ++ * @pending.count: Number of currently pending packets. ++ * @tx: Transmitter subsystem. ++ * @tx.thread_signal: Signal notifying transmitter thread of data to be sent. ++ * @tx.thread: Transmitter thread. ++ * @tx.thread_wq: Waitqueue-head for transmitter thread. ++ * @tx.packet_wq: Waitqueue-head for packet transmit completion. ++ * @tx.packet: Currently sent packet. ++ * @tx.offset: Data-offset into the packet currently being transmitted. ++ * @rx: Receiver subsystem. ++ * @rx.thread: Receiver thread. ++ * @rx.wq: Waitqueue-head for receiver thread. ++ * @rx.fifo: Buffer for receiving data/pushing data to receiver thread. ++ * @rx.buf: Buffer for evaluating data on receiver thread. ++ * @rx.blocked: List of recent/blocked sequence IDs to detect retransmission. ++ * @rx.blocked.seqs: Array of blocked sequence IDs. ++ * @rx.blocked.offset: Offset indicating where a new ID should be inserted. ++ * @rtx_timeout: Retransmission timeout subsystem. ++ * @rtx_timeout.timeout: Timout inverval for retransmission. ++ * @rtx_timeout.expires: Time specifying when the reaper work is next scheduled. ++ * @rtx_timeout.reaper: Work performing timeout checks and subsequent actions. ++ * @ops: Packet layer operations. ++ */ +struct ssh_ptl { + struct serdev_device *serdev; + unsigned long state; @@ -12833,12 +14077,6 @@ index 0000000000000..74e5adba888ea + struct ssh_ptl_ops ops; +}; + -+struct ssh_packet_args { -+ unsigned long type; -+ u8 priority; -+ const struct ssh_packet_ops *ops; -+}; -+ + +#define __ssam_prcond(func, p, fmt, ...) \ + do { \ @@ -12861,23 +14099,32 @@ index 0000000000000..74e5adba888ea + +void ssh_ptl_destroy(struct ssh_ptl *ptl); + ++/** ++ * ssh_ptl_get_device() - Get device associated with packet transport layer. ++ * @ptl: The packet transport layer. ++ * ++ * Return: Returns the device on which the given packet transport layer builds ++ * upon. ++ */ +static inline struct device *ssh_ptl_get_device(struct ssh_ptl *ptl) +{ + return ptl->serdev ? &ptl->serdev->dev : NULL; +} + +int ssh_ptl_tx_start(struct ssh_ptl *ptl); ++int ssh_ptl_tx_stop(struct ssh_ptl *ptl); +int ssh_ptl_rx_start(struct ssh_ptl *ptl); ++int ssh_ptl_rx_stop(struct ssh_ptl *ptl); +void ssh_ptl_shutdown(struct ssh_ptl *ptl); + +int ssh_ptl_submit(struct ssh_ptl *ptl, struct ssh_packet *p); +void ssh_ptl_cancel(struct ssh_packet *p); + +int ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n); -+void ssh_ptl_tx_wakeup(struct ssh_ptl *ptl, bool force); ++void ssh_ptl_tx_wakeup(struct ssh_ptl *ptl); + -+void ssh_packet_init(struct ssh_packet *packet, -+ const struct ssh_packet_args *args); ++void ssh_packet_init(struct ssh_packet *packet, unsigned long type, ++ u8 priority, const struct ssh_packet_ops *ops); + +int ssh_ctrl_packet_cache_init(void); +void ssh_ctrl_packet_cache_destroy(void); @@ -12885,10 +14132,10 @@ index 0000000000000..74e5adba888ea +#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..7ba879ca3e243 +index 0000000000000..3e28d502d5eb2 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_parser.c -@@ -0,0 +1,215 @@ +@@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -12904,8 +14151,11 @@ index 0000000000000..7ba879ca3e243 + * + * Computes the CRC of the provided data span (@src), compares it to the CRC + * stored at the given address (@crc), and returns the result of this -+ * comparison, i.e. true iff equal. This function is intended to run on raw ++ * comparison, i.e. %true iff equal. This function is intended to run on raw + * input/message data. ++ * ++ * Return: Returns %true iff the computed CRC matches the stored CRC, %false ++ * otherwise. + */ +static inline bool sshp_validate_crc(const struct ssam_span *src, const u8 *crc) +{ @@ -12932,17 +14182,20 @@ index 0000000000000..7ba879ca3e243 + * + * Search for SSH SYN bytes in the given source span. If found, set the @rem + * span to the remaining data, starting with the first SYN bytes and capped by -+ * the source span length, and return ``true``. This function does not copy ++ * the source span length, and return %true. This function does not copy + * any data, but rather only sets pointers to the respecitve start addresses + * and length values. + * + * If no SSH SYN bytes could be found, set the @rem span to the zero-length -+ * span at the end of the source span and return false. ++ * span at the end of the source span and return %false. + * + * If partial SSH SYN bytes could be found at the end of the source span, set + * the @rem span to cover these partial SYN bytes, capped by the end of the -+ * source span, and return false. This function should then be re-run once ++ * source span, and return %false. This function should then be re-run once + * more data is available. ++ * ++ * Return: Returns %true iff a complete SSG SYN sequence could be found, ++ * %false otherwise. + */ +bool sshp_find_syn(const struct ssam_span *src, struct ssam_span *rem) +{ @@ -13106,7 +14359,7 @@ index 0000000000000..7ba879ca3e243 +} diff --git a/drivers/misc/surface_sam/ssh_parser.h b/drivers/misc/surface_sam/ssh_parser.h new file mode 100644 -index 0000000000000..4216ac4b45bcf +index 0000000000000..1ee455de7898a --- /dev/null +++ b/drivers/misc/surface_sam/ssh_parser.h @@ -0,0 +1,151 @@ @@ -13214,7 +14467,7 @@ index 0000000000000..4216ac4b45bcf + * limited either by the remaining space in the buffer or by the number of + * bytes available in the fifo. + * -+ * Returns the number of bytes transfered. ++ * Return: Returns the number of bytes transfered. + */ +static inline size_t sshp_buf_read_from_fifo(struct sshp_buf *buf, + struct kfifo *fifo) @@ -13263,10 +14516,10 @@ index 0000000000000..4216ac4b45bcf +#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..2192398b2e12d +index 0000000000000..4f35ce93f02e2 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_protocol.h -@@ -0,0 +1,102 @@ +@@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _SSAM_SSH_PROTOCOL_H @@ -13303,7 +14556,8 @@ index 0000000000000..2192398b2e12d + * @buf: The pointer pointing to the data for which the CRC should be computed. + * @len: The length of the data for which the CRC should be computed. + * -+ * Compute and return the CRC of the provided data, as used for SSH messages. ++ * Return: Returns the CRC computed on the provided data, as used for SSH ++ * messages. + */ +static inline u16 ssh_crc(const u8 *buf, size_t len) +{ @@ -13314,9 +14568,9 @@ index 0000000000000..2192398b2e12d + * ssh_rqid_next_valid() - Return the next valid request ID. + * @rqid: The current request ID. + * -+ * Compute and return the next valid request ID, following the current request -+ * ID provided to this function. This function skips any request IDs reserved -+ * for events. ++ * Return: Returns the next valid request ID, following the current request ID ++ * provided to this function. This function skips any request IDs reserved for ++ * events. + */ +static inline u16 ssh_rqid_next_valid(u16 rqid) +{ @@ -13345,7 +14599,7 @@ index 0000000000000..2192398b2e12d + * ssh_tc_to_rqid() - Convert target category to its corresponding request ID. + * @tc: The target category to convert. + */ -+static inline int ssh_tc_to_rqid(u8 tc) ++static inline u16 ssh_tc_to_rqid(u8 tc) +{ + return tc; +} @@ -13371,10 +14625,10 @@ index 0000000000000..2192398b2e12d +#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..b61c45b572978 +index 0000000000000..24aaf6da5b545 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_request_layer.c -@@ -0,0 +1,1100 @@ +@@ -0,0 +1,1246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -13394,9 +14648,31 @@ index 0000000000000..b61c45b572978 +#include "ssam_trace.h" + + ++/* ++ * SSH_RTL_REQUEST_TIMEOUT - Request timeout. ++ * ++ * Timeout as ktime_t delta for request responses. If we have not received a ++ * response in this time-frame after finishing the underlying packet ++ * transmission, the request will be completed with %-ETIMEDOUT as status ++ * code. ++ */ +#define SSH_RTL_REQUEST_TIMEOUT ms_to_ktime(3000) ++ ++/* ++ * SSH_RTL_REQUEST_TIMEOUT_RESOLUTION - Request timeout granularity. ++ * ++ * Time-resolution for timeouts. Should be larger than one jiffy to avoid ++ * direct re-scheduling of reaper work_struct. ++ */ +#define SSH_RTL_REQUEST_TIMEOUT_RESOLUTION ms_to_ktime(max(2000 / HZ, 50)) + ++/* ++ * SSH_RTL_MAX_PENDING - Maximum number of pending requests. ++ * ++ * Maximum number of requests concurrently waiting to be completed (i.e. ++ * waiting for the corresponding packet transmission to finish if they don't ++ * have a response or waiting for a response if they have one). ++ */ +#define SSH_RTL_MAX_PENDING 3 + + @@ -13434,7 +14710,7 @@ index 0000000000000..b61c45b572978 +static inline u32 ssh_request_get_rqid_safe(struct ssh_request *rqst) +{ + if (!rqst->packet.data.ptr) -+ return -1; ++ return (u32)-1; + + return ssh_request_get_rqid(rqst); +} @@ -13443,18 +14719,18 @@ index 0000000000000..b61c45b572978 +static void ssh_rtl_queue_remove(struct ssh_request *rqst) +{ + struct ssh_rtl *rtl = ssh_request_rtl(rqst); -+ bool remove; + + spin_lock(&rtl->queue.lock); + -+ remove = test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state); -+ if (remove) -+ list_del(&rqst->node); ++ if (!test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state)) { ++ spin_unlock(&rtl->queue.lock); ++ return; ++ } ++ ++ list_del(&rqst->node); + + spin_unlock(&rtl->queue.lock); -+ -+ if (remove) -+ ssh_request_put(rqst); ++ ssh_request_put(rqst); +} + +static bool ssh_rtl_queue_empty(struct ssh_rtl *rtl) @@ -13472,20 +14748,20 @@ index 0000000000000..b61c45b572978 +static void ssh_rtl_pending_remove(struct ssh_request *rqst) +{ + struct ssh_rtl *rtl = ssh_request_rtl(rqst); -+ bool remove; + + spin_lock(&rtl->pending.lock); + -+ remove = test_and_clear_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state); -+ if (remove) { -+ atomic_dec(&rtl->pending.count); -+ list_del(&rqst->node); ++ if (!test_and_clear_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state)) { ++ spin_unlock(&rtl->pending.lock); ++ return; + } + ++ atomic_dec(&rtl->pending.count); ++ list_del(&rqst->node); ++ + spin_unlock(&rtl->pending.lock); + -+ if (remove) -+ ssh_request_put(rqst); ++ ssh_request_put(rqst); +} + +static int ssh_rtl_tx_pending_push(struct ssh_request *rqst) @@ -13522,9 +14798,6 @@ index 0000000000000..b61c45b572978 + rtl_dbg_cond(rtl, "rtl: completing request (rqid: 0x%04x, status: %d)\n", + ssh_request_get_rqid_safe(rqst), status); + -+ if (status && status != -ECANCELED) -+ rtl_dbg_cond(rtl, "rtl: request error: %d\n", status); -+ + rqst->ops->complete(rqst, NULL, NULL, status); +} + @@ -13667,9 +14940,9 @@ index 0000000000000..b61c45b572978 + int i, status; + + /* -+ * Try to be nice and not block the workqueue: Run a maximum of 10 -+ * tries, then re-submit if necessary. This should not be neccesary, -+ * for normal execution, but guarantee it anyway. ++ * Try to be nice and not block/live-lock the workqueue: Run a maximum ++ * of 10 tries, then re-submit if necessary. This should not be ++ * neccesary for normal execution, but guarantee it anyway. + */ + for (i = 0; i < 10; i++) { + status = ssh_rtl_tx_try_process_one(rtl); @@ -13693,25 +14966,65 @@ index 0000000000000..b61c45b572978 +} + + ++/** ++ * ssh_rtl_submit() - Submit a request to the transport layer. ++ * @rtl: The request transport layer. ++ * @rqst: The request to submit. ++ * ++ * Submits a request to the transport layer. A single request may not be ++ * submitted multiple times without reinitializing it. ++ * ++ * Return: Returns zero on success, %-EINVAL if the request type is invalid or ++ * the request has been canceled prior to submission, %-EALREADY if the ++ * request has already been submitted, or %-ESHUTDOWN in case the request ++ * transport layer has been shut down. ++ */ +int ssh_rtl_submit(struct ssh_rtl *rtl, struct ssh_request *rqst) +{ + trace_ssam_request_submit(rqst); + + /* + * Ensure that requests expecting a response are sequenced. If this -+ * invariant ever changes, see the comment in ssh_rtl_complete on what ++ * invariant ever changes, see the comment in ssh_rtl_complete() on what + * is required to be changed in the code. + */ + if (test_bit(SSH_REQUEST_TY_HAS_RESPONSE_BIT, &rqst->state)) + if (!test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &rqst->packet.state)) + return -EINVAL; + -+ // try to set ptl and check if this request has already been submitted -+ if (cmpxchg(&rqst->packet.ptl, NULL, &rtl->ptl) != NULL) -+ return -EALREADY; -+ + spin_lock(&rtl->queue.lock); + ++ /* ++ * Try to set ptl and check if this request has already been submitted. ++ * ++ * Must be inside lock as we might run into a lost update problem ++ * otherwise: If this were outside of the lock, cancellation in ++ * ssh_rtl_cancel_nonpending() may run after we've set the ptl ++ * reference but before we enter the lock. In that case, we'd detect ++ * that the request is being added to the queue and would try to remove ++ * it from that, but removal might fail because it hasn't actually been ++ * added yet. By putting this cmpxchg in the critical section, we ++ * ensure that the queuing detection only triggers when we are already ++ * in the critical section and the remove process will wait until the ++ * push operation has been completed (via lock) due to that. Only then, ++ * we can safely try to remove it. ++ */ ++ if (cmpxchg(&rqst->packet.ptl, NULL, &rtl->ptl) != NULL) { ++ spin_unlock(&rtl->queue.lock); ++ return -EALREADY; ++ } ++ ++ /* ++ * Ensure that we set ptl reference before we continue modifying state. ++ * This is required for non-pending cancellation. This barrier is paired ++ * with the one in ssh_rtl_cancel_nonpending(). ++ * ++ * By setting the ptl reference before we test for "locked", we can ++ * check if the "locked" test may have already run. See comments in ++ * ssh_rtl_cancel_nonpending() for more detail. ++ */ ++ smp_mb__after_atomic(); ++ + if (test_bit(SSH_RTL_SF_SHUTDOWN_BIT, &rtl->state)) { + spin_unlock(&rtl->queue.lock); + return -ESHUTDOWN; @@ -13736,15 +15049,20 @@ index 0000000000000..b61c45b572978 +{ + unsigned long delta = msecs_to_jiffies(ktime_ms_delta(expires, now)); + ktime_t aexp = ktime_add(expires, SSH_RTL_REQUEST_TIMEOUT_RESOLUTION); -+ ktime_t old; ++ ktime_t old_exp, old_act; + + // re-adjust / schedule reaper if it is above resolution delta -+ old = READ_ONCE(rtl->rtx_timeout.expires); -+ while (ktime_before(aexp, old)) -+ old = cmpxchg64(&rtl->rtx_timeout.expires, old, expires); ++ old_act = READ_ONCE(rtl->rtx_timeout.expires); ++ if (ktime_after(aexp, old_act)) ++ return; ++ ++ do { ++ old_exp = old_act; ++ old_act = cmpxchg64(&rtl->rtx_timeout.expires, old_exp, expires); ++ } while (old_exp != old_act && ktime_before(aexp, old_act)); + + // if we updated the reaper expiration, modify work timeout -+ if (old == expires) ++ if (old_exp == old_act && old_act != expires) + mod_delayed_work(system_wq, &rtl->rtx_timeout.reaper, delta); +} + @@ -13760,7 +15078,7 @@ index 0000000000000..b61c45b572978 + 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 ++ * implicit barrier following check on ssh_request_get_expiration() in + * ssh_rtl_timeout_reap. + */ + smp_mb__after_atomic(); @@ -13887,7 +15205,7 @@ index 0000000000000..b61c45b572978 +static bool ssh_rtl_cancel_nonpending(struct ssh_request *r) +{ + struct ssh_rtl *rtl; -+ unsigned long state, fixed; ++ unsigned long flags, fixed; + bool remove; + + /* @@ -13896,9 +15214,14 @@ index 0000000000000..b61c45b572978 + * setting the state worked, we might still be adding the packet to the + * queue in a currently executing submit call. In that case, however, + * ptl reference must have been set previously, as locked is checked -+ * after setting ptl. Thus only if we successfully lock this request and -+ * ptl is NULL, we have successfully removed the request. -+ * Otherwise we need to try and grab it from the queue. ++ * after setting ptl. Furthermore, when the ptl reference is set, the ++ * submission process is guaranteed to have entered the critical ++ * section. Thus only if we successfully locked this request and ptl is ++ * NULL, we have successfully removed the request, i.e. we are ++ * guaranteed that, due to the "locked" check in ssh_rtl_submit(), the ++ * packet will never be added. Otherwise, we need to try and grab it ++ * from the queue, where we are now guaranteed that the packet is or has ++ * been due to the critical section. + * + * Note that if the CMPXCHG fails, we are guaranteed that ptl has + * been set and is non-NULL, as states can only be nonzero after this @@ -13906,8 +15229,18 @@ index 0000000000000..b61c45b572978 + * to ensure that they don't cause the cmpxchg to fail. + */ + fixed = READ_ONCE(r->state) & SSH_REQUEST_FLAGS_TY_MASK; -+ state = cmpxchg(&r->state, fixed, SSH_REQUEST_SF_LOCKED_BIT); -+ if (!state && !READ_ONCE(r->packet.ptl)) { ++ flags = cmpxchg(&r->state, fixed, SSH_REQUEST_SF_LOCKED_BIT); ++ ++ /* ++ * Force correct ordering with regards to state and ptl reference access ++ * to safe-guard cancellation to concurrent submission against a ++ * lost-update problem. First try to exchange state, then also check ++ * ptl if that worked. This barrier is paired with the ++ * one in ssh_rtl_submit(). ++ */ ++ smp_mb__after_atomic(); ++ ++ if (flags == fixed && !READ_ONCE(r->packet.ptl)) { + if (test_and_set_bit(SSH_REQUEST_SF_COMPLETED_BIT, &r->state)) + return true; + @@ -13953,12 +15286,16 @@ index 0000000000000..b61c45b572978 + + /* + * Now that we have locked the packet, we have guaranteed that it can't -+ * be added to the system any more. If rtl is zero, the locked -+ * check in ssh_rtl_submit has not been run and any submission, ++ * be added to the system any more. If ptl is zero, the locked ++ * check in ssh_rtl_submit() has not been run and any submission, + * currently in progress or called later, won't add the packet. Thus we + * can directly complete it. ++ * ++ * The implicit memory barrier of test_and_set_bit() should be enough ++ * to ensure that the correct order (first lock, then check ptl) is ++ * ensured. This is paired with the barrier in ssh_rtl_submit(). + */ -+ if (!ssh_request_rtl(r)) { ++ if (!READ_ONCE(r->packet.ptl)) { + if (test_and_set_bit(SSH_REQUEST_SF_COMPLETED_BIT, &r->state)) + return true; + @@ -13989,6 +15326,29 @@ index 0000000000000..b61c45b572978 + return true; +} + ++/** ++ * ssh_rtl_cancel() - Cancel request. ++ * @rqst: The request to cancel. ++ * @pending: Whether to also cancel pending requests. ++ * ++ * Cancels the given request. If @pending is %false, this will not cancel ++ * pending requests, i.e. requests that have already been submitted to the ++ * packet layer but not been completed yet. If @pending is %true, this will ++ * cancel the given request regardless of the state it is in. ++ * ++ * If the request has been canceled by calling this function, both completion ++ * and release callbacks of the request will be executed in a reasonable ++ * time-frame. This may happen during execution of this function, however, ++ * there is no guarantee for this. For example, a request currently ++ * transmitting will be canceled/completed only after transmission has ++ * completed, and the respective callbacks will be executed on the transmitter ++ * thread, which may happen during, but also some time after execution of the ++ * cancel function. ++ * ++ * Return: Returns %true iff the given request has been canceled or completed, ++ * either by this function or prior to calling this function, %false ++ * otherwise. If @pending is %true, this function will always return %true. ++ */ +bool ssh_rtl_cancel(struct ssh_request *rqst, bool pending) +{ + struct ssh_rtl *rtl; @@ -14015,7 +15375,7 @@ index 0000000000000..b61c45b572978 + +static void ssh_rtl_packet_callback(struct ssh_packet *p, int status) +{ -+ struct ssh_request *r = to_ssh_request(p, packet); ++ struct ssh_request *r = to_ssh_request(p); + + if (unlikely(status)) { + set_bit(SSH_REQUEST_SF_LOCKED_BIT, &r->state); @@ -14069,12 +15429,12 @@ index 0000000000000..b61c45b572978 +} + + -+static ktime_t ssh_request_get_expiration(struct ssh_request *r, ktime_t timeo) ++static ktime_t ssh_request_get_expiration(struct ssh_request *r, ktime_t timeout) +{ + ktime_t timestamp = READ_ONCE(r->timestamp); + + if (timestamp != KTIME_MAX) -+ return ktime_add(timestamp, timeo); ++ return ktime_add(timestamp, timeout); + else + return KTIME_MAX; +} @@ -14191,6 +15551,11 @@ index 0000000000000..b61c45b572978 + +static void ssh_rtl_rx_data(struct ssh_ptl *p, const struct ssam_span *data) +{ ++ if (!data->len) { ++ ptl_err(p, "rtl: rx: no data frame payload\n"); ++ return; ++ } ++ + switch (data->ptr[0]) { + case SSH_PLD_TYPE_CMD: + ssh_rtl_rx_command(p, data); @@ -14204,40 +15569,66 @@ index 0000000000000..b61c45b572978 +} + + -+bool ssh_rtl_tx_flush(struct ssh_rtl *rtl) ++static void ssh_rtl_packet_release(struct ssh_packet *p) +{ -+ return flush_work(&rtl->tx.work); ++ struct ssh_request *rqst; ++ ++ rqst = to_ssh_request(p); ++ rqst->ops->release(rqst); +} + -+int ssh_rtl_rx_start(struct ssh_rtl *rtl) ++static const struct ssh_packet_ops ssh_rtl_packet_ops = { ++ .complete = ssh_rtl_packet_callback, ++ .release = ssh_rtl_packet_release, ++}; ++ ++/** ++ * ssh_request_init() - Initialize SSH request. ++ * @rqst: The request to initialize. ++ * @flags: Request flags, determining the type of the request. ++ * @ops: Request operations. ++ * ++ * Initializes the given SSH request and underlying packet. Sets the message ++ * buffer pointer to %NULL and the message buffer length to zero. This buffer ++ * has to be set separately via ssh_request_set_data() before submission and ++ * must contain a valid SSH request message. ++ */ ++void ssh_request_init(struct ssh_request *rqst, enum ssam_request_flags flags, ++ const struct ssh_request_ops *ops) +{ -+ return ssh_ptl_rx_start(&rtl->ptl); ++ unsigned long type = BIT(SSH_PACKET_TY_BLOCKING_BIT); ++ ++ if (!(flags & SSAM_REQUEST_UNSEQUENCED)) ++ type |= BIT(SSH_PACKET_TY_SEQUENCED_BIT); ++ ++ ssh_packet_init(&rqst->packet, type, SSH_PACKET_PRIORITY(DATA, 0), ++ &ssh_rtl_packet_ops); ++ ++ INIT_LIST_HEAD(&rqst->node); ++ ++ rqst->state = 0; ++ if (flags & SSAM_REQUEST_HAS_RESPONSE) ++ rqst->state |= BIT(SSH_REQUEST_TY_HAS_RESPONSE_BIT); ++ ++ rqst->timestamp = KTIME_MAX; ++ rqst->ops = ops; +} + -+int ssh_rtl_tx_start(struct ssh_rtl *rtl) -+{ -+ int status; -+ bool sched; -+ -+ status = ssh_ptl_tx_start(&rtl->ptl); -+ if (status) -+ return status; -+ -+ /* -+ * If the packet layer has been shut down and restarted without shutting -+ * down the request layer, there may still be requests queued and not -+ * handled. -+ */ -+ spin_lock(&rtl->queue.lock); -+ sched = !list_empty(&rtl->queue.head); -+ spin_unlock(&rtl->queue.lock); -+ -+ if (sched) -+ ssh_rtl_tx_schedule(rtl); -+ -+ return 0; -+} + ++/** ++ * ssh_rtl_init() - Initialize request transport layer. ++ * @rtl: The request transport layer to initialize. ++ * @serdev: The underlying serial device, i.e. the lower-level transport. ++ * @ops: Request transport layer operations. ++ * ++ * Initializes the given request transport layer and associated packet ++ * transport layer. Transmitter and receiver threads must be started ++ * separately via ssh_rtl_tx_start() and ssh_rtl_rx_start(), after the ++ * request-layer has been initialized and the lower-level serial device layer ++ * has been set up. ++ * ++ * Return: Returns zero on success and a nonzero error code on failure. ++ */ +int ssh_rtl_init(struct ssh_rtl *rtl, struct serdev_device *serdev, + const struct ssh_rtl_ops *ops) +{ @@ -14268,49 +15659,46 @@ index 0000000000000..b61c45b572978 + return 0; +} + ++/** ++ * ssh_rtl_destroy() - Deinitialize request transport layer. ++ * @rtl: The request transport layer to deinitialize. ++ * ++ * Deinitializes the given request transport layer and frees resources ++ * associated with it. If receiver and/or transmitter threads have been ++ * started, the layer must first be shut down via ssh_rtl_shutdown() before ++ * this function can be called. ++ */ +void ssh_rtl_destroy(struct ssh_rtl *rtl) +{ + ssh_ptl_destroy(&rtl->ptl); +} + -+ -+static void ssh_rtl_packet_release(struct ssh_packet *p) ++/** ++ * ssh_rtl_tx_start() - Start request transmitter and receiver. ++ * @rtl: The request transport layer. ++ * ++ * Return: Returns zero on success, a negative error code on failure. ++ */ ++int ssh_rtl_start(struct ssh_rtl *rtl) +{ -+ struct ssh_request *rqst; ++ int status; + -+ rqst = to_ssh_request(p, packet); -+ rqst->ops->release(rqst); ++ status = ssh_ptl_tx_start(&rtl->ptl); ++ if (status) ++ return status; ++ ++ ssh_rtl_tx_schedule(rtl); ++ ++ status = ssh_ptl_rx_start(&rtl->ptl); ++ if (status) { ++ ssh_rtl_flush(rtl, msecs_to_jiffies(5000)); ++ ssh_ptl_tx_stop(&rtl->ptl); ++ return status; ++ } ++ ++ return 0; +} + -+static const struct ssh_packet_ops ssh_rtl_packet_ops = { -+ .complete = ssh_rtl_packet_callback, -+ .release = ssh_rtl_packet_release, -+}; -+ -+void ssh_request_init(struct ssh_request *rqst, enum ssam_request_flags flags, -+ const struct ssh_request_ops *ops) -+{ -+ struct ssh_packet_args packet_args; -+ -+ packet_args.type = BIT(SSH_PACKET_TY_BLOCKING_BIT); -+ if (!(flags & SSAM_REQUEST_UNSEQUENCED)) -+ packet_args.type |= BIT(SSH_PACKET_TY_SEQUENCED_BIT); -+ -+ packet_args.priority = SSH_PACKET_PRIORITY(DATA, 0); -+ packet_args.ops = &ssh_rtl_packet_ops; -+ -+ ssh_packet_init(&rqst->packet, &packet_args); -+ INIT_LIST_HEAD(&rqst->node); -+ -+ rqst->state = 0; -+ if (flags & SSAM_REQUEST_HAS_RESPONSE) -+ rqst->state |= BIT(SSH_REQUEST_TY_HAS_RESPONSE_BIT); -+ -+ rqst->timestamp = KTIME_MAX; -+ rqst->ops = ops; -+} -+ -+ +struct ssh_flush_request { + struct ssh_request base; + struct completion completion; @@ -14342,15 +15730,15 @@ index 0000000000000..b61c45b572978 +}; + +/** -+ * ssh_rtl_flush() - Flush the request transmission layer. -+ * @rtl: request transmission layer ++ * ssh_rtl_flush() - Flush the request transport layer. ++ * @rtl: request transport layer + * @timeout: timeout for the flush operation in jiffies + * + * Queue a special flush request and wait for its completion. This request + * will be completed after all other currently queued and pending requests + * have been completed. Instead of a normal data packet, this request submits + * a special flush packet, meaning that upon completion, also the underlying -+ * packet transmission layer has been flushed. ++ * packet transport layer has been flushed. + * + * Flushing the request layer gurarantees that all previously submitted + * requests have been fully completed before this call returns. Additinally, @@ -14358,14 +15746,14 @@ index 0000000000000..b61c45b572978 + * has been completed. + * + * If the caller ensures that no new requests are submitted after a call to -+ * this function, the request transmission layer is guaranteed to have no ++ * this function, the request transport layer is guaranteed to have no + * remaining requests when this call returns. The same guarantee does not hold + * for the packet layer, on which control packets may still be queued after + * this call. + * + * Return: Returns zero on success, %-ETIMEDOUT if the flush timed out and has + * been canceled as a result of the timeout, or %-ESHUTDOWN if the packet -+ * and/or request transmission layer has been shut down before this call. May ++ * and/or request transport layer has been shut down before this call. May + * also return %-EINTR if the underlying packet transmission has been + * interrupted. + */ @@ -14388,19 +15776,31 @@ index 0000000000000..b61c45b572978 + + ssh_request_put(&rqst.base); + -+ if (wait_for_completion_timeout(&rqst.completion, timeout)) -+ return 0; -+ -+ ssh_rtl_cancel(&rqst.base, true); -+ wait_for_completion(&rqst.completion); ++ if (!wait_for_completion_timeout(&rqst.completion, timeout)) { ++ ssh_rtl_cancel(&rqst.base, true); ++ wait_for_completion(&rqst.completion); ++ } + + WARN_ON(rqst.status != 0 && rqst.status != -ECANCELED + && rqst.status != -ESHUTDOWN && rqst.status != -EINTR); + -+ return rqst.status == -ECANCELED ? -ETIMEDOUT : status; ++ return rqst.status == -ECANCELED ? -ETIMEDOUT : rqst.status; +} + + ++/** ++ * ssh_rtl_shutdown() - Shut down request transport layer. ++ * @rtl: The request transport layer. ++ * ++ * Shuts down the request transport layer, removing and canceling all queued ++ * and pending requests. Requests canceled by this operation will be completed ++ * with %-ESHUTDOWN as status. Receiver and transmitter threads will be ++ * stopped, the lower-level packet layer will be shutdown. ++ * ++ * As a result of this function, the transport layer will be marked as shut ++ * down. Submission of requests after the transport layer has been shut down ++ * will fail with %-ESHUTDOWN. ++ */ +void ssh_rtl_shutdown(struct ssh_rtl *rtl) +{ + struct ssh_request *r, *n; @@ -14410,7 +15810,7 @@ index 0000000000000..b61c45b572978 + 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 ++ * 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. + */ @@ -14432,8 +15832,8 @@ index 0000000000000..b61c45b572978 + /* + * We have now guaranteed that the queue is empty and no more new + * requests can be submitted (i.e. it will stay empty). This means that -+ * calling ssh_rtl_tx_schedule will not schedule tx.work any more. So we -+ * can simply call cancel_work_sync on tx.work here and when that ++ * calling ssh_rtl_tx_schedule() will not schedule tx.work any more. So ++ * we can simply call cancel_work_sync() on tx.work here and when that + * returns, we've locked it down. This also means that after this call, + * we don't submit any more packets to the underlying packet layer, so + * we can also shut that down. @@ -14464,23 +15864,23 @@ index 0000000000000..b61c45b572978 + spin_unlock(&rtl->pending.lock); + } + -+ // finally cancel and complete requests ++ // finally, cancel and complete the requests we claimed before + list_for_each_entry_safe(r, n, &claimed, node) { + // test_and_set because we still might compete with cancellation + if (!test_and_set_bit(SSH_REQUEST_SF_COMPLETED_BIT, &r->state)) + ssh_rtl_complete_with_status(r, -ESHUTDOWN); + -+ // drop the reference we've obtained by removing it from list ++ // drop reference we've obtained by removing it from the lists + list_del(&r->node); + ssh_request_put(r); + } +} 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..ffbb43719f853 +index 0000000000000..954f571862a69 --- /dev/null +++ b/drivers/misc/surface_sam/ssh_request_layer.h -@@ -0,0 +1,93 @@ +@@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _SSAM_SSH_REQUEST_LAYER_H @@ -14497,15 +15897,48 @@ index 0000000000000..ffbb43719f853 +#include "ssh_packet_layer.h" + + ++/** ++ * enum ssh_rtl_state_flags - State-flags for &struct ssh_rtl. ++ * ++ * @SSH_RTL_SF_SHUTDOWN_BIT: ++ * Indicates that the request transport layer has been shut down or is ++ * being shut down and should not accept any new requests. ++ */ +enum ssh_rtl_state_flags { + SSH_RTL_SF_SHUTDOWN_BIT, +}; + ++/** ++ * struct ssh_rtl_ops - Callback operations for request transport layer. ++ * @handle_event: Function called when a SSH event has been received. The ++ * specified function takes the request layer, received command ++ * struct, and corresponding payload as arguments. If the event ++ * has no payload, the payload span is empty (not %NULL). ++ */ +struct ssh_rtl_ops { + void (*handle_event)(struct ssh_rtl *rtl, const struct ssh_command *cmd, + const struct ssam_span *data); +}; + ++/** ++ * struct ssh_rtl - SSH request transport layer. ++ * @ptl: Underlying packet transport layer. ++ * @state: State(-flags) of the transport layer. ++ * @queue: Request submission queue. ++ * @queue.lock: Lock for modifying the request submission queue. ++ * @queue.head: List-head of the request submission queue. ++ * @pending: Set/list of pending requests. ++ * @pending.lock: Lock for modifying the request set. ++ * @pending.head: List-head of the pending set/list. ++ * @pending.count: Number of currently pending requests. ++ * @tx: Transmitter subsystem. ++ * @tx.work: Transmitter work item. ++ * @rtx_timeout: Retransmission timeout subsystem. ++ * @rtx_timeout.timeout: Timout inverval for retransmission. ++ * @rtx_timeout.expires: Time specifying when the reaper work is next scheduled. ++ * @rtx_timeout.reaper: Work performing timeout checks and subsequent actions. ++ * @ops: Request layer operations. ++ */ +struct ssh_rtl { + struct ssh_ptl ptl; + unsigned long state; @@ -14543,11 +15976,24 @@ index 0000000000000..ffbb43719f853 +#define to_ssh_rtl(ptr, member) \ + container_of(ptr, struct ssh_rtl, member) + ++/** ++ * ssh_rtl_get_device() - Get device associated with request transport layer. ++ * @rtl: The request transport layer. ++ * ++ * Return: Returns the device on which the given request transport layer ++ * builds upon. ++ */ +static inline struct device *ssh_rtl_get_device(struct ssh_rtl *rtl) +{ + return ssh_ptl_get_device(&rtl->ptl); +} + ++/** ++ * ssh_request_rtl() - Get request transport layer associated with request. ++ * @rqst: The request to get the request transport layer reference for. ++ * ++ * Return: Returns the &struct ssh_rtl associated with the given SSH request. ++ */ +static inline struct ssh_rtl *ssh_request_rtl(struct ssh_request *rqst) +{ + struct ssh_ptl *ptl; @@ -14562,10 +16008,7 @@ index 0000000000000..ffbb43719f853 +int ssh_rtl_init(struct ssh_rtl *rtl, struct serdev_device *serdev, + const struct ssh_rtl_ops *ops); + -+bool ssh_rtl_tx_flush(struct ssh_rtl *rtl); -+int ssh_rtl_rx_start(struct ssh_rtl *rtl); -+int ssh_rtl_tx_start(struct ssh_rtl *rtl); -+ ++int ssh_rtl_start(struct ssh_rtl *rtl); +int ssh_rtl_flush(struct ssh_rtl *rtl, unsigned long timeout); +void ssh_rtl_shutdown(struct ssh_rtl *rtl); +void ssh_rtl_destroy(struct ssh_rtl *rtl); @@ -14602,10 +16045,10 @@ index e14cbe444afcc..82c8660a5c869 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..a02d4996a604c +index 0000000000000..ab57517c8bfe9 --- /dev/null +++ b/include/linux/surface_aggregator_module.h -@@ -0,0 +1,1006 @@ +@@ -0,0 +1,1719 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Interface for Surface System Aggregator Module (SSAM) via Surface Serial @@ -14638,8 +16081,8 @@ index 0000000000000..a02d4996a604c + * + * @SSH_FRAME_TYPE_DATA_SEQ: + * Indicates a data frame, followed by a payload with the length specified -+ * in the ssh_frame.len field. This frame is sequenced, meaning that an ACK -+ * is required. ++ * in the ``struct ssh_frame.len`` field. This frame is sequenced, meaning ++ * that an ACK is required. + * + * @SSH_FRAME_TYPE_DATA_NSQ: + * Same as %SSH_FRAME_TYPE_DATA_SEQ, but unsequenced, meaning that the @@ -14728,25 +16171,6 @@ index 0000000000000..a02d4996a604c +#define SSH_COMMAND_MAX_PAYLOAD_SIZE \ + (SSH_FRAME_MAX_PAYLOAD_SIZE - sizeof(struct ssh_command)) + -+/** -+ * struct ssh_notification_params - Command payload to enable/disable SSH -+ * notifications. -+ * @target_category: The target category for which notifications should be -+ * enabled/disabled. -+ * @flags: Flags determining how notifications are being sent. -+ * @request_id: The request ID that is used to send these notifications. -+ * @instance_id: The specific instance in the given target category for -+ * which notifications should be enabled. -+ */ -+struct ssh_notification_params { -+ u8 target_category; -+ u8 flags; -+ __le16 request_id; -+ u8 instance_id; -+} __packed; -+ -+static_assert(sizeof(struct ssh_notification_params) == 5); -+ +/* + * SSH_MSG_LEN_BASE - Base-length of a SSH message. + * @@ -14793,8 +16217,8 @@ index 0000000000000..a02d4996a604c + (sizeof(u16) + offsetof(struct ssh_frame, field)) + +/** -+ * SSH_MSGOFFSET_FRAME() - Compute offset in SSH message to specified field in -+ * command. ++ * SSH_MSGOFFSET_COMMAND() - Compute offset in SSH message to specified field ++ * in command. + * @field: The field for which the offset should be computed. + * + * Return: Returns the offset of the specified &struct ssh_command field in @@ -14822,23 +16246,29 @@ index 0000000000000..a02d4996a604c +/* -- Packet transport layer (ptl). ----------------------------------------- */ + +/** -+ * enum ssh_packet_priority - Base priorities for &struct ssh_packet. ++ * enum ssh_packet_base_priority - Base priorities for &struct ssh_packet. + * @SSH_PACKET_PRIORITY_FLUSH: Base priority for flush packets. + * @SSH_PACKET_PRIORITY_DATA: Base priority for normal data paackets. + * @SSH_PACKET_PRIORITY_NAK: Base priority for NAK packets. + * @SSH_PACKET_PRIORITY_ACK: Base priority for ACK packets. + */ -+enum ssh_packet_priority { -+ SSH_PACKET_PRIORITY_FLUSH = 0, ++enum ssh_packet_base_priority { ++ SSH_PACKET_PRIORITY_FLUSH = 0, /* same as DATA to sequence flush */ + SSH_PACKET_PRIORITY_DATA = 0, -+ SSH_PACKET_PRIORITY_NAK = 1 << 4, -+ SSH_PACKET_PRIORITY_ACK = 2 << 4, ++ SSH_PACKET_PRIORITY_NAK = 1, ++ SSH_PACKET_PRIORITY_ACK = 2, +}; + ++/* ++ * Same as SSH_PACKET_PRIORITY() below, only with actual values. ++ */ ++#define __SSH_PACKET_PRIORITY(base, try) \ ++ (((base) << 4) | ((try) & 0x0f)) ++ +/** + * SSH_PACKET_PRIORITY() - Compute packet priority from base priority and + * number of tries. -+ * @base: The base priority as suffix of &enum ssh_packet_priority, e.g. ++ * @base: The base priority as suffix of &enum ssh_packet_base_priority, e.g. + * ``FLUSH``, ``DATA``, ``ACK``, or ``NAK``. + * @try: The number of tries (must be less than 16). + * @@ -14851,16 +16281,30 @@ index 0000000000000..a02d4996a604c + * higher number means a higher priority. + */ +#define SSH_PACKET_PRIORITY(base, try) \ -+ ((SSH_PACKET_PRIORITY_##base) | ((try) & 0x0f)) ++ __SSH_PACKET_PRIORITY(SSH_PACKET_PRIORITY_##base, (try)) + +/** + * ssh_packet_priority_get_try() - Get number of tries from packet priority. -+ * @p: The packet priority. ++ * @priority: The packet priority. + * + * Return: Returns the number of tries encoded in the specified packet + * priority. + */ -+#define ssh_packet_priority_get_try(p) ((p) & 0x0f) ++static inline u8 ssh_packet_priority_get_try(u8 priority) ++{ ++ return priority & 0x0f; ++} ++ ++/** ++ * ssh_packet_priority_get_base - Get base priority from packet priority. ++ * @priority: The packet priority. ++ * ++ * Return: Returns the base priority encoded in the given packet priority. ++ */ ++static inline u8 ssh_packet_priority_get_base(u8 priority) ++{ ++ return (priority & 0xf0) >> 4; ++} + + +enum ssh_packet_flags { @@ -14901,11 +16345,44 @@ index 0000000000000..a02d4996a604c +struct ssh_ptl; +struct ssh_packet; + ++/** ++ * struct ssh_packet_ops - Callback operations for a SSH packet. ++ * @release: Function called when the packet reference count reaches zero. ++ * This callback must be relied upon to ensure that the packet has ++ * left the transport system(s). ++ * @complete: Function called when the packet is completed, either with ++ * success or failure. In case of failure, the reason for the ++ * failure is indicated by the value of the provided status code ++ * argument. This value will be zero in case of success. Note that ++ * a call to this callback does not guarantee that the packet is ++ * not in use by the transport system any more. ++ */ +struct ssh_packet_ops { + void (*release)(struct ssh_packet *p); + void (*complete)(struct ssh_packet *p, int status); +}; + ++/** ++ * struct ssh_packet - SSH transport packet. ++ * @ptl: Pointer to the packet transport layer. May be %NULL if the packet ++ * (or enclosing request) has not been submitted yet. ++ * @refcnt: Reference count of the packet. ++ * @priority: Priority of the packet. Must be computed via ++ * SSH_PACKET_PRIORITY(). ++ * @data: Raw message data. ++ * @data.len: Length of the raw message data. ++ * @data.ptr: Pointer to the raw message data buffer. ++ * @state: State and type flags describing current packet state (dynamic) ++ * and type (static). See &enum ssh_packet_flags for possible ++ * options. ++ * @timestamp: Timestamp specifying when the latest transmission of a ++ * currently pending packet has been started. May be %KTIME_MAX ++ * before or in-between transmission attempts. Used for the packet ++ * timeout implementation. ++ * @queue_node: The list node for the packet queue. ++ * @pending_node: The list node for the set of pending packets. ++ * @ops: Packet operations. ++ */ +struct ssh_packet { + struct ssh_ptl *ptl; + struct kref refcnt; @@ -14926,18 +16403,23 @@ index 0000000000000..a02d4996a604c + const struct ssh_packet_ops *ops; +}; + -+#define to_ssh_packet(ptr, member) \ -+ container_of(ptr, struct ssh_packet, member) -+ -+ -+static inline struct ssh_packet *ssh_packet_get(struct ssh_packet *packet) -+{ -+ kref_get(&packet->refcnt); -+ return packet; -+} -+ ++struct ssh_packet *ssh_packet_get(struct ssh_packet *p); +void ssh_packet_put(struct ssh_packet *p); + ++/** ++ * ssh_packet_set_data() - Set raw message data of packet. ++ * @p: The packet for which the message data should be set. ++ * @ptr: Pointer to the memory holding the message data. ++ * @len: Length of the message data. ++ * ++ * Sets the raw message data buffer of the packet to the provided memory. The ++ * memory is not copied. Instead, the caller is responsible for management ++ * (i.e. allocation and deallocation) of the memory. The caller must ensure ++ * that the provided memory is valid and contains a valid SSH message, ++ * starting from the time of submission of the packet until the ``release`` ++ * callback has been called. During this time, the memory may not be altered ++ * in any way. ++ */ +static inline void ssh_packet_set_data(struct ssh_packet *p, u8 *ptr, size_t len) +{ + p->data.ptr = ptr; @@ -14948,6 +16430,7 @@ index 0000000000000..a02d4996a604c +/* -- Request transport layer (rtl). ---------------------------------------- */ + +enum ssh_request_flags { ++ /* state flags */ + SSH_REQUEST_SF_LOCKED_BIT, + SSH_REQUEST_SF_QUEUED_BIT, + SSH_REQUEST_SF_PENDING_BIT, @@ -14957,9 +16440,11 @@ index 0000000000000..a02d4996a604c + SSH_REQUEST_SF_CANCELED_BIT, + SSH_REQUEST_SF_COMPLETED_BIT, + ++ /* type flags */ + SSH_REQUEST_TY_FLUSH_BIT, + SSH_REQUEST_TY_HAS_RESPONSE_BIT, + ++ /* mask for state flags */ + SSH_REQUEST_FLAGS_SF_MASK = + BIT(SSH_REQUEST_SF_LOCKED_BIT) + | BIT(SSH_REQUEST_SF_QUEUED_BIT) @@ -14970,6 +16455,7 @@ index 0000000000000..a02d4996a604c + | BIT(SSH_REQUEST_SF_CANCELED_BIT) + | BIT(SSH_REQUEST_SF_COMPLETED_BIT), + ++ /* mask for type flags */ + SSH_REQUEST_FLAGS_TY_MASK = + BIT(SSH_REQUEST_TY_FLUSH_BIT) + | BIT(SSH_REQUEST_TY_HAS_RESPONSE_BIT), @@ -14979,6 +16465,29 @@ index 0000000000000..a02d4996a604c +struct ssh_rtl; +struct ssh_request; + ++/** ++ * struct ssh_request_ops - Callback operations for a SSH request. ++ * @release: Function called when the request's reference count reaches zero. ++ * This callback must be relied upon to ensure that the request has ++ * left the transport systems (both, packet an request systems). ++ * @complete: Function called when the request is completed, either with ++ * success or failure. The command data for the request response ++ * is provided via the &struct ssh_command parameter (``cmd``), ++ * the command payload of the request response via the &struct ++ * ssh_span parameter (``data``). ++ * ++ * If the request does not have any response or has not been ++ * completed with success, both ``cmd`` and ``data`` parameters will ++ * be NULL. If the request response does not have any command ++ * payload, the ``data`` span will be an empty (zero-length) span. ++ * ++ * In case of failure, the reason for the failure is indicated by ++ * the value of the provided status code argument (``status``). This ++ * value will be zero in case of success. ++ * ++ * Note that a call to this callback does not guarantee that the ++ * request is not in use by the transport systems any more. ++ */ +struct ssh_request_ops { + void (*release)(struct ssh_request *rqst); + void (*complete)(struct ssh_request *rqst, @@ -14986,6 +16495,19 @@ index 0000000000000..a02d4996a604c + const struct ssam_span *data, int status); +}; + ++/** ++ * struct ssh_request - SSH transport request. ++ * @packet: The underlying SSH transport packet. ++ * @node: List node for the request queue and pending set. ++ * @state: State and type flags describing current request state (dynamic) ++ * and type (static). See &enum ssh_request_flags for possible ++ * options. ++ * @timestamp: Timestamp specifying when we start waiting on the respnse of the ++ * request. This is set once the underlying packet has been completed ++ * and may be %KTIME_MAX before that, or when the request does not ++ * expect a response. Used for the request timeout implementation. ++ * @ops: Request Operations. ++ */ +struct ssh_request { + struct ssh_packet packet; + struct list_head node; @@ -14996,21 +16518,65 @@ index 0000000000000..a02d4996a604c + const struct ssh_request_ops *ops; +}; + -+#define to_ssh_request(ptr, member) \ -+ container_of(ptr, struct ssh_request, member) -+ ++/** ++ * to_ssh_request() - Cast a SSH packet to its enclosing SSH request. ++ * @p: The packet to cast. ++ * ++ * Casts the given &struct ssh_packet to its enclosing &struct ssh_request. ++ * The caller is responsible for making sure that the packet is actually ++ * wrapped in a &struct ssh_request. ++ * ++ * Return: Returns the &struct ssh_request wrapping the provided packet. ++ */ ++static inline struct ssh_request *to_ssh_request(struct ssh_packet *p) ++{ ++ return container_of(p, struct ssh_request, packet); ++} + ++/** ++ * ssh_request_get() - Increment reference count of request. ++ * @r: The request to increment the reference count of. ++ * ++ * Increments the reference count of the given request by incrementing the ++ * reference count of the underlying &struct ssh_packet, enclosed in it. ++ * ++ * See also ssh_request_put(), ssh_packet_get(). ++ * ++ * Return: Returns the request provided as input. ++ */ +static inline struct ssh_request *ssh_request_get(struct ssh_request *r) +{ + ssh_packet_get(&r->packet); + return r; +} + ++/** ++ * ssh_request_put() - Decrement reference count of request. ++ * @r: The request to decrement the reference count of. ++ * ++ * Decrements the reference count of the given request by decrementing the ++ * reference count of the underlying &struct ssh_packet, enclosed in it. If ++ * the reference count reaches zero, the ``release`` callback specified in the ++ * request's &struct ssh_request_ops, i.e. ``r->ops->release``, will be ++ * called. ++ * ++ * See also ssh_request_get(), ssh_packet_put(). ++ */ +static inline void ssh_request_put(struct ssh_request *r) +{ + ssh_packet_put(&r->packet); +} + ++/** ++ * ssh_request_set_data() - Set raw message data of request. ++ * @r: The request for which the message data should be set. ++ * @ptr: Pointer to the memory holding the message data. ++ * @len: Length of the message data. ++ * ++ * Sets the raw message data buffer of the underlying packet to the specified ++ * buffer. Does not copy the actual message data, just sets the buffer pointer ++ * and length. Refer to ssh_packet_set_data() for more details. ++ */ +static inline void ssh_request_set_data(struct ssh_request *r, u8 *ptr, size_t len) +{ + ssh_packet_set_data(&r->packet, ptr, len); @@ -15020,6 +16586,8 @@ index 0000000000000..a02d4996a604c +/* -- Main data types and definitions --------------------------------------- */ + +enum ssam_ssh_tc { ++ /* Known SSH/EC target categories. */ ++ // category 0x00 is invalid for EC use + SSAM_SSH_TC_SAM = 0x01, // generic system functionality, real-time clock + SSAM_SSH_TC_BAT = 0x02, // battery/power subsystem + SSAM_SSH_TC_TMP = 0x03, // thermal subsystem @@ -15054,19 +16622,29 @@ index 0000000000000..a02d4996a604c + SSAM_SSH_TC_KPD = 0x20, + SSAM_SSH_TC_REG = 0x21, + -+ SSAM_SSH_TC__HUB = 0x00, // not an actual ID, used in for hubs ++ /* Special values. For driver use only, do not use with EC. */ ++ SSAM_SSH_TC__HUB = 0x00, // not an actual category, used in for hubs +}; + +struct ssam_controller; + +/** -+ * enum ssam_event_flags - Flags for enabling/disabling SAM-over-SSH events ++ * enum ssam_event_flags - Flags for enabling/disabling SAM events + * @SSAM_EVENT_SEQUENCED: The event will be sent via a sequenced data frame. + */ +enum ssam_event_flags { + SSAM_EVENT_SEQUENCED = BIT(0), +}; + ++/** ++ * struct ssam_event - SAM event sent from the EC to the host. ++ * @target_category: Target category of the event source. See &enum ssam_ssh_tc. ++ * @target_id: Target ID of the event source. ++ * @command_id: Command ID of the event. ++ * @instance_id: Instance ID of the event source. ++ * @length: Length of the event payload in bytes. ++ * @data: Event payload data. ++ */ +struct ssam_event { + u8 target_category; + u8 target_id; @@ -15076,11 +16654,39 @@ index 0000000000000..a02d4996a604c + u8 data[0]; +}; + ++/** ++ * enum ssam_request_flags - Flags for SAM requests. ++ * ++ * @SSAM_REQUEST_HAS_RESPONSE: ++ * Specifies that the request expects a response. If not set, the request ++ * will be directly completed after its underlying packet has been ++ * transmitted. If set, the request transport system waits for a response ++ * of the request. ++ * ++ * @SSAM_REQUEST_UNSEQUENCED: ++ * Specifies that the request should be transmitted via an unsequenced ++ * packet. If set, the request must not have a response, meaning that this ++ * flag and the %SSAM_REQUEST_HAS_RESPONSE flag are mutually exclusive. ++ */ +enum ssam_request_flags { + SSAM_REQUEST_HAS_RESPONSE = BIT(0), + SSAM_REQUEST_UNSEQUENCED = BIT(1), +}; + ++/** ++ * struct ssam_request - SAM request description. ++ * @target_category: Category of the request's target. See &enum ssam_ssh_tc. ++ * @target_id: ID of the request's target. ++ * @command_id: Command ID of the request. ++ * @instance_id: Instance ID of the request's target. ++ * @flags: Flags for the request. See &enum ssam_request_flags. ++ * @length: Length of the request payload in bytes. ++ * @payload: Request payload data. ++ * ++ * This struct fully describes a SAM request with payload. It is intended to ++ * help set up the actual transport struct, e.g. &struct ssam_request_sync, ++ * and specifically its raw message data via ssam_request_write_data(). ++ */ +struct ssam_request { + u8 target_category; + u8 target_id; @@ -15091,6 +16697,13 @@ index 0000000000000..a02d4996a604c + const u8 *payload; +}; + ++/** ++ * struct ssam_response - Response buffer for SAM request. ++ * @capacity: Capacity of the buffer, in bytes. ++ * @length: Length of the actual data stored in the memory pointed to by ++ * @pointer, in bytes. Set by the transport system. ++ * @pointer: Pointer to the buffer's memory, storing the response payload data. ++ */ +struct ssam_response { + size_t capacity; + size_t length; @@ -15117,6 +16730,17 @@ index 0000000000000..a02d4996a604c + +/* -- Synchronous request interface. ---------------------------------------- */ + ++/** ++ * struct ssam_request_sync - Synchronous SAM request struct. ++ * @base: Underlying SSH request. ++ * @comp: Completion used to signal full completion of the request. After the ++ * request has been submitted, this struct may only be modified or ++ * deallocated after the completion has been signaled. ++ * request has been submitted, ++ * @resp: Buffer to store the response. ++ * @status: Status of the request, set after the base request has been ++ * completed or has failed. ++ */ +struct ssam_request_sync { + struct ssh_request base; + struct completion comp; @@ -15171,13 +16795,17 @@ index 0000000000000..a02d4996a604c + * @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. ++ * function terminates, the request is guaranteed to have left the transport ++ * 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. ++ * This function must not be called if the request has not been submitted yet ++ * and may lead to a deadlock/infinite wait if a subsequent request submission ++ * fails in that case, due to the completion never triggering. ++ * ++ * Return: Returns the status of the given request, which is set on completion ++ * of the packet. This value is zero on success and negative on failure. + */ +static inline int ssam_request_sync_wait(struct ssam_request_sync *rqst) +{ @@ -15210,7 +16838,8 @@ index 0000000000000..a02d4996a604c + * 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. ++ * Return: Returns the status of the request or any failure during setup, i.e. ++ * zero on success and a negative value on failure. + */ +#define ssam_request_sync_onstack(ctrl, rqst, rsp, payload_len) \ + ({ \ @@ -15221,6 +16850,20 @@ index 0000000000000..a02d4996a604c + }) + + ++/** ++ * struct ssam_request_spec - Blue-print specification of SAM request. ++ * @target_category: Category of the request's target. See &enum ssam_ssh_tc. ++ * @target_id: ID of the request's target. ++ * @command_id: Command ID of the request. ++ * @instance_id: Instance ID of the request's target. ++ * @flags: Flags for the request. See &enum ssam_request_flags. ++ * ++ * Blue-print specification for a SAM request. This struct describes the ++ * unique static parameters of a request (i.e. type) without specifying any of ++ * its instance-specific data (e.g. payload). It is intended to be used as base ++ * for defining simple request functions via the ++ * ``SSAM_DEFINE_SYNC_REQUEST_x()`` family of macros. ++ */ +struct ssam_request_spec { + u8 target_category; + u8 target_id; @@ -15229,12 +16872,50 @@ index 0000000000000..a02d4996a604c + u8 flags; +}; + ++/** ++ * struct ssam_request_spec_md - Blue-print specification for multi-device SAM ++ * request. ++ * @target_category: Category of the request's target. See &enum ssam_ssh_tc. ++ * @command_id: Command ID of the request. ++ * @flags: Flags for the request. See &enum ssam_request_flags. ++ * ++ * Blue-print specification for a multi-device SAM request, i.e. a request ++ * that is applicable to multiple device instances, described by their ++ * individual target and instance IDs. This struct describes the unique static ++ * parameters of a request (i.e. type) without specifying any of its ++ * instance-specific data (e.g. payload) and without specifying any of its ++ * device specific IDs (i.e. target and instance ID). It is intended to be ++ * used as base for defining simple multi-device request functions via the ++ * ``SSAM_DEFINE_SYNC_REQUEST_MD_x()`` and ``SSAM_DEFINE_SYNC_REQUEST_CL_x()`` ++ * families of macros. ++ */ +struct ssam_request_spec_md { + u8 target_category; + u8 command_id; + u8 flags; +}; + ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_N() - Define synchronous SAM request function ++ * with neither argument nor return value. ++ * @name: Name of the generated function. ++ * @spec: Specification (&struct ssam_request_spec) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request having neither argument nor return value. The ++ * generated function takes care of setting up the request struct and buffer ++ * allocation, as well as execution of the request itself, returning once the ++ * request has been fully completed. The required transport buffer will be ++ * allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_controller ++ * *ctrl)``, returning the status of the request, which is zero on success and ++ * negative on failure. The ``ctrl`` parameter is the controller via which the ++ * request is being sent. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ +#define SSAM_DEFINE_SYNC_REQUEST_N(name, spec...) \ + int name(struct ssam_controller *ctrl) \ + { \ @@ -15252,8 +16933,31 @@ index 0000000000000..a02d4996a604c + return ssam_request_sync_onstack(ctrl, &rqst, NULL, 0); \ + } + -+#define SSAM_DEFINE_SYNC_REQUEST_W(name, wtype, spec...) \ -+ int name(struct ssam_controller *ctrl, const wtype *in) \ ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_W() - Define synchronous SAM request function with ++ * argument. ++ * @name: Name of the generated function. ++ * @atype: Type of the request's argument. ++ * @spec: Specification (&struct ssam_request_spec) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request taking an argument of type @atype and having no ++ * return value. The generated function takes care of setting up the request ++ * struct, buffer allocation, as well as execution of the request itself, ++ * returning once the request has been fully completed. The required transport ++ * buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_controller ++ * *ctrl, const atype *arg)``, returning the status of the request, which is ++ * zero on success and negative on failure. The ``ctrl`` parameter is the ++ * controller via which the request is sent. The request argument is specified ++ * via the ``arg`` pointer. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ ++#define SSAM_DEFINE_SYNC_REQUEST_W(name, atype, spec...) \ ++ int name(struct ssam_controller *ctrl, const atype *arg) \ + { \ + struct ssam_request_spec s = (struct ssam_request_spec)spec; \ + struct ssam_request rqst; \ @@ -15263,15 +16967,38 @@ index 0000000000000..a02d4996a604c + rqst.command_id = s.command_id; \ + rqst.instance_id = s.instance_id; \ + rqst.flags = s.flags; \ -+ rqst.length = sizeof(wtype); \ -+ rqst.payload = (u8 *)in; \ ++ rqst.length = sizeof(atype); \ ++ rqst.payload = (u8 *)arg; \ + \ + return ssam_request_sync_onstack(ctrl, &rqst, NULL, \ -+ sizeof(wtype)); \ ++ sizeof(atype)); \ + } + ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_R() - Define synchronous SAM request function with ++ * return value. ++ * @name: Name of the generated function. ++ * @rtype: Type of the request's return value. ++ * @spec: Specification (&struct ssam_request_spec) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request taking no argument but having a return value of ++ * type @rtype. The generated function takes care of setting up the request ++ * and response structs, buffer allocation, as well as execution of the ++ * request itself, returning once the request has been fully completed. The ++ * required transport buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_controller ++ * *ctrl, rtype *ret)``, returning the status of the request, which is zero on ++ * success and negative on failure. The ``ctrl`` parameter is the controller ++ * via which the request is sent. The request's return value is written to the ++ * memory pointed to by the ``ret`` parameter. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ +#define SSAM_DEFINE_SYNC_REQUEST_R(name, rtype, spec...) \ -+ int name(struct ssam_controller *ctrl, rtype *out) \ ++ int name(struct ssam_controller *ctrl, rtype *ret) \ + { \ + struct ssam_request_spec s = (struct ssam_request_spec)spec; \ + struct ssam_request rqst; \ @@ -15288,7 +17015,7 @@ index 0000000000000..a02d4996a604c + \ + rsp.capacity = sizeof(rtype); \ + rsp.length = 0; \ -+ rsp.pointer = (u8 *)out; \ ++ rsp.pointer = (u8 *)ret; \ + \ + status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, 0); \ + if (status) \ @@ -15306,8 +17033,31 @@ index 0000000000000..a02d4996a604c + return 0; \ + } + -+#define SSAM_DEFINE_SYNC_REQUEST_MD_W(name, wtype, spec...) \ -+ int name(struct ssam_controller *ctrl, u8 tid, u8 iid, const wtype *in) \ ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_MD_N() - Define synchronous multi-device SAM ++ * request function with neither argument nor return value. ++ * @name: Name of the generated function. ++ * @spec: Specification (&struct ssam_request_spec_md) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request having neither argument nor return value. Device ++ * specifying parameters are not hard-coded, but instead must be provided to ++ * the function. The generated function takes care of setting up the request ++ * struct, buffer allocation, as well as execution of the request itself, ++ * returning once the request has been fully completed. The required transport ++ * buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_controller ++ * *ctrl, u8 tid, u8 iid)``, returning the status of the request, which is ++ * zero on success and negative on failure. The ``ctrl`` parameter is the ++ * controller via which the request is sent, ``tid`` the target ID for the ++ * request, and ``iid`` the instance ID. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ ++#define SSAM_DEFINE_SYNC_REQUEST_MD_N(name, spec...) \ ++ int name(struct ssam_controller *ctrl, u8 tid, u8 iid) \ + { \ + struct ssam_request_spec_md s \ + = (struct ssam_request_spec_md)spec; \ @@ -15318,15 +17068,83 @@ index 0000000000000..a02d4996a604c + rqst.command_id = s.command_id; \ + rqst.instance_id = iid; \ + rqst.flags = s.flags; \ -+ rqst.length = sizeof(wtype); \ -+ rqst.payload = (u8 *)in; \ ++ rqst.length = 0; \ ++ rqst.payload = NULL; \ + \ -+ return ssam_request_sync_onstack(ctrl, &rqst, NULL, \ -+ sizeof(wtype)); \ ++ return ssam_request_sync_onstack(ctrl, &rqst, NULL, 0); \ + } + ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_MD_W() - Define synchronous multi-device SAM ++ * request function with argument. ++ * @name: Name of the generated function. ++ * @atype: Type of the request's argument. ++ * @spec: Specification (&struct ssam_request_spec_md) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request taking an argument of type @atype and having no ++ * return value. Device specifying parameters are not hard-coded, but instead ++ * must be provided to the function. The generated function takes care of ++ * setting up the request struct, buffer allocation, as well as execution of ++ * the request itself, returning once the request has been fully completed. ++ * The required transport buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_controller ++ * *ctrl, u8 tid, u8 iid, const atype *arg)``, returning the status of the ++ * request, which is zero on success and negative on failure. The ``ctrl`` ++ * parameter is the controller via which the request is sent, ``tid`` the ++ * target ID for the request, and ``iid`` the instance ID. The request argument ++ * is specified via the ``arg`` pointer. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ ++#define SSAM_DEFINE_SYNC_REQUEST_MD_W(name, atype, spec...) \ ++ int name(struct ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg)\ ++ { \ ++ 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.flags = s.flags; \ ++ rqst.length = sizeof(atype); \ ++ rqst.payload = (u8 *)arg; \ ++ \ ++ return ssam_request_sync_onstack(ctrl, &rqst, NULL, \ ++ sizeof(atype)); \ ++ } ++ ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_MD_R() - Define synchronous multi-device SAM ++ * request function with return value. ++ * @name: Name of the generated function. ++ * @rtype: Type of the request's return value. ++ * @spec: Specification (&struct ssam_request_spec_md) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request taking no argument but having a return value of ++ * type @rtype. Device specifying parameters are not hard-coded, but instead ++ * must be provided to the function. The generated function takes care of ++ * setting up the request and response structs, buffer allocation, as well as ++ * execution of the request itself, returning once the request has been fully ++ * completed. The required transport buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_controller ++ * *ctrl, u8 tid, u8 iid, rtype *ret)``, returning the status of the request, ++ * which is zero on success and negative on failure. The ``ctrl`` parameter is ++ * the controller via which the request is sent, ``tid`` the target ID for the ++ * request, and ``iid`` the instance ID. The request's return value is written ++ * to the memory pointed to by the ``ret`` parameter. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ +#define SSAM_DEFINE_SYNC_REQUEST_MD_R(name, rtype, spec...) \ -+ int name(struct ssam_controller *ctrl, u8 tid, u8 iid, rtype *out) \ ++ int name(struct ssam_controller *ctrl, u8 tid, u8 iid, rtype *ret) \ + { \ + struct ssam_request_spec_md s \ + = (struct ssam_request_spec_md)spec; \ @@ -15344,7 +17162,7 @@ index 0000000000000..a02d4996a604c + \ + rsp.capacity = sizeof(rtype); \ + rsp.length = 0; \ -+ rsp.pointer = (u8 *)out; \ ++ rsp.pointer = (u8 *)ret; \ + \ + status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, 0); \ + if (status) \ @@ -15368,22 +17186,69 @@ index 0000000000000..a02d4996a604c +#define SSAM_NOTIF_STATE_SHIFT 2 +#define SSAM_NOTIF_STATE_MASK ((1 << SSAM_NOTIF_STATE_SHIFT) - 1) + -+#define SSAM_NOTIF_HANDLED BIT(0) -+#define SSAM_NOTIF_STOP BIT(1) ++/** ++ * enum ssam_notif_flags - Flags used in return values from SSAM notifier ++ * callback functions. ++ * ++ * @SSAM_NOTIF_HANDLED: ++ * Indicates that the notification has been handled. This flag should be ++ * set by the handler if the handler can act/has acted upon the event ++ * provided to it. This flag should not be set if the handler is not a ++ * primary handler intended for the provided event. ++ * ++ * If this flag has not been set by any handler after the notifier chain ++ * has been traversed, a warning will be emitted, stating that the event ++ * has not been handled. ++ * ++ * @SSAM_NOTIF_STOP: ++ * Indicates that the notifier traversal should stop. If this flag is ++ * returned from a notifier callback, notifier chain traversal will ++ * immediately stop and any remaining notifiers will not be called. This ++ * flag is automatically set when ssam_notifier_from_errno() is called ++ * with a negative error value. ++ */ ++enum ssam_notif_flags { ++ SSAM_NOTIF_HANDLED = BIT(0), ++ SSAM_NOTIF_STOP = BIT(1), ++}; + + -+struct ssam_notifier_block; ++struct ssam_event_notifier; + -+typedef u32 (*ssam_notifier_fn_t)(struct ssam_notifier_block *nb, ++typedef u32 (*ssam_notifier_fn_t)(struct ssam_event_notifier *nf, + const struct ssam_event *event); + ++/** ++ * struct ssam_notifier_block - Base notifier block for SSAM event ++ * notifications. ++ * @next: The next notifier block in order of priority. ++ * @fn: The callback function of this notifier. This function takes the ++ * respective notifier block and event as input and should return ++ * a notifier value, which can either be obtained from the flags ++ * provided in &enum ssam_notif_flags, converted from a standard ++ * error value via ssam_notifier_from_errno(), or a combination of ++ * both (e.g. ``ssam_notifier_from_errno(e) | SSAM_NOTIF_HANDLED``). ++ * @priority: Priority value determining the order in which notifier callbacks ++ * will be called. A higher value means higher priority, i.e. the ++ * associated callback will be executed earlier than other (lower ++ * priority) callbacks. ++ */ +struct ssam_notifier_block { + struct ssam_notifier_block __rcu *next; + ssam_notifier_fn_t fn; + int priority; +}; + -+ ++/** ++ * ssam_notifier_from_errno() - Convert standard error value to notifier ++ * return code. ++ * @err: The error code to convert, must be negative (in case of failure) or ++ * zero (in case of success). ++ * ++ * Return: Returns the notifier return value obtained by converting the ++ * specified @err value. In case @err is negative, the %SSAM_NOTIF_STOP flag ++ * will be set, causing notifier call chain traversal to abort. ++ */ +static inline u32 ssam_notifier_from_errno(int err) +{ + if (WARN_ON(err > 0) || err == 0) @@ -15392,6 +17257,14 @@ index 0000000000000..a02d4996a604c + return ((-err) << SSAM_NOTIF_STATE_SHIFT) | SSAM_NOTIF_STOP; +} + ++/** ++ * ssam_notifier_to_errno() - Convert notifier return code to standard error ++ * value. ++ * @ret: The notifier return value to convert. ++ * ++ * Return: Returns the negative error value encoded in @ret or zero if @ret ++ * indicates success. ++ */ +static inline int ssam_notifier_to_errno(u32 ret) +{ + return -(ret >> SSAM_NOTIF_STATE_SHIFT); @@ -15400,6 +17273,18 @@ index 0000000000000..a02d4996a604c + +/* -- Event/notification registry. ------------------------------------------ */ + ++/** ++ * struct ssam_event_registry - Registry specification used for enabling events. ++ * @target_category: Target category for the event registry requests. ++ * @target_id: Target ID for the event registry requests. ++ * @cid_enable: Command ID for the event-enable request. ++ * @cid_disable: Command ID for the event-disable request. ++ * ++ * This struct describes a SAM event registry via the minimal collection of ++ * SAM IDs specifying the requests to use for enabling and disabling an event. ++ * The individual event to be enabled/disabled itself is specified via &struct ++ * ssam_event_id. ++ */ +struct ssam_event_registry { + u8 target_category; + u8 target_id; @@ -15407,12 +17292,61 @@ index 0000000000000..a02d4996a604c + u8 cid_disable; +}; + ++/** ++ * struct ssam_event_id - Unique event ID used for enabling events. ++ * @target_category: Target category of the event source. ++ * @instance: Instance ID of the event source. ++ * ++ * This struct specifies the event to be enabled/disabled via an externally ++ * provided registry. It does not specify the registry to be used itself, this ++ * is done via &struct ssam_event_registry. ++ */ +struct ssam_event_id { + u8 target_category; + u8 instance; +}; + ++/** ++ * enum ssam_event_mask - Flags specifying how events are matched to notifiers. ++ * ++ * @SSAM_EVENT_MASK_NONE: ++ * Run the callback for any event with matching target category. Do not ++ * do any additional filtering. ++ * ++ * @SSAM_EVENT_MASK_TARGET: ++ * In addition to filtering by target category, only execute the notifier ++ * callback for events with a target ID matching to the one of the ++ * registry used for enabling/disabling the event. ++ * ++ * @SSAM_EVENT_MASK_INSTANCE: ++ * In addition to filtering by target category, only execute the notifier ++ * callback for events with an instance ID matching to the instance ID ++ * used when enabling the event. ++ * ++ * @SSAM_EVENT_MASK_STRICT: ++ * Do all the filtering above. ++ */ ++enum ssam_event_mask { ++ SSAM_EVENT_MASK_TARGET = BIT(0), ++ SSAM_EVENT_MASK_INSTANCE = BIT(1), + ++ SSAM_EVENT_MASK_NONE = 0, ++ SSAM_EVENT_MASK_STRICT ++ = SSAM_EVENT_MASK_TARGET ++ | SSAM_EVENT_MASK_INSTANCE, ++}; ++ ++ ++/** ++ * SSAM_EVENT_REGISTRY() - Define a new event registry. ++ * @tc: Target category for the event registry requests. ++ * @tid: Target ID for the event registry requests. ++ * @cid_en: Command ID for the event-enable request. ++ * @cid_dis: Command ID for the event-disable request. ++ * ++ * Return: Returns the &struct ssam_event_registry specified by the given ++ * parameters. ++ */ +#define SSAM_EVENT_REGISTRY(tc, tid, cid_en, cid_dis) \ + ((struct ssam_event_registry) { \ + .target_category = (tc), \ @@ -15421,13 +17355,6 @@ index 0000000000000..a02d4996a604c + .cid_disable = (cid_dis), \ + }) + -+#define SSAM_EVENT_ID(tc, iid) \ -+ ((struct ssam_event_id) { \ -+ .target_category = (tc), \ -+ .instance = (iid), \ -+ }) -+ -+ +#define SSAM_EVENT_REGISTRY_SAM \ + SSAM_EVENT_REGISTRY(SSAM_SSH_TC_SAM, 0x01, 0x0b, 0x0c) + @@ -15438,12 +17365,22 @@ index 0000000000000..a02d4996a604c + SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02) + + ++/** ++ * struct ssam_event_notifier - Notifier block for SSAM events. ++ * @base: The base notifier block with callback function and priority. ++ * @event: The event for which this block will receive notifications. ++ * @event.reg: Registry via which the event will be enabled/disabled. ++ * @event.id: ID specifying the event. ++ * @event.mask: Flags determining how events are matched to the notifier. ++ * @event.flags: Flags used for enabling the event. ++ */ +struct ssam_event_notifier { + struct ssam_notifier_block base; + + struct { + struct ssam_event_registry reg; + struct ssam_event_id id; ++ enum ssam_event_mask mask; + u8 flags; + } event; +}; @@ -15457,6 +17394,16 @@ index 0000000000000..a02d4996a604c + +/* -- Surface System Aggregator Module Bus. --------------------------------- */ + ++/** ++ * struct ssam_device_uid - Unique identifier for SSAM device. ++ * @category: Target category of the device. ++ * @target: Target ID of the device. ++ * @instance: Instance ID of the device. ++ * @function: Sub-function of the device. This field can be used to split a ++ * single SAM device into multiple virtual subdevices to separate ++ * different functionality of that device and allow one driver per ++ * such functionality. ++ */ +struct ssam_device_uid { + u8 category; + u8 target; @@ -15464,42 +17411,91 @@ index 0000000000000..a02d4996a604c + u8 function; +}; + -+#define SSAM_DUID(__cat, __tid, __iid, __fun) \ -+ ((struct ssam_device_uid) { \ -+ .category = SSAM_SSH_TC_##__cat, \ -+ .target = (__tid), \ -+ .instance = (__iid), \ -+ .function = (__fun) \ ++/** ++ * SSAM_DUID() - Define a &struct ssam_device_uid. ++ * @cat: Target category of the device. ++ * @tid: Target ID of the device. ++ * @iid: Instance ID of the device. ++ * @fun: Sub-function of the (virtual) device. ++ * ++ * Return: The &struct ssam_device_uid specified by the given parameters. ++ */ ++#define SSAM_DUID(cat, tid, iid, fun) \ ++ ((struct ssam_device_uid) { \ ++ .category = SSAM_SSH_TC_##cat, \ ++ .target = (tid), \ ++ .instance = (iid), \ ++ .function = (fun) \ + }) + ++/* ++ * SSAM_DUID_NULL - Null device UID. ++ */ +#define SSAM_DUID_NULL ((struct ssam_device_uid) { 0 }) + ++/* ++ * Special values for device matching. ++ */ +#define SSAM_ANY_TID 0xffff +#define SSAM_ANY_IID 0xffff +#define SSAM_ANY_FUN 0xffff + -+#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, \ -+ .target = ((__tid) != SSAM_ANY_TID) ? (__tid) : 0, \ -+ .instance = ((__iid) != SSAM_ANY_IID) ? (__iid) : 0, \ -+ .function = ((__fun) != SSAM_ANY_FUN) ? (__fun) : 0 \ ++/** ++ * SSAM_DEVICE() - Initialize a &struct ssam_device_id with the given ++ * parameters. ++ * @cat: Target category of the device. ++ * @tid: Target ID of the device. ++ * @iid: Instance ID of the device. ++ * @fun: Sub-function of the device. ++ * ++ * Initializes a &struct ssam_device_id with the given parameters. See &struct ++ * ssam_device_uid for details regarding the parameters. The special values ++ * %SSAM_ANY_TID, %SSAM_ANY_IID, and %SSAM_ANY_FUN can be used to specify that ++ * matching should ignore target ID, instance ID, and/or sub-function, ++ * respectively. This macro initializes the ``match_flags`` field based on the ++ * given parameters. ++ */ ++#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, \ ++ .target = ((tid) != SSAM_ANY_TID) ? (tid) : 0, \ ++ .instance = ((iid) != SSAM_ANY_IID) ? (iid) : 0, \ ++ .function = ((fun) != SSAM_ANY_FUN) ? (fun) : 0 \ + + ++/** ++ * ssam_device_uid_equal() - Compare SSAM device UIDs for equality. ++ * @u1: The first UID. ++ * @u2: The second UID. ++ * ++ * Return: Returns %true iff both UIDs are equal, %false otherwise. ++ */ +static inline bool ssam_device_uid_equal(const struct ssam_device_uid u1, + const struct ssam_device_uid u2) +{ + return memcmp(&u1, &u2, sizeof(struct ssam_device_uid)) == 0; +} + ++/** ++ * ssam_device_uid_is_null() - Check if a SSAM device UID is null. ++ * @uid: The UID to check. ++ * ++ * Return: Returns %true iff the given UID is null, %false otherwise. ++ */ +static inline bool ssam_device_uid_is_null(const struct ssam_device_uid uid) +{ + return ssam_device_uid_equal(uid, (struct ssam_device_uid){}); +} + + ++/** ++ * struct ssam_device - SSAM client device. ++ * @dev: Driver model representation of the device. ++ * @ctrl: SSAM controller managing this device. ++ * @uid: UID identifying the device. ++ */ +struct ssam_device { + struct device dev; + struct ssam_controller *ctrl; @@ -15507,6 +17503,13 @@ index 0000000000000..a02d4996a604c + struct ssam_device_uid uid; +}; + ++/** ++ * struct ssam_device_driver - SSAM client device driver. ++ * @driver: Base driver model structure. ++ * @match_table: Match table specifying which devices the driver should bind to. ++ * @probe: Called when the driver is being bound to a device. ++ * @remove: Called when the driver is being unbound from the device. ++ */ +struct ssam_device_driver { + struct device_driver driver; + @@ -15520,16 +17523,47 @@ index 0000000000000..a02d4996a604c +extern const struct device_type ssam_device_type; + + -+static inline bool is_ssam_device(struct device *device) ++/** ++ * is_ssam_device() - Check if the given device is a SSAM client device. ++ * @d: The device to test the type of. ++ * ++ * Return: Returns %true iff the specified device is of type &struct ++ * ssam_device, i.e. the device type points to %ssam_device_type, and %false ++ * otherwise. ++ */ ++static inline bool is_ssam_device(struct device *d) +{ -+ return device->type == &ssam_device_type; ++ return d->type == &ssam_device_type; +} + ++/** ++ * to_ssam_device() - Casts the given device to a SSAM client device. ++ * @d: The device to cast. ++ * ++ * Casts the given &struct device to a &struct ssam_device. The caller has to ++ * ensure that the given device is actually enclosed in a &struct ssam_device, ++ * e.g. by calling is_ssam_device(). ++ * ++ * Return: Returns a pointer to the &struct ssam_device wrapping the given ++ * device @d. ++ */ +static inline struct ssam_device *to_ssam_device(struct device *d) +{ + return container_of(d, struct ssam_device, dev); +} + ++/** ++ * to_ssam_device_driver() - Casts the given device driver to a SSAM client ++ * device driver. ++ * @d: The driver to cast. ++ * ++ * Casts the given &struct device_driver to a &struct ssam_device_driver. The ++ * caller has to ensure that the given driver is actually enclosed in a ++ * &struct ssam_device_driver. ++ * ++ * Return: Returns the pointer to the &struct ssam_device_driver wrapping the ++ * given device driver @d. ++ */ +static inline +struct ssam_device_driver *to_ssam_device_driver(struct device_driver *d) +{ @@ -15552,21 +17586,56 @@ index 0000000000000..a02d4996a604c +int ssam_device_add(struct ssam_device *sdev); +void ssam_device_remove(struct ssam_device *sdev); + -+static inline void ssam_device_get(struct ssam_device *sdev) ++/** ++ * ssam_device_get() - Increment reference count of SSAM client device. ++ * @sdev: The device to increment the reference count of. ++ * ++ * Increments the reference count of the given SSAM client device by ++ * incrementing the reference count of the enclosed &struct device via ++ * get_device(). ++ * ++ * See ssam_device_put() for the counter-part of this function. ++ * ++ * Return: Returns the device provided as input. ++ */ ++static inline struct ssam_device *ssam_device_get(struct ssam_device *sdev) +{ + get_device(&sdev->dev); ++ return sdev; +} + ++/** ++ * ssam_device_put() - Decrement reference count of SSAM client device. ++ * @sdev: The device to decrement the reference count of. ++ * ++ * Decrements the reference count of the given SSAM client device by ++ * decrementing the reference count of the enclosed &struct device via ++ * put_device(). ++ * ++ * See ssam_device_get() for the counter-part of this function. ++ */ +static inline void ssam_device_put(struct ssam_device *sdev) +{ + put_device(&sdev->dev); +} + ++/** ++ * ssam_device_get_drvdata() - Get driver-data of SSAM client device. ++ * @sdev: The device to get the driver-data from. ++ * ++ * Return: Returns the driver-data of the given device, previously set via ++ * ssam_device_set_drvdata(). ++ */ +static inline void *ssam_device_get_drvdata(struct ssam_device *sdev) +{ + return dev_get_drvdata(&sdev->dev); +} + ++/** ++ * ssam_device_set_drvdata() - Set driver-data of SSAM client device. ++ * @sdev: The device to set the driver-data of. ++ * @data: The data to set the device's driver-data pointer to. ++ */ +static inline void ssam_device_set_drvdata(struct ssam_device *sdev, void *data) +{ + dev_set_drvdata(&sdev->dev, data); @@ -15576,41 +17645,128 @@ index 0000000000000..a02d4996a604c +int __ssam_device_driver_register(struct ssam_device_driver *d, struct module *o); +void ssam_device_driver_unregister(struct ssam_device_driver *d); + ++/** ++ * ssam_device_driver_register() - Register a SSAM client device driver. ++ * @drv: The driver to register. ++ */ +#define ssam_device_driver_register(drv) \ + __ssam_device_driver_register(drv, THIS_MODULE) + -+#define module_ssam_device_driver(__drv) \ -+ module_driver(__drv, ssam_device_driver_register, \ ++/** ++ * module_ssam_device_driver() - Helper macro for SSAM device driver ++ * registration. ++ * @drv: The driver managed by this module. ++ * ++ * Helper macro to register a SSAM device driver via module_init() and ++ * module_exit(). This macro may only be used once per module and replaces ++ * the afforementioned definitions. ++ */ ++#define module_ssam_device_driver(drv) \ ++ module_driver(drv, ssam_device_driver_register, \ + ssam_device_driver_unregister) + + +/* -- Helpers for client-device requests. ----------------------------------- */ + -+#define SSAM_DEFINE_SYNC_REQUEST_CL_W(name, wtype, spec...) \ -+ SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_##name, wtype, spec) \ -+ int name(struct ssam_device *sdev, const wtype *in) \ ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_CL_N() - Define synchronous client-device SAM ++ * request function with neither argument nor return value. ++ * @name: Name of the generated function. ++ * @spec: Specification (&struct ssam_request_spec_md) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request having neither argument nor return value. Device ++ * specifying parameters are not hard-coded, but instead are provided via the ++ * client device, specifically its UID, supplied when calling this function. ++ * The generated function takes care of setting up the request struct, buffer ++ * allocation, as well as execution of the request itself, returning once the ++ * request has been fully completed. The required transport buffer will be ++ * allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_device *sdev)``, ++ * returning the status of the request, which is zero on success and negative ++ * on failure. The ``sdev`` parameter specifies both the target device of the ++ * request and by association the controller via which the request is sent. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ ++#define SSAM_DEFINE_SYNC_REQUEST_CL_N(name, spec...) \ ++ SSAM_DEFINE_SYNC_REQUEST_MD_N(__raw_##name, spec) \ ++ int name(struct ssam_device *sdev) \ + { \ + return __raw_##name(sdev->ctrl, sdev->uid.target, \ -+ sdev->uid.instance, in); \ ++ sdev->uid.instance); \ + } + ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_CL_W() - Define synchronous client-device SAM ++ * request function with argument. ++ * @name: Name of the generated function. ++ * @atype: Type of the request's argument. ++ * @spec: Specification (&struct ssam_request_spec_md) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request taking an argument of type @atype and having no ++ * return value. Device specifying parameters are not hard-coded, but instead ++ * are provided via the client device, specifically its UID, supplied when ++ * calling this function. The generated function takes care of setting up the ++ * request struct, buffer allocation, as well as execution of the request ++ * itself, returning once the request has been fully completed. The required ++ * transport buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_device *sdev, ++ * const atype *arg)``, returning the status of the request, which is zero on ++ * success and negative on failure. The ``sdev`` parameter specifies both the ++ * target device of the request and by association the controller via which ++ * the request is sent. The request's argument is specified via the ``arg`` ++ * pointer. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ ++#define SSAM_DEFINE_SYNC_REQUEST_CL_W(name, atype, spec...) \ ++ SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_##name, atype, spec) \ ++ int name(struct ssam_device *sdev, const atype *arg) \ ++ { \ ++ return __raw_##name(sdev->ctrl, sdev->uid.target, \ ++ sdev->uid.instance, arg); \ ++ } ++ ++/** ++ * SSAM_DEFINE_SYNC_REQUEST_CL_R() - Define synchronous client-device SAM ++ * request function with return value. ++ * @name: Name of the generated function. ++ * @rtype: Type of the request's return value. ++ * @spec: Specification (&struct ssam_request_spec_md) defining the request. ++ * ++ * Defines a function executing the synchronous SAM request specified by ++ * @spec, with the request taking no argument but having a return value of ++ * type @rtype. Device specifying parameters are not hard-coded, but instead ++ * are provided via the client device, specifically its UID, supplied when ++ * calling this function. The generated function takes care of setting up the ++ * request struct, buffer allocation, as well as execution of the request ++ * itself, returning once the request has been fully completed. The required ++ * transport buffer will be allocated on the stack. ++ * ++ * The generated function is defined as ``int name(struct ssam_device *sdev, ++ * rtype *ret)``, returning the status of the request, which is zero on ++ * success and negative on failure. The ``sdev`` parameter specifies both the ++ * target device of the request and by association the controller via which ++ * the request is sent. The request's return value is written to the memory ++ * pointed to by the ``ret`` parameter. ++ * ++ * Refer to ssam_request_sync_onstack() for more details on the behavior of ++ * the generated function. ++ */ +#define SSAM_DEFINE_SYNC_REQUEST_CL_R(name, rtype, spec...) \ + SSAM_DEFINE_SYNC_REQUEST_MD_R(__raw_##name, rtype, spec) \ -+ int name(struct ssam_device *sdev, rtype *out) \ ++ int name(struct ssam_device *sdev, rtype *ret) \ + { \ + return __raw_##name(sdev->ctrl, sdev->uid.target, \ -+ sdev->uid.instance, out); \ ++ sdev->uid.instance, ret); \ + } + -+ -+static inline bool ssam_event_matches_device(struct ssam_device_uid uid, -+ const struct ssam_event *event) -+{ -+ return uid.category == event->target_category -+ && 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 010be8ba21160..ad40fb4462803 100644 diff --git a/patches/5.8/0005-surface-sam-over-hid.patch b/patches/5.8/0005-surface-sam-over-hid.patch index bc162a442..d7b437fbb 100644 --- a/patches/5.8/0005-surface-sam-over-hid.patch +++ b/patches/5.8/0005-surface-sam-over-hid.patch @@ -1,11 +1,15 @@ -From 6e0b3fb32080f8059019df7ee0bc0aec064ae7e7 Mon Sep 17 00:00:00 2001 +From 06d09ff5e8976c38cee7a3989461e5f0c6192a13 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH 5/6] surface-sam-over-hid --- - drivers/i2c/i2c-core-acpi.c | 35 +++++++++++++++++++++++++++++++++++ - 1 file changed, 35 insertions(+) + drivers/i2c/i2c-core-acpi.c | 35 +++++++ + drivers/platform/x86/Kconfig | 7 ++ + drivers/platform/x86/Makefile | 1 + + drivers/platform/x86/sb1_dgpu_sw.c | 162 +++++++++++++++++++++++++++++ + 4 files changed, 205 insertions(+) + create mode 100644 drivers/platform/x86/sb1_dgpu_sw.c diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 2ade99b105b91..60b9cb51d5f7e 100644 @@ -60,6 +64,204 @@ index 2ade99b105b91..60b9cb51d5f7e 100644 default: dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", accessor_type, client->addr); +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index 0581a54cf562f..14db2795ff638 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -879,6 +879,13 @@ config SURFACE_PRO3_BUTTON + help + This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. + ++config SURFACE_BOOK1_DGPU_SWITCH ++ tristate "Surface Book 1 dGPU Switch Driver" ++ depends on ACPI && SYSFS ++ help ++ This driver provides a sysfs switch to set the power-state of the ++ discrete GPU found on the Microsoft Surface Book 1. ++ + config MSI_LAPTOP + tristate "MSI Laptop Extras" + depends on ACPI +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index 2b85852a1a872..5156523b58639 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -85,6 +85,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o + obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o + obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o + obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o ++obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += sb1_dgpu_sw.o + + # MSI + obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o +diff --git a/drivers/platform/x86/sb1_dgpu_sw.c b/drivers/platform/x86/sb1_dgpu_sw.c +new file mode 100644 +index 0000000000000..8c66ed5110fda +--- /dev/null ++++ b/drivers/platform/x86/sb1_dgpu_sw.c +@@ -0,0 +1,162 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++ ++ ++#ifdef pr_fmt ++#undef pr_fmt ++#endif ++#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ ++ ++ ++static const guid_t dgpu_sw_guid = GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, ++ 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35); ++ ++#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI" ++#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON" ++#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF" ++ ++ ++static int sb1_dgpu_sw_dsmcall(void) ++{ ++ union acpi_object *ret; ++ acpi_handle handle; ++ acpi_status status; ++ ++ status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle); ++ if (status) ++ return -EINVAL; ++ ++ ret = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER); ++ if (!ret) ++ return -EINVAL; ++ ++ ACPI_FREE(ret); ++ return 0; ++} ++ ++static int sb1_dgpu_sw_hgon(void) ++{ ++ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; ++ acpi_status status; ++ ++ status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf); ++ if (status) { ++ pr_err("failed to run HGON: %d\n", status); ++ return -EINVAL; ++ } ++ ++ if (buf.pointer) ++ ACPI_FREE(buf.pointer); ++ ++ pr_info("turned-on dGPU via HGON\n"); ++ return 0; ++} ++ ++static int sb1_dgpu_sw_hgof(void) ++{ ++ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; ++ acpi_status status; ++ ++ status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf); ++ if (status) { ++ pr_err("failed to run HGOF: %d\n", status); ++ return -EINVAL; ++ } ++ ++ if (buf.pointer) ++ ACPI_FREE(buf.pointer); ++ ++ pr_info("turned-off dGPU via HGOF\n"); ++ return 0; ++} ++ ++ ++static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ int status, value; ++ ++ status = kstrtoint(buf, 0, &value); ++ if (status < 0) ++ return status; ++ ++ if (value != 1) ++ return -EINVAL; ++ ++ status = sb1_dgpu_sw_dsmcall(); ++ ++ return status < 0 ? status : len; ++} ++ ++static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ bool power; ++ int status; ++ ++ status = kstrtobool(buf, &power); ++ if (status < 0) ++ return status; ++ ++ if (power) ++ status = sb1_dgpu_sw_hgon(); ++ else ++ status = sb1_dgpu_sw_hgof(); ++ ++ return status < 0 ? status : len; ++} ++ ++static DEVICE_ATTR_WO(dgpu_dsmcall); ++static DEVICE_ATTR_WO(dgpu_power); ++ ++static struct attribute *sb1_dgpu_sw_attrs[] = { ++ &dev_attr_dgpu_dsmcall.attr, ++ &dev_attr_dgpu_power.attr, ++ NULL, ++}; ++ ++static const struct attribute_group sb1_dgpu_sw_attr_group = { ++ .attrs = sb1_dgpu_sw_attrs, ++}; ++ ++ ++static int sb1_dgpu_sw_probe(struct platform_device *pdev) ++{ ++ return sysfs_create_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group); ++} ++ ++static int sb1_dgpu_sw_remove(struct platform_device *pdev) ++{ ++ sysfs_remove_group(&pdev->dev.kobj, &sb1_dgpu_sw_attr_group); ++ return 0; ++} ++ ++/* ++ * The dGPU power seems to be actually handled by MSHW0040. However, that is ++ * also the power-/volume-button device with a mainline driver. So let's use ++ * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device. ++ */ ++static const struct acpi_device_id sb1_dgpu_sw_match[] = { ++ { "MSHW0041", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match); ++ ++static struct platform_driver sb1_dgpu_sw = { ++ .probe = sb1_dgpu_sw_probe, ++ .remove = sb1_dgpu_sw_remove, ++ .driver = { ++ .name = "sb1_dgpu_sw", ++ .acpi_match_table = sb1_dgpu_sw_match, ++ .probe_type = PROBE_PREFER_ASYNCHRONOUS, ++ }, ++}; ++module_platform_driver(sb1_dgpu_sw); ++ ++MODULE_AUTHOR("Maximilian Luz "); ++MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1"); ++MODULE_LICENSE("GPL"); -- 2.28.0 diff --git a/patches/5.8/0006-surface-gpe.patch b/patches/5.8/0006-surface-gpe.patch index dbcaf03b8..42fc0254f 100644 --- a/patches/5.8/0006-surface-gpe.patch +++ b/patches/5.8/0006-surface-gpe.patch @@ -1,4 +1,4 @@ -From d82b636e7d95da69503a18f55dea28503c23511a Mon Sep 17 00:00:00 2001 +From dfe3b509e98f3f8b1df867927490e3862655b7a4 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 16 Aug 2020 23:39:56 +0200 Subject: [PATCH 6/6] surface-gpe @@ -6,17 +6,17 @@ Subject: [PATCH 6/6] surface-gpe --- drivers/platform/x86/Kconfig | 9 + drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface_gpe.c | 302 +++++++++++++++++++++++++++++ - 3 files changed, 312 insertions(+) + drivers/platform/x86/surface_gpe.c | 299 +++++++++++++++++++++++++++++ + 3 files changed, 309 insertions(+) create mode 100644 drivers/platform/x86/surface_gpe.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 0581a54cf562f..2d0b403842396 100644 +index 14db2795ff638..c64267319696c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -879,6 +879,15 @@ config SURFACE_PRO3_BUTTON - help - This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. +@@ -886,6 +886,15 @@ config SURFACE_BOOK1_DGPU_SWITCH + This driver provides a sysfs switch to set the power-state of the + discrete GPU found on the Microsoft Surface Book 1. +config SURFACE_GPE + tristate "Surface GPE/Lid Driver" @@ -31,23 +31,23 @@ index 0581a54cf562f..2d0b403842396 100644 tristate "MSI Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 2b85852a1a872..a64ce216719ab 100644 +index 5156523b58639..ef0c3fcab3194 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile -@@ -85,6 +85,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o - obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o +@@ -86,6 +86,7 @@ obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o + obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += sb1_dgpu_sw.o +obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o # MSI obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o diff --git a/drivers/platform/x86/surface_gpe.c b/drivers/platform/x86/surface_gpe.c new file mode 100644 -index 0000000000000..3031a94cddeb7 +index 0000000000000..4c141fef7a5f4 --- /dev/null +++ b/drivers/platform/x86/surface_gpe.c -@@ -0,0 +1,302 @@ +@@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface GPE/Lid driver to enable wakeup from suspend via the lid by @@ -282,12 +282,9 @@ index 0000000000000..3031a94cddeb7 +{ + const struct dmi_system_id *match; + const struct surface_lid_device *lid; -+ + struct platform_device *pdev; + int status; + -+ surface_gpe_device = NULL; -+ + match = dmi_first_match(dmi_lid_device_table); + if (!match) { + pr_info(KBUILD_MODNAME": no device detected, exiting\n"); diff --git a/pkg/arch/kernel/PKGBUILD b/pkg/arch/kernel/PKGBUILD index 39b25ec99..c8fd31e54 100644 --- a/pkg/arch/kernel/PKGBUILD +++ b/pkg/arch/kernel/PKGBUILD @@ -38,14 +38,14 @@ validpgpkeys=( ) sha256sums=('d44dfae454432db7bdf8f0a12461626c35d5133da9a1445410c174395ac20c76' '181330a9cf4517abbbe29b93165bc859ad8ca14a43582f4e1d69aae2b5ecc2c9' - '28d8988615b71b80710cec7d110ad31d078bfd3cb61852c381ab8b6d8bc870b0' + '1f65b3f042db87952468e99be3f1f688f62dda18401bf9716cb734f5571288b5' '8dbaa21d2c03621b0c5d96c4fbcc7a839bea5a34a5f2279a409c3b404756e753' - '240312181b9049bb38cbc629d2f6ec2fccc6ebd2c9f9a417ce4dace4b1671cc2' - '17320989d284da8d45f7ef9db4b8acb0bbd660e334f4501b11391bf250a4d47f' - '6ae7a48aabf3cd19928c43c7fa89f311da21315efc5612e3e9f10e126488ae50' - '0c07cbe9bea55d8059c9f785e53b49019cf7e46eb3f866968c3da1425f370827' - 'c6776a89b3d8cd52d8f9920b6b7b2449f26f20b539f07e945ef90a01aa0a0f1b' - 'd1b9badf1ca432e6df7e6042a8a5fefa5b0ac6c65e1c122957db67b4f3f3aeab') + 'a4077505868f3ce12601948e01c42e6486d022681019702238c5ef2f73ce7b41' + 'db1a236a5f3ee524ae13c410561962a663660b33156d79a60bd71b4b5f094677' + '984be5bde800d60d1c64c7018a65b0931cd74335919b393429646685e18ce7bc' + '91c5bd34b2d9bbb981720296e81fd47914c327c1fc83a3501471f298a7b27c11' + '14bd1b3e74af2b6e60afae9bc008d59f789ffd4d1e9e9721ed0e950c2d48585a' + 'b07f659bdbf60a1992657cfc581fa3b4a97d1caca2c26d54f252f722e8ae6c46') export KBUILD_BUILD_HOST=archlinux