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
24static inline struct device *
25get_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(hid: serial_ctrl_hid, uid: serial_ctrl_uid, hrv: -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(error: -ENODEV);
40 }
41
42 /* get_first_physical_node() returns a weak ref */
43 ctrl_dev = get_device(dev: acpi_get_first_physical_node(adev: 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(error: -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(buf: name, size: sizeof(name), fmt: "%s:0", dev_name(dev: ctrl_dev));
56 break;
57 case 1:
58 snprintf(buf: name, size: sizeof(name), fmt: "%s.%d",
59 dev_name(dev: 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(parent: ctrl_dev, name);
67 put_device(dev: ctrl_dev);
68 if (!child) {
69 pr_err("error could not find '%s' device\n", name);
70 ctrl_dev = ERR_PTR(error: -ENODEV);
71 goto put_ctrl_adev;
72 }
73
74 ctrl_dev = child;
75 }
76
77put_ctrl_adev:
78 acpi_dev_put(adev: ctrl_adev);
79 return ctrl_dev;
80}
81

source code of linux/drivers/platform/x86/serdev_helpers.h