From 98b639fc503e16cffa902d3ab89b29b9b5dcbf57 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:36 -0700 Subject: [PATCH 01/24] usb: emul: Remove maxpacketsize in usb_emul_setup_device() This parameter is never used. Signed-off-by: Bin Meng --- drivers/usb/emul/sandbox_flash.c | 3 +-- drivers/usb/emul/sandbox_hub.c | 3 +-- drivers/usb/emul/sandbox_keyb.c | 3 +-- drivers/usb/emul/usb-emul-uclass.c | 4 ++-- include/usb.h | 5 ++--- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/usb/emul/sandbox_flash.c b/drivers/usb/emul/sandbox_flash.c index 98d20c0bc1..2f84b360ec 100644 --- a/drivers/usb/emul/sandbox_flash.c +++ b/drivers/usb/emul/sandbox_flash.c @@ -390,8 +390,7 @@ static int sandbox_flash_bind(struct udevice *dev) fs[2].id = STRINGID_SERIAL; fs[2].s = dev->name; - return usb_emul_setup_device(dev, PACKET_SIZE_64, plat->flash_strings, - flash_desc_list); + return usb_emul_setup_device(dev, plat->flash_strings, flash_desc_list); } static int sandbox_flash_probe(struct udevice *dev) diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c index 1432858fd5..9ed700036e 100644 --- a/drivers/usb/emul/sandbox_hub.c +++ b/drivers/usb/emul/sandbox_hub.c @@ -274,8 +274,7 @@ static int sandbox_hub_submit_control_msg(struct udevice *bus, static int sandbox_hub_bind(struct udevice *dev) { - return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings, - hub_desc_list); + return usb_emul_setup_device(dev, hub_strings, hub_desc_list); } static int sandbox_child_post_bind(struct udevice *dev) diff --git a/drivers/usb/emul/sandbox_keyb.c b/drivers/usb/emul/sandbox_keyb.c index 27359851df..cff017668f 100644 --- a/drivers/usb/emul/sandbox_keyb.c +++ b/drivers/usb/emul/sandbox_keyb.c @@ -208,8 +208,7 @@ static int sandbox_keyb_bind(struct udevice *dev) fs[2].id = STRINGID_SERIAL; fs[2].s = dev->name; - return usb_emul_setup_device(dev, PACKET_SIZE_8, plat->keyb_strings, - keyb_desc_list); + return usb_emul_setup_device(dev, plat->keyb_strings, keyb_desc_list); } static int sandbox_keyb_probe(struct udevice *dev) diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c index 6e03c1e0d9..e4a267bf00 100644 --- a/drivers/usb/emul/usb-emul-uclass.c +++ b/drivers/usb/emul/usb-emul-uclass.c @@ -229,8 +229,8 @@ int usb_emul_int(struct udevice *emul, struct usb_device *udev, return ops->interrupt(emul, udev, pipe, buffer, length, interval); } -int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, - struct usb_string *strings, void **desc_list) +int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings, + void **desc_list) { struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); struct usb_generic_descriptor **ptr; diff --git a/include/usb.h b/include/usb.h index 0ddc0822b4..1563c9abf3 100644 --- a/include/usb.h +++ b/include/usb.h @@ -976,7 +976,6 @@ int usb_get_max_xfer_size(struct usb_device *dev, size_t *size); * the USB emulation uclass about the features of the emulator. * * @dev: Emulation device - * @maxpacketsize: Maximum packet size (e.g. PACKET_SIZE_64) * @strings: List of USB string descriptors, terminated by a NULL * entry * @desc_list: List of points or USB descriptors, terminated by NULL. @@ -984,8 +983,8 @@ int usb_get_max_xfer_size(struct usb_device *dev, size_t *size); * and others follow on after that. * @return 0 if OK, -ENOSYS if not implemented, other -ve on error */ -int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, - struct usb_string *strings, void **desc_list); +int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings, + void **desc_list); /** * usb_emul_control() - Send a control packet to an emulator From 8d36c6874173918612495b8e5925a7000ed8058e Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:37 -0700 Subject: [PATCH 02/24] usb: Fix comments of usb_emul_find_for_dev() There is no such a parameter called 'bus'. Signed-off-by: Bin Meng --- include/usb.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/usb.h b/include/usb.h index 1563c9abf3..3d517316e5 100644 --- a/include/usb.h +++ b/include/usb.h @@ -1035,7 +1035,6 @@ int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp); /** * usb_emul_find_for_dev() - Find an emulator for a particular device * - * @bus: USB bus (controller) * @dev: USB device to check * @emulp: Returns pointer to emulator, or NULL if not found * @return 0 if found, -ve on error From 813f74ea47d0f77f809d85619153923a99b07222 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:38 -0700 Subject: [PATCH 03/24] usb: sandbox: Initialize root hub's device speed to high speed At present 'usb tree' shows that the root hub on the Sandbox USB controller is at full speed. But its device descriptor says it's USB 2.0, so let's report it as a high speed device. Signed-off-by: Bin Meng --- drivers/usb/host/usb-sandbox.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/usb/host/usb-sandbox.c b/drivers/usb/host/usb-sandbox.c index 5e3d96c208..f85d36ba42 100644 --- a/drivers/usb/host/usb-sandbox.c +++ b/drivers/usb/host/usb-sandbox.c @@ -12,6 +12,10 @@ DECLARE_GLOBAL_DATA_PTR; +struct sandbox_usb_ctrl { + int rootdev; +}; + static void usbmon_trace(struct udevice *bus, ulong pipe, struct devrequest *setup, struct udevice *emul) { @@ -40,6 +44,7 @@ static int sandbox_submit_control(struct udevice *bus, void *buffer, int length, struct devrequest *setup) { + struct sandbox_usb_ctrl *ctrl = dev_get_priv(bus); struct udevice *emul; int ret; @@ -49,6 +54,14 @@ static int sandbox_submit_control(struct udevice *bus, usbmon_trace(bus, pipe, setup, emul); if (ret) return ret; + + if (usb_pipedevice(pipe) == ctrl->rootdev) { + if (setup->request == USB_REQ_SET_ADDRESS) { + debug("%s: Set root hub's USB address\n", __func__); + ctrl->rootdev = le16_to_cpu(setup->value); + } + } + ret = usb_emul_control(emul, udev, pipe, buffer, length, setup); if (ret < 0) { debug("ret=%d\n", ret); @@ -107,6 +120,16 @@ static int sandbox_submit_int(struct udevice *bus, struct usb_device *udev, static int sandbox_alloc_device(struct udevice *dev, struct usb_device *udev) { + struct sandbox_usb_ctrl *ctrl = dev_get_priv(dev); + + /* + * Root hub will be the first device to be initailized. + * If this device is a root hub, initialize its device speed + * to high speed as we are a USB 2.0 controller. + */ + if (ctrl->rootdev == 0) + udev->speed = USB_SPEED_HIGH; + return 0; } @@ -133,4 +156,5 @@ U_BOOT_DRIVER(usb_sandbox) = { .of_match = sandbox_usb_ids, .probe = sandbox_usb_probe, .ops = &sandbox_usb_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_usb_ctrl), }; From 84aa8536f0197e439832f56cc7b554af488fc3c8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:39 -0700 Subject: [PATCH 04/24] usb: sandbox: Fix emulator device select logic in usb_emul_find_devnum() Current emulator select logic in usb_emul_find_devnum() is to test the USB address. The USB address of the device being enumerated is initialized to zero at the beginning of the enumeration process in usb_setup_device(). At this point, the saved USB address in the platform data has not been assigned to any valid USB address either. This means: the logic will select an emulator device according to its sequence of declaring order in the device tree. Take test.dts for example, flash-stick@0 will be selected before flash-stick@1. But unfortunately such logic is wrong. In fact USB devices show up in a random order during the enumeration which means usb_emul_find_devnum() may be called on port 3 for keyb@3 before on port 0 for flash-stick@0. To fix this, we introduce a new emulator uclass specific platdata to store the USB device's port number on its parent hub, and update the logic to test the port number instead. Signed-off-by: Bin Meng --- drivers/usb/emul/sandbox_hub.c | 2 ++ drivers/usb/emul/usb-emul-uclass.c | 42 ++++++++++++++++++++++++++---- drivers/usb/host/usb-sandbox.c | 6 ++--- include/usb.h | 18 +++++++++++-- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c index 9ed700036e..8ed7a0f434 100644 --- a/drivers/usb/emul/sandbox_hub.c +++ b/drivers/usb/emul/sandbox_hub.c @@ -280,8 +280,10 @@ static int sandbox_hub_bind(struct udevice *dev) static int sandbox_child_post_bind(struct udevice *dev) { struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev); + struct usb_emul_platdata *emul = dev_get_uclass_platdata(dev); plat->port = dev_read_u32_default(dev, "reg", -1); + emul->port1 = plat->port + 1; return 0; } diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c index e4a267bf00..1203eb2f11 100644 --- a/drivers/usb/emul/usb-emul-uclass.c +++ b/drivers/usb/emul/usb-emul-uclass.c @@ -107,7 +107,7 @@ static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value, return upto ? upto : length ? -EIO : 0; } -static int usb_emul_find_devnum(int devnum, struct udevice **emulp) +static int usb_emul_find_devnum(int devnum, int port1, struct udevice **emulp) { struct udevice *dev; struct uclass *uc; @@ -120,7 +120,37 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp) uclass_foreach_dev(dev, uc) { struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); - if (udev->devnum == devnum) { + /* + * devnum is initialzied to zero at the beginning of the + * enumeration process in usb_setup_device(). At this + * point, udev->devnum has not been assigned to any valid + * USB address either, so we can't rely on the comparison + * result between udev->devnum and devnum to select an + * emulator device. + */ + if (!devnum) { + struct usb_emul_platdata *plat; + + /* + * If the parent is sandbox USB controller, we are + * the root hub. And there is only one root hub + * in the system. + */ + if (device_get_uclass_id(dev->parent) == UCLASS_USB) { + debug("%s: Found emulator '%s'\n", + __func__, dev->name); + *emulp = dev; + return 0; + } + + plat = dev_get_uclass_platdata(dev); + if (plat->port1 == port1) { + debug("%s: Found emulator '%s', port %d\n", + __func__, dev->name, port1); + *emulp = dev; + return 0; + } + } else if (udev->devnum == devnum) { debug("%s: Found emulator '%s', addr %d\n", __func__, dev->name, udev->devnum); *emulp = dev; @@ -132,18 +162,19 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp) return -ENOENT; } -int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp) +int usb_emul_find(struct udevice *bus, ulong pipe, int port1, + struct udevice **emulp) { int devnum = usb_pipedevice(pipe); - return usb_emul_find_devnum(devnum, emulp); + return usb_emul_find_devnum(devnum, port1, emulp); } int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp) { struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); - return usb_emul_find_devnum(udev->devnum, emulp); + return usb_emul_find_devnum(udev->devnum, 0, emulp); } int usb_emul_control(struct udevice *emul, struct usb_device *udev, @@ -276,6 +307,7 @@ UCLASS_DRIVER(usb_emul) = { .id = UCLASS_USB_EMUL, .name = "usb_emul", .post_bind = dm_scan_fdt_dev, + .per_device_platdata_auto_alloc_size = sizeof(struct usb_emul_platdata), .per_child_auto_alloc_size = sizeof(struct usb_device), .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), }; diff --git a/drivers/usb/host/usb-sandbox.c b/drivers/usb/host/usb-sandbox.c index f85d36ba42..15055b351a 100644 --- a/drivers/usb/host/usb-sandbox.c +++ b/drivers/usb/host/usb-sandbox.c @@ -50,7 +50,7 @@ static int sandbox_submit_control(struct udevice *bus, /* Just use child of dev as emulator? */ debug("%s: bus=%s\n", __func__, bus->name); - ret = usb_emul_find(bus, pipe, &emul); + ret = usb_emul_find(bus, pipe, udev->portnr, &emul); usbmon_trace(bus, pipe, setup, emul); if (ret) return ret; @@ -83,7 +83,7 @@ static int sandbox_submit_bulk(struct udevice *bus, struct usb_device *udev, /* Just use child of dev as emulator? */ debug("%s: bus=%s\n", __func__, bus->name); - ret = usb_emul_find(bus, pipe, &emul); + ret = usb_emul_find(bus, pipe, udev->portnr, &emul); usbmon_trace(bus, pipe, NULL, emul); if (ret) return ret; @@ -109,7 +109,7 @@ static int sandbox_submit_int(struct udevice *bus, struct usb_device *udev, /* Just use child of dev as emulator? */ debug("%s: bus=%s\n", __func__, bus->name); - ret = usb_emul_find(bus, pipe, &emul); + ret = usb_emul_find(bus, pipe, udev->portnr, &emul); usbmon_trace(bus, pipe, NULL, emul); if (ret) return ret; diff --git a/include/usb.h b/include/usb.h index 3d517316e5..63eddddc91 100644 --- a/include/usb.h +++ b/include/usb.h @@ -652,6 +652,18 @@ struct usb_bus_priv { bool companion; }; +/** + * struct usb_emul_platdata - platform data about the USB emulator + * + * Given a USB emulator (UCLASS_USB_EMUL) 'dev', this is + * dev_get_uclass_platdata(dev). + * + * @port1: USB emulator device port number on the parent hub + */ +struct usb_emul_platdata { + int port1; /* Port number (numbered from 1) */ +}; + /** * struct dm_usb_ops - USB controller operations * @@ -1023,14 +1035,16 @@ int usb_emul_int(struct udevice *emul, struct usb_device *udev, /** * usb_emul_find() - Find an emulator for a particular device * - * Check @pipe to find a device number on bus @bus and return it. + * Check @pipe and @port1 to find a device number on bus @bus and return it. * * @bus: USB bus (controller) * @pipe: Describes pipe being used, and includes the device number + * @port1: Describes port number on the parent hub * @emulp: Returns pointer to emulator, or NULL if not found * @return 0 if found, -ve on error */ -int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp); +int usb_emul_find(struct udevice *bus, ulong pipe, int port1, + struct udevice **emulp); /** * usb_emul_find_for_dev() - Find an emulator for a particular device From 848436a48d21447fc78bef67a4cbf11392536de2 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:40 -0700 Subject: [PATCH 05/24] usb: emul: Expose find_descriptor() as a public API This can be useful outside of the sandbox usb emulation uclass driver. Expose it as a public API with a proper prefix (usb_emul_). Signed-off-by: Bin Meng --- drivers/usb/emul/usb-emul-uclass.c | 5 ++--- include/usb.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c index 1203eb2f11..359d0da07e 100644 --- a/drivers/usb/emul/usb-emul-uclass.c +++ b/drivers/usb/emul/usb-emul-uclass.c @@ -52,7 +52,7 @@ static int usb_emul_get_string(struct usb_string *strings, int index, return -EINVAL; } -static struct usb_generic_descriptor **find_descriptor( +struct usb_generic_descriptor **usb_emul_find_descriptor( struct usb_generic_descriptor **ptr, int type, int index) { debug("%s: type=%x, index=%d\n", __func__, type, index); @@ -91,8 +91,7 @@ static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value, length); } - ptr = find_descriptor((struct usb_generic_descriptor **)plat->desc_list, - type, index); + ptr = usb_emul_find_descriptor(plat->desc_list, type, index); if (!ptr) { debug("%s: Could not find descriptor type %d, index %d\n", __func__, type, index); diff --git a/include/usb.h b/include/usb.h index 63eddddc91..3766514635 100644 --- a/include/usb.h +++ b/include/usb.h @@ -1055,6 +1055,17 @@ int usb_emul_find(struct udevice *bus, ulong pipe, int port1, */ int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp); +/** + * usb_emul_find_descriptor() - Find a USB descriptor of a particular device + * + * @ptr: a pointer to a list of USB descriptor pointers + * @type: type of USB descriptor to find + * @index: if @type is USB_DT_CONFIG, this is the configuration value + * @return a pointer to the USB descriptor found, NULL if not found + */ +struct usb_generic_descriptor **usb_emul_find_descriptor( + struct usb_generic_descriptor **ptr, int type, int index); + /** * usb_emul_reset() - Reset all emulators ready for use * From d7771f0c5cd429286c7555360d779d21568e3ba0 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:41 -0700 Subject: [PATCH 06/24] usb: emul: hub: Report the actual device speed of the emulation device At present the usb hub emulator always reports its downstream port speed as full speed. Actually it is high speed for sandbox-flash, and low speed for sandbox-keyb. We can determine the device speed by checking its device descriptor bcdUSB field, and do the proper hub port status report based on that. Signed-off-by: Bin Meng --- drivers/usb/emul/sandbox_hub.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c index 8ed7a0f434..9a0f47b81c 100644 --- a/drivers/usb/emul/sandbox_hub.c +++ b/drivers/usb/emul/sandbox_hub.c @@ -121,9 +121,12 @@ struct sandbox_hub_priv { int change[SANDBOX_NUM_PORTS]; }; -static struct udevice *hub_find_device(struct udevice *hub, int port) +static struct udevice *hub_find_device(struct udevice *hub, int port, + enum usb_device_speed *speed) { struct udevice *dev; + struct usb_generic_descriptor **gen_desc; + struct usb_device_descriptor **dev_desc; for (device_find_first_child(hub, &dev); dev; @@ -131,8 +134,27 @@ static struct udevice *hub_find_device(struct udevice *hub, int port) struct sandbox_hub_platdata *plat; plat = dev_get_parent_platdata(dev); - if (plat->port == port) + if (plat->port == port) { + gen_desc = plat->plat.desc_list; + gen_desc = usb_emul_find_descriptor(gen_desc, + USB_DT_DEVICE, 0); + dev_desc = (struct usb_device_descriptor **)gen_desc; + + switch (le16_to_cpu((*dev_desc)->bcdUSB)) { + case 0x0100: + *speed = USB_SPEED_LOW; + break; + case 0x0101: + *speed = USB_SPEED_FULL; + break; + case 0x0200: + default: + *speed = USB_SPEED_HIGH; + break; + } + return dev; + } } return NULL; @@ -146,7 +168,8 @@ static int clrset_post_state(struct udevice *hub, int port, int clear, int set) int ret = 0; if ((clear | set) & USB_PORT_STAT_POWER) { - struct udevice *dev = hub_find_device(hub, port); + enum usb_device_speed speed; + struct udevice *dev = hub_find_device(hub, port, &speed); if (dev) { if (set & USB_PORT_STAT_POWER) { @@ -156,6 +179,10 @@ static int clrset_post_state(struct udevice *hub, int port, int clear, int set) if (!ret) { set |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE; + if (speed == USB_SPEED_LOW) + set |= USB_PORT_STAT_LOW_SPEED; + else if (speed == USB_SPEED_HIGH) + set |= USB_PORT_STAT_HIGH_SPEED; } } else if (clear & USB_PORT_STAT_POWER) { From d4efefe32ea8a45b7b30f4769b3928c28e181c73 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:42 -0700 Subject: [PATCH 07/24] dm: usb: Fix broken usb_stop() At present we only do device_remove() during usb stop. The DM API device_remove() only marks the device state as inactivated, but still keeps its USB topology (eg: parent, children, etc) in the DM device structure. There is no issue if we only start USB subsystem once and never stop it. But a big issue occurs when we do 'usb stop' and 'usb start' multiple times. Strange things may be observed with current implementation, like: - the enumeration may report only 1 mass storage device is detected, but the total number of USB devices is correct. - USB keyboard does not work anymore after a bunch of 'usb reset' even if 'usb tree' shows it is correctly identified. - read/write flash drive via 'fatload usb' may complain "Bad device" In fact, every time when USB host controller starts the enumeration process, it takes random time for each USB port to show up online, hence each USB device may appear in a different order from previous enumeration, and gets assigned to a totally different USB address. As a result, we end up using a stale USB topology in the DM device structure which still reflects the previous enumeration result, and it may create an exact same DM device name like generic_bus_0_dev_7 that is already in the DM device structure. And since the DM device structure is there, there is no device_bind() call to bind driver to the device during current enumeration process, eventually creating an inconsistent software representation of the hardware topology, a non-working USB subsystem. The fix is to clear the unused USB topology in the usb_stop(), by calling device_unbind() on each controller's root hub device, and the unbinding will unbind all of its children automatically. 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. Signed-off-by: Bin Meng --- drivers/usb/host/usb-uclass.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index bc44fc3394..8a9f810bf8 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -164,6 +164,7 @@ int usb_get_max_xfer_size(struct usb_device *udev, size_t *size) int usb_stop(void) { struct udevice *bus; + struct udevice *rh; struct uclass *uc; struct usb_uclass_priv *uc_priv; int err = 0, ret; @@ -179,6 +180,18 @@ int usb_stop(void) ret = device_remove(bus, DM_REMOVE_NORMAL); if (ret && !err) err = 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; + } } #ifdef CONFIG_BLK ret = blk_unbind_all(IF_TYPE_USB); @@ -262,6 +275,21 @@ int usb_init(void) /* init low_level USB */ printf("USB%d: ", count); count++; + +#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"); From ad0a9378bf5cc9280e117b7db94b6bfa1b6e8e76 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:43 -0700 Subject: [PATCH 08/24] dm: usb: Remove no longer needed blk_unbind_all() With the root hub unbinding in usb_stop(), there is no need to do a blk uclass specific unbind operation. Signed-off-by: Bin Meng --- drivers/usb/host/usb-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 8a9f810bf8..bfc0556d7d 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -193,11 +193,7 @@ int usb_stop(void) err = ret; } } -#ifdef CONFIG_BLK - ret = blk_unbind_all(IF_TYPE_USB); - if (ret && !err) - err = ret; -#endif + #ifdef CONFIG_SANDBOX struct udevice *dev; From ad56e4b684a97565cdce15c28df1ccff9032d594 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:44 -0700 Subject: [PATCH 09/24] dm: usb: emul: Drop usb_emul_reset() With the root hub unbinding in usb_stop(), there is no need to do a Sandbox-specific reset operation. usb_emul_reset() is no longer used anywhere, drop it. Signed-off-by: Bin Meng --- drivers/usb/emul/usb-emul-uclass.c | 8 -------- drivers/usb/host/usb-uclass.c | 11 ----------- include/usb.h | 8 -------- 3 files changed, 27 deletions(-) diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c index 359d0da07e..fbe11f3135 100644 --- a/drivers/usb/emul/usb-emul-uclass.c +++ b/drivers/usb/emul/usb-emul-uclass.c @@ -294,14 +294,6 @@ int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings, return 0; } -void usb_emul_reset(struct udevice *dev) -{ - struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); - - plat->devnum = 0; - plat->configno = 0; -} - UCLASS_DRIVER(usb_emul) = { .id = UCLASS_USB_EMUL, .name = "usb_emul", diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index bfc0556d7d..4e40f4bc3d 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -194,17 +194,6 @@ int usb_stop(void) } } -#ifdef CONFIG_SANDBOX - struct udevice *dev; - - /* Reset all enulation devices */ - ret = uclass_get(UCLASS_USB_EMUL, &uc); - if (ret) - return ret; - - uclass_foreach_dev(dev, uc) - usb_emul_reset(dev); -#endif #ifdef CONFIG_USB_STORAGE usb_stor_reset(); #endif diff --git a/include/usb.h b/include/usb.h index 3766514635..57a7d8d0ea 100644 --- a/include/usb.h +++ b/include/usb.h @@ -1066,14 +1066,6 @@ int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp); struct usb_generic_descriptor **usb_emul_find_descriptor( struct usb_generic_descriptor **ptr, int type, int index); -/** - * usb_emul_reset() - Reset all emulators ready for use - * - * Clear out any address information in the emulators and make then ready for - * a new USB scan - */ -void usb_emul_reset(struct udevice *dev); - /** * usb_show_tree() - show the USB device tree * From f4d4f7d41cec8c8bfe0fc60d13da059839a56671 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 1 Oct 2017 06:19:45 -0700 Subject: [PATCH 10/24] test: dm: usb: Update test cases for USB Now that we have changed to remove all devices under the root hub in usb_stop(), and corrected the USB emulator select logic, it makes no sense to do various tests based on 'usb tree' output since the order of devices is no longer fixed. Remove these USB test cases related to 'usb tree'. For the USB remove test, ideally we should remove an emulator device node from the device tree, but this is so far not working. Change to test the 'usb stop' only. Signed-off-by: Bin Meng --- test/dm/usb.c | 163 ++------------------------------------------------ 1 file changed, 5 insertions(+), 158 deletions(-) diff --git a/test/dm/usb.c b/test/dm/usb.c index b46ae60602..4fd249bf64 100644 --- a/test/dm/usb.c +++ b/test/dm/usb.c @@ -99,10 +99,10 @@ static int count_usb_devices(void) return count; } -/* test that we can remove an emulated device and it is then not found */ -static int dm_test_usb_remove(struct unit_test_state *uts) +/* test that no USB devices are found after we stop the stack */ +static int dm_test_usb_stop(struct unit_test_state *uts) { - struct udevice *dev, *emul; + struct udevice *dev; /* Scan and check that all devices are present */ state_set_skip_delays(true); @@ -112,164 +112,11 @@ static int dm_test_usb_remove(struct unit_test_state *uts) ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); ut_asserteq(6, count_usb_devices()); ut_assertok(usb_stop()); - ut_asserteq(6, count_usb_devices()); - - /* Remove the second emulation device */ - ut_assertok(uclass_find_device_by_name(UCLASS_USB_EMUL, "flash-stick@1", - &dev)); - ut_assertok(device_unbind(dev)); - - /* Rescan - only the first and third should be present */ - ut_assertok(usb_init()); - ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); - ut_assertok(usb_emul_find_for_dev(dev, &emul)); - ut_asserteq_str("flash-stick@0", emul->name); - ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev)); - ut_assertok(usb_emul_find_for_dev(dev, &emul)); - ut_asserteq_str("flash-stick@2", emul->name); - - ut_asserteq(-ENODEV, uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); - - ut_asserteq(5, count_usb_devices()); - ut_assertok(usb_stop()); - ut_asserteq(5, count_usb_devices()); + ut_asserteq(0, count_usb_devices()); return 0; } -DM_TEST(dm_test_usb_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); - -const char usb_tree_base[] = -" 1 Hub (12 Mb/s, 100mA)\n" -" | sandbox hub 2345\n" -" |\n" -" |\b+-2 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@0\n" -" | \n" -" |\b+-3 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@1\n" -" | \n" -" |\b+-4 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@2\n" -" | \n" -" |\b+-5 Human Interface (12 Mb/s, 100mA)\n" -" sandbox keyboard keyb@3\n" -" \n"; - -/* test that the 'usb tree' command output looks correct */ -static int dm_test_usb_tree(struct unit_test_state *uts) -{ - char *data; - int len; - - state_set_skip_delays(true); - ut_assertok(usb_init()); - console_record_reset_enable(); - usb_show_tree(); - len = membuff_getraw(&gd->console_out, -1, true, &data); - if (len) - data[len] = '\0'; - ut_asserteq_str(usb_tree_base, data); - ut_assertok(usb_stop()); - - return 0; -} -DM_TEST(dm_test_usb_tree, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); - -const char usb_tree_remove[] = -" 1 Hub (12 Mb/s, 100mA)\n" -" | sandbox hub 2345\n" -" |\n" -" |\b+-2 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@0\n" -" | \n" -" |\b+-3 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@2\n" -" | \n" -" |\b+-4 Human Interface (12 Mb/s, 100mA)\n" -" sandbox keyboard keyb@3\n" -" \n"; - -/* - * test that the 'usb tree' command output looks correct when we remove a - * device - */ -static int dm_test_usb_tree_remove(struct unit_test_state *uts) -{ - struct udevice *dev; - char *data; - int len; - - /* Remove the second emulation device */ - ut_assertok(uclass_find_device_by_name(UCLASS_USB_EMUL, "flash-stick@1", - &dev)); - ut_assertok(device_unbind(dev)); - - state_set_skip_delays(true); - ut_assertok(usb_init()); - console_record_reset_enable(); - usb_show_tree(); - len = membuff_getraw(&gd->console_out, -1, true, &data); - if (len) - data[len] = '\0'; - ut_asserteq_str(usb_tree_remove, data); - ut_assertok(usb_stop()); - - return 0; -} -DM_TEST(dm_test_usb_tree_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); - -const char usb_tree_reorder[] = -" 1 Hub (12 Mb/s, 100mA)\n" -" | sandbox hub 2345\n" -" |\n" -" |\b+-2 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@0\n" -" | \n" -" |\b+-3 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@2\n" -" | \n" -" |\b+-4 Human Interface (12 Mb/s, 100mA)\n" -" | sandbox keyboard keyb@3\n" -" | \n" -" |\b+-5 Mass Storage (12 Mb/s, 100mA)\n" -" sandbox flash flash-stick@1\n" -" \n"; - -/* - * test that the 'usb tree' command output looks correct when we reorder two - * devices. - */ -static int dm_test_usb_tree_reorder(struct unit_test_state *uts) -{ - struct udevice *dev, *parent; - char *data; - int len; - - /* Remove the second emulation device */ - ut_assertok(uclass_find_device_by_name(UCLASS_USB_EMUL, "flash-stick@1", - &dev)); - parent = dev->parent; - - /* Reorder the devices in the parent list and uclass list */ - list_del(&dev->sibling_node); - list_add_tail(&dev->sibling_node, &parent->child_head); - - list_del(&dev->uclass_node); - list_add_tail(&dev->uclass_node, &dev->uclass->dev_head); - - state_set_skip_delays(true); - ut_assertok(usb_init()); - console_record_reset_enable(); - usb_show_tree(); - len = membuff_getraw(&gd->console_out, -1, true, &data); - if (len) - data[len] = '\0'; - ut_asserteq_str(usb_tree_reorder, data); - ut_assertok(usb_stop()); - - return 0; -} -DM_TEST(dm_test_usb_tree_reorder, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +DM_TEST(dm_test_usb_stop, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); static int dm_test_usb_keyb(struct unit_test_state *uts) { From 8a0e6d83070a977442aaba2c5a74cbe34e157012 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:39 -0700 Subject: [PATCH 11/24] usb: xhci: Don't assume LS/FS devices are always behind a HS hub At present xHCI driver assumes LS/FS devices are attached directly to a HS hub. If they are connected to a LS/FS hub, the driver will fail to perform the USB enumeration process on such devices. This is fixed by looking from the device itself all the way up to the HS hub where the TT that serves the device is located. Signed-off-by: Bin Meng --- drivers/usb/host/xhci-mem.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d5eab3a615..84982a92d6 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -786,12 +786,22 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, #ifdef CONFIG_DM_USB /* Set up TT fields to support FS/LS devices */ if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) { - dev = dev_get_parent_priv(udev->dev); - if (dev->speed == USB_SPEED_HIGH) { - hub = dev_get_uclass_priv(udev->dev); + struct udevice *parent = udev->dev; + + dev = udev; + do { + port_num = dev->portnr; + dev = dev_get_parent_priv(parent); + if (usb_hub_is_root_hub(dev->dev)) + break; + parent = dev->dev->parent; + } while (dev->speed != USB_SPEED_HIGH); + + if (!usb_hub_is_root_hub(dev->dev)) { + hub = dev_get_uclass_priv(dev->dev); if (hub->tt.multi) slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); - slot_ctx->tt_info |= cpu_to_le32(TT_PORT(udev->portnr)); + slot_ctx->tt_info |= cpu_to_le32(TT_PORT(port_num)); slot_ctx->tt_info |= cpu_to_le32(TT_SLOT(dev->slot_id)); } } From 2f0eb2ac4b84f94e2f7de6e4ec9b63ce07cfec7a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:40 -0700 Subject: [PATCH 12/24] usb: Handle audio extension endpoint descriptor in usb_parse_config() Normal endpoint descriptor size is 7, but for audio extension it is 9. Handle that correctly when parsing endpoint descriptor. Signed-off-by: Bin Meng --- common/usb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/usb.c b/common/usb.c index 0904259757..6cb92ef4f5 100644 --- a/common/usb.c +++ b/common/usb.c @@ -437,12 +437,13 @@ static int usb_parse_config(struct usb_device *dev, } break; case USB_DT_ENDPOINT: - if (head->bLength != USB_DT_ENDPOINT_SIZE) { + if (head->bLength != USB_DT_ENDPOINT_SIZE && + head->bLength != USB_DT_ENDPOINT_AUDIO_SIZE) { printf("ERROR: Invalid USB EP length (%d)\n", head->bLength); break; } - if (index + USB_DT_ENDPOINT_SIZE > + if (index + head->bLength > dev->config.desc.wTotalLength) { puts("USB EP descriptor overflowed buffer!\n"); break; From 1897d60130976ece389d5875187b78ba0d41428f Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:41 -0700 Subject: [PATCH 13/24] usb: xhci: Add interrupt transfer support xHCI uses normal TRBs for both bulk and interrupt. This adds the missing interrupt transfer support to xHCI so that devices like USB keyboard that uses interrupt transfer can work. Signed-off-by: Bin Meng --- drivers/usb/host/xhci.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 04eb1eb14d..4b3d58d56e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -890,11 +890,18 @@ unknown: static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, int interval) { + if (usb_pipetype(pipe) != PIPE_INTERRUPT) { + printf("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + /* - * TODO: Not addressing any interrupt type transfer requests - * Add support for it later. + * xHCI uses normal TRBs for both bulk and interrupt. When the + * interrupt endpoint is to be serviced, the xHC will consume + * (at most) one TD. A TD (comprised of sg list entries) can + * take several service intervals to transmit. */ - return -EINVAL; + return xhci_bulk_tx(udev, pipe, length, buffer); } /** From c008faa77358bb5b313696dd1d5bb8afa03a6ca2 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:42 -0700 Subject: [PATCH 14/24] usb: Only get 64 bytes device descriptor for full speed devices Full speed device endpoint 0 can have 8/16/32/64 bMaxPacketSize0. Other speed devices report fixed value per USB spec. So it only makes sense if we send a get device descriptor with 64 bytes to full speed devices. While we are here, update the comment block to be within 80 cols. Signed-off-by: Bin Meng --- common/usb.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/common/usb.c b/common/usb.c index 6cb92ef4f5..88cee810fa 100644 --- a/common/usb.c +++ b/common/usb.c @@ -970,23 +970,24 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; - if (do_read) { + if (do_read && dev->speed == USB_SPEED_FULL) { int err; /* - * Validate we've received only at least 8 bytes, not that we've - * received the entire descriptor. The reasoning is: - * - The code only uses fields in the first 8 bytes, so that's all we - * need to have fetched at this stage. - * - The smallest maxpacket size is 8 bytes. Before we know the actual - * maxpacket the device uses, the USB controller may only accept a - * single packet. Consequently we are only guaranteed to receive 1 - * packet (at least 8 bytes) even in a non-error case. + * Validate we've received only at least 8 bytes, not that + * we've received the entire descriptor. The reasoning is: + * - The code only uses fields in the first 8 bytes, so + * that's all we need to have fetched at this stage. + * - The smallest maxpacket size is 8 bytes. Before we know + * the actual maxpacket the device uses, the USB controller + * may only accept a single packet. Consequently we are only + * guaranteed to receive 1 packet (at least 8 bytes) even in + * a non-error case. * - * At least the DWC2 controller needs to be programmed with the number - * of packets in addition to the number of bytes. A request for 64 - * bytes of data with the maxpacket guessed as 64 (above) yields a - * request for 1 packet. + * At least the DWC2 controller needs to be programmed with + * the number of packets in addition to the number of bytes. + * A request for 64 bytes of data with the maxpacket guessed + * as 64 (above) yields a request for 1 packet. */ err = get_descriptor_len(dev, 64, 8); if (err) @@ -1009,7 +1010,7 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) dev->maxpacketsize = PACKET_SIZE_64; break; default: - printf("usb_new_device: invalid max packet size\n"); + printf("%s: invalid max packet size\n", __func__); return -EIO; } From 932bb668bb2464115f2d08abbed44e58cfce9536 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:43 -0700 Subject: [PATCH 15/24] usb: Read device descriptor after device is addressed for xHCI For xHCI it is not possible to read a device descriptor before it has been assigned an address. That's why usb_setup_descriptor() was called with 'do_read' being false. But we really need try to read the device descriptor before starting any real communication with the default control endpoint. Signed-off-by: Bin Meng --- common/usb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/usb.c b/common/usb.c index 88cee810fa..8d27bc7060 100644 --- a/common/usb.c +++ b/common/usb.c @@ -1052,6 +1052,17 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, mdelay(10); /* Let the SET_ADDRESS settle */ + /* + * If we haven't read device descriptor before, read it here + * after device is assigned an address. This is only applicable + * to xHCI so far. + */ + if (!do_read) { + err = usb_setup_descriptor(dev, true); + if (err) + return err; + } + return 0; } From b5aa857b95194c15126245e99a384ec2fd9536e8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:44 -0700 Subject: [PATCH 16/24] usb: xhci: Fix max packet size for full speed device endpoint 0 In xhci_check_maxpacket(), the control endpoint 0 max packet size is wrongly taken from the interface's endpoint descriptor. However the default endpoint 0 does not come with an endpoint descriptor hence is not included in the interface structure. Change to use epmaxpacketin[0] instead. The other bug in this routine is that when setting max packet size to the xHC endpoint 0 context, it does not clear its previous value at all before programming a new one. Signed-off-by: Bin Meng --- drivers/usb/host/xhci.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4b3d58d56e..ec82fa664e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -546,16 +546,13 @@ int xhci_check_maxpacket(struct usb_device *udev) int max_packet_size; int hw_max_packet_size; int ret = 0; - struct usb_interface *ifdesc; - - ifdesc = &udev->config.if_desc[0]; out_ctx = ctrl->devs[slot_id]->out_ctx; xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = usb_endpoint_maxp(&ifdesc->ep_desc[0]); + max_packet_size = udev->epmaxpacketin[0]; if (hw_max_packet_size != max_packet_size) { debug("Max Packet Size for ep 0 changed.\n"); debug("Max packet size in usb_device = %d\n", max_packet_size); @@ -567,7 +564,8 @@ int xhci_check_maxpacket(struct usb_device *udev) ctrl->devs[slot_id]->out_ctx, ep_index); in_ctx = ctrl->devs[slot_id]->in_ctx; ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); - ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); + ep_ctx->ep_info2 &= cpu_to_le32(~((0xffff & MAX_PACKET_MASK) + << MAX_PACKET_SHIFT)); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); /* From b90203526f2c5bcc05b4a65241ea226b7b9f52d0 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:45 -0700 Subject: [PATCH 17/24] usb: hub: Clear port reset before usb_hub_port_connect_change() During usb_hub_port_connect_change(), a port reset set feature request is issued to the port, and later a port reset clear feature is done to the same port before the function returns. However at the end of usb_scan_port(), we attempt to clear port reset again on a cached port status change variable, which should not be done. Adjust the call to clear port reset to right before the call to usb_hub_port_connect_change(). Signed-off-by: Bin Meng --- common/usb_hub.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/usb_hub.c b/common/usb_hub.c index 86a3477664..a9d21bca5e 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -489,6 +489,11 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) return 0; } + if (portchange & USB_PORT_STAT_C_RESET) { + debug("port %d reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); + } + /* A new USB device is ready at this point */ debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1); @@ -543,11 +548,6 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) hub->overcurrent_count[i]); } - if (portchange & USB_PORT_STAT_C_RESET) { - debug("port %d reset change\n", i + 1); - usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); - } - /* * We're done with this device, so let's remove this device from * scanning list From 061895fbe57d29f50bb3c6c8609d56a668d1387d Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:46 -0700 Subject: [PATCH 18/24] usb: hub: Clear BH reset status change for a 3.0 hub USB 3.0 hubs report bit[5] in the port status change response as BH reset. The hub shall set the C_BH_PORT_RESET field for this port. Signed-off-by: Bin Meng --- common/usb_hub.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/usb_hub.c b/common/usb_hub.c index a9d21bca5e..325d16dfc8 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -494,6 +494,12 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } + if ((portchange & USB_SS_PORT_STAT_C_BH_RESET) && + usb_hub_is_superspeed(dev)) { + debug("port %d BH reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_SS_PORT_FEAT_C_BH_RESET); + } + /* A new USB device is ready at this point */ debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1); From f51966bf7afe44151756e9a2432705bb56bc2007 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:47 -0700 Subject: [PATCH 19/24] usb: xhci: Honor endpoint's interval USB endpoint reports the period between consecutive requests to send or receive data as bInverval in its endpoint descriptor. So far this is ignored by xHCI driver and the 'Interval' field in xHC's endpoint context is always programmed to zero which means 1ms for low speed or full speed , or 125us for high speed or super speed. We should honor the interval by getting it from endpoint descriptor. Signed-off-by: Bin Meng --- drivers/usb/host/xhci.c | 195 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 5 +- include/linux/usb/ch9.h | 20 +++++ 3 files changed, 218 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ec82fa664e..8aed4283ed 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -257,6 +257,172 @@ static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc) return index; } +/* + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of + * microframes, rounded down to nearest power of 2. + */ +static unsigned int xhci_microframes_to_exponent(unsigned int desc_interval, + unsigned int min_exponent, + unsigned int max_exponent) +{ + unsigned int interval; + + interval = fls(desc_interval) - 1; + interval = clamp_val(interval, min_exponent, max_exponent); + if ((1 << interval) != desc_interval) + debug("rounding interval to %d microframes, "\ + "ep desc says %d microframes\n", + 1 << interval, desc_interval); + + return interval; +} + +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + if (endpt_desc->bInterval == 0) + return 0; + + return xhci_microframes_to_exponent(endpt_desc->bInterval, 0, 15); +} + +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + return xhci_microframes_to_exponent(endpt_desc->bInterval * 8, 3, 10); +} + +/* + * Convert interval expressed as 2^(bInterval - 1) == interval into + * straight exponent value 2^n == interval. + */ +static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + unsigned int interval; + + interval = clamp_val(endpt_desc->bInterval, 1, 16) - 1; + if (interval != endpt_desc->bInterval - 1) + debug("ep %#x - rounding interval to %d %sframes\n", + endpt_desc->bEndpointAddress, 1 << interval, + udev->speed == USB_SPEED_FULL ? "" : "micro"); + + if (udev->speed == USB_SPEED_FULL) { + /* + * Full speed isoc endpoints specify interval in frames, + * not microframes. We are using microframes everywhere, + * so adjust accordingly. + */ + interval += 3; /* 1 frame = 2^3 uframes */ + } + + return interval; +} + +/* + * Return the polling or NAK interval. + * + * The polling interval is expressed in "microframes". If xHCI's Interval field + * is set to N, it will service the endpoint every 2^(Interval)*125us. + * + * The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval + * is set to 0. + */ +static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + unsigned int interval = 0; + + switch (udev->speed) { + case USB_SPEED_HIGH: + /* Max NAK rate */ + if (usb_endpoint_xfer_control(endpt_desc) || + usb_endpoint_xfer_bulk(endpt_desc)) { + interval = xhci_parse_microframe_interval(udev, + endpt_desc); + break; + } + /* Fall through - SS and HS isoc/int have same decoding */ + + case USB_SPEED_SUPER: + if (usb_endpoint_xfer_int(endpt_desc) || + usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_exponent_interval(udev, + endpt_desc); + } + break; + + case USB_SPEED_FULL: + if (usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_exponent_interval(udev, + endpt_desc); + break; + } + /* + * Fall through for interrupt endpoint interval decoding + * since it uses the same rules as low speed interrupt + * endpoints. + */ + + case USB_SPEED_LOW: + if (usb_endpoint_xfer_int(endpt_desc) || + usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_frame_interval(udev, endpt_desc); + } + break; + + default: + BUG(); + } + + return interval; +} + +/* + * The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. + * High speed endpoint descriptors can define "the number of additional + * transaction opportunities per microframe", but that goes in the Max Burst + * endpoint context field. + */ +static u32 xhci_get_endpoint_mult(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + if (udev->speed < USB_SPEED_SUPER || + !usb_endpoint_xfer_isoc(endpt_desc)) + return 0; + + return ss_ep_comp_desc->bmAttributes; +} + +/* + * Return the maximum endpoint service interval time (ESIT) payload. + * Basically, this is the maxpacket size, multiplied by the burst size + * and mult size. + */ +static u32 xhci_get_max_esit_payload(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + int max_burst; + int max_packet; + + /* Only applies for interrupt or isochronous endpoints */ + if (usb_endpoint_xfer_control(endpt_desc) || + usb_endpoint_xfer_bulk(endpt_desc)) + return 0; + + /* SuperSpeed Isoc ep with less than 48k per esit */ + if (udev->speed >= USB_SPEED_SUPER) + return le16_to_cpu(ss_ep_comp_desc->wBytesPerInterval); + + max_packet = usb_endpoint_maxp(endpt_desc); + max_burst = usb_endpoint_maxp_mult(endpt_desc); + + /* A 0 in max burst means 1 transfer per ESIT */ + return max_packet * max_burst; +} + /** * Issue a configure endpoint command or evaluate context command * and wait for it to finish. @@ -324,6 +490,10 @@ static int xhci_set_configuration(struct usb_device *udev) int slot_id = udev->slot_id; struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; struct usb_interface *ifdesc; + u32 max_esit_payload; + unsigned int interval; + unsigned int mult; + unsigned int avg_trb_len; out_ctx = virt_dev->out_ctx; in_ctx = virt_dev->in_ctx; @@ -357,10 +527,26 @@ static int xhci_set_configuration(struct usb_device *udev) /* filling up ep contexts */ for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { struct usb_endpoint_descriptor *endpt_desc = NULL; + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc = NULL; endpt_desc = &ifdesc->ep_desc[cur_ep]; + ss_ep_comp_desc = &ifdesc->ss_ep_comp_desc[cur_ep]; trb_64 = 0; + /* + * Get values to fill the endpoint context, mostly from ep + * descriptor. The average TRB buffer lengt for bulk endpoints + * is unclear as we have no clue on scatter gather list entry + * size. For Isoc and Int, set it to max available. + * See xHCI 1.1 spec 4.14.1.1 for details. + */ + max_esit_payload = xhci_get_max_esit_payload(udev, endpt_desc, + ss_ep_comp_desc); + interval = xhci_get_endpoint_interval(udev, endpt_desc); + mult = xhci_get_endpoint_mult(udev, endpt_desc, + ss_ep_comp_desc); + avg_trb_len = max_esit_payload; + ep_index = xhci_get_ep_index(endpt_desc); ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); @@ -372,6 +558,11 @@ static int xhci_set_configuration(struct usb_device *udev) /*NOTE: ep_desc[0] actually represents EP1 and so on */ dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7); ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2)); + + ep_ctx[ep_index]->ep_info = + cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | + EP_INTERVAL(interval) | EP_MULT(mult)); + ep_ctx[ep_index]->ep_info2 = cpu_to_le32(ep_type << EP_TYPE_SHIFT); ep_ctx[ep_index]->ep_info2 |= @@ -386,6 +577,10 @@ static int xhci_set_configuration(struct usb_device *udev) virt_dev->eps[ep_index].ring->enqueue; ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | virt_dev->eps[ep_index].ring->cycle_state); + + ep_ctx[ep_index]->tx_info = + cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | + EP_AVG_TRB_LENGTH(avg_trb_len)); } return xhci_configure_endpoints(udev, false); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3377450fca..ba5f650144 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -663,8 +663,9 @@ struct xhci_ep_ctx { #define GET_MAX_PACKET(p) ((p) & 0x7ff) /* tx_info bitmasks */ -#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) -#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff) +#define EP_MAX_ESIT_PAYLOAD_LO(p) (((p) & 0xffff) << 16) +#define EP_MAX_ESIT_PAYLOAD_HI(p) ((((p) >> 16) & 0xff) << 24) #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 0ad4782a36..264c9712a3 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -418,6 +418,12 @@ struct __packed usb_class_report_descriptor { #define USB_ENDPOINT_XFER_INT 3 #define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 +#define USB_ENDPOINT_MAXP_MASK 0x07ff +#define USB_EP_MAXP_MULT_SHIFT 11 +#define USB_EP_MAXP_MULT_MASK (3 << USB_EP_MAXP_MULT_SHIFT) +#define USB_EP_MAXP_MULT(m) \ + (((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT) + /* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */ #define USB_ENDPOINT_INTRTYPE 0x30 #define USB_ENDPOINT_INTR_PERIODIC (0 << 4) @@ -625,6 +631,20 @@ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) return __le16_to_cpu(get_unaligned(&epd->wMaxPacketSize)); } +/** + * usb_endpoint_maxp_mult - get endpoint's transactional opportunities + * @epd: endpoint to be checked + * + * Return @epd's wMaxPacketSize[12:11] + 1 + */ +static inline int +usb_endpoint_maxp_mult(const struct usb_endpoint_descriptor *epd) +{ + int maxp = __le16_to_cpu(epd->wMaxPacketSize); + + return USB_EP_MAXP_MULT(maxp) + 1; +} + static inline int usb_endpoint_interrupt_type( const struct usb_endpoint_descriptor *epd) { From fa483b2c750f6ebdb5946f46b217aa3f9a449531 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:48 -0700 Subject: [PATCH 20/24] usb: xhci: Program max burst size for endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'Max Burst Size' indicates to the xHC the maximum number of consecutive USB transactions that should be executed per scheduling opportunity. This is a “zero-based” value, where 0 to 15 represents burst sizes of 1 to 16, but at present this is always set to zero. Let's program the required value according to real needs. Signed-off-by: Bin Meng --- drivers/usb/host/xhci.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8aed4283ed..dfb188d5e4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -395,6 +395,22 @@ static u32 xhci_get_endpoint_mult(struct usb_device *udev, return ss_ep_comp_desc->bmAttributes; } +static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + /* Super speed and Plus have max burst in ep companion desc */ + if (udev->speed >= USB_SPEED_SUPER) + return ss_ep_comp_desc->bMaxBurst; + + if (udev->speed == USB_SPEED_HIGH && + (usb_endpoint_xfer_isoc(endpt_desc) || + usb_endpoint_xfer_int(endpt_desc))) + return usb_endpoint_maxp_mult(endpt_desc) - 1; + + return 0; +} + /* * Return the maximum endpoint service interval time (ESIT) payload. * Basically, this is the maxpacket size, multiplied by the burst size @@ -493,6 +509,7 @@ static int xhci_set_configuration(struct usb_device *udev) u32 max_esit_payload; unsigned int interval; unsigned int mult; + unsigned int max_burst; unsigned int avg_trb_len; out_ctx = virt_dev->out_ctx; @@ -545,6 +562,8 @@ static int xhci_set_configuration(struct usb_device *udev) interval = xhci_get_endpoint_interval(udev, endpt_desc); mult = xhci_get_endpoint_mult(udev, endpt_desc, ss_ep_comp_desc); + max_burst = xhci_get_endpoint_max_burst(udev, endpt_desc, + ss_ep_comp_desc); avg_trb_len = max_esit_payload; ep_index = xhci_get_ep_index(endpt_desc); @@ -570,7 +589,7 @@ static int xhci_set_configuration(struct usb_device *udev) (get_unaligned(&endpt_desc->wMaxPacketSize))); ep_ctx[ep_index]->ep_info2 |= - cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | + cpu_to_le32(MAX_BURST(max_burst) | ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); trb_64 = (uintptr_t) From ab2b727dc03113fe35d6a9c937911055be3d3990 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:49 -0700 Subject: [PATCH 21/24] usb: xhci: Set 'Error Count' to 0 for isoch endpoints Per xHCI spec, 'Error Count' should be set to 0 for isoch endpoints. Signed-off-by: Bin Meng --- drivers/usb/host/xhci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index dfb188d5e4..93737b083c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -511,6 +511,7 @@ static int xhci_set_configuration(struct usb_device *udev) unsigned int mult; unsigned int max_burst; unsigned int avg_trb_len; + unsigned int err_count = 0; out_ctx = virt_dev->out_ctx; in_ctx = virt_dev->in_ctx; @@ -588,9 +589,12 @@ static int xhci_set_configuration(struct usb_device *udev) cpu_to_le32(MAX_PACKET (get_unaligned(&endpt_desc->wMaxPacketSize))); + /* Allow 3 retries for everything but isoc, set CErr = 3 */ + if (!usb_endpoint_xfer_isoc(endpt_desc)) + err_count = 3; ep_ctx[ep_index]->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst) | - ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + ERROR_COUNT(err_count)); trb_64 = (uintptr_t) virt_dev->eps[ep_index].ring->enqueue; From fae35857e1c38776854442f59d6b56c17e93fc39 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 18 Sep 2017 06:40:50 -0700 Subject: [PATCH 22/24] usb: xhci: Set 'Average TRB Length' to 8 for control endpoints Update the codes to conform with xHCI spec chapter 6.2.3. Signed-off-by: Bin Meng --- drivers/usb/host/xhci-mem.c | 6 ++++++ drivers/usb/host/xhci.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 84982a92d6..0582a9be40 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -850,6 +850,12 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs; ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state); + /* + * xHCI spec 6.2.3: + * software shall set 'Average TRB Length' to 8 for control endpoints. + */ + ep0_ctx->tx_info = cpu_to_le32(EP_AVG_TRB_LENGTH(8)); + /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 93737b083c..4673738d1e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -601,6 +601,12 @@ static int xhci_set_configuration(struct usb_device *udev) ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | virt_dev->eps[ep_index].ring->cycle_state); + /* + * xHCI spec 6.2.3: + * 'Average TRB Length' should be 8 for control endpoints. + */ + if (usb_endpoint_xfer_control(endpt_desc)) + avg_trb_len = 8; ep_ctx[ep_index]->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | EP_AVG_TRB_LENGTH(avg_trb_len)); From 72ac8f3fc29016a31ee309b4d025b487e78906ab Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 27 Sep 2017 21:50:07 -0700 Subject: [PATCH 23/24] usb: storage: Fix overwritten in usb_stor_set_max_xfer_blk() The stored 'blk' value is overwritten to 'size / 512' before it can be used in usb_stor_set_max_xfer_blk(). This is not what we want. In fact, when 'size' exceeds the upper limit (USHRT_MAX * 512), we should simply assign 'size' to the upper limit. Reported-by: Coverity (CID: 167250) Signed-off-by: Bin Meng --- common/usb_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/usb_storage.c b/common/usb_storage.c index a57570b73f..a91b1c0d2f 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -964,7 +964,7 @@ static void usb_stor_set_max_xfer_blk(struct usb_device *udev, blk = 20; } else { if (size > USHRT_MAX * 512) - blk = USHRT_MAX; + size = USHRT_MAX * 512; blk = size / 512; } #endif From dc04b35ef2c8c04cb362758ec467777348ef3f15 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 28 Sep 2017 22:01:00 +0900 Subject: [PATCH 24/24] usb: dwc3: add UniPhier specific glue layer Add UniPhier platform specific glue layer to support USB3 Host mode on Synopsys DWC3 IP. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut --- drivers/usb/dwc3/Kconfig | 7 ++ drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-uniphier.c | 120 +++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-uniphier.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index a291ceb6ae..ae7fc1c630 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -37,6 +37,13 @@ config USB_DWC3_OMAP Say 'Y' here if you have one such device +config USB_DWC3_UNIPHIER + bool "DesignWare USB3 Host Support on UniPhier Platforms" + depends on ARCH_UNIPHIER && USB_XHCI_DWC3 + help + Support of USB2/3 functionality in Socionext UniPhier platforms. + Say 'Y' here if you have one such device. + menu "PHY Subsystem" config USB_DWC3_PHY_OMAP diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 2964bae0d8..51497768b2 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -9,5 +9,6 @@ dwc3-y := core.o obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o +obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o diff --git a/drivers/usb/dwc3/dwc3-uniphier.c b/drivers/usb/dwc3/dwc3-uniphier.c new file mode 100644 index 0000000000..0d13770d40 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-uniphier.c @@ -0,0 +1,120 @@ +/* + * UniPhier Specific Glue Layer for DWC3 + * + * Copyright (C) 2016-2017 Socionext Inc. + * Author: Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define UNIPHIER_PRO4_DWC3_RESET 0x40 +#define UNIPHIER_PRO4_DWC3_RESET_XIOMMU BIT(5) +#define UNIPHIER_PRO4_DWC3_RESET_XLINK BIT(4) +#define UNIPHIER_PRO4_DWC3_RESET_PHY_SS BIT(2) + +#define UNIPHIER_PRO5_DWC3_RESET 0x00 +#define UNIPHIER_PRO5_DWC3_RESET_PHY_S1 BIT(17) +#define UNIPHIER_PRO5_DWC3_RESET_PHY_S0 BIT(16) +#define UNIPHIER_PRO5_DWC3_RESET_XLINK BIT(15) +#define UNIPHIER_PRO5_DWC3_RESET_XIOMMU BIT(14) + +#define UNIPHIER_PXS2_DWC3_RESET 0x00 +#define UNIPHIER_PXS2_DWC3_RESET_XLINK BIT(15) + +static int uniphier_pro4_dwc3_init(void __iomem *regs) +{ + u32 tmp; + + tmp = readl(regs + UNIPHIER_PRO4_DWC3_RESET); + tmp &= ~UNIPHIER_PRO4_DWC3_RESET_PHY_SS; + tmp |= UNIPHIER_PRO4_DWC3_RESET_XIOMMU | UNIPHIER_PRO4_DWC3_RESET_XLINK; + writel(tmp, regs + UNIPHIER_PRO4_DWC3_RESET); + + return 0; +} + +static int uniphier_pro5_dwc3_init(void __iomem *regs) +{ + u32 tmp; + + tmp = readl(regs + UNIPHIER_PRO5_DWC3_RESET); + tmp &= ~(UNIPHIER_PRO5_DWC3_RESET_PHY_S1 | + UNIPHIER_PRO5_DWC3_RESET_PHY_S0); + tmp |= UNIPHIER_PRO5_DWC3_RESET_XLINK | UNIPHIER_PRO5_DWC3_RESET_XIOMMU; + writel(tmp, regs + UNIPHIER_PRO5_DWC3_RESET); + + return 0; +} + +static int uniphier_pxs2_dwc3_init(void __iomem *regs) +{ + u32 tmp; + + tmp = readl(regs + UNIPHIER_PXS2_DWC3_RESET); + tmp |= UNIPHIER_PXS2_DWC3_RESET_XLINK; + writel(tmp, regs + UNIPHIER_PXS2_DWC3_RESET); + + return 0; +} + +static int uniphier_dwc3_probe(struct udevice *dev) +{ + fdt_addr_t base; + void __iomem *regs; + int (*init)(void __iomem *regs); + int ret; + + base = devfdt_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; + + regs = ioremap(base, SZ_32K); + if (!regs) + return -ENOMEM; + + init = (typeof(init))dev_get_driver_data(dev); + ret = init(regs); + if (ret) + dev_err(dev, "failed to init glue layer\n"); + + iounmap(regs); + + return ret; +} + +static const struct udevice_id uniphier_dwc3_match[] = { + { + .compatible = "socionext,uniphier-pro4-dwc3", + .data = (ulong)uniphier_pro4_dwc3_init, + }, + { + .compatible = "socionext,uniphier-pro5-dwc3", + .data = (ulong)uniphier_pro5_dwc3_init, + }, + { + .compatible = "socionext,uniphier-pxs2-dwc3", + .data = (ulong)uniphier_pxs2_dwc3_init, + }, + { + .compatible = "socionext,uniphier-ld20-dwc3", + .data = (ulong)uniphier_pxs2_dwc3_init, + }, + { + .compatible = "socionext,uniphier-pxs3-dwc3", + .data = (ulong)uniphier_pxs2_dwc3_init, + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "uniphier-dwc3", + .id = UCLASS_SIMPLE_BUS, + .of_match = uniphier_dwc3_match, + .probe = uniphier_dwc3_probe, +};