Commit dc5afd72 authored by Hans de Goede's avatar Hans de Goede
Browse files

platform/x86: Add new get_serdev_controller() helper



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,
so far this was handled by the x86-android-tablets code. But since
commit b286f4e8 ("serial: core: Move tty and serdev to be children of
serial core port device") the serdev-controller device has moved in the
device hierarchy from (e.g.) /sys/devices/pci0000:00/8086228A:00/serial0 to
/sys/devices/pci0000:00/8086228A:00/8086228A:00:0/8086228A:00:0.0/serial0 .

This makes this a bit trickier to do and another driver is in the works
which will also need this functionality.

Add a new helper to get the serdev-controller device, so that the new
code for this can be shared.

Fixes: b286f4e8 ("serial: core: Move tty and serdev to be children of serial core port device")
Cc: Tony Lindgren <tony@atomide.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20240216201721.239791-3-hdegoede@redhat.com
parent bd8905d7
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
/* 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;
}