mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
Merge git://git.denx.de/u-boot-usb
This commit is contained in:
commit
4d1c166fee
18 changed files with 612 additions and 251 deletions
45
common/usb.c
45
common/usb.c
|
@ -437,12 +437,13 @@ static int usb_parse_config(struct usb_device *dev,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case USB_DT_ENDPOINT:
|
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",
|
printf("ERROR: Invalid USB EP length (%d)\n",
|
||||||
head->bLength);
|
head->bLength);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (index + USB_DT_ENDPOINT_SIZE >
|
if (index + head->bLength >
|
||||||
dev->config.desc.wTotalLength) {
|
dev->config.desc.wTotalLength) {
|
||||||
puts("USB EP descriptor overflowed buffer!\n");
|
puts("USB EP descriptor overflowed buffer!\n");
|
||||||
break;
|
break;
|
||||||
|
@ -969,23 +970,24 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
|
||||||
dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
|
dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
|
||||||
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
|
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
|
||||||
|
|
||||||
if (do_read) {
|
if (do_read && dev->speed == USB_SPEED_FULL) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Validate we've received only at least 8 bytes, not that we've
|
* Validate we've received only at least 8 bytes, not that
|
||||||
* received the entire descriptor. The reasoning is:
|
* we've received the entire descriptor. The reasoning is:
|
||||||
* - The code only uses fields in the first 8 bytes, so that's all we
|
* - The code only uses fields in the first 8 bytes, so
|
||||||
* need to have fetched at this stage.
|
* that's all we need to have fetched at this stage.
|
||||||
* - The smallest maxpacket size is 8 bytes. Before we know the actual
|
* - The smallest maxpacket size is 8 bytes. Before we know
|
||||||
* maxpacket the device uses, the USB controller may only accept a
|
* the actual maxpacket the device uses, the USB controller
|
||||||
* single packet. Consequently we are only guaranteed to receive 1
|
* may only accept a single packet. Consequently we are only
|
||||||
* packet (at least 8 bytes) even in a non-error case.
|
* 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
|
* At least the DWC2 controller needs to be programmed with
|
||||||
* of packets in addition to the number of bytes. A request for 64
|
* the number of packets in addition to the number of bytes.
|
||||||
* bytes of data with the maxpacket guessed as 64 (above) yields a
|
* A request for 64 bytes of data with the maxpacket guessed
|
||||||
* request for 1 packet.
|
* as 64 (above) yields a request for 1 packet.
|
||||||
*/
|
*/
|
||||||
err = get_descriptor_len(dev, 64, 8);
|
err = get_descriptor_len(dev, 64, 8);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -1008,7 +1010,7 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
|
||||||
dev->maxpacketsize = PACKET_SIZE_64;
|
dev->maxpacketsize = PACKET_SIZE_64;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("usb_new_device: invalid max packet size\n");
|
printf("%s: invalid max packet size\n", __func__);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,6 +1052,17 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read,
|
||||||
|
|
||||||
mdelay(10); /* Let the SET_ADDRESS settle */
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -489,6 +489,17 @@ static int usb_scan_port(struct usb_device_scan *usb_scan)
|
||||||
return 0;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
/* A new USB device is ready at this point */
|
||||||
debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1);
|
debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1);
|
||||||
|
|
||||||
|
@ -543,11 +554,6 @@ static int usb_scan_port(struct usb_device_scan *usb_scan)
|
||||||
hub->overcurrent_count[i]);
|
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
|
* We're done with this device, so let's remove this device from
|
||||||
* scanning list
|
* scanning list
|
||||||
|
|
|
@ -964,7 +964,7 @@ static void usb_stor_set_max_xfer_blk(struct usb_device *udev,
|
||||||
blk = 20;
|
blk = 20;
|
||||||
} else {
|
} else {
|
||||||
if (size > USHRT_MAX * 512)
|
if (size > USHRT_MAX * 512)
|
||||||
blk = USHRT_MAX;
|
size = USHRT_MAX * 512;
|
||||||
blk = size / 512;
|
blk = size / 512;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -37,6 +37,13 @@ config USB_DWC3_OMAP
|
||||||
|
|
||||||
Say 'Y' here if you have one such device
|
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"
|
menu "PHY Subsystem"
|
||||||
|
|
||||||
config USB_DWC3_PHY_OMAP
|
config USB_DWC3_PHY_OMAP
|
||||||
|
|
|
@ -9,5 +9,6 @@ dwc3-y := core.o
|
||||||
obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o
|
obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o
|
||||||
|
|
||||||
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.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_OMAP) += ti_usb_phy.o
|
||||||
obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o
|
obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o
|
||||||
|
|
120
drivers/usb/dwc3/dwc3-uniphier.c
Normal file
120
drivers/usb/dwc3/dwc3-uniphier.c
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* UniPhier Specific Glue Layer for DWC3
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016-2017 Socionext Inc.
|
||||||
|
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <dm.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
|
#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,
|
||||||
|
};
|
|
@ -390,8 +390,7 @@ static int sandbox_flash_bind(struct udevice *dev)
|
||||||
fs[2].id = STRINGID_SERIAL;
|
fs[2].id = STRINGID_SERIAL;
|
||||||
fs[2].s = dev->name;
|
fs[2].s = dev->name;
|
||||||
|
|
||||||
return usb_emul_setup_device(dev, PACKET_SIZE_64, plat->flash_strings,
|
return usb_emul_setup_device(dev, plat->flash_strings, flash_desc_list);
|
||||||
flash_desc_list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sandbox_flash_probe(struct udevice *dev)
|
static int sandbox_flash_probe(struct udevice *dev)
|
||||||
|
|
|
@ -121,9 +121,12 @@ struct sandbox_hub_priv {
|
||||||
int change[SANDBOX_NUM_PORTS];
|
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 udevice *dev;
|
||||||
|
struct usb_generic_descriptor **gen_desc;
|
||||||
|
struct usb_device_descriptor **dev_desc;
|
||||||
|
|
||||||
for (device_find_first_child(hub, &dev);
|
for (device_find_first_child(hub, &dev);
|
||||||
dev;
|
dev;
|
||||||
|
@ -131,8 +134,27 @@ static struct udevice *hub_find_device(struct udevice *hub, int port)
|
||||||
struct sandbox_hub_platdata *plat;
|
struct sandbox_hub_platdata *plat;
|
||||||
|
|
||||||
plat = dev_get_parent_platdata(dev);
|
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 dev;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -146,7 +168,8 @@ static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if ((clear | set) & USB_PORT_STAT_POWER) {
|
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 (dev) {
|
||||||
if (set & USB_PORT_STAT_POWER) {
|
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) {
|
if (!ret) {
|
||||||
set |= USB_PORT_STAT_CONNECTION |
|
set |= USB_PORT_STAT_CONNECTION |
|
||||||
USB_PORT_STAT_ENABLE;
|
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) {
|
} else if (clear & USB_PORT_STAT_POWER) {
|
||||||
|
@ -274,15 +301,16 @@ static int sandbox_hub_submit_control_msg(struct udevice *bus,
|
||||||
|
|
||||||
static int sandbox_hub_bind(struct udevice *dev)
|
static int sandbox_hub_bind(struct udevice *dev)
|
||||||
{
|
{
|
||||||
return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings,
|
return usb_emul_setup_device(dev, hub_strings, hub_desc_list);
|
||||||
hub_desc_list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sandbox_child_post_bind(struct udevice *dev)
|
static int sandbox_child_post_bind(struct udevice *dev)
|
||||||
{
|
{
|
||||||
struct sandbox_hub_platdata *plat = dev_get_parent_platdata(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);
|
plat->port = dev_read_u32_default(dev, "reg", -1);
|
||||||
|
emul->port1 = plat->port + 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,8 +208,7 @@ static int sandbox_keyb_bind(struct udevice *dev)
|
||||||
fs[2].id = STRINGID_SERIAL;
|
fs[2].id = STRINGID_SERIAL;
|
||||||
fs[2].s = dev->name;
|
fs[2].s = dev->name;
|
||||||
|
|
||||||
return usb_emul_setup_device(dev, PACKET_SIZE_8, plat->keyb_strings,
|
return usb_emul_setup_device(dev, plat->keyb_strings, keyb_desc_list);
|
||||||
keyb_desc_list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sandbox_keyb_probe(struct udevice *dev)
|
static int sandbox_keyb_probe(struct udevice *dev)
|
||||||
|
|
|
@ -52,7 +52,7 @@ static int usb_emul_get_string(struct usb_string *strings, int index,
|
||||||
return -EINVAL;
|
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)
|
struct usb_generic_descriptor **ptr, int type, int index)
|
||||||
{
|
{
|
||||||
debug("%s: type=%x, index=%d\n", __func__, type, 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);
|
length);
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = find_descriptor((struct usb_generic_descriptor **)plat->desc_list,
|
ptr = usb_emul_find_descriptor(plat->desc_list, type, index);
|
||||||
type, index);
|
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
debug("%s: Could not find descriptor type %d, index %d\n",
|
debug("%s: Could not find descriptor type %d, index %d\n",
|
||||||
__func__, type, index);
|
__func__, type, index);
|
||||||
|
@ -107,7 +106,7 @@ static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value,
|
||||||
return upto ? upto : length ? -EIO : 0;
|
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 udevice *dev;
|
||||||
struct uclass *uc;
|
struct uclass *uc;
|
||||||
|
@ -120,7 +119,37 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp)
|
||||||
uclass_foreach_dev(dev, uc) {
|
uclass_foreach_dev(dev, uc) {
|
||||||
struct usb_dev_platdata *udev = dev_get_parent_platdata(dev);
|
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__,
|
debug("%s: Found emulator '%s', addr %d\n", __func__,
|
||||||
dev->name, udev->devnum);
|
dev->name, udev->devnum);
|
||||||
*emulp = dev;
|
*emulp = dev;
|
||||||
|
@ -132,18 +161,19 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp)
|
||||||
return -ENOENT;
|
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);
|
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)
|
int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp)
|
||||||
{
|
{
|
||||||
struct usb_dev_platdata *udev = dev_get_parent_platdata(dev);
|
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,
|
int usb_emul_control(struct udevice *emul, struct usb_device *udev,
|
||||||
|
@ -229,8 +259,8 @@ int usb_emul_int(struct udevice *emul, struct usb_device *udev,
|
||||||
return ops->interrupt(emul, udev, pipe, buffer, length, interval);
|
return ops->interrupt(emul, udev, pipe, buffer, length, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
int usb_emul_setup_device(struct udevice *dev, int maxpacketsize,
|
int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings,
|
||||||
struct usb_string *strings, void **desc_list)
|
void **desc_list)
|
||||||
{
|
{
|
||||||
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
|
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
|
||||||
struct usb_generic_descriptor **ptr;
|
struct usb_generic_descriptor **ptr;
|
||||||
|
@ -264,18 +294,11 @@ int usb_emul_setup_device(struct udevice *dev, int maxpacketsize,
|
||||||
return 0;
|
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) = {
|
UCLASS_DRIVER(usb_emul) = {
|
||||||
.id = UCLASS_USB_EMUL,
|
.id = UCLASS_USB_EMUL,
|
||||||
.name = "usb_emul",
|
.name = "usb_emul",
|
||||||
.post_bind = dm_scan_fdt_dev,
|
.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_auto_alloc_size = sizeof(struct usb_device),
|
||||||
.per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
|
.per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
struct sandbox_usb_ctrl {
|
||||||
|
int rootdev;
|
||||||
|
};
|
||||||
|
|
||||||
static void usbmon_trace(struct udevice *bus, ulong pipe,
|
static void usbmon_trace(struct udevice *bus, ulong pipe,
|
||||||
struct devrequest *setup, struct udevice *emul)
|
struct devrequest *setup, struct udevice *emul)
|
||||||
{
|
{
|
||||||
|
@ -40,15 +44,24 @@ static int sandbox_submit_control(struct udevice *bus,
|
||||||
void *buffer, int length,
|
void *buffer, int length,
|
||||||
struct devrequest *setup)
|
struct devrequest *setup)
|
||||||
{
|
{
|
||||||
|
struct sandbox_usb_ctrl *ctrl = dev_get_priv(bus);
|
||||||
struct udevice *emul;
|
struct udevice *emul;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Just use child of dev as emulator? */
|
/* Just use child of dev as emulator? */
|
||||||
debug("%s: bus=%s\n", __func__, bus->name);
|
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);
|
usbmon_trace(bus, pipe, setup, emul);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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);
|
ret = usb_emul_control(emul, udev, pipe, buffer, length, setup);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
debug("ret=%d\n", ret);
|
debug("ret=%d\n", ret);
|
||||||
|
@ -70,7 +83,7 @@ static int sandbox_submit_bulk(struct udevice *bus, struct usb_device *udev,
|
||||||
|
|
||||||
/* Just use child of dev as emulator? */
|
/* Just use child of dev as emulator? */
|
||||||
debug("%s: bus=%s\n", __func__, bus->name);
|
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);
|
usbmon_trace(bus, pipe, NULL, emul);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -96,7 +109,7 @@ static int sandbox_submit_int(struct udevice *bus, struct usb_device *udev,
|
||||||
|
|
||||||
/* Just use child of dev as emulator? */
|
/* Just use child of dev as emulator? */
|
||||||
debug("%s: bus=%s\n", __func__, bus->name);
|
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);
|
usbmon_trace(bus, pipe, NULL, emul);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return 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)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,4 +156,5 @@ U_BOOT_DRIVER(usb_sandbox) = {
|
||||||
.of_match = sandbox_usb_ids,
|
.of_match = sandbox_usb_ids,
|
||||||
.probe = sandbox_usb_probe,
|
.probe = sandbox_usb_probe,
|
||||||
.ops = &sandbox_usb_ops,
|
.ops = &sandbox_usb_ops,
|
||||||
|
.priv_auto_alloc_size = sizeof(struct sandbox_usb_ctrl),
|
||||||
};
|
};
|
||||||
|
|
|
@ -164,6 +164,7 @@ int usb_get_max_xfer_size(struct usb_device *udev, size_t *size)
|
||||||
int usb_stop(void)
|
int usb_stop(void)
|
||||||
{
|
{
|
||||||
struct udevice *bus;
|
struct udevice *bus;
|
||||||
|
struct udevice *rh;
|
||||||
struct uclass *uc;
|
struct uclass *uc;
|
||||||
struct usb_uclass_priv *uc_priv;
|
struct usb_uclass_priv *uc_priv;
|
||||||
int err = 0, ret;
|
int err = 0, ret;
|
||||||
|
@ -179,23 +180,20 @@ int usb_stop(void)
|
||||||
ret = device_remove(bus, DM_REMOVE_NORMAL);
|
ret = device_remove(bus, DM_REMOVE_NORMAL);
|
||||||
if (ret && !err)
|
if (ret && !err)
|
||||||
err = ret;
|
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);
|
|
||||||
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;
|
|
||||||
|
|
||||||
uclass_foreach_dev(dev, uc)
|
|
||||||
usb_emul_reset(dev);
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_USB_STORAGE
|
#ifdef CONFIG_USB_STORAGE
|
||||||
usb_stor_reset();
|
usb_stor_reset();
|
||||||
#endif
|
#endif
|
||||||
|
@ -262,6 +260,21 @@ int usb_init(void)
|
||||||
/* init low_level USB */
|
/* init low_level USB */
|
||||||
printf("USB%d: ", count);
|
printf("USB%d: ", count);
|
||||||
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);
|
ret = device_probe(bus);
|
||||||
if (ret == -ENODEV) { /* No such device. */
|
if (ret == -ENODEV) { /* No such device. */
|
||||||
puts("Port not available.\n");
|
puts("Port not available.\n");
|
||||||
|
|
|
@ -786,12 +786,22 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
|
||||||
#ifdef CONFIG_DM_USB
|
#ifdef CONFIG_DM_USB
|
||||||
/* Set up TT fields to support FS/LS devices */
|
/* Set up TT fields to support FS/LS devices */
|
||||||
if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) {
|
if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) {
|
||||||
dev = dev_get_parent_priv(udev->dev);
|
struct udevice *parent = udev->dev;
|
||||||
if (dev->speed == USB_SPEED_HIGH) {
|
|
||||||
hub = dev_get_uclass_priv(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)
|
if (hub->tt.multi)
|
||||||
slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
|
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));
|
slot_ctx->tt_info |= cpu_to_le32(TT_SLOT(dev->slot_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -840,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;
|
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);
|
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() */
|
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
|
||||||
|
|
||||||
xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx));
|
xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx));
|
||||||
|
|
|
@ -257,6 +257,188 @@ static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc)
|
||||||
return index;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
* 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
|
* Issue a configure endpoint command or evaluate context command
|
||||||
* and wait for it to finish.
|
* and wait for it to finish.
|
||||||
|
@ -324,6 +506,12 @@ static int xhci_set_configuration(struct usb_device *udev)
|
||||||
int slot_id = udev->slot_id;
|
int slot_id = udev->slot_id;
|
||||||
struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
|
struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
|
||||||
struct usb_interface *ifdesc;
|
struct usb_interface *ifdesc;
|
||||||
|
u32 max_esit_payload;
|
||||||
|
unsigned int interval;
|
||||||
|
unsigned int mult;
|
||||||
|
unsigned int max_burst;
|
||||||
|
unsigned int avg_trb_len;
|
||||||
|
unsigned int err_count = 0;
|
||||||
|
|
||||||
out_ctx = virt_dev->out_ctx;
|
out_ctx = virt_dev->out_ctx;
|
||||||
in_ctx = virt_dev->in_ctx;
|
in_ctx = virt_dev->in_ctx;
|
||||||
|
@ -357,10 +545,28 @@ static int xhci_set_configuration(struct usb_device *udev)
|
||||||
/* filling up ep contexts */
|
/* filling up ep contexts */
|
||||||
for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
|
for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
|
||||||
struct usb_endpoint_descriptor *endpt_desc = NULL;
|
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];
|
endpt_desc = &ifdesc->ep_desc[cur_ep];
|
||||||
|
ss_ep_comp_desc = &ifdesc->ss_ep_comp_desc[cur_ep];
|
||||||
trb_64 = 0;
|
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);
|
||||||
|
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);
|
ep_index = xhci_get_ep_index(endpt_desc);
|
||||||
ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
|
ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
|
||||||
|
|
||||||
|
@ -372,20 +578,38 @@ static int xhci_set_configuration(struct usb_device *udev)
|
||||||
/*NOTE: ep_desc[0] actually represents EP1 and so on */
|
/*NOTE: ep_desc[0] actually represents EP1 and so on */
|
||||||
dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7);
|
dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7);
|
||||||
ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2));
|
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 =
|
ep_ctx[ep_index]->ep_info2 =
|
||||||
cpu_to_le32(ep_type << EP_TYPE_SHIFT);
|
cpu_to_le32(ep_type << EP_TYPE_SHIFT);
|
||||||
ep_ctx[ep_index]->ep_info2 |=
|
ep_ctx[ep_index]->ep_info2 |=
|
||||||
cpu_to_le32(MAX_PACKET
|
cpu_to_le32(MAX_PACKET
|
||||||
(get_unaligned(&endpt_desc->wMaxPacketSize)));
|
(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 |=
|
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));
|
ERROR_COUNT(err_count));
|
||||||
|
|
||||||
trb_64 = (uintptr_t)
|
trb_64 = (uintptr_t)
|
||||||
virt_dev->eps[ep_index].ring->enqueue;
|
virt_dev->eps[ep_index].ring->enqueue;
|
||||||
ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 |
|
ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 |
|
||||||
virt_dev->eps[ep_index].ring->cycle_state);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
return xhci_configure_endpoints(udev, false);
|
return xhci_configure_endpoints(udev, false);
|
||||||
|
@ -546,16 +770,13 @@ int xhci_check_maxpacket(struct usb_device *udev)
|
||||||
int max_packet_size;
|
int max_packet_size;
|
||||||
int hw_max_packet_size;
|
int hw_max_packet_size;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct usb_interface *ifdesc;
|
|
||||||
|
|
||||||
ifdesc = &udev->config.if_desc[0];
|
|
||||||
|
|
||||||
out_ctx = ctrl->devs[slot_id]->out_ctx;
|
out_ctx = ctrl->devs[slot_id]->out_ctx;
|
||||||
xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
|
xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
|
||||||
|
|
||||||
ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index);
|
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));
|
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) {
|
if (hw_max_packet_size != max_packet_size) {
|
||||||
debug("Max Packet Size for ep 0 changed.\n");
|
debug("Max Packet Size for ep 0 changed.\n");
|
||||||
debug("Max packet size in usb_device = %d\n", max_packet_size);
|
debug("Max packet size in usb_device = %d\n", max_packet_size);
|
||||||
|
@ -567,7 +788,8 @@ int xhci_check_maxpacket(struct usb_device *udev)
|
||||||
ctrl->devs[slot_id]->out_ctx, ep_index);
|
ctrl->devs[slot_id]->out_ctx, ep_index);
|
||||||
in_ctx = ctrl->devs[slot_id]->in_ctx;
|
in_ctx = ctrl->devs[slot_id]->in_ctx;
|
||||||
ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
|
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));
|
ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -890,11 +1112,18 @@ unknown:
|
||||||
static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
|
static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
|
||||||
void *buffer, int length, int interval)
|
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
|
* xHCI uses normal TRBs for both bulk and interrupt. When the
|
||||||
* Add support for it later.
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -663,8 +663,9 @@ struct xhci_ep_ctx {
|
||||||
#define GET_MAX_PACKET(p) ((p) & 0x7ff)
|
#define GET_MAX_PACKET(p) ((p) & 0x7ff)
|
||||||
|
|
||||||
/* tx_info bitmasks */
|
/* tx_info bitmasks */
|
||||||
#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff)
|
#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff)
|
||||||
#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16)
|
#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)
|
#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
|
||||||
|
|
||||||
/* deq bitmasks */
|
/* deq bitmasks */
|
||||||
|
|
|
@ -418,6 +418,12 @@ struct __packed usb_class_report_descriptor {
|
||||||
#define USB_ENDPOINT_XFER_INT 3
|
#define USB_ENDPOINT_XFER_INT 3
|
||||||
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
|
#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. */
|
/* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */
|
||||||
#define USB_ENDPOINT_INTRTYPE 0x30
|
#define USB_ENDPOINT_INTRTYPE 0x30
|
||||||
#define USB_ENDPOINT_INTR_PERIODIC (0 << 4)
|
#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));
|
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(
|
static inline int usb_endpoint_interrupt_type(
|
||||||
const struct usb_endpoint_descriptor *epd)
|
const struct usb_endpoint_descriptor *epd)
|
||||||
{
|
{
|
||||||
|
|
|
@ -652,6 +652,18 @@ struct usb_bus_priv {
|
||||||
bool companion;
|
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
|
* struct dm_usb_ops - USB controller operations
|
||||||
*
|
*
|
||||||
|
@ -976,7 +988,6 @@ int usb_get_max_xfer_size(struct usb_device *dev, size_t *size);
|
||||||
* the USB emulation uclass about the features of the emulator.
|
* the USB emulation uclass about the features of the emulator.
|
||||||
*
|
*
|
||||||
* @dev: Emulation device
|
* @dev: Emulation device
|
||||||
* @maxpacketsize: Maximum packet size (e.g. PACKET_SIZE_64)
|
|
||||||
* @strings: List of USB string descriptors, terminated by a NULL
|
* @strings: List of USB string descriptors, terminated by a NULL
|
||||||
* entry
|
* entry
|
||||||
* @desc_list: List of points or USB descriptors, terminated by NULL.
|
* @desc_list: List of points or USB descriptors, terminated by NULL.
|
||||||
|
@ -984,8 +995,8 @@ int usb_get_max_xfer_size(struct usb_device *dev, size_t *size);
|
||||||
* and others follow on after that.
|
* and others follow on after that.
|
||||||
* @return 0 if OK, -ENOSYS if not implemented, other -ve on error
|
* @return 0 if OK, -ENOSYS if not implemented, other -ve on error
|
||||||
*/
|
*/
|
||||||
int usb_emul_setup_device(struct udevice *dev, int maxpacketsize,
|
int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings,
|
||||||
struct usb_string *strings, void **desc_list);
|
void **desc_list);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_emul_control() - Send a control packet to an emulator
|
* usb_emul_control() - Send a control packet to an emulator
|
||||||
|
@ -1024,19 +1035,20 @@ int usb_emul_int(struct udevice *emul, struct usb_device *udev,
|
||||||
/**
|
/**
|
||||||
* usb_emul_find() - Find an emulator for a particular device
|
* 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)
|
* @bus: USB bus (controller)
|
||||||
* @pipe: Describes pipe being used, and includes the device number
|
* @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
|
* @emulp: Returns pointer to emulator, or NULL if not found
|
||||||
* @return 0 if found, -ve on error
|
* @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
|
* usb_emul_find_for_dev() - Find an emulator for a particular device
|
||||||
*
|
*
|
||||||
* @bus: USB bus (controller)
|
|
||||||
* @dev: USB device to check
|
* @dev: USB device to check
|
||||||
* @emulp: Returns pointer to emulator, or NULL if not found
|
* @emulp: Returns pointer to emulator, or NULL if not found
|
||||||
* @return 0 if found, -ve on error
|
* @return 0 if found, -ve on error
|
||||||
|
@ -1044,12 +1056,15 @@ int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp);
|
||||||
int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp);
|
int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_emul_reset() - Reset all emulators ready for use
|
* usb_emul_find_descriptor() - Find a USB descriptor of a particular device
|
||||||
*
|
*
|
||||||
* Clear out any address information in the emulators and make then ready for
|
* @ptr: a pointer to a list of USB descriptor pointers
|
||||||
* a new USB scan
|
* @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
|
||||||
*/
|
*/
|
||||||
void usb_emul_reset(struct udevice *dev);
|
struct usb_generic_descriptor **usb_emul_find_descriptor(
|
||||||
|
struct usb_generic_descriptor **ptr, int type, int index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_show_tree() - show the USB device tree
|
* usb_show_tree() - show the USB device tree
|
||||||
|
|
163
test/dm/usb.c
163
test/dm/usb.c
|
@ -99,10 +99,10 @@ static int count_usb_devices(void)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* test that we can remove an emulated device and it is then not found */
|
/* test that no USB devices are found after we stop the stack */
|
||||||
static int dm_test_usb_remove(struct unit_test_state *uts)
|
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 */
|
/* Scan and check that all devices are present */
|
||||||
state_set_skip_delays(true);
|
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_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
|
||||||
ut_asserteq(6, count_usb_devices());
|
ut_asserteq(6, count_usb_devices());
|
||||||
ut_assertok(usb_stop());
|
ut_assertok(usb_stop());
|
||||||
ut_asserteq(6, count_usb_devices());
|
ut_asserteq(0, 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());
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DM_TEST(dm_test_usb_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
DM_TEST(dm_test_usb_stop, 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);
|
|
||||||
|
|
||||||
static int dm_test_usb_keyb(struct unit_test_state *uts)
|
static int dm_test_usb_keyb(struct unit_test_state *uts)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue