Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Surface System Aggregator Module (SSAM) client device registry.
0004  *
0005  * Registry for non-platform/non-ACPI SSAM client devices, i.e. devices that
0006  * cannot be auto-detected. Provides device-hubs and performs instantiation
0007  * for these devices.
0008  *
0009  * Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
0010  */
0011 
0012 #include <linux/acpi.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/property.h>
0017 #include <linux/types.h>
0018 
0019 #include <linux/surface_aggregator/device.h>
0020 
0021 
0022 /* -- Device registry. ------------------------------------------------------ */
0023 
0024 /*
0025  * SSAM device names follow the SSAM module alias, meaning they are prefixed
0026  * with 'ssam:', followed by domain, category, target ID, instance ID, and
0027  * function, each encoded as two-digit hexadecimal, separated by ':'. In other
0028  * words, it follows the scheme
0029  *
0030  *      ssam:dd:cc:tt:ii:ff
0031  *
0032  * Where, 'dd', 'cc', 'tt', 'ii', and 'ff' are the two-digit hexadecimal
0033  * values mentioned above, respectively.
0034  */
0035 
0036 /* Root node. */
0037 static const struct software_node ssam_node_root = {
0038     .name = "ssam_platform_hub",
0039 };
0040 
0041 /* KIP device hub (connects keyboard cover devices on Surface Pro 8). */
0042 static const struct software_node ssam_node_hub_kip = {
0043     .name = "ssam:00:00:01:0e:00",
0044     .parent = &ssam_node_root,
0045 };
0046 
0047 /* Base device hub (devices attached to Surface Book 3 base). */
0048 static const struct software_node ssam_node_hub_base = {
0049     .name = "ssam:00:00:02:11:00",
0050     .parent = &ssam_node_root,
0051 };
0052 
0053 /* AC adapter. */
0054 static const struct software_node ssam_node_bat_ac = {
0055     .name = "ssam:01:02:01:01:01",
0056     .parent = &ssam_node_root,
0057 };
0058 
0059 /* Primary battery. */
0060 static const struct software_node ssam_node_bat_main = {
0061     .name = "ssam:01:02:01:01:00",
0062     .parent = &ssam_node_root,
0063 };
0064 
0065 /* Secondary battery (Surface Book 3). */
0066 static const struct software_node ssam_node_bat_sb3base = {
0067     .name = "ssam:01:02:02:01:00",
0068     .parent = &ssam_node_hub_base,
0069 };
0070 
0071 /* Platform profile / performance-mode device. */
0072 static const struct software_node ssam_node_tmp_pprof = {
0073     .name = "ssam:01:03:01:00:01",
0074     .parent = &ssam_node_root,
0075 };
0076 
0077 /* Tablet-mode switch via KIP subsystem. */
0078 static const struct software_node ssam_node_kip_tablet_switch = {
0079     .name = "ssam:01:0e:01:00:01",
0080     .parent = &ssam_node_root,
0081 };
0082 
0083 /* DTX / detachment-system device (Surface Book 3). */
0084 static const struct software_node ssam_node_bas_dtx = {
0085     .name = "ssam:01:11:01:00:00",
0086     .parent = &ssam_node_root,
0087 };
0088 
0089 /* HID keyboard (SAM, TID=1). */
0090 static const struct software_node ssam_node_hid_sam_keyboard = {
0091     .name = "ssam:01:15:01:01:00",
0092     .parent = &ssam_node_root,
0093 };
0094 
0095 /* HID pen stash (SAM, TID=1; pen taken / stashed away evens). */
0096 static const struct software_node ssam_node_hid_sam_penstash = {
0097     .name = "ssam:01:15:01:02:00",
0098     .parent = &ssam_node_root,
0099 };
0100 
0101 /* HID touchpad (SAM, TID=1). */
0102 static const struct software_node ssam_node_hid_sam_touchpad = {
0103     .name = "ssam:01:15:01:03:00",
0104     .parent = &ssam_node_root,
0105 };
0106 
0107 /* HID device instance 6 (SAM, TID=1, HID sensor collection). */
0108 static const struct software_node ssam_node_hid_sam_sensors = {
0109     .name = "ssam:01:15:01:06:00",
0110     .parent = &ssam_node_root,
0111 };
0112 
0113 /* HID device instance 7 (SAM, TID=1, UCM UCSI HID client). */
0114 static const struct software_node ssam_node_hid_sam_ucm_ucsi = {
0115     .name = "ssam:01:15:01:07:00",
0116     .parent = &ssam_node_root,
0117 };
0118 
0119 /* HID system controls (SAM, TID=1). */
0120 static const struct software_node ssam_node_hid_sam_sysctrl = {
0121     .name = "ssam:01:15:01:08:00",
0122     .parent = &ssam_node_root,
0123 };
0124 
0125 /* HID keyboard. */
0126 static const struct software_node ssam_node_hid_main_keyboard = {
0127     .name = "ssam:01:15:02:01:00",
0128     .parent = &ssam_node_root,
0129 };
0130 
0131 /* HID touchpad. */
0132 static const struct software_node ssam_node_hid_main_touchpad = {
0133     .name = "ssam:01:15:02:03:00",
0134     .parent = &ssam_node_root,
0135 };
0136 
0137 /* HID device instance 5 (unknown HID device). */
0138 static const struct software_node ssam_node_hid_main_iid5 = {
0139     .name = "ssam:01:15:02:05:00",
0140     .parent = &ssam_node_root,
0141 };
0142 
0143 /* HID keyboard (base hub). */
0144 static const struct software_node ssam_node_hid_base_keyboard = {
0145     .name = "ssam:01:15:02:01:00",
0146     .parent = &ssam_node_hub_base,
0147 };
0148 
0149 /* HID touchpad (base hub). */
0150 static const struct software_node ssam_node_hid_base_touchpad = {
0151     .name = "ssam:01:15:02:03:00",
0152     .parent = &ssam_node_hub_base,
0153 };
0154 
0155 /* HID device instance 5 (unknown HID device, base hub). */
0156 static const struct software_node ssam_node_hid_base_iid5 = {
0157     .name = "ssam:01:15:02:05:00",
0158     .parent = &ssam_node_hub_base,
0159 };
0160 
0161 /* HID device instance 6 (unknown HID device, base hub). */
0162 static const struct software_node ssam_node_hid_base_iid6 = {
0163     .name = "ssam:01:15:02:06:00",
0164     .parent = &ssam_node_hub_base,
0165 };
0166 
0167 /* HID keyboard (KIP hub). */
0168 static const struct software_node ssam_node_hid_kip_keyboard = {
0169     .name = "ssam:01:15:02:01:00",
0170     .parent = &ssam_node_hub_kip,
0171 };
0172 
0173 /* HID pen stash (KIP hub; pen taken / stashed away evens). */
0174 static const struct software_node ssam_node_hid_kip_penstash = {
0175     .name = "ssam:01:15:02:02:00",
0176     .parent = &ssam_node_hub_kip,
0177 };
0178 
0179 /* HID touchpad (KIP hub). */
0180 static const struct software_node ssam_node_hid_kip_touchpad = {
0181     .name = "ssam:01:15:02:03:00",
0182     .parent = &ssam_node_hub_kip,
0183 };
0184 
0185 /* HID device instance 5 (KIP hub, type-cover firmware update). */
0186 static const struct software_node ssam_node_hid_kip_fwupd = {
0187     .name = "ssam:01:15:02:05:00",
0188     .parent = &ssam_node_hub_kip,
0189 };
0190 
0191 /* Tablet-mode switch via POS subsystem. */
0192 static const struct software_node ssam_node_pos_tablet_switch = {
0193     .name = "ssam:01:26:01:00:01",
0194     .parent = &ssam_node_root,
0195 };
0196 
0197 /*
0198  * Devices for 5th- and 6th-generations models:
0199  * - Surface Book 2,
0200  * - Surface Laptop 1 and 2,
0201  * - Surface Pro 5 and 6.
0202  */
0203 static const struct software_node *ssam_node_group_gen5[] = {
0204     &ssam_node_root,
0205     &ssam_node_tmp_pprof,
0206     NULL,
0207 };
0208 
0209 /* Devices for Surface Book 3. */
0210 static const struct software_node *ssam_node_group_sb3[] = {
0211     &ssam_node_root,
0212     &ssam_node_hub_base,
0213     &ssam_node_bat_ac,
0214     &ssam_node_bat_main,
0215     &ssam_node_bat_sb3base,
0216     &ssam_node_tmp_pprof,
0217     &ssam_node_bas_dtx,
0218     &ssam_node_hid_base_keyboard,
0219     &ssam_node_hid_base_touchpad,
0220     &ssam_node_hid_base_iid5,
0221     &ssam_node_hid_base_iid6,
0222     NULL,
0223 };
0224 
0225 /* Devices for Surface Laptop 3 and 4. */
0226 static const struct software_node *ssam_node_group_sl3[] = {
0227     &ssam_node_root,
0228     &ssam_node_bat_ac,
0229     &ssam_node_bat_main,
0230     &ssam_node_tmp_pprof,
0231     &ssam_node_hid_main_keyboard,
0232     &ssam_node_hid_main_touchpad,
0233     &ssam_node_hid_main_iid5,
0234     NULL,
0235 };
0236 
0237 /* Devices for Surface Laptop Studio. */
0238 static const struct software_node *ssam_node_group_sls[] = {
0239     &ssam_node_root,
0240     &ssam_node_bat_ac,
0241     &ssam_node_bat_main,
0242     &ssam_node_tmp_pprof,
0243     &ssam_node_pos_tablet_switch,
0244     &ssam_node_hid_sam_keyboard,
0245     &ssam_node_hid_sam_penstash,
0246     &ssam_node_hid_sam_touchpad,
0247     &ssam_node_hid_sam_sensors,
0248     &ssam_node_hid_sam_ucm_ucsi,
0249     &ssam_node_hid_sam_sysctrl,
0250     NULL,
0251 };
0252 
0253 /* Devices for Surface Laptop Go. */
0254 static const struct software_node *ssam_node_group_slg1[] = {
0255     &ssam_node_root,
0256     &ssam_node_bat_ac,
0257     &ssam_node_bat_main,
0258     &ssam_node_tmp_pprof,
0259     NULL,
0260 };
0261 
0262 /* Devices for Surface Pro 7 and Surface Pro 7+. */
0263 static const struct software_node *ssam_node_group_sp7[] = {
0264     &ssam_node_root,
0265     &ssam_node_bat_ac,
0266     &ssam_node_bat_main,
0267     &ssam_node_tmp_pprof,
0268     NULL,
0269 };
0270 
0271 static const struct software_node *ssam_node_group_sp8[] = {
0272     &ssam_node_root,
0273     &ssam_node_hub_kip,
0274     &ssam_node_bat_ac,
0275     &ssam_node_bat_main,
0276     &ssam_node_tmp_pprof,
0277     &ssam_node_kip_tablet_switch,
0278     &ssam_node_hid_kip_keyboard,
0279     &ssam_node_hid_kip_penstash,
0280     &ssam_node_hid_kip_touchpad,
0281     &ssam_node_hid_kip_fwupd,
0282     &ssam_node_hid_sam_sensors,
0283     &ssam_node_hid_sam_ucm_ucsi,
0284     NULL,
0285 };
0286 
0287 
0288 /* -- SSAM platform/meta-hub driver. ---------------------------------------- */
0289 
0290 static const struct acpi_device_id ssam_platform_hub_match[] = {
0291     /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
0292     { "MSHW0081", (unsigned long)ssam_node_group_gen5 },
0293 
0294     /* Surface Pro 6 (OMBR >= 0x10) */
0295     { "MSHW0111", (unsigned long)ssam_node_group_gen5 },
0296 
0297     /* Surface Pro 7 */
0298     { "MSHW0116", (unsigned long)ssam_node_group_sp7 },
0299 
0300     /* Surface Pro 7+ */
0301     { "MSHW0119", (unsigned long)ssam_node_group_sp7 },
0302 
0303     /* Surface Pro 8 */
0304     { "MSHW0263", (unsigned long)ssam_node_group_sp8 },
0305 
0306     /* Surface Book 2 */
0307     { "MSHW0107", (unsigned long)ssam_node_group_gen5 },
0308 
0309     /* Surface Book 3 */
0310     { "MSHW0117", (unsigned long)ssam_node_group_sb3 },
0311 
0312     /* Surface Laptop 1 */
0313     { "MSHW0086", (unsigned long)ssam_node_group_gen5 },
0314 
0315     /* Surface Laptop 2 */
0316     { "MSHW0112", (unsigned long)ssam_node_group_gen5 },
0317 
0318     /* Surface Laptop 3 (13", Intel) */
0319     { "MSHW0114", (unsigned long)ssam_node_group_sl3 },
0320 
0321     /* Surface Laptop 3 (15", AMD) and 4 (15", AMD) */
0322     { "MSHW0110", (unsigned long)ssam_node_group_sl3 },
0323 
0324     /* Surface Laptop 4 (13", Intel) */
0325     { "MSHW0250", (unsigned long)ssam_node_group_sl3 },
0326 
0327     /* Surface Laptop Go 1 */
0328     { "MSHW0118", (unsigned long)ssam_node_group_slg1 },
0329 
0330     /* Surface Laptop Go 2 */
0331     { "MSHW0290", (unsigned long)ssam_node_group_slg1 },
0332 
0333     /* Surface Laptop Studio */
0334     { "MSHW0123", (unsigned long)ssam_node_group_sls },
0335 
0336     { },
0337 };
0338 MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match);
0339 
0340 static int ssam_platform_hub_probe(struct platform_device *pdev)
0341 {
0342     const struct software_node **nodes;
0343     struct ssam_controller *ctrl;
0344     struct fwnode_handle *root;
0345     int status;
0346 
0347     nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev);
0348     if (!nodes)
0349         return -ENODEV;
0350 
0351     /*
0352      * As we're adding the SSAM client devices as children under this device
0353      * and not the SSAM controller, we need to add a device link to the
0354      * controller to ensure that we remove all of our devices before the
0355      * controller is removed. This also guarantees proper ordering for
0356      * suspend/resume of the devices on this hub.
0357      */
0358     ctrl = ssam_client_bind(&pdev->dev);
0359     if (IS_ERR(ctrl))
0360         return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
0361 
0362     status = software_node_register_node_group(nodes);
0363     if (status)
0364         return status;
0365 
0366     root = software_node_fwnode(&ssam_node_root);
0367     if (!root) {
0368         software_node_unregister_node_group(nodes);
0369         return -ENOENT;
0370     }
0371 
0372     set_secondary_fwnode(&pdev->dev, root);
0373 
0374     status = __ssam_register_clients(&pdev->dev, ctrl, root);
0375     if (status) {
0376         set_secondary_fwnode(&pdev->dev, NULL);
0377         software_node_unregister_node_group(nodes);
0378     }
0379 
0380     platform_set_drvdata(pdev, nodes);
0381     return status;
0382 }
0383 
0384 static int ssam_platform_hub_remove(struct platform_device *pdev)
0385 {
0386     const struct software_node **nodes = platform_get_drvdata(pdev);
0387 
0388     ssam_remove_clients(&pdev->dev);
0389     set_secondary_fwnode(&pdev->dev, NULL);
0390     software_node_unregister_node_group(nodes);
0391     return 0;
0392 }
0393 
0394 static struct platform_driver ssam_platform_hub_driver = {
0395     .probe = ssam_platform_hub_probe,
0396     .remove = ssam_platform_hub_remove,
0397     .driver = {
0398         .name = "surface_aggregator_platform_hub",
0399         .acpi_match_table = ssam_platform_hub_match,
0400         .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0401     },
0402 };
0403 module_platform_driver(ssam_platform_hub_driver);
0404 
0405 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
0406 MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");
0407 MODULE_LICENSE("GPL");