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 24 static inline struct device * 25 get_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 77 put_ctrl_adev: 78 acpi_dev_put(ctrl_adev); 79 return ctrl_dev; 80 } 81