X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fusb-uclass.c;h=7f6a9a6d0548a44bf49efb2bbd377d3a5282b8fd;hb=60b25259a5540686add02cf6c94cd7494a3e2d23;hp=ad778b481f66dfe71eba19ed7a9d9003dcb4fa86;hpb=a24a0e91590bb7452ddf531b9ae0e93216bd1237;p=u-boot diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index ad778b481f..7f6a9a6d05 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,10 @@ DECLARE_GLOBAL_DATA_PTR; extern bool usb_started; /* flag for the started/stopped USB status */ static bool asynch_allowed; +struct usb_uclass_priv { + int companion_device_count; +}; + int usb_disable_asynch(int disable) { int old_value = asynch_allowed; @@ -46,11 +51,22 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe, { struct udevice *bus = udev->controller_dev; struct dm_usb_ops *ops = usb_get_ops(bus); + struct usb_uclass_priv *uc_priv = bus->uclass->priv; + int err; if (!ops->control) return -ENOSYS; - return ops->control(bus, udev, pipe, buffer, length, setup); + err = ops->control(bus, udev, pipe, buffer, length, setup); + if (setup->request == USB_REQ_SET_FEATURE && + setup->requesttype == USB_RT_PORT && + setup->value == cpu_to_le16(USB_PORT_FEAT_RESET) && + err == -ENXIO) { + /* Device handed over to companion after port reset */ + uc_priv->companion_device_count++; + } + + return err; } int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, @@ -113,20 +129,38 @@ int usb_alloc_device(struct usb_device *udev) return ops->alloc_device(bus, udev); } +int usb_reset_root_port(struct usb_device *udev) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->reset_root_port) + return -ENOSYS; + + return ops->reset_root_port(bus, udev); +} + int usb_stop(void) { struct udevice *bus; struct uclass *uc; + struct usb_uclass_priv *uc_priv; int err = 0, ret; /* De-activate any devices that have been activated */ ret = uclass_get(UCLASS_USB, &uc); if (ret) return ret; + + uc_priv = uc->priv; + uclass_foreach_dev(bus, uc) { ret = device_remove(bus); if (ret && !err) err = ret; + ret = device_unbind_children(bus); + if (ret && !err) + err = ret; } #ifdef CONFIG_SANDBOX @@ -140,8 +174,11 @@ int usb_stop(void) uclass_foreach_dev(dev, uc) usb_emul_reset(dev); #endif +#ifdef CONFIG_USB_STORAGE usb_stor_reset(); +#endif usb_hub_reset(); + uc_priv->companion_device_count = 0; usb_started = 0; return err; @@ -171,6 +208,8 @@ static void usb_scan_bus(struct udevice *bus, bool recurse) int usb_init(void) { int controllers_initialized = 0; + struct usb_uclass_priv *uc_priv; + struct usb_bus_priv *priv; struct udevice *bus; struct uclass *uc; int count = 0; @@ -183,6 +222,8 @@ int usb_init(void) if (ret) return ret; + uc_priv = uc->priv; + uclass_foreach_dev(bus, uc) { /* init low_level USB */ printf("USB%d: ", count); @@ -198,15 +239,39 @@ int usb_init(void) printf("probe failed, error %d\n", ret); continue; } - /* - * lowlevel init is OK, now scan the bus for devices - * i.e. search HUBs and configure them - */ controllers_initialized++; - usb_scan_bus(bus, true); usb_started = true; } + /* + * lowlevel init done, now scan the bus for devices i.e. search HUBs + * and configure them, first scan primary controllers. + */ + uclass_foreach_dev(bus, uc) { + if (!device_active(bus)) + continue; + + priv = dev_get_uclass_priv(bus); + if (!priv->companion) + usb_scan_bus(bus, true); + } + + /* + * Now that the primary controllers have been scanned and have handed + * over any devices they do not understand to their companions, scan + * the companions if necessary. + */ + if (uc_priv->companion_device_count) { + uclass_foreach_dev(bus, uc) { + if (!device_active(bus)) + continue; + + priv = dev_get_uclass_priv(bus); + if (priv->companion) + usb_scan_bus(bus, true); + } + } + debug("scan end\n"); /* if we were not able to find at least one working bus, bail out */ if (!count) @@ -217,11 +282,6 @@ int usb_init(void) return usb_started ? 0 : -1; } -int usb_reset_root_port(void) -{ - return -ENOSYS; -} - static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) { struct usb_device *udev; @@ -229,7 +289,7 @@ static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) if (!device_active(parent)) return NULL; - udev = dev_get_parentdata(parent); + udev = dev_get_parent_priv(parent); if (udev->devnum == devnum) return udev; @@ -246,14 +306,14 @@ static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) struct usb_device *usb_get_dev_index(struct udevice *bus, int index) { - struct udevice *hub; + struct udevice *dev; int devnum = index + 1; /* Addresses are allocated from 1 on USB */ - device_find_first_child(bus, &hub); - if (device_get_uclass_id(hub) == UCLASS_USB_HUB) - return find_child_devnum(hub, devnum); + device_find_first_child(bus, &dev); + if (!dev) + return NULL; - return NULL; + return find_child_devnum(dev, devnum); } int usb_post_bind(struct udevice *dev) @@ -262,35 +322,6 @@ int usb_post_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int usb_port_reset(struct usb_device *parent, int portnr) -{ - unsigned short portstatus; - int ret; - - debug("%s: start\n", __func__); - - if (parent) { - /* reset the port for the second time */ - assert(portnr > 0); - debug("%s: reset %d\n", __func__, portnr - 1); - ret = legacy_hub_port_reset(parent, portnr - 1, &portstatus); - if (ret < 0) { - printf("\n Couldn't reset port %i\n", portnr); - return ret; - } - } else { - debug("%s: reset root\n", __func__); - usb_reset_root_port(); - } - - return 0; -} - -int usb_legacy_port_reset(struct usb_device *parent, int portnr) -{ - return usb_port_reset(parent, portnr); -} - int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) { struct usb_platdata *plat; @@ -463,15 +494,14 @@ error: } /** - * usb_find_child() - Find an existing device which matches our needs - * - * + * usb_find_emul_child() - Find an existing device for emulated devices */ -static int usb_find_child(struct udevice *parent, - struct usb_device_descriptor *desc, - struct usb_interface_descriptor *iface, - struct udevice **devp) +static int usb_find_emul_child(struct udevice *parent, + struct usb_device_descriptor *desc, + struct usb_interface_descriptor *iface, + struct udevice **devp) { +#ifdef CONFIG_SANDBOX struct udevice *dev; *devp = NULL; @@ -490,7 +520,7 @@ static int usb_find_child(struct udevice *parent, return 0; } } - +#endif return -ENOENT; } @@ -545,13 +575,13 @@ int usb_scan_device(struct udevice *parent, int port, udev->portnr = port; debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr); parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ? - dev_get_parentdata(parent) : NULL; - ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev, port); + dev_get_parent_priv(parent) : NULL; + ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev); debug("read_descriptor for '%s': ret=%d\n", parent->name, ret); if (ret) return ret; - ret = usb_find_child(parent, &udev->descriptor, iface, &dev); - debug("** usb_find_child returns %d\n", ret); + ret = usb_find_emul_child(parent, &udev->descriptor, iface, &dev); + debug("** usb_find_emul_child returns %d\n", ret); if (ret) { if (ret != -ENOENT) return ret; @@ -580,6 +610,49 @@ int usb_scan_device(struct udevice *parent, int port, return 0; } +/* + * Detect if a USB device has been plugged or unplugged. + */ +int usb_detect_change(void) +{ + struct udevice *hub; + struct uclass *uc; + int change = 0; + int ret; + + ret = uclass_get(UCLASS_USB_HUB, &uc); + if (ret) + return ret; + + uclass_foreach_dev(hub, uc) { + struct usb_device *udev; + struct udevice *dev; + + if (!device_active(hub)) + continue; + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + struct usb_port_status status; + + if (!device_active(dev)) + continue; + + udev = dev_get_parent_priv(dev); + if (usb_get_port_status(udev, udev->portnr, &status) + < 0) + /* USB request failed */ + continue; + + if (le16_to_cpu(status.wPortChange) & + USB_PORT_STAT_C_CONNECTION) + change++; + } + } + + return change; +} + int usb_child_post_bind(struct udevice *dev) { struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); @@ -621,7 +694,7 @@ struct udevice *usb_get_bus(struct udevice *dev) int usb_child_pre_probe(struct udevice *dev) { - struct usb_device *udev = dev_get_parentdata(dev); + struct usb_device *udev = dev_get_parent_priv(dev); struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); int ret; @@ -662,6 +735,7 @@ UCLASS_DRIVER(usb) = { .name = "usb", .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = usb_post_bind, + .priv_auto_alloc_size = sizeof(struct usb_uclass_priv), .per_child_auto_alloc_size = sizeof(struct usb_device), .per_device_auto_alloc_size = sizeof(struct usb_bus_priv), .child_post_bind = usb_child_post_bind,