summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBin Meng <bmeng.cn@gmail.com>2017-07-19 13:51:19 (GMT)
committerPrabhakar Kushwaha <prabhakar.kushwaha@nxp.com>2017-08-08 08:42:23 (GMT)
commitd0c4403453fa90c146e5209fe14d235444c9135f (patch)
tree9167e045f7f65ccda56d40a6d9c1131bb963af52
parentec6ae08af891c52b0541e28a5bc960f0898f1849 (diff)
downloadu-boot-d0c4403453fa90c146e5209fe14d235444c9135f.tar.xz
usb: xhci: Implement update_hub_device() operation
There is no way to know whether the attached device is a hub or not in advance before the device's descriptor is fetched. But once we know it's a high speed hub, per the xHCI spec, we need to tell xHC it's a hub device by initializing hub-related fields in the input slot context. Signed-off-by: Bin Meng <bmeng.cn@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r--drivers/usb/host/xhci.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1148127..e64637b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1170,6 +1170,64 @@ static int xhci_alloc_device(struct udevice *dev, struct usb_device *udev)
return _xhci_alloc_device(udev);
}
+static int xhci_update_hub_device(struct udevice *dev, struct usb_device *udev)
+{
+ struct xhci_ctrl *ctrl = dev_get_priv(dev);
+ struct usb_hub_device *hub = dev_get_uclass_priv(udev->dev);
+ struct xhci_virt_device *virt_dev;
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_container_ctx *out_ctx;
+ struct xhci_container_ctx *in_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+ int slot_id = udev->slot_id;
+ unsigned think_time;
+
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+
+ /* Ignore root hubs */
+ if (usb_hub_is_root_hub(udev->dev))
+ return 0;
+
+ virt_dev = ctrl->devs[slot_id];
+ BUG_ON(!virt_dev);
+
+ out_ctx = virt_dev->out_ctx;
+ in_ctx = virt_dev->in_ctx;
+
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
+ /* Initialize the input context control */
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ ctrl_ctx->drop_flags = 0;
+
+ xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
+
+ /* slot context */
+ xhci_slot_copy(ctrl, in_ctx, out_ctx);
+ slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
+
+ /* Update hub related fields */
+ slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
+ if (hub->tt.multi && udev->speed == USB_SPEED_HIGH)
+ slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+ slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(udev->maxchild));
+ /*
+ * Set TT think time - convert from ns to FS bit times.
+ * Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns
+ *
+ * 0 = 8 FS bit times, 1 = 16 FS bit times,
+ * 2 = 24 FS bit times, 3 = 32 FS bit times.
+ *
+ * This field shall be 0 if the device is not a high-spped hub.
+ */
+ think_time = hub->tt.think_time;
+ if (think_time != 0)
+ think_time = (think_time / 666) - 1;
+ if (udev->speed == USB_SPEED_HIGH)
+ slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time));
+
+ return xhci_configure_endpoints(udev, false);
+}
+
int xhci_register(struct udevice *dev, struct xhci_hccr *hccr,
struct xhci_hcor *hcor)
{
@@ -1222,6 +1280,7 @@ struct dm_usb_ops xhci_usb_ops = {
.bulk = xhci_submit_bulk_msg,
.interrupt = xhci_submit_int_msg,
.alloc_device = xhci_alloc_device,
+ .update_hub_device = xhci_update_hub_device,
};
#endif