Merge tag 'kvm-x86-misc-6.9' of https://github.com/kvm-x86/linux into HEAD
[linux-2.6-block.git] / drivers / platform / x86 / serdev_helpers.h
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * In some cases UART attached devices which require an in kernel driver,
4  * e.g. UART attached Bluetooth HCIs are described in the ACPI tables
5  * by an ACPI device with a broken or missing UartSerialBusV2() resource.
6  *
7  * This causes the kernel to create a /dev/ttyS# char-device for the UART
8  * instead of creating an in kernel serdev-controller + serdev-device pair
9  * for the in kernel driver.
10  *
11  * The quirk handling in acpi_quirk_skip_serdev_enumeration() makes the kernel
12  * create a serdev-controller device for these UARTs instead of a /dev/ttyS#.
13  *
14  * Instantiating the actual serdev-device to bind to is up to pdx86 code,
15  * this header provides a helper for getting the serdev-controller device.
16  */
17 #include <linux/acpi.h>
18 #include <linux/device.h>
19 #include <linux/err.h>
20 #include <linux/printk.h>
21 #include <linux/sprintf.h>
22 #include <linux/string.h>
23
24 static inline struct device *
25 get_serdev_controller(const char *serial_ctrl_hid,
26                       const char *serial_ctrl_uid,
27                       int serial_ctrl_port,
28                       const char *serdev_ctrl_name)
29 {
30         struct device *ctrl_dev, *child;
31         struct acpi_device *ctrl_adev;
32         char name[32];
33         int i;
34
35         ctrl_adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1);
36         if (!ctrl_adev) {
37                 pr_err("error could not get %s/%s serial-ctrl adev\n",
38                        serial_ctrl_hid, serial_ctrl_uid);
39                 return ERR_PTR(-ENODEV);
40         }
41
42         /* get_first_physical_node() returns a weak ref */
43         ctrl_dev = get_device(acpi_get_first_physical_node(ctrl_adev));
44         if (!ctrl_dev) {
45                 pr_err("error could not get %s/%s serial-ctrl physical node\n",
46                        serial_ctrl_hid, serial_ctrl_uid);
47                 ctrl_dev = ERR_PTR(-ENODEV);
48                 goto put_ctrl_adev;
49         }
50
51         /* Walk host -> uart-ctrl -> port -> serdev-ctrl */
52         for (i = 0; i < 3; i++) {
53                 switch (i) {
54                 case 0:
55                         snprintf(name, sizeof(name), "%s:0", dev_name(ctrl_dev));
56                         break;
57                 case 1:
58                         snprintf(name, sizeof(name), "%s.%d",
59                                  dev_name(ctrl_dev), serial_ctrl_port);
60                         break;
61                 case 2:
62                         strscpy(name, serdev_ctrl_name, sizeof(name));
63                         break;
64                 }
65
66                 child = device_find_child_by_name(ctrl_dev, name);
67                 put_device(ctrl_dev);
68                 if (!child) {
69                         pr_err("error could not find '%s' device\n", name);
70                         ctrl_dev = ERR_PTR(-ENODEV);
71                         goto put_ctrl_adev;
72                 }
73
74                 ctrl_dev = child;
75         }
76
77 put_ctrl_adev:
78         acpi_dev_put(ctrl_adev);
79         return ctrl_dev;
80 }