Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/surface: Set up Surface Aggregator device registry

The Surface System Aggregator Module (SSAM) subsystem provides various
functionalities, which are separated by spreading them across multiple
devices and corresponding drivers. Parts of that functionality / some of
those devices, however, can (as far as we currently know) not be
auto-detected by conventional means. While older (specifically 5th- and
6th-)generation models do advertise most of their functionality via
standard platform devices in ACPI, newer generations do not.

As we are currently also not aware of any feasible way to query said
functionalities dynamically, this poses a problem. There is, however, a
device in ACPI that seems to be used by Windows for identifying
different Surface models: The Windows Surface Integration Device (WSID).
This device seems to have a HID corresponding to the overall set of
functionalities SSAM provides for the associated model.

This commit introduces a registry providing non-detectable device
information via software nodes. In addition, a SSAM platform hub driver
is introduced, which takes care of creating and managing the SSAM
devices specified in this registry. This approach allows for a
hierarchical setup akin to ACPI and is easily extendable, e.g. via
firmware node properties.

Note that this commit only provides the basis for the platform hub and
registry, and does not add any content to it. The registry will be
expanded in subsequent commits.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
Link: https://lore.kernel.org/r/20210212115439.1525216-2-luzmaximilian@gmail.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Maximilian Luz and committed by
Hans de Goede
fc622b3d a38fd874

