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-2022 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/of.h> 16 #include <linux/platform_device.h> 17 #include <linux/property.h> 18 #include <linux/types.h> 19 20 #include <linux/surface_aggregator/device.h> 21 22 23 /* -- Device registry. ------------------------------------------------------ */ 24 25 /* 26 * SSAM device names follow the SSAM module alias, meaning they are prefixed 27 * with 'ssam:', followed by domain, category, target ID, instance ID, and 28 * function, each encoded as two-digit hexadecimal, separated by ':'. In other 29 * words, it follows the scheme 30 * 31 * ssam:dd:cc:tt:ii:ff 32 * 33 * Where, 'dd', 'cc', 'tt', 'ii', and 'ff' are the two-digit hexadecimal 34 * values mentioned above, respectively. 35 */ 36 37 /* Root node. */ 38 static const struct software_node ssam_node_root = { 39 .name = "ssam_platform_hub", 40 }; 41 42 /* KIP device hub (connects keyboard cover devices on Surface Pro 8). */ 43 static const struct software_node ssam_node_hub_kip = { 44 .name = "ssam:00:00:01:0e:00", 45 .parent = &ssam_node_root, 46 }; 47 48 /* Base device hub (devices attached to Surface Book 3 base). */ 49 static const struct software_node ssam_node_hub_base = { 50 .name = "ssam:00:00:01:11:00", 51 .parent = &ssam_node_root, 52 }; 53 54 /* AC adapter. */ 55 static const struct software_node ssam_node_bat_ac = { 56 .name = "ssam:01:02:01:01:01", 57 .parent = &ssam_node_root, 58 }; 59 60 /* Primary battery. */ 61 static const struct software_node ssam_node_bat_main = { 62 .name = "ssam:01:02:01:01:00", 63 .parent = &ssam_node_root, 64 }; 65 66 /* Secondary battery (Surface Book 3). */ 67 static const struct software_node ssam_node_bat_sb3base = { 68 .name = "ssam:01:02:02:01:00", 69 .parent = &ssam_node_hub_base, 70 }; 71 72 /* Platform profile / performance-mode device without a fan. */ 73 static const struct software_node ssam_node_tmp_perf_profile = { 74 .name = "ssam:01:03:01:00:01", 75 .parent = &ssam_node_root, 76 }; 77 78 /* Platform profile / performance-mode device with a fan, such that 79 * the fan controller profile can also be switched. 80 */ 81 static const struct property_entry ssam_node_tmp_perf_profile_has_fan[] = { 82 PROPERTY_ENTRY_BOOL("has_fan"), 83 { } 84 }; 85 86 static const struct software_node ssam_node_tmp_perf_profile_with_fan = { 87 .name = "ssam:01:03:01:00:01", 88 .parent = &ssam_node_root, 89 .properties = ssam_node_tmp_perf_profile_has_fan, 90 }; 91 92 /* Thermal sensors. */ 93 static const struct software_node ssam_node_tmp_sensors = { 94 .name = "ssam:01:03:01:00:02", 95 .parent = &ssam_node_root, 96 }; 97 98 /* Fan speed function. */ 99 static const struct software_node ssam_node_fan_speed = { 100 .name = "ssam:01:05:01:01:01", 101 .parent = &ssam_node_root, 102 }; 103 104 /* Tablet-mode switch via KIP subsystem. */ 105 static const struct software_node ssam_node_kip_tablet_switch = { 106 .name = "ssam:01:0e:01:00:01", 107 .parent = &ssam_node_root, 108 }; 109 110 /* DTX / detachment-system device (Surface Book 3). */ 111 static const struct software_node ssam_node_bas_dtx = { 112 .name = "ssam:01:11:01:00:00", 113 .parent = &ssam_node_root, 114 }; 115 116 /* HID keyboard (SAM, TID=1). */ 117 static const struct software_node ssam_node_hid_sam_keyboard = { 118 .name = "ssam:01:15:01:01:00", 119 .parent = &ssam_node_root, 120 }; 121 122 /* HID pen stash (SAM, TID=1; pen taken / stashed away evens). */ 123 static const struct software_node ssam_node_hid_sam_penstash = { 124 .name = "ssam:01:15:01:02:00", 125 .parent = &ssam_node_root, 126 }; 127 128 /* HID touchpad (SAM, TID=1). */ 129 static const struct software_node ssam_node_hid_sam_touchpad = { 130 .name = "ssam:01:15:01:03:00", 131 .parent = &ssam_node_root, 132 }; 133 134 /* HID device instance 6 (SAM, TID=1, HID sensor collection). */ 135 static const struct software_node ssam_node_hid_sam_sensors = { 136 .name = "ssam:01:15:01:06:00", 137 .parent = &ssam_node_root, 138 }; 139 140 /* HID device instance 7 (SAM, TID=1, UCM UCSI HID client). */ 141 static const struct software_node ssam_node_hid_sam_ucm_ucsi = { 142 .name = "ssam:01:15:01:07:00", 143 .parent = &ssam_node_root, 144 }; 145 146 /* HID system controls (SAM, TID=1). */ 147 static const struct software_node ssam_node_hid_sam_sysctrl = { 148 .name = "ssam:01:15:01:08:00", 149 .parent = &ssam_node_root, 150 }; 151 152 /* HID keyboard. */ 153 static const struct software_node ssam_node_hid_main_keyboard = { 154 .name = "ssam:01:15:02:01:00", 155 .parent = &ssam_node_root, 156 }; 157 158 /* HID touchpad. */ 159 static const struct software_node ssam_node_hid_main_touchpad = { 160 .name = "ssam:01:15:02:03:00", 161 .parent = &ssam_node_root, 162 }; 163 164 /* HID device instance 5 (unknown HID device). */ 165 static const struct software_node ssam_node_hid_main_iid5 = { 166 .name = "ssam:01:15:02:05:00", 167 .parent = &ssam_node_root, 168 }; 169 170 /* HID keyboard (base hub). */ 171 static const struct software_node ssam_node_hid_base_keyboard = { 172 .name = "ssam:01:15:02:01:00", 173 .parent = &ssam_node_hub_base, 174 }; 175 176 /* HID touchpad (base hub). */ 177 static const struct software_node ssam_node_hid_base_touchpad = { 178 .name = "ssam:01:15:02:03:00", 179 .parent = &ssam_node_hub_base, 180 }; 181 182 /* HID device instance 5 (unknown HID device, base hub). */ 183 static const struct software_node ssam_node_hid_base_iid5 = { 184 .name = "ssam:01:15:02:05:00", 185 .parent = &ssam_node_hub_base, 186 }; 187 188 /* HID device instance 6 (unknown HID device, base hub). */ 189 static const struct software_node ssam_node_hid_base_iid6 = { 190 .name = "ssam:01:15:02:06:00", 191 .parent = &ssam_node_hub_base, 192 }; 193 194 /* HID keyboard (KIP hub). */ 195 static const struct software_node ssam_node_hid_kip_keyboard = { 196 .name = "ssam:01:15:02:01:00", 197 .parent = &ssam_node_hub_kip, 198 }; 199 200 /* HID pen stash (KIP hub; pen taken / stashed away evens). */ 201 static const struct software_node ssam_node_hid_kip_penstash = { 202 .name = "ssam:01:15:02:02:00", 203 .parent = &ssam_node_hub_kip, 204 }; 205 206 /* HID touchpad (KIP hub). */ 207 static const struct software_node ssam_node_hid_kip_touchpad = { 208 .name = "ssam:01:15:02:03:00", 209 .parent = &ssam_node_hub_kip, 210 }; 211 212 /* HID device instance 5 (KIP hub, type-cover firmware update). */ 213 static const struct software_node ssam_node_hid_kip_fwupd = { 214 .name = "ssam:01:15:02:05:00", 215 .parent = &ssam_node_hub_kip, 216 }; 217 218 /* Tablet-mode switch via POS subsystem. */ 219 static const struct software_node ssam_node_pos_tablet_switch = { 220 .name = "ssam:01:26:01:00:01", 221 .parent = &ssam_node_root, 222 }; 223 224 /* 225 * Devices for 5th- and 6th-generations models: 226 * - Surface Book 2, 227 * - Surface Laptop 1 and 2, 228 * - Surface Pro 5 and 6. 229 */ 230 static const struct software_node *ssam_node_group_gen5[] = { 231 &ssam_node_root, 232 &ssam_node_tmp_perf_profile, 233 NULL, 234 }; 235 236 /* Devices for Surface Book 3. */ 237 static const struct software_node *ssam_node_group_sb3[] = { 238 &ssam_node_root, 239 &ssam_node_hub_base, 240 &ssam_node_bat_ac, 241 &ssam_node_bat_main, 242 &ssam_node_bat_sb3base, 243 &ssam_node_tmp_perf_profile, 244 &ssam_node_bas_dtx, 245 &ssam_node_hid_base_keyboard, 246 &ssam_node_hid_base_touchpad, 247 &ssam_node_hid_base_iid5, 248 &ssam_node_hid_base_iid6, 249 NULL, 250 }; 251 252 /* Devices for Surface Laptop 3 and 4. */ 253 static const struct software_node *ssam_node_group_sl3[] = { 254 &ssam_node_root, 255 &ssam_node_bat_ac, 256 &ssam_node_bat_main, 257 &ssam_node_tmp_perf_profile, 258 &ssam_node_hid_main_keyboard, 259 &ssam_node_hid_main_touchpad, 260 &ssam_node_hid_main_iid5, 261 NULL, 262 }; 263 264 /* Devices for Surface Laptop 5. */ 265 static const struct software_node *ssam_node_group_sl5[] = { 266 &ssam_node_root, 267 &ssam_node_bat_ac, 268 &ssam_node_bat_main, 269 &ssam_node_tmp_perf_profile_with_fan, 270 &ssam_node_tmp_sensors, 271 &ssam_node_fan_speed, 272 &ssam_node_hid_main_keyboard, 273 &ssam_node_hid_main_touchpad, 274 &ssam_node_hid_main_iid5, 275 &ssam_node_hid_sam_ucm_ucsi, 276 NULL, 277 }; 278 279 /* Devices for Surface Laptop 6. */ 280 static const struct software_node *ssam_node_group_sl6[] = { 281 &ssam_node_root, 282 &ssam_node_bat_ac, 283 &ssam_node_bat_main, 284 &ssam_node_tmp_perf_profile_with_fan, 285 &ssam_node_tmp_sensors, 286 &ssam_node_fan_speed, 287 &ssam_node_hid_main_keyboard, 288 &ssam_node_hid_main_touchpad, 289 &ssam_node_hid_main_iid5, 290 &ssam_node_hid_sam_sensors, 291 &ssam_node_hid_sam_ucm_ucsi, 292 NULL, 293 }; 294 295 /* Devices for Surface Laptop 7. */ 296 static const struct software_node *ssam_node_group_sl7[] = { 297 &ssam_node_root, 298 &ssam_node_bat_ac, 299 &ssam_node_bat_main, 300 &ssam_node_tmp_perf_profile_with_fan, 301 &ssam_node_fan_speed, 302 &ssam_node_hid_sam_keyboard, 303 /* TODO: evaluate thermal sensors devices when we get a driver for that */ 304 NULL, 305 }; 306 307 /* Devices for Surface Laptop Studio 1. */ 308 static const struct software_node *ssam_node_group_sls1[] = { 309 &ssam_node_root, 310 &ssam_node_bat_ac, 311 &ssam_node_bat_main, 312 &ssam_node_tmp_perf_profile, 313 &ssam_node_pos_tablet_switch, 314 &ssam_node_hid_sam_keyboard, 315 &ssam_node_hid_sam_penstash, 316 &ssam_node_hid_sam_touchpad, 317 &ssam_node_hid_sam_sensors, 318 &ssam_node_hid_sam_ucm_ucsi, 319 &ssam_node_hid_sam_sysctrl, 320 NULL, 321 }; 322 323 /* Devices for Surface Laptop Studio 2. */ 324 static const struct software_node *ssam_node_group_sls2[] = { 325 &ssam_node_root, 326 &ssam_node_bat_ac, 327 &ssam_node_bat_main, 328 &ssam_node_tmp_perf_profile_with_fan, 329 &ssam_node_tmp_sensors, 330 &ssam_node_fan_speed, 331 &ssam_node_pos_tablet_switch, 332 &ssam_node_hid_sam_keyboard, 333 &ssam_node_hid_sam_penstash, 334 &ssam_node_hid_sam_sensors, 335 &ssam_node_hid_sam_ucm_ucsi, 336 NULL, 337 }; 338 339 /* Devices for Surface Laptop Go. */ 340 static const struct software_node *ssam_node_group_slg1[] = { 341 &ssam_node_root, 342 &ssam_node_bat_ac, 343 &ssam_node_bat_main, 344 &ssam_node_tmp_perf_profile, 345 NULL, 346 }; 347 348 /* Devices for Surface Pro 7 and Surface Pro 7+. */ 349 static const struct software_node *ssam_node_group_sp7[] = { 350 &ssam_node_root, 351 &ssam_node_bat_ac, 352 &ssam_node_bat_main, 353 &ssam_node_tmp_perf_profile, 354 NULL, 355 }; 356 357 /* Devices for Surface Pro 8 */ 358 static const struct software_node *ssam_node_group_sp8[] = { 359 &ssam_node_root, 360 &ssam_node_hub_kip, 361 &ssam_node_bat_ac, 362 &ssam_node_bat_main, 363 &ssam_node_tmp_perf_profile, 364 &ssam_node_kip_tablet_switch, 365 &ssam_node_hid_kip_keyboard, 366 &ssam_node_hid_kip_penstash, 367 &ssam_node_hid_kip_touchpad, 368 &ssam_node_hid_kip_fwupd, 369 &ssam_node_hid_sam_sensors, 370 &ssam_node_hid_sam_ucm_ucsi, 371 NULL, 372 }; 373 374 /* Devices for Surface Pro 9 (Intel/x86) and 10 */ 375 static const struct software_node *ssam_node_group_sp9[] = { 376 &ssam_node_root, 377 &ssam_node_hub_kip, 378 &ssam_node_bat_ac, 379 &ssam_node_bat_main, 380 &ssam_node_tmp_perf_profile_with_fan, 381 &ssam_node_tmp_sensors, 382 &ssam_node_fan_speed, 383 &ssam_node_pos_tablet_switch, 384 &ssam_node_hid_kip_keyboard, 385 &ssam_node_hid_kip_penstash, 386 &ssam_node_hid_kip_touchpad, 387 &ssam_node_hid_kip_fwupd, 388 &ssam_node_hid_sam_sensors, 389 &ssam_node_hid_sam_ucm_ucsi, 390 NULL, 391 }; 392 393 /* Devices for Surface Pro 9 5G (ARM/QCOM) */ 394 static const struct software_node *ssam_node_group_sp9_5g[] = { 395 &ssam_node_root, 396 &ssam_node_hub_kip, 397 &ssam_node_bat_ac, 398 &ssam_node_bat_main, 399 &ssam_node_tmp_sensors, 400 &ssam_node_hid_kip_keyboard, 401 &ssam_node_hid_kip_penstash, 402 &ssam_node_hid_kip_touchpad, 403 &ssam_node_hid_kip_fwupd, 404 &ssam_node_hid_sam_sensors, 405 &ssam_node_kip_tablet_switch, 406 NULL, 407 }; 408 409 /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ 410 411 static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { 412 /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */ 413 { "MSHW0081", (unsigned long)ssam_node_group_gen5 }, 414 415 /* Surface Pro 6 (OMBR >= 0x10) */ 416 { "MSHW0111", (unsigned long)ssam_node_group_gen5 }, 417 418 /* Surface Pro 7 */ 419 { "MSHW0116", (unsigned long)ssam_node_group_sp7 }, 420 421 /* Surface Pro 7+ */ 422 { "MSHW0119", (unsigned long)ssam_node_group_sp7 }, 423 424 /* Surface Pro 8 */ 425 { "MSHW0263", (unsigned long)ssam_node_group_sp8 }, 426 427 /* Surface Pro 9 */ 428 { "MSHW0343", (unsigned long)ssam_node_group_sp9 }, 429 430 /* Surface Pro 10 */ 431 { "MSHW0510", (unsigned long)ssam_node_group_sp9 }, 432 433 /* Surface Book 2 */ 434 { "MSHW0107", (unsigned long)ssam_node_group_gen5 }, 435 436 /* Surface Book 3 */ 437 { "MSHW0117", (unsigned long)ssam_node_group_sb3 }, 438 439 /* Surface Laptop 1 */ 440 { "MSHW0086", (unsigned long)ssam_node_group_gen5 }, 441 442 /* Surface Laptop 2 */ 443 { "MSHW0112", (unsigned long)ssam_node_group_gen5 }, 444 445 /* Surface Laptop 3 (13", Intel) */ 446 { "MSHW0114", (unsigned long)ssam_node_group_sl3 }, 447 448 /* Surface Laptop 3 (15", AMD) and 4 (15", AMD) */ 449 { "MSHW0110", (unsigned long)ssam_node_group_sl3 }, 450 451 /* Surface Laptop 4 (13", Intel) */ 452 { "MSHW0250", (unsigned long)ssam_node_group_sl3 }, 453 454 /* Surface Laptop 5 */ 455 { "MSHW0350", (unsigned long)ssam_node_group_sl5 }, 456 457 /* Surface Laptop 6 */ 458 { "MSHW0530", (unsigned long)ssam_node_group_sl6 }, 459 460 /* Surface Laptop Go 1 */ 461 { "MSHW0118", (unsigned long)ssam_node_group_slg1 }, 462 463 /* Surface Laptop Go 2 */ 464 { "MSHW0290", (unsigned long)ssam_node_group_slg1 }, 465 466 /* Surface Laptop Go 3 */ 467 { "MSHW0440", (unsigned long)ssam_node_group_slg1 }, 468 469 /* Surface Laptop Studio 1 */ 470 { "MSHW0123", (unsigned long)ssam_node_group_sls1 }, 471 472 /* Surface Laptop Studio 2 */ 473 { "MSHW0360", (unsigned long)ssam_node_group_sls2 }, 474 475 { }, 476 }; 477 MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_acpi_match); 478 479 static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = { 480 /* Surface Pro 9 5G (ARM/QCOM) */ 481 { .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g }, 482 /* Surface Laptop 7 */ 483 { .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 }, 484 { .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 }, 485 { }, 486 }; 487 488 static int ssam_platform_hub_probe(struct platform_device *pdev) 489 { 490 const struct software_node **nodes; 491 const struct of_device_id *match; 492 struct device_node *fdt_root; 493 struct ssam_controller *ctrl; 494 struct fwnode_handle *root; 495 int status; 496 497 nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev); 498 if (!nodes) { 499 fdt_root = of_find_node_by_path("/"); 500 if (!fdt_root) 501 return -ENODEV; 502 503 match = of_match_node(ssam_platform_hub_of_match, fdt_root); 504 of_node_put(fdt_root); 505 if (!match) 506 return -ENODEV; 507 508 nodes = (const struct software_node **)match->data; 509 if (!nodes) 510 return -ENODEV; 511 } 512 513 /* 514 * As we're adding the SSAM client devices as children under this device 515 * and not the SSAM controller, we need to add a device link to the 516 * controller to ensure that we remove all of our devices before the 517 * controller is removed. This also guarantees proper ordering for 518 * suspend/resume of the devices on this hub. 519 */ 520 ctrl = ssam_client_bind(&pdev->dev); 521 if (IS_ERR(ctrl)) 522 return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); 523 524 status = software_node_register_node_group(nodes); 525 if (status) 526 return status; 527 528 root = software_node_fwnode(&ssam_node_root); 529 if (!root) { 530 software_node_unregister_node_group(nodes); 531 return -ENOENT; 532 } 533 534 set_secondary_fwnode(&pdev->dev, root); 535 536 status = __ssam_register_clients(&pdev->dev, ctrl, root); 537 if (status) { 538 set_secondary_fwnode(&pdev->dev, NULL); 539 software_node_unregister_node_group(nodes); 540 } 541 542 platform_set_drvdata(pdev, nodes); 543 return status; 544 } 545 546 static void ssam_platform_hub_remove(struct platform_device *pdev) 547 { 548 const struct software_node **nodes = platform_get_drvdata(pdev); 549 550 ssam_remove_clients(&pdev->dev); 551 set_secondary_fwnode(&pdev->dev, NULL); 552 software_node_unregister_node_group(nodes); 553 } 554 555 static struct platform_driver ssam_platform_hub_driver = { 556 .probe = ssam_platform_hub_probe, 557 .remove_new = ssam_platform_hub_remove, 558 .driver = { 559 .name = "surface_aggregator_platform_hub", 560 .acpi_match_table = ssam_platform_hub_acpi_match, 561 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 562 }, 563 }; 564 module_platform_driver(ssam_platform_hub_driver); 565 566 MODULE_ALIAS("platform:surface_aggregator_platform_hub"); 567 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 568 MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module"); 569 MODULE_LICENSE("GPL"); 570