onboard_usb_dev_pdevs.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * API for creating and destroying USB onboard platform devices
  4. *
  5. * Copyright (c) 2022, Google LLC
  6. */
  7. #include <linux/device.h>
  8. #include <linux/export.h>
  9. #include <linux/kernel.h>
  10. #include <linux/list.h>
  11. #include <linux/of.h>
  12. #include <linux/of_platform.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/usb.h>
  15. #include <linux/usb/hcd.h>
  16. #include <linux/usb/of.h>
  17. #include <linux/usb/onboard_dev.h>
  18. #include "onboard_usb_dev.h"
  19. struct pdev_list_entry {
  20. struct platform_device *pdev;
  21. struct list_head node;
  22. };
  23. static bool of_is_onboard_usb_dev(struct device_node *np)
  24. {
  25. return !!of_match_node(onboard_dev_match, np);
  26. }
  27. /**
  28. * onboard_dev_create_pdevs -- create platform devices for onboard USB devices
  29. * @parent_hub : parent hub to scan for connected onboard devices
  30. * @pdev_list : list of onboard platform devices owned by the parent hub
  31. *
  32. * Creates a platform device for each supported onboard device that is connected
  33. * to the given parent hub. The platform device is in charge of initializing the
  34. * device (enable regulators, take the device out of reset, ...). For onboard
  35. * hubs, it can optionally control whether the device remains powered during
  36. * system suspend or not.
  37. *
  38. * To keep track of the platform devices they are added to a list that is owned
  39. * by the parent hub.
  40. *
  41. * Some background about the logic in this function, which can be a bit hard
  42. * to follow:
  43. *
  44. * Root hubs don't have dedicated device tree nodes, but use the node of their
  45. * HCD. The primary and secondary HCD are usually represented by a single DT
  46. * node. That means the root hubs of the primary and secondary HCD share the
  47. * same device tree node (the HCD node). As a result this function can be called
  48. * twice with the same DT node for root hubs. We only want to create a single
  49. * platform device for each physical onboard device, hence for root hubs the
  50. * loop is only executed for the root hub of the primary HCD. Since the function
  51. * scans through all child nodes it still creates pdevs for onboard devices
  52. * connected to the root hub of the secondary HCD if needed.
  53. *
  54. * Further there must be only one platform device for onboard hubs with a peer
  55. * hub (the hub is a single physical device). To achieve this two measures are
  56. * taken: pdevs for onboard hubs with a peer are only created when the function
  57. * is called on behalf of the parent hub that is connected to the primary HCD
  58. * (directly or through other hubs). For onboard hubs connected to root hubs
  59. * the function processes the nodes of both peers. A platform device is only
  60. * created if the peer hub doesn't have one already.
  61. */
  62. void onboard_dev_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list)
  63. {
  64. int i;
  65. struct usb_hcd *hcd = bus_to_hcd(parent_hub->bus);
  66. struct device_node *np, *npc;
  67. struct platform_device *pdev;
  68. struct pdev_list_entry *pdle;
  69. if (!parent_hub->dev.of_node)
  70. return;
  71. if (!parent_hub->parent && !usb_hcd_is_primary_hcd(hcd))
  72. return;
  73. for (i = 1; i <= parent_hub->maxchild; i++) {
  74. np = usb_of_get_device_node(parent_hub, i);
  75. if (!np)
  76. continue;
  77. if (!of_is_onboard_usb_dev(np))
  78. goto node_put;
  79. npc = of_parse_phandle(np, "peer-hub", 0);
  80. if (npc) {
  81. if (!usb_hcd_is_primary_hcd(hcd)) {
  82. of_node_put(npc);
  83. goto node_put;
  84. }
  85. pdev = of_find_device_by_node(npc);
  86. of_node_put(npc);
  87. if (pdev) {
  88. put_device(&pdev->dev);
  89. goto node_put;
  90. }
  91. }
  92. pdev = of_platform_device_create(np, NULL, &parent_hub->dev);
  93. if (!pdev) {
  94. dev_err(&parent_hub->dev,
  95. "failed to create platform device for onboard dev '%pOF'\n", np);
  96. goto node_put;
  97. }
  98. pdle = kzalloc(sizeof(*pdle), GFP_KERNEL);
  99. if (!pdle) {
  100. of_platform_device_destroy(&pdev->dev, NULL);
  101. goto node_put;
  102. }
  103. pdle->pdev = pdev;
  104. list_add(&pdle->node, pdev_list);
  105. node_put:
  106. of_node_put(np);
  107. }
  108. }
  109. EXPORT_SYMBOL_GPL(onboard_dev_create_pdevs);
  110. /**
  111. * onboard_dev_destroy_pdevs -- free resources of onboard platform devices
  112. * @pdev_list : list of onboard platform devices
  113. *
  114. * Destroys the platform devices in the given list and frees the memory associated
  115. * with the list entry.
  116. */
  117. void onboard_dev_destroy_pdevs(struct list_head *pdev_list)
  118. {
  119. struct pdev_list_entry *pdle, *tmp;
  120. list_for_each_entry_safe(pdle, tmp, pdev_list, node) {
  121. list_del(&pdle->node);
  122. of_platform_device_destroy(&pdle->pdev->dev, NULL);
  123. kfree(pdle);
  124. }
  125. }
  126. EXPORT_SYMBOL_GPL(onboard_dev_destroy_pdevs);