+313
+1
MAINTAINERS
··· 11897 11897 F: drivers/platform/surface/aggregator/ 11898 11898 F: drivers/platform/surface/surface_acpi_notify.c 11899 11899 F: drivers/platform/surface/surface_aggregator_cdev.c 11900 + F: drivers/platform/surface/surface_aggregator_registry.c 11900 11901 F: include/linux/surface_acpi_notify.h 11901 11902 F: include/linux/surface_aggregator/ 11902 11903 F: include/uapi/linux/surface_aggregator/
+27
drivers/platform/surface/Kconfig
··· 77 77 The provided interface is intended for debugging and development only, 78 78 and should not be used otherwise. 79 79 80 + config SURFACE_AGGREGATOR_REGISTRY 81 + tristate "Surface System Aggregator Module Device Registry" 82 + depends on SURFACE_AGGREGATOR 83 + depends on SURFACE_AGGREGATOR_BUS 84 + help 85 + Device-registry and device-hubs for Surface System Aggregator Module 86 + (SSAM) devices. 87 + 88 + Provides a module and driver which act as a device-registry for SSAM 89 + client devices that cannot be detected automatically, e.g. via ACPI. 90 + Such devices are instead provided via this registry and attached via 91 + device hubs, also provided in this module. 92 + 93 + Devices provided via this registry are: 94 + - Platform profile (performance-/cooling-mode) device (5th- and later 95 + generations). 96 + - Battery/AC devices (7th-generation). 97 + - HID input devices (7th-generation). 98 + 99 + Select M (recommended) or Y here if you want support for the above 100 + mentioned devices on the corresponding Surface models. Without this 101 + module, the respective devices will not be instantiated and thus any 102 + functionality provided by them will be missing, even when drivers for 103 + these devices are present. In other words, this module only provides 104 + the respective client devices. Drivers for these devices still need to 105 + be selected via the other options. 106 + 80 107 config SURFACE_GPE 81 108 tristate "Surface GPE/Lid Support Driver" 82 109 depends on DMI
+1
drivers/platform/surface/Makefile
··· 10 10 obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o 11 11 obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/ 12 12 obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o 13 + obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o 13 14 obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o 14 15 obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o 15 16 obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
+284
drivers/platform/surface/surface_aggregator_registry.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Surface System Aggregator Module (SSAM) client device registry. 4 + * 5 + * Registry for non-platform/non-ACPI SSAM client devices, i.e. devices that 6 + * cannot be auto-detected. Provides device-hubs and performs instantiation 7 + * for these devices. 8 + * 9 + * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com> 10 + */ 11 + 12 + #include <linux/acpi.h> 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/property.h> 17 + 18 + #include <linux/surface_aggregator/controller.h> 19 + #include <linux/surface_aggregator/device.h> 20 + 21 + 22 + /* -- Device registry. ------------------------------------------------------ */ 23 + 24 + /* 25 + * SSAM device names follow the SSAM module alias, meaning they are prefixed 26 + * with 'ssam:', followed by domain, category, target ID, instance ID, and 27 + * function, each encoded as two-digit hexadecimal, separated by ':'. In other 28 + * words, it follows the scheme 29 + * 30 + * ssam:dd:cc:tt:ii:ff 31 + * 32 + * Where, 'dd', 'cc', 'tt', 'ii', and 'ff' are the two-digit hexadecimal 33 + * values mentioned above, respectively. 34 + */ 35 + 36 + /* Root node. */ 37 + static const struct software_node ssam_node_root = { 38 + .name = "ssam_platform_hub", 39 + }; 40 + 41 + /* Devices for Surface Book 2. */ 42 + static const struct software_node *ssam_node_group_sb2[] = { 43 + &ssam_node_root, 44 + NULL, 45 + }; 46 + 47 + /* Devices for Surface Book 3. */ 48 + static const struct software_node *ssam_node_group_sb3[] = { 49 + &ssam_node_root, 50 + NULL, 51 + }; 52 + 53 + /* Devices for Surface Laptop 1. */ 54 + static const struct software_node *ssam_node_group_sl1[] = { 55 + &ssam_node_root, 56 + NULL, 57 + }; 58 + 59 + /* Devices for Surface Laptop 2. */ 60 + static const struct software_node *ssam_node_group_sl2[] = { 61 + &ssam_node_root, 62 + NULL, 63 + }; 64 + 65 + /* Devices for Surface Laptop 3. */ 66 + static const struct software_node *ssam_node_group_sl3[] = { 67 + &ssam_node_root, 68 + NULL, 69 + }; 70 + 71 + /* Devices for Surface Laptop Go. */ 72 + static const struct software_node *ssam_node_group_slg1[] = { 73 + &ssam_node_root, 74 + NULL, 75 + }; 76 + 77 + /* Devices for Surface Pro 5. */ 78 + static const struct software_node *ssam_node_group_sp5[] = { 79 + &ssam_node_root, 80 + NULL, 81 + }; 82 + 83 + /* Devices for Surface Pro 6. */ 84 + static const struct software_node *ssam_node_group_sp6[] = { 85 + &ssam_node_root, 86 + NULL, 87 + }; 88 + 89 + /* Devices for Surface Pro 7. */ 90 + static const struct software_node *ssam_node_group_sp7[] = { 91 + &ssam_node_root, 92 + NULL, 93 + }; 94 + 95 + 96 + /* -- Device registry helper functions. ------------------------------------- */ 97 + 98 + static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid) 99 + { 100 + u8 d, tc, tid, iid, fn; 101 + int n; 102 + 103 + n = sscanf(str, "ssam:%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn); 104 + if (n != 5) 105 + return -EINVAL; 106 + 107 + uid->domain = d; 108 + uid->category = tc; 109 + uid->target = tid; 110 + uid->instance = iid; 111 + uid->function = fn; 112 + 113 + return 0; 114 + } 115 + 116 + static int ssam_hub_remove_devices_fn(struct device *dev, void *data) 117 + { 118 + if (!is_ssam_device(dev)) 119 + return 0; 120 + 121 + ssam_device_remove(to_ssam_device(dev)); 122 + return 0; 123 + } 124 + 125 + static void ssam_hub_remove_devices(struct device *parent) 126 + { 127 + device_for_each_child_reverse(parent, NULL, ssam_hub_remove_devices_fn); 128 + } 129 + 130 + static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl, 131 + struct fwnode_handle *node) 132 + { 133 + struct ssam_device_uid uid; 134 + struct ssam_device *sdev; 135 + int status; 136 + 137 + status = ssam_uid_from_string(fwnode_get_name(node), &uid); 138 + if (status) 139 + return status; 140 + 141 + sdev = ssam_device_alloc(ctrl, uid); 142 + if (!sdev) 143 + return -ENOMEM; 144 + 145 + sdev->dev.parent = parent; 146 + sdev->dev.fwnode = node; 147 + 148 + status = ssam_device_add(sdev); 149 + if (status) 150 + ssam_device_put(sdev); 151 + 152 + return status; 153 + } 154 + 155 + static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *ctrl, 156 + struct fwnode_handle *node) 157 + { 158 + struct fwnode_handle *child; 159 + int status; 160 + 161 + fwnode_for_each_child_node(node, child) { 162 + /* 163 + * Try to add the device specified in the firmware node. If 164 + * this fails with -EINVAL, the node does not specify any SSAM 165 + * device, so ignore it and continue with the next one. 166 + */ 167 + 168 + status = ssam_hub_add_device(parent, ctrl, child); 169 + if (status && status != -EINVAL) 170 + goto err; 171 + } 172 + 173 + return 0; 174 + err: 175 + ssam_hub_remove_devices(parent); 176 + return status; 177 + } 178 + 179 + 180 + /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ 181 + 182 + static const struct acpi_device_id ssam_platform_hub_match[] = { 183 + /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */ 184 + { "MSHW0081", (unsigned long)ssam_node_group_sp5 }, 185 + 186 + /* Surface Pro 6 (OMBR >= 0x10) */ 187 + { "MSHW0111", (unsigned long)ssam_node_group_sp6 }, 188 + 189 + /* Surface Pro 7 */ 190 + { "MSHW0116", (unsigned long)ssam_node_group_sp7 }, 191 + 192 + /* Surface Book 2 */ 193 + { "MSHW0107", (unsigned long)ssam_node_group_sb2 }, 194 + 195 + /* Surface Book 3 */ 196 + { "MSHW0117", (unsigned long)ssam_node_group_sb3 }, 197 + 198 + /* Surface Laptop 1 */ 199 + { "MSHW0086", (unsigned long)ssam_node_group_sl1 }, 200 + 201 + /* Surface Laptop 2 */ 202 + { "MSHW0112", (unsigned long)ssam_node_group_sl2 }, 203 + 204 + /* Surface Laptop 3 (13", Intel) */ 205 + { "MSHW0114", (unsigned long)ssam_node_group_sl3 }, 206 + 207 + /* Surface Laptop 3 (15", AMD) */ 208 + { "MSHW0110", (unsigned long)ssam_node_group_sl3 }, 209 + 210 + /* Surface Laptop Go 1 */ 211 + { "MSHW0118", (unsigned long)ssam_node_group_slg1 }, 212 + 213 + { }, 214 + }; 215 + MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match); 216 + 217 + static int ssam_platform_hub_probe(struct platform_device *pdev) 218 + { 219 + const struct software_node **nodes; 220 + struct ssam_controller *ctrl; 221 + struct fwnode_handle *root; 222 + int status; 223 + 224 + nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev); 225 + if (!nodes) 226 + return -ENODEV; 227 + 228 + /* 229 + * As we're adding the SSAM client devices as children under this device 230 + * and not the SSAM controller, we need to add a device link to the 231 + * controller to ensure that we remove all of our devices before the 232 + * controller is removed. This also guarantees proper ordering for 233 + * suspend/resume of the devices on this hub. 234 + */ 235 + ctrl = ssam_client_bind(&pdev->dev); 236 + if (IS_ERR(ctrl)) 237 + return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); 238 + 239 + status = software_node_register_node_group(nodes); 240 + if (status) 241 + return status; 242 + 243 + root = software_node_fwnode(&ssam_node_root); 244 + if (!root) { 245 + software_node_unregister_node_group(nodes); 246 + return -ENOENT; 247 + } 248 + 249 + set_secondary_fwnode(&pdev->dev, root); 250 + 251 + status = ssam_hub_add_devices(&pdev->dev, ctrl, root); 252 + if (status) { 253 + set_secondary_fwnode(&pdev->dev, NULL); 254 + software_node_unregister_node_group(nodes); 255 + } 256 + 257 + platform_set_drvdata(pdev, nodes); 258 + return status; 259 + } 260 + 261 + static int ssam_platform_hub_remove(struct platform_device *pdev) 262 + { 263 + const struct software_node **nodes = platform_get_drvdata(pdev); 264 + 265 + ssam_hub_remove_devices(&pdev->dev); 266 + set_secondary_fwnode(&pdev->dev, NULL); 267 + software_node_unregister_node_group(nodes); 268 + return 0; 269 + } 270 + 271 + static struct platform_driver ssam_platform_hub_driver = { 272 + .probe = ssam_platform_hub_probe, 273 + .remove = ssam_platform_hub_remove, 274 + .driver = { 275 + .name = "surface_aggregator_platform_hub", 276 + .acpi_match_table = ssam_platform_hub_match, 277 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 278 + }, 279 + }; 280 + module_platform_driver(ssam_platform_hub_driver); 281 + 282 + MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 283 + MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module"); 284 + MODULE_LICENSE("GPL");