serdev_helpers.h 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. static inline struct device *
  24. get_serdev_controller_from_parent(struct device *ctrl_dev,
  25. int serial_ctrl_port,
  26. const char *serdev_ctrl_name)
  27. {
  28. struct device *child;
  29. char name[32];
  30. int i;
  31. /* Walk host -> uart-ctrl -> port -> serdev-ctrl */
  32. for (i = 0; i < 3; i++) {
  33. switch (i) {
  34. case 0:
  35. snprintf(name, sizeof(name), "%s:0", dev_name(ctrl_dev));
  36. break;
  37. case 1:
  38. snprintf(name, sizeof(name), "%s.%d",
  39. dev_name(ctrl_dev), serial_ctrl_port);
  40. break;
  41. case 2:
  42. strscpy(name, serdev_ctrl_name, sizeof(name));
  43. break;
  44. }
  45. child = device_find_child_by_name(ctrl_dev, name);
  46. put_device(ctrl_dev);
  47. if (!child) {
  48. pr_err("error could not find '%s' device\n", name);
  49. return ERR_PTR(-ENODEV);
  50. }
  51. ctrl_dev = child;
  52. }
  53. return ctrl_dev;
  54. }
  55. static inline struct device *
  56. get_serdev_controller(const char *serial_ctrl_hid,
  57. const char *serial_ctrl_uid,
  58. int serial_ctrl_port,
  59. const char *serdev_ctrl_name)
  60. {
  61. struct acpi_device *adev;
  62. struct device *parent;
  63. adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1);
  64. if (!adev) {
  65. pr_err("error could not get %s/%s serial-ctrl adev\n",
  66. serial_ctrl_hid, serial_ctrl_uid ?: "*");
  67. return ERR_PTR(-ENODEV);
  68. }
  69. /* get_first_physical_node() returns a weak ref */
  70. parent = get_device(acpi_get_first_physical_node(adev));
  71. acpi_dev_put(adev);
  72. if (!parent) {
  73. pr_err("error could not get %s/%s serial-ctrl physical node\n",
  74. serial_ctrl_hid, serial_ctrl_uid ?: "*");
  75. return ERR_PTR(-ENODEV);
  76. }
  77. /* This puts our reference on parent and returns a ref on the ctrl */
  78. return get_serdev_controller_from_parent(parent, serial_ctrl_port, serdev_ctrl_name);
  79. }