Thread (28 messages) 28 messages, 9 authors, 2021-11-16

Re: [PATCH v16 1/7] usb: misc: Add onboard_usb_hub driver

From: Doug Anderson <dianders@chromium.org>
Date: 2021-11-11 23:31:50
Also in: linux-devicetree, lkml

Hi,

On Fri, Aug 13, 2021 at 12:52 PM Matthias Kaehlcke [off-list ref] wrote:
quoted hunk ↗ jump to hunk
+++ b/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub
@@ -0,0 +1,8 @@
+What:          /sys/bus/platform/devices/<dev>/always_powered_in_suspend
+Date:          March 2021
+KernelVersion: 5.13
I dunno how stuff like this is usually managed, but March 2021 and
5.13 is no longer correct.

+ONBOARD USB HUB DRIVER
+M:     Matthias Kaehlcke [off-list ref]
+L:     linux-usb@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/usb/onboard_usb_hub.yaml
I'm confused. Where is this .yaml file? It doesn't look landed and it
doesn't look to be in your series. I guess this should be updated to:

F: Documentation/devicetree/bindings/usb/realtek,rts5411.yaml

Also: should this have:

F: Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub

+struct udev_node {
+       struct usb_device *udev;
+       struct list_head list;
+};
nit: 'udev' has a whole different connotation to me. Maybe just go
with `usbdev_node` ?

+static int __maybe_unused onboard_hub_suspend(struct device *dev)
+{
+       struct onboard_hub *hub = dev_get_drvdata(dev);
+       struct udev_node *node;
+       bool power_off;
+       int rc = 0;
+
+       if (hub->always_powered_in_suspend)
+               return 0;
+
+       power_off = true;
+
+       mutex_lock(&hub->lock);
+
+       list_for_each_entry(node, &hub->udev_list, list) {
+               if (!device_may_wakeup(node->udev->bus->controller))
+                       continue;
+
+               if (usb_wakeup_enabled_descendants(node->udev)) {
+                       power_off = false;
+                       break;
+               }
+       }
+
+       mutex_unlock(&hub->lock);
+
+       if (power_off)
+               rc = onboard_hub_power_off(hub);
+
+       return rc;
optional nit: get rid of "rc" and write the above as:

if (power_off)
  return onboard_hub_power_off(hub);

return 0;

+static int __maybe_unused onboard_hub_resume(struct device *dev)
+{
+       struct onboard_hub *hub = dev_get_drvdata(dev);
+       int rc = 0;
+
+       if (!hub->is_powered_on)
+               rc = onboard_hub_power_on(hub);
+
+       return rc;
optional nit: get rid of "rc" and write the above as:

if (!hub->is_powered_on)
  return onboard_hub_power_on(hub);

return 0;

+static void onboard_hub_remove_usbdev(struct onboard_hub *hub, struct usb_device *udev)
+{
+       struct udev_node *node;
+       char link_name[64];
+
+       snprintf(link_name, sizeof(link_name), "usb_dev.%s", dev_name(&udev->dev));
+       sysfs_remove_link(&hub->dev->kobj, link_name);
I would be at least moderately worried about the duplicate snprintf
between here and the add function. Any way that could be a helper?

+static struct onboard_hub *_find_onboard_hub(struct device *dev)
+{
+       struct platform_device *pdev;
+       struct device_node *np;
+       phandle ph;
+
+       pdev = of_find_device_by_node(dev->of_node);
+       if (!pdev) {
+               if (of_property_read_u32(dev->of_node, "companion-hub", &ph)) {
+                       dev_err(dev, "failed to read 'companion-hub' property\n");
+                       return ERR_PTR(-EINVAL);
+               }
+
+               np = of_find_node_by_phandle(ph);
+               if (!np) {
+                       dev_err(dev, "failed to find device node for companion hub\n");
+                       return ERR_PTR(-EINVAL);
+               }
Aren't the above two calls equivalent to this?

npc = of_parse_phandle(dev->of_node, "companion-hub", 0)

+
+               pdev = of_find_device_by_node(np);
+               of_node_put(np);
+
+               if (!pdev)
+                       return ERR_PTR(-EPROBE_DEFER);
Shouldn't you also defer if the dev_get_drvdata() returns NULL? What
if you're racing the probe of the platform device?

+       }
+
+       put_device(&pdev->dev);
+
+       return dev_get_drvdata(&pdev->dev);
It feels like it would be safer to call dev_get_drvdata() before
putting the device? ...and actually, are you sure you should even be
putting the device? Maybe we should wait to put it until
onboard_hub_usbdev_disconnect()

+static struct usb_device_driver onboard_hub_usbdev_driver = {
+
+       .name = "onboard-usb-hub",
Remove the extra blank line at the start of the structure?

+void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list)
+{
+       int i;
+       phandle ph;
+       struct device_node *np, *npc;
+       struct platform_device *pdev;
+       struct pdev_list_entry *pdle;
Should the `INIT_LIST_HEAD(pdev_list);` go here? Is there any reason
why we need to push this into the caller?

+       for (i = 1; i <= parent_hub->maxchild; i++) {
+               np = usb_of_get_device_node(parent_hub, i);
+               if (!np)
+                       continue;
+
+               if (!of_is_onboard_usb_hub(np))
+                       goto node_put;
+
+               if (of_property_read_u32(np, "companion-hub", &ph))
+                       goto node_put;
+
+               npc = of_find_node_by_phandle(ph);
+               if (!npc)
+                       goto node_put;
Aren't the above two calls equivalent to this?

npc = of_parse_phandle(np, "companion-hub", 0)

I'm also curious why a companion-hub is a _required_ property.
Couldn't you support USB 2.0 hubs better by just allowing
companion-hub to be optional? I guess that could be a future
improvement, but it also seems trivial to support from the start.

+               pdev = of_find_device_by_node(npc);
+               of_node_put(npc);
+
+               if (pdev) {
+                       /* the companion hub already has a platform device, nothing to do here */
+                       put_device(&pdev->dev);
+                       goto node_put;
+               }
+
+               pdev = of_platform_device_create(np, NULL, &parent_hub->dev);
+               if (pdev) {
+                       pdle = kzalloc(sizeof(*pdle), GFP_KERNEL);
Maybe devm_kzalloc(&pdev->dev, GFP_KERNEL) ? Then you can get rid of
the free in the destroy function?

+                       if (!pdle)
+                               goto node_put;
If your memory allocation fails here, don't you need to
of_platform_device_destroy() ?

+                       INIT_LIST_HEAD(&pdle->node);
I don't believe that the INIT_LIST_HEAD() does anything useful here.
&pdle->node is not a list head--it's a list element. Adding it to the
end of the existing list will fully initialize its ->next and ->prev
pointers but won't look at what they were.

+                       pdle->pdev = pdev;
+                       list_add(&pdle->node, pdev_list);
+               } else {
+                       dev_err(&parent_hub->dev,
+                               "failed to create platform device for onboard hub '%s'\n",
+                               of_node_full_name(np));
Use "%pOF" instead of open-coding.

+void onboard_hub_destroy_pdevs(struct list_head *pdev_list)
+{
+       struct pdev_list_entry *pdle, *tmp;
+
+       list_for_each_entry_safe(pdle, tmp, pdev_list, node) {
+               of_platform_device_destroy(&pdle->pdev->dev, NULL);
+               kfree(pdle);
It feels like you should be removing the node from the list too,
right? Otherwise if you unbind / bind the USB driver you'll still have
garbage in your list the 2nd time?
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help