surface_aggregator_registry.c 15 KB

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