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_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 61 static inline struct device * 62 get_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 } 89