+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015 Google, Inc
*
* usb_match_device() modified from Linux kernel v4.0.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
+#include <log.h>
#include <memalign.h>
#include <usb.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
-DECLARE_GLOBAL_DATA_PTR;
-
extern bool usb_started; /* flag for the started/stopped USB status */
static bool asynch_allowed;
int companion_device_count;
};
+int usb_lock_async(struct usb_device *udev, int lock)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->lock_async)
+ return -ENOSYS;
+
+ return ops->lock_async(bus, lock);
+}
+
int usb_disable_asynch(int disable)
{
int old_value = asynch_allowed;
}
int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length, int interval)
+ int length, int interval, bool nonblock)
{
struct udevice *bus = udev->controller_dev;
struct dm_usb_ops *ops = usb_get_ops(bus);
if (!ops->interrupt)
return -ENOSYS;
- return ops->interrupt(bus, udev, pipe, buffer, length, interval);
+ return ops->interrupt(bus, udev, pipe, buffer, length, interval,
+ nonblock);
}
int submit_control_msg(struct usb_device *udev, unsigned long pipe,
return ops->reset_root_port(bus, udev);
}
+int usb_update_hub_device(struct usb_device *udev)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->update_hub_device)
+ return -ENOSYS;
+
+ return ops->update_hub_device(bus, udev);
+}
+
+int usb_get_max_xfer_size(struct usb_device *udev, size_t *size)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->get_max_xfer_size)
+ return -ENOSYS;
+
+ return ops->get_max_xfer_size(bus, size);
+}
+
int usb_stop(void)
{
struct udevice *bus;
+ struct udevice *rh;
struct uclass *uc;
struct usb_uclass_priv *uc_priv;
int err = 0, ret;
ret = device_remove(bus, DM_REMOVE_NORMAL);
if (ret && !err)
err = ret;
- }
-#ifdef CONFIG_BLK
- ret = blk_unbind_all(IF_TYPE_USB);
- if (ret && !err)
- err = ret;
-#endif
-#ifdef CONFIG_SANDBOX
- struct udevice *dev;
- /* Reset all enulation devices */
- ret = uclass_get(UCLASS_USB_EMUL, &uc);
- if (ret)
- return ret;
+ /* Locate root hub device */
+ device_find_first_child(bus, &rh);
+ if (rh) {
+ /*
+ * All USB devices are children of root hub.
+ * Unbinding root hub will unbind all of its children.
+ */
+ ret = device_unbind(rh);
+ if (ret && !err)
+ err = ret;
+ }
+ }
- uclass_foreach_dev(dev, uc)
- usb_emul_reset(dev);
-#endif
#ifdef CONFIG_USB_STORAGE
usb_stor_reset();
#endif
assert(recurse); /* TODO: Support non-recusive */
- printf("scanning bus %d for devices... ", bus->seq);
+ printf("scanning bus %s for devices... ", bus->name);
debug("\n");
ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
if (ret)
struct usb_bus_priv *priv;
struct udevice *bus;
struct uclass *uc;
- int count = 0;
int ret;
asynch_allowed = 1;
uclass_foreach_dev(bus, uc) {
/* init low_level USB */
- printf("USB%d: ", count);
- count++;
+ printf("Bus %s: ", bus->name);
+
+#ifdef CONFIG_SANDBOX
+ /*
+ * For Sandbox, we need scan the device tree each time when we
+ * start the USB stack, in order to re-create the emulated USB
+ * devices and bind drivers for them before we actually do the
+ * driver probe.
+ */
+ ret = dm_scan_fdt_dev(bus);
+ if (ret) {
+ printf("Sandbox USB device scan failed (%d)\n", ret);
+ continue;
+ }
+#endif
+
ret = device_probe(bus);
if (ret == -ENODEV) { /* No such device. */
puts("Port not available.\n");
remove_inactive_children(uc, bus);
/* if we were not able to find at least one working bus, bail out */
- if (!count)
- printf("No controllers found\n");
- else if (controllers_initialized == 0)
- printf("USB error: all controllers failed lowlevel init\n");
+ if (controllers_initialized == 0)
+ printf("No working controllers found\n");
return usb_started ? 0 : -1;
}
int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp)
{
- struct usb_platdata *plat;
+ struct usb_plat *plat;
struct udevice *dev;
int ret;
if (ret)
return ret;
- plat = dev_get_platdata(dev);
+ plat = dev_get_plat(dev);
plat->init_type = USB_INIT_DEVICE;
ret = device_probe(dev);
if (ret)
return 0;
}
+int usb_remove_ehci_gadget(struct ehci_ctrl **ctlrp)
+{
+ struct udevice *dev;
+ int ret;
+
+ /* Find the old device and remove it */
+ ret = uclass_find_device_by_seq(UCLASS_USB, 0, true, &dev);
+ if (ret)
+ return ret;
+ ret = device_remove(dev, DM_REMOVE_NORMAL);
+ if (ret)
+ return ret;
+
+ *ctlrp = NULL;
+
+ return 0;
+}
+
/* returns 0 if no match, 1 if match */
static int usb_match_device(const struct usb_device_descriptor *desc,
const struct usb_device_id *id)
{
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
- id->idVendor != le16_to_cpu(desc->idVendor))
+ id->idVendor != desc->idVendor)
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
- id->idProduct != le16_to_cpu(desc->idProduct))
+ id->idProduct != desc->idProduct)
return 0;
/* No need to test id->bcdDevice_lo != 0, since 0 is never
greater than any unsigned number. */
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
- (id->bcdDevice_lo > le16_to_cpu(desc->bcdDevice)))
+ (id->bcdDevice_lo > desc->bcdDevice))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
- (id->bcdDevice_hi < le16_to_cpu(desc->bcdDevice)))
+ (id->bcdDevice_hi < desc->bcdDevice))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
return usb_match_one_id_intf(desc, int_desc, id);
}
+static ofnode usb_get_ofnode(struct udevice *hub, int port)
+{
+ ofnode node;
+ u32 reg;
+
+ if (!dev_has_of_node(hub))
+ return ofnode_null();
+
+ /*
+ * The USB controller and its USB hub are two different udevices,
+ * but the device tree has only one node for both. Thus we are
+ * assigning this node to both udevices.
+ * If port is zero, the controller scans its root hub, thus we
+ * are using the same ofnode as the controller here.
+ */
+ if (!port)
+ return dev_ofnode(hub);
+
+ ofnode_for_each_subnode(node, dev_ofnode(hub)) {
+ if (ofnode_read_u32(node, "reg", ®))
+ continue;
+
+ if (reg == port)
+ return node;
+ }
+
+ return ofnode_null();
+}
+
/**
* usb_find_and_bind_driver() - Find and bind the right USB driver
*
static int usb_find_and_bind_driver(struct udevice *parent,
struct usb_device_descriptor *desc,
struct usb_interface_descriptor *iface,
- int bus_seq, int devnum,
+ int bus_seq, int devnum, int port,
struct udevice **devp)
{
struct usb_driver_entry *start, *entry;
int n_ents;
int ret;
char name[30], *str;
+ ofnode node = usb_get_ofnode(parent, port);
*devp = NULL;
debug("%s: Searching for driver\n", __func__);
const struct usb_device_id *id;
struct udevice *dev;
const struct driver *drv;
- struct usb_dev_platdata *plat;
+ struct usb_dev_plat *plat;
for (id = entry->match; id->match_flags; id++) {
if (!usb_match_one_id(desc, iface, id))
drv = entry->driver;
/*
* We could pass the descriptor to the driver as
- * platdata (instead of NULL) and allow its bind()
+ * plat (instead of NULL) and allow its bind()
* method to return -ENOENT if it doesn't support this
* device. That way we could continue the search to
* find another driver. For now this doesn't seem
* necesssary, so just bind the first match.
*/
- ret = device_bind(parent, drv, drv->name, NULL, -1,
+ ret = device_bind(parent, drv, drv->name, NULL, node,
&dev);
if (ret)
goto error;
debug("%s: Match found: %s\n", __func__, drv->name);
dev->driver_data = id->driver_info;
- plat = dev_get_parent_platdata(dev);
+ plat = dev_get_parent_plat(dev);
plat->id = *id;
*devp = dev;
return 0;
for (device_find_first_child(parent, &dev);
dev;
device_find_next_child(&dev)) {
- struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+ struct usb_dev_plat *plat = dev_get_parent_plat(dev);
/* If this device is already in use, skip it */
if (device_active(dev))
{
struct udevice *dev;
bool created = false;
- struct usb_dev_platdata *plat;
+ struct usb_dev_plat *plat;
struct usb_bus_priv *priv;
struct usb_device *parent_udev;
int ret;
if (ret) {
if (ret != -ENOENT)
return ret;
- ret = usb_find_and_bind_driver(parent, &udev->descriptor, iface,
+ ret = usb_find_and_bind_driver(parent, &udev->descriptor,
+ iface,
udev->controller_dev->seq,
- udev->devnum, &dev);
+ udev->devnum, port, &dev);
if (ret)
return ret;
created = true;
}
- plat = dev_get_parent_platdata(dev);
+ plat = dev_get_parent_plat(dev);
debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat);
plat->devnum = udev->devnum;
plat->udev = udev;
static int usb_child_post_bind(struct udevice *dev)
{
- struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+ struct usb_dev_plat *plat = dev_get_parent_plat(dev);
int val;
if (!dev_of_valid(dev))
int usb_child_pre_probe(struct udevice *dev)
{
struct usb_device *udev = dev_get_parent_priv(dev);
- struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
+ struct usb_dev_plat *plat = dev_get_parent_plat(dev);
int ret;
if (plat->udev) {
.name = "usb",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_bind = dm_scan_fdt_dev,
- .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),
+ .priv_auto = sizeof(struct usb_uclass_priv),
+ .per_child_auto = sizeof(struct usb_device),
+ .per_device_auto = sizeof(struct usb_bus_priv),
.child_post_bind = usb_child_post_bind,
.child_pre_probe = usb_child_pre_probe,
- .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
+ .per_child_plat_auto = sizeof(struct usb_dev_plat),
};
UCLASS_DRIVER(usb_dev_generic) = {