81 lines · 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 tables5 * 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 UART8 * instead of creating an in kernel serdev-controller + serdev-device pair9 * for the in kernel driver.10 *11 * The quirk handling in acpi_quirk_skip_serdev_enumeration() makes the kernel12 * 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(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 77put_ctrl_adev:78 acpi_dev_put(ctrl_adev);79 return ctrl_dev;80}81