+// SPDX-License-Identifier: GPL-2.0+
/*
* USB HOST XHCI Controller stack
*
* Copyright (C) 2013 Samsung Electronics Co.Ltd
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
* Vikas Sajjan <vikas.sajjan@samsung.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
/**
#include <watchdog.h>
#include <asm/cache.h>
#include <asm/unaligned.h>
-#include <asm-generic/errno.h>
+#include <linux/errno.h>
#include "xhci.h"
#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
cpu_to_le16(0x8), /* wHubCharacteristics */
10, /* bPwrOn2PwrGood */
0, /* bHubCntrCurrent */
- {}, /* Device removable */
- {} /* at most 7 ports! XXX */
+ { /* Device removable */
+ } /* at most 7 ports! XXX */
},
{
0x12, /* bLength */
},
};
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
static struct xhci_ctrl xhcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
#endif
struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev)
{
-#ifdef CONFIG_DM_USB
+#if CONFIG_IS_ENABLED(DM_USB)
struct udevice *dev;
/* Find the USB controller */
* @param hcor pointer to host controller operation registers
* @return -EBUSY if XHCI Controller is not halted else status of handshake
*/
-int xhci_reset(struct xhci_hcor *hcor)
+static int xhci_reset(struct xhci_hcor *hcor)
{
u32 cmd;
u32 state;
int ret;
/* Halting the Host first */
- debug("// Halt the HC\n");
+ debug("// Halt the HC: %p\n", hcor);
state = xhci_readl(&hcor->or_usbsts) & STS_HALT;
if (!state) {
cmd = xhci_readl(&hcor->or_usbcmd);
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.
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;
ifdesc = &udev->config.if_desc[0];
ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
- /* Zero the input context control */
- ctrl_ctx->add_flags = 0;
+ /* Initialize the input context control */
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
ctrl_ctx->drop_flags = 0;
/* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */
/* slot context */
xhci_slot_copy(ctrl, in_ctx, out_ctx);
slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
- slot_ctx->dev_info &= ~(LAST_CTX_MASK);
+ slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK));
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0);
xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0);
/* 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);
/*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);
* so setting up the slot context.
*/
debug("Setting up addressable devices %p\n", ctrl->dcbaa);
- xhci_setup_addressable_virt_dev(ctrl, udev->slot_id, udev->speed,
- root_portnr);
+ xhci_setup_addressable_virt_dev(ctrl, udev, root_portnr);
ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
* @param udev pointer to the Device Data Structure
* @return Returns 0 on succes else return error code on failure
*/
-int _xhci_alloc_device(struct usb_device *udev)
+static int _xhci_alloc_device(struct usb_device *udev)
{
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
union xhci_trb *event;
return 0;
}
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
int usb_alloc_device(struct usb_device *udev)
{
return _xhci_alloc_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);
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));
/*
uint32_t reg;
volatile uint32_t *status_reg;
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
+ struct xhci_hccr *hccr = ctrl->hccr;
struct xhci_hcor *hcor = ctrl->hcor;
+ int max_ports = HCS_MAX_PORTS(xhci_readl(&hccr->cr_hcsparams1));
if ((req->requesttype & USB_RT_PORT) &&
- le16_to_cpu(req->index) > CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS) {
- printf("The request port(%d) is not configured\n",
- le16_to_cpu(req->index) - 1);
+ le16_to_cpu(req->index) > max_ports) {
+ printf("The request port(%d) exceeds maximum port number\n",
+ le16_to_cpu(req->index) - 1);
return -EINVAL;
}
case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8):
switch (le16_to_cpu(req->value) >> 8) {
case USB_DT_HUB:
+ case USB_DT_SS_HUB:
debug("USB_DT_HUB config\n");
srcptr = &descriptor.hub;
srclen = 0x8;
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);
}
/**
if (usb_pipedevice(pipe) == ctrl->rootdev)
return xhci_submit_root(udev, pipe, buffer, setup);
- if (setup->request == USB_REQ_SET_ADDRESS)
+ if (setup->request == USB_REQ_SET_ADDRESS &&
+ (setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD)
return xhci_address_device(udev, root_portnr);
- if (setup->request == USB_REQ_SET_CONFIGURATION) {
+ if (setup->request == USB_REQ_SET_CONFIGURATION &&
+ (setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
ret = xhci_set_configuration(udev);
if (ret) {
puts("Failed to configure xHCI endpoint\n");
return 0;
}
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
int submit_control_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length, struct devrequest *setup)
{
struct xhci_ctrl *ctrl;
int ret;
+ *controller = NULL;
+
if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
return -ENODEV;
ret = xhci_lowlevel_init(ctrl);
- *controller = &xhcic[index];
+ if (ret) {
+ ctrl->hccr = NULL;
+ ctrl->hcor = NULL;
+ } else {
+ *controller = &xhcic[index];
+ }
return ret;
}
{
struct xhci_ctrl *ctrl = (xhcic + index);
- xhci_lowlevel_stop(ctrl);
- xhci_hcd_stop(index);
- xhci_cleanup(ctrl);
+ if (ctrl->hcor) {
+ xhci_lowlevel_stop(ctrl);
+ xhci_hcd_stop(index);
+ xhci_cleanup(ctrl);
+ }
return 0;
}
-#endif /* CONFIG_DM_USB */
-
-#ifdef CONFIG_DM_USB
-/*
-static struct usb_device *get_usb_device(struct udevice *dev)
-{
- struct usb_device *udev;
-
- if (device_get_uclass_id(dev) == UCLASS_USB)
- udev = dev_get_uclass_priv(dev);
- else
- udev = dev_get_parentdata(dev);
-
- return udev;
-}
-*/
-static bool is_root_hub(struct udevice *dev)
-{
- if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB)
- return true;
+#endif /* CONFIG_IS_ENABLED(DM_USB) */
- return false;
-}
+#if CONFIG_IS_ENABLED(DM_USB)
static int xhci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
hub = udev->dev;
if (device_get_uclass_id(hub) == UCLASS_USB_HUB) {
/* Figure out our port number on the root hub */
- if (is_root_hub(hub)) {
+ if (usb_hub_is_root_hub(hub)) {
root_portnr = udev->portnr;
} else {
- while (!is_root_hub(hub->parent))
+ while (!usb_hub_is_root_hub(hub->parent))
hub = hub->parent;
- uhop = dev_get_parentdata(hub);
+ uhop = dev_get_parent_priv(hub);
root_portnr = uhop->portnr;
}
}
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);
+ /*
+ * refer to section 6.2.2: MTT should be 0 for full speed hub,
+ * but it may be already set to 1 when setup an xHCI virtual
+ * device, so clear it anyway.
+ */
+ if (hub->tt.multi)
+ slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+ else if (udev->speed == USB_SPEED_FULL)
+ 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));
+ slot_ctx->dev_state = 0;
+
+ 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)
{
.bulk = xhci_submit_bulk_msg,
.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