File Index
Files and directories
Sign in
FILE

serdev_helpers.h

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/serdev_helpers.h C
 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_from_parent(struct device *ctrl_dev,
26				  int serial_ctrl_port,
27				  const char *serdev_ctrl_name)
28{
29	struct device *child;
30	char name[32];
31	int i;
32
33	/* Walk host -> uart-ctrl -> port -> serdev-ctrl */
34	for (i = 0; i < 3; i++) {
35		switch (i) {
36		case 0:
37			snprintf(name, sizeof(name), "%s:0", dev_name(ctrl_dev));
38			break;
39		case 1:
40			snprintf(name, sizeof(name), "%s.%d",
41				 dev_name(ctrl_dev), serial_ctrl_port);
42			break;
43		case 2:
44			strscpy(name, serdev_ctrl_name, sizeof(name));
45			break;
46		}
47
48		child = device_find_child_by_name(ctrl_dev, name);
49		put_device(ctrl_dev);
50		if (!child) {
51			pr_err("error could not find '%s' device\n", name);
52			return ERR_PTR(-ENODEV);
53		}
54
55		ctrl_dev = child;
56	}
57
58	return ctrl_dev;
59}
60
61static inline struct device *
62get_serdev_controller(const char *serial_ctrl_hid,
63		      const char *serial_ctrl_uid,
64		      int serial_ctrl_port,
65		      const char *serdev_ctrl_name)
66{
67	struct acpi_device *adev;
68	struct device *parent;
69
70	adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1);
71	if (!adev) {
72		pr_err("error could not get %s/%s serial-ctrl adev\n",
73		       serial_ctrl_hid, serial_ctrl_uid ?: "*");
74		return ERR_PTR(-ENODEV);
75	}
76
77	/* get_first_physical_node() returns a weak ref */
78	parent = get_device(acpi_get_first_physical_node(adev));
79	acpi_dev_put(adev);
80	if (!parent) {
81		pr_err("error could not get %s/%s serial-ctrl physical node\n",
82		       serial_ctrl_hid, serial_ctrl_uid ?: "*");
83		return ERR_PTR(-ENODEV);
84	}
85
86	/* This puts our reference on parent and returns a ref on the ctrl */
87	return get_serdev_controller_from_parent(parent, serial_ctrl_port, serdev_ctrl_name);
88}
~/ Read-only file preview.