summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/dwc2.c4
-rw-r--r--drivers/usb/host/dwc3-sti-glue.c28
-rw-r--r--drivers/usb/host/ehci-generic.c20
-rw-r--r--drivers/usb/host/ehci-hcd.c12
-rw-r--r--drivers/usb/host/ohci-generic.c18
-rw-r--r--drivers/usb/host/usb-sandbox.c30
-rw-r--r--drivers/usb/host/usb-uclass.c54
-rw-r--r--drivers/usb/host/xhci-dwc3.c6
-rw-r--r--drivers/usb/host/xhci-mem.c24
-rw-r--r--drivers/usb/host/xhci-rockchip.c16
-rw-r--r--drivers/usb/host/xhci.c264
-rw-r--r--drivers/usb/host/xhci.h9
12 files changed, 402 insertions, 83 deletions
diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index 64c42ac4715..1293e18f75e 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -179,7 +179,7 @@ static int dwc_vbus_supply_init(struct udevice *dev)
ret = regulator_set_enable(vbus_supply, true);
if (ret) {
- error("Error enabling vbus supply\n");
+ pr_err("Error enabling vbus supply\n");
return ret;
}
@@ -1245,7 +1245,7 @@ static int dwc2_usb_ofdata_to_platdata(struct udevice *dev)
struct dwc2_priv *priv = dev_get_priv(dev);
fdt_addr_t addr;
- addr = devfdt_get_addr(dev);
+ addr = dev_read_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = (struct dwc2_core_regs *)addr;
diff --git a/drivers/usb/host/dwc3-sti-glue.c b/drivers/usb/host/dwc3-sti-glue.c
index 02ad3115df4..6dc656af89b 100644
--- a/drivers/usb/host/dwc3-sti-glue.c
+++ b/drivers/usb/host/dwc3-sti-glue.c
@@ -71,7 +71,7 @@ static int sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata *plat)
break;
default:
- error("Unsupported mode of operation %d\n", plat->mode);
+ pr_err("Unsupported mode of operation %d\n", plat->mode);
return -EINVAL;
}
writel(val, plat->syscfg_base + plat->syscfg_offset);
@@ -113,7 +113,7 @@ static int sti_dwc3_glue_ofdata_to_platdata(struct udevice *dev)
ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
"reg", reg, ARRAY_SIZE(reg));
if (ret) {
- error("unable to find st,stih407-dwc3 reg property(%d)\n", ret);
+ pr_err("unable to find st,stih407-dwc3 reg property(%d)\n", ret);
return ret;
}
@@ -124,14 +124,14 @@ static int sti_dwc3_glue_ofdata_to_platdata(struct udevice *dev)
ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "st,syscfg",
&syscon);
if (ret) {
- error("unable to find syscon device (%d)\n", ret);
+ pr_err("unable to find syscon device (%d)\n", ret);
return ret;
}
/* get syscfg-reg base address */
regmap = syscon_get_regmap(syscon);
if (!regmap) {
- error("unable to find regmap\n");
+ pr_err("unable to find regmap\n");
return -ENODEV;
}
plat->syscfg_base = regmap->base;
@@ -139,14 +139,14 @@ static int sti_dwc3_glue_ofdata_to_platdata(struct udevice *dev)
/* get powerdown reset */
ret = reset_get_by_name(dev, "powerdown", &plat->powerdown_ctl);
if (ret) {
- error("can't get powerdown reset for %s (%d)", dev->name, ret);
+ pr_err("can't get powerdown reset for %s (%d)", dev->name, ret);
return ret;
}
/* get softreset reset */
ret = reset_get_by_name(dev, "softreset", &plat->softreset_ctl);
if (ret)
- error("can't get soft reset for %s (%d)", dev->name, ret);
+ pr_err("can't get soft reset for %s (%d)", dev->name, ret);
return ret;
};
@@ -159,14 +159,14 @@ static int sti_dwc3_glue_bind(struct udevice *dev)
/* check if one subnode is present */
dwc3_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
if (dwc3_node <= 0) {
- error("Can't find subnode for %s\n", dev->name);
+ pr_err("Can't find subnode for %s\n", dev->name);
return -ENODEV;
}
/* check if the subnode compatible string is the dwc3 one*/
if (fdt_node_check_compatible(gd->fdt_blob, dwc3_node,
"snps,dwc3") != 0) {
- error("Can't find dwc3 subnode for %s\n", dev->name);
+ pr_err("Can't find dwc3 subnode for %s\n", dev->name);
return -ENODEV;
}
@@ -187,13 +187,13 @@ static int sti_dwc3_glue_probe(struct udevice *dev)
/* deassert both powerdown and softreset */
ret = reset_deassert(&plat->powerdown_ctl);
if (ret < 0) {
- error("DWC3 powerdown reset deassert failed: %d", ret);
+ pr_err("DWC3 powerdown reset deassert failed: %d", ret);
return ret;
}
ret = reset_deassert(&plat->softreset_ctl);
if (ret < 0) {
- error("DWC3 soft reset deassert failed: %d", ret);
+ pr_err("DWC3 soft reset deassert failed: %d", ret);
goto softreset_err;
}
@@ -208,14 +208,14 @@ static int sti_dwc3_glue_probe(struct udevice *dev)
init_err:
ret = reset_assert(&plat->softreset_ctl);
if (ret < 0) {
- error("DWC3 soft reset deassert failed: %d", ret);
+ pr_err("DWC3 soft reset deassert failed: %d", ret);
return ret;
}
softreset_err:
ret = reset_assert(&plat->powerdown_ctl);
if (ret < 0)
- error("DWC3 powerdown reset deassert failed: %d", ret);
+ pr_err("DWC3 powerdown reset deassert failed: %d", ret);
return ret;
}
@@ -228,13 +228,13 @@ static int sti_dwc3_glue_remove(struct udevice *dev)
/* assert both powerdown and softreset */
ret = reset_assert(&plat->powerdown_ctl);
if (ret < 0) {
- error("DWC3 powerdown reset deassert failed: %d", ret);
+ pr_err("DWC3 powerdown reset deassert failed: %d", ret);
return ret;
}
ret = reset_assert(&plat->softreset_ctl);
if (ret < 0)
- error("DWC3 soft reset deassert failed: %d", ret);
+ pr_err("DWC3 soft reset deassert failed: %d", ret);
return ret;
}
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c
index 03f8d321af1..1cb92c03387 100644
--- a/drivers/usb/host/ehci-generic.c
+++ b/drivers/usb/host/ehci-generic.c
@@ -51,7 +51,7 @@ static int ehci_usb_probe(struct udevice *dev)
break;
err = clk_enable(&priv->clocks[i]);
if (err) {
- error("failed to enable clock %d\n", i);
+ pr_err("failed to enable clock %d\n", i);
clk_free(&priv->clocks[i]);
goto clk_err;
}
@@ -59,7 +59,7 @@ static int ehci_usb_probe(struct udevice *dev)
}
} else {
if (clock_nb != -ENOENT) {
- error("failed to get clock phandle(%d)\n", clock_nb);
+ pr_err("failed to get clock phandle(%d)\n", clock_nb);
return clock_nb;
}
}
@@ -80,7 +80,7 @@ static int ehci_usb_probe(struct udevice *dev)
break;
if (reset_deassert(&priv->resets[i])) {
- error("failed to deassert reset %d\n", i);
+ pr_err("failed to deassert reset %d\n", i);
reset_free(&priv->resets[i]);
goto reset_err;
}
@@ -88,7 +88,7 @@ static int ehci_usb_probe(struct udevice *dev)
}
} else {
if (reset_nb != -ENOENT) {
- error("failed to get reset phandle(%d)\n", reset_nb);
+ pr_err("failed to get reset phandle(%d)\n", reset_nb);
goto clk_err;
}
}
@@ -96,19 +96,19 @@ static int ehci_usb_probe(struct udevice *dev)
err = generic_phy_get_by_index(dev, 0, &priv->phy);
if (err) {
if (err != -ENOENT) {
- error("failed to get usb phy\n");
+ pr_err("failed to get usb phy\n");
goto reset_err;
}
} else {
err = generic_phy_init(&priv->phy);
if (err) {
- error("failed to init usb phy\n");
+ pr_err("failed to init usb phy\n");
goto reset_err;
}
}
- hccr = map_physmem(devfdt_get_addr(dev), 0x100, MAP_NOCACHE);
+ hccr = map_physmem(dev_read_addr(dev), 0x100, MAP_NOCACHE);
hcor = (struct ehci_hcor *)((uintptr_t)hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
@@ -122,17 +122,17 @@ phy_err:
if (generic_phy_valid(&priv->phy)) {
ret = generic_phy_exit(&priv->phy);
if (ret)
- error("failed to release phy\n");
+ pr_err("failed to release phy\n");
}
reset_err:
ret = reset_release_all(priv->resets, priv->reset_count);
if (ret)
- error("failed to assert all resets\n");
+ pr_err("failed to assert all resets\n");
clk_err:
ret = clk_release_all(priv->clocks, priv->clock_count);
if (ret)
- error("failed to disable all clocks\n");
+ pr_err("failed to disable all clocks\n");
return err;
}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 3243c1d1cf2..be3e842dcc3 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1596,6 +1596,17 @@ static int ehci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
return _ehci_destroy_int_queue(udev, queue);
}
+static int ehci_get_max_xfer_size(struct udevice *dev, size_t *size)
+{
+ /*
+ * EHCD can handle any transfer length as long as there is enough
+ * free heap space left, hence set the theoretical max number here.
+ */
+ *size = SIZE_MAX;
+
+ return 0;
+}
+
int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
struct ehci_hcor *hcor, const struct ehci_ops *ops,
uint tweaks, enum usb_init_type init)
@@ -1658,6 +1669,7 @@ struct dm_usb_ops ehci_usb_ops = {
.create_int_queue = ehci_create_int_queue,
.poll_int_queue = ehci_poll_int_queue,
.destroy_int_queue = ehci_destroy_int_queue,
+ .get_max_xfer_size = ehci_get_max_xfer_size,
};
#endif
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c
index e22ee979391..bf55a71d66c 100644
--- a/drivers/usb/host/ohci-generic.c
+++ b/drivers/usb/host/ohci-generic.c
@@ -47,14 +47,14 @@ static int ohci_usb_probe(struct udevice *dev)
err = clk_enable(&priv->clocks[i]);
if (err) {
- error("failed to enable clock %d\n", i);
+ pr_err("failed to enable clock %d\n", i);
clk_free(&priv->clocks[i]);
goto clk_err;
}
priv->clock_count++;
}
} else if (clock_nb != -ENOENT) {
- error("failed to get clock phandle(%d)\n", clock_nb);
+ pr_err("failed to get clock phandle(%d)\n", clock_nb);
return clock_nb;
}
@@ -74,28 +74,28 @@ static int ohci_usb_probe(struct udevice *dev)
err = reset_deassert(&priv->resets[i]);
if (err) {
- error("failed to deassert reset %d\n", i);
+ pr_err("failed to deassert reset %d\n", i);
reset_free(&priv->resets[i]);
goto reset_err;
}
priv->reset_count++;
}
} else if (reset_nb != -ENOENT) {
- error("failed to get reset phandle(%d)\n", reset_nb);
+ pr_err("failed to get reset phandle(%d)\n", reset_nb);
goto clk_err;
}
err = generic_phy_get_by_index(dev, 0, &priv->phy);
if (err) {
if (err != -ENOENT) {
- error("failed to get usb phy\n");
+ pr_err("failed to get usb phy\n");
goto reset_err;
}
} else {
err = generic_phy_init(&priv->phy);
if (err) {
- error("failed to init usb phy\n");
+ pr_err("failed to init usb phy\n");
goto reset_err;
}
}
@@ -110,17 +110,17 @@ phy_err:
if (generic_phy_valid(&priv->phy)) {
ret = generic_phy_exit(&priv->phy);
if (ret)
- error("failed to release phy\n");
+ pr_err("failed to release phy\n");
}
reset_err:
ret = reset_release_all(priv->resets, priv->reset_count);
if (ret)
- error("failed to assert all resets\n");
+ pr_err("failed to assert all resets\n");
clk_err:
ret = clk_release_all(priv->clocks, priv->clock_count);
if (ret)
- error("failed to disable all clocks\n");
+ pr_err("failed to disable all clocks\n");
return err;
}
diff --git a/drivers/usb/host/usb-sandbox.c b/drivers/usb/host/usb-sandbox.c
index 5e3d96c208e..15055b351a5 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,15 +44,24 @@ 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;
/* 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;
+
+ 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);
@@ -70,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;
@@ -96,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;
@@ -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),
};
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index 0b8a501ce88..4e40f4bc3d2 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -150,9 +150,21 @@ int usb_update_hub_device(struct usb_device *udev)
return ops->update_hub_device(bus, udev);
}
+int usb_get_max_xfer_size(struct usb_device *udev, size_t *size)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->get_max_xfer_size)
+ return -ENOSYS;
+
+ return ops->get_max_xfer_size(bus, size);
+}
+
int usb_stop(void)
{
struct udevice *bus;
+ struct udevice *rh;
struct uclass *uc;
struct usb_uclass_priv *uc_priv;
int err = 0, ret;
@@ -168,23 +180,20 @@ int usb_stop(void)
ret = device_remove(bus, DM_REMOVE_NORMAL);
if (ret && !err)
err = ret;
- }
-#ifdef CONFIG_BLK
- ret = blk_unbind_all(IF_TYPE_USB);
- if (ret && !err)
- err = ret;
-#endif
-#ifdef CONFIG_SANDBOX
- struct udevice *dev;
- /* Reset all enulation devices */
- ret = uclass_get(UCLASS_USB_EMUL, &uc);
- if (ret)
- return ret;
+ /* Locate root hub device */
+ device_find_first_child(bus, &rh);
+ if (rh) {
+ /*
+ * All USB devices are children of root hub.
+ * Unbinding root hub will unbind all of its children.
+ */
+ ret = device_unbind(rh);
+ if (ret && !err)
+ err = ret;
+ }
+ }
- uclass_foreach_dev(dev, uc)
- usb_emul_reset(dev);
-#endif
#ifdef CONFIG_USB_STORAGE
usb_stor_reset();
#endif
@@ -251,6 +260,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");
diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c
index 4191a894218..258d1cd00a0 100644
--- a/drivers/usb/host/xhci-dwc3.c
+++ b/drivers/usb/host/xhci-dwc3.c
@@ -128,13 +128,13 @@ static int xhci_dwc3_probe(struct udevice *dev)
ret = generic_phy_get_by_index(dev, 0, &plat->usb_phy);
if (ret) {
if (ret != -ENOENT) {
- error("Failed to get USB PHY for %s\n", dev->name);
+ pr_err("Failed to get USB PHY for %s\n", dev->name);
return ret;
}
} else {
ret = generic_phy_init(&plat->usb_phy);
if (ret) {
- error("Can't init USB PHY for %s\n", dev->name);
+ pr_err("Can't init USB PHY for %s\n", dev->name);
return ret;
}
}
@@ -161,7 +161,7 @@ static int xhci_dwc3_remove(struct udevice *dev)
if (generic_phy_valid(&plat->usb_phy)) {
ret = generic_phy_exit(&plat->usb_phy);
if (ret) {
- error("Can't deinit USB PHY for %s\n", dev->name);
+ pr_err("Can't deinit USB PHY for %s\n", dev->name);
return ret;
}
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index d5eab3a6154..0582a9be40a 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));
}
}
@@ -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;
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-rockchip.c b/drivers/usb/host/xhci-rockchip.c
index ec55f4e59f7..b1f98842739 100644
--- a/drivers/usb/host/xhci-rockchip.c
+++ b/drivers/usb/host/xhci-rockchip.c
@@ -6,8 +6,6 @@
*/
#include <common.h>
#include <dm.h>
-#include <fdtdec.h>
-#include <libfdt.h>
#include <malloc.h>
#include <usb.h>
#include <watchdog.h>
@@ -46,9 +44,9 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
/*
* Get the base address for XHCI controller from the device node
*/
- plat->hcd_base = devfdt_get_addr(dev);
+ plat->hcd_base = dev_read_addr(dev);
if (plat->hcd_base == FDT_ADDR_T_NONE) {
- error("Can't get the XHCI register base address\n");
+ pr_err("Can't get the XHCI register base address\n");
return -ENXIO;
}
@@ -62,7 +60,7 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
}
if (plat->phy_base == FDT_ADDR_T_NONE) {
- error("Can't get the usbphy register address\n");
+ pr_err("Can't get the usbphy register address\n");
return -ENXIO;
}
@@ -119,7 +117,7 @@ static int rockchip_xhci_core_init(struct rockchip_xhci *rkxhci,
ret = dwc3_core_init(rkxhci->dwc3_reg);
if (ret) {
- error("failed to initialize core\n");
+ pr_err("failed to initialize core\n");
return ret;
}
@@ -151,14 +149,14 @@ static int xhci_usb_probe(struct udevice *dev)
if (plat->vbus_supply) {
ret = regulator_set_enable(plat->vbus_supply, true);
if (ret) {
- error("XHCI: failed to set VBus supply\n");
+ pr_err("XHCI: failed to set VBus supply\n");
return ret;
}
}
ret = rockchip_xhci_core_init(ctx, dev);
if (ret) {
- error("XHCI: failed to initialize controller\n");
+ pr_err("XHCI: failed to initialize controller\n");
return ret;
}
@@ -181,7 +179,7 @@ static int xhci_usb_remove(struct udevice *dev)
if (plat->vbus_supply) {
ret = regulator_set_enable(plat->vbus_supply, false);
if (ret)
- error("XHCI: failed to set VBus supply\n");
+ pr_err("XHCI: failed to set VBus supply\n");
}
return ret;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 9b82ee5c602..4673738d1e8 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -257,6 +257,188 @@ 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;
+}
+
+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
* 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;
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 max_burst;
+ unsigned int avg_trb_len;
+ unsigned int err_count = 0;
out_ctx = virt_dev->out_ctx;
in_ctx = virt_dev->in_ctx;
@@ -357,10 +545,28 @@ 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);
+ 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_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 */
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 |=
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(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) |
- ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT));
+ cpu_to_le32(MAX_BURST(max_burst) |
+ ERROR_COUNT(err_count));
trb_64 = (uintptr_t)
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);
+
+ /*
+ * 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);
@@ -546,16 +770,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 +788,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));
/*
@@ -890,11 +1112,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);
}
/**
@@ -1228,6 +1457,20 @@ static int xhci_update_hub_device(struct udevice *dev, struct usb_device *udev)
return xhci_configure_endpoints(udev, false);
}
+static int xhci_get_max_xfer_size(struct udevice *dev, size_t *size)
+{
+ /*
+ * xHCD allocates one segment which includes 64 TRBs for each endpoint
+ * and the last TRB in this segment is configured as a link TRB to form
+ * a TRB ring. Each TRB can transfer up to 64K bytes, however data
+ * buffers referenced by transfer TRBs shall not span 64KB boundaries.
+ * Hence the maximum number of TRBs we can use in one transfer is 62.
+ */
+ *size = (TRBS_PER_SEGMENT - 2) * TRB_MAX_BUFF_SIZE;
+
+ return 0;
+}
+
int xhci_register(struct udevice *dev, struct xhci_hccr *hccr,
struct xhci_hcor *hcor)
{
@@ -1281,6 +1524,7 @@ struct dm_usb_ops xhci_usb_ops = {
.interrupt = xhci_submit_int_msg,
.alloc_device = xhci_alloc_device,
.update_hub_device = xhci_update_hub_device,
+ .get_max_xfer_size = xhci_get_max_xfer_size,
};
#endif
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index a497d9d830f..ba5f650144f 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 */
@@ -1045,9 +1046,9 @@ struct xhci_scratchpad {
* (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
* meaning 64 ring segments.
* Initial allocated size of the ERST, in number of entries */
-#define ERST_NUM_SEGS 3
+#define ERST_NUM_SEGS 1
/* Initial number of event segment rings allocated */
-#define ERST_ENTRIES 3
+#define ERST_ENTRIES 1
/* Initial allocated size of the ERST, in number of entries */
#define ERST_SIZE 64
/* Poll every 60 seconds */