81 lines
2.3 KiB
C
81 lines
2.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* In some cases UART attached devices which require an in kernel driver,
|
|
* e.g. UART attached Bluetooth HCIs are described in the ACPI tables
|
|
* by an ACPI device with a broken or missing UartSerialBusV2() resource.
|
|
*
|
|
* This causes the kernel to create a /dev/ttyS# char-device for the UART
|
|
* instead of creating an in kernel serdev-controller + serdev-device pair
|
|
* for the in kernel driver.
|
|
*
|
|
* The quirk handling in acpi_quirk_skip_serdev_enumeration() makes the kernel
|
|
* create a serdev-controller device for these UARTs instead of a /dev/ttyS#.
|
|
*
|
|
* Instantiating the actual serdev-device to bind to is up to pdx86 code,
|
|
* this header provides a helper for getting the serdev-controller device.
|
|
*/
|
|
#include <linux/acpi.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/sprintf.h>
|
|
#include <linux/string.h>
|
|
|
|
static inline struct device *
|
|
get_serdev_controller(const char *serial_ctrl_hid,
|
|
const char *serial_ctrl_uid,
|
|
int serial_ctrl_port,
|
|
const char *serdev_ctrl_name)
|
|
{
|
|
struct device *ctrl_dev, *child;
|
|
struct acpi_device *ctrl_adev;
|
|
char name[32];
|
|
int i;
|
|
|
|
ctrl_adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1);
|
|
if (!ctrl_adev) {
|
|
pr_err("error could not get %s/%s serial-ctrl adev\n",
|
|
serial_ctrl_hid, serial_ctrl_uid);
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
/* get_first_physical_node() returns a weak ref */
|
|
ctrl_dev = get_device(acpi_get_first_physical_node(ctrl_adev));
|
|
if (!ctrl_dev) {
|
|
pr_err("error could not get %s/%s serial-ctrl physical node\n",
|
|
serial_ctrl_hid, serial_ctrl_uid);
|
|
ctrl_dev = ERR_PTR(-ENODEV);
|
|
goto put_ctrl_adev;
|
|
}
|
|
|
|
/* Walk host -> uart-ctrl -> port -> serdev-ctrl */
|
|
for (i = 0; i < 3; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
snprintf(name, sizeof(name), "%s:0", dev_name(ctrl_dev));
|
|
break;
|
|
case 1:
|
|
snprintf(name, sizeof(name), "%s.%d",
|
|
dev_name(ctrl_dev), serial_ctrl_port);
|
|
break;
|
|
case 2:
|
|
strscpy(name, serdev_ctrl_name, sizeof(name));
|
|
break;
|
|
}
|
|
|
|
child = device_find_child_by_name(ctrl_dev, name);
|
|
put_device(ctrl_dev);
|
|
if (!child) {
|
|
pr_err("error could not find '%s' device\n", name);
|
|
ctrl_dev = ERR_PTR(-ENODEV);
|
|
goto put_ctrl_adev;
|
|
}
|
|
|
|
ctrl_dev = child;
|
|
}
|
|
|
|
put_ctrl_adev:
|
|
acpi_dev_put(ctrl_adev);
|
|
return ctrl_dev;
|
|
}
|