*/
#include <common.h>
+#include <dm.h>
#include <asm/byteorder.h>
#include <usb.h>
#include <malloc.h>
#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
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
+ struct udevice *dev;
+
+ /* Find the USB controller */
+ for (dev = udev->dev;
+ device_get_uclass_id(dev) != UCLASS_USB;
+ dev = dev->parent)
+ ;
+ return dev_get_priv(dev);
+#else
+ return udev->controller;
+#endif
+}
/**
* Waits for as per specified amount of time
* @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);
{
struct xhci_container_ctx *in_ctx;
struct xhci_virt_device *virt_dev;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
union xhci_trb *event;
virt_dev = ctrl->devs[udev->slot_id];
in_ctx = virt_dev->in_ctx;
- xhci_flush_cache((uint32_t)in_ctx->bytes, in_ctx->size);
+ xhci_flush_cache((uintptr_t)in_ctx->bytes, in_ctx->size);
xhci_queue_command(ctrl, in_ctx->bytes, udev->slot_id, 0,
ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
int ep_index;
unsigned int dir;
unsigned int ep_type;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int num_of_ep;
int ep_flag = 0;
u64 trb_64 = 0;
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 */
max_ep_flag = ep_flag;
}
- xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size);
+ xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
/* slot context */
xhci_slot_copy(ctrl, in_ctx, out_ctx);
* @param udev pointer to the Device Data Structure
* @return 0 if successful else error code on failure
*/
-static int xhci_address_device(struct usb_device *udev)
+static int xhci_address_device(struct usb_device *udev, int root_portnr)
{
int ret = 0;
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_virt_device *virt_dev;
* This is the first Set Address since device plug-in
* so setting up the slot context.
*/
- debug("Setting up addressable devices\n");
- xhci_setup_addressable_virt_dev(udev);
+ debug("Setting up addressable devices %p\n", ctrl->dcbaa);
+ 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);
*/
return ret;
- xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes,
- virt_dev->out_ctx->size);
+ xhci_inval_cache((uintptr_t)virt_dev->out_ctx->bytes,
+ virt_dev->out_ctx->size);
slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->out_ctx);
debug("xHC internal address is: %d\n",
* @param udev pointer to the Device Data Structure
* @return Returns 0 on succes else return error code on failure
*/
-int usb_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;
- struct xhci_ctrl *ctrl = udev->controller;
int ret;
/*
xhci_acknowledge_event(ctrl);
- ret = xhci_alloc_virt_device(udev);
+ ret = xhci_alloc_virt_device(ctrl, udev->slot_id);
if (ret < 0) {
/*
* TODO: Unsuccessful Address Device command shall leave
return 0;
}
+#ifndef CONFIG_DM_USB
+int usb_alloc_device(struct usb_device *udev)
+{
+ return _xhci_alloc_device(udev);
+}
+#endif
+
/*
* Full speed devices may have a max packet size greater than 8 bytes, but the
* USB core doesn't know that until it reads the first 8 bytes of the
*/
int xhci_check_maxpacket(struct usb_device *udev)
{
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
unsigned int slot_id = udev->slot_id;
int ep_index = 0; /* control endpoint */
struct xhci_container_ctx *in_ctx;
ifdesc = &udev->config.if_desc[0];
out_ctx = ctrl->devs[slot_id]->out_ctx;
- xhci_inval_cache((uint32_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);
hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
int len, srclen;
uint32_t reg;
volatile uint32_t *status_reg;
- struct xhci_ctrl *ctrl = udev->controller;
+ 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);
+ if ((req->requesttype & USB_RT_PORT) &&
+ 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;
}
srclen = 4;
break;
case 1: /* Vendor String */
- srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
+ srcptr = "\16\3U\0-\0B\0o\0o\0t\0";
srclen = 14;
break;
case 2: /* Product Name */
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;
debug("scrlen = %d\n req->length = %d\n",
srclen, le16_to_cpu(req->length));
- len = min(srclen, le16_to_cpu(req->length));
+ len = min(srclen, (int)le16_to_cpu(req->length));
if (srcptr != NULL && len > 0)
memcpy(buffer, srcptr, len);
* @param interval interval of the interrupt
* @return 0
*/
-int
-submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length, int interval)
+static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length, int interval)
{
/*
* TODO: Not addressing any interrupt type transfer requests
* @param length length of the buffer
* @return returns 0 if successful else -1 on failure
*/
-int
-submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length)
+static int _xhci_submit_bulk_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length)
{
if (usb_pipetype(pipe) != PIPE_BULK) {
printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
* @param buffer buffer to be read/written based on the request
* @param length length of the buffer
* @param setup Request type
+ * @param root_portnr Root port number that this device is on
* @return returns 0 if successful else -1 on failure
*/
-int
-submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
- int length, struct devrequest *setup)
+static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length,
+ struct devrequest *setup, int root_portnr)
{
- struct xhci_ctrl *ctrl = udev->controller;
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int ret = 0;
if (usb_pipetype(pipe) != PIPE_CONTROL) {
if (usb_pipedevice(pipe) == ctrl->rootdev)
return xhci_submit_root(udev, pipe, buffer, setup);
- if (setup->request == USB_REQ_SET_ADDRESS)
- return xhci_address_device(udev);
+ 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 xhci_ctrl_tx(udev, pipe, setup, length, buffer);
}
-/**
- * Intialises the XHCI host controller
- * and allocates the necessary data structures
- *
- * @param index index to the host controller data structure
- * @return pointer to the intialised controller
- */
-int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
+static int xhci_lowlevel_init(struct xhci_ctrl *ctrl)
{
+ struct xhci_hccr *hccr;
+ struct xhci_hcor *hcor;
uint32_t val;
uint32_t val2;
uint32_t reg;
- struct xhci_hccr *hccr;
- struct xhci_hcor *hcor;
- struct xhci_ctrl *ctrl;
-
- if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
- return -ENODEV;
-
- if (xhci_reset(hcor) != 0)
- return -ENODEV;
-
- ctrl = &xhcic[index];
-
- ctrl->hccr = hccr;
- ctrl->hcor = hcor;
+ hccr = ctrl->hccr;
+ hcor = ctrl->hcor;
/*
* Program the Number of Device Slots Enabled field in the CONFIG
* register with the max value of slots the HC can handle.
reg = HC_VERSION(xhci_readl(&hccr->cr_capbase));
printf("USB XHCI %x.%02x\n", reg >> 8, reg & 0xff);
- *controller = &xhcic[index];
+ return 0;
+}
+
+static int xhci_lowlevel_stop(struct xhci_ctrl *ctrl)
+{
+ u32 temp;
+
+ xhci_reset(ctrl->hcor);
+
+ debug("// Disabling event ring interrupts\n");
+ temp = xhci_readl(&ctrl->hcor->or_usbsts);
+ xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT);
+ temp = xhci_readl(&ctrl->ir_set->irq_pending);
+ xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
return 0;
}
+#ifndef CONFIG_DM_USB
+int submit_control_msg(struct usb_device *udev, unsigned long pipe,
+ void *buffer, int length, struct devrequest *setup)
+{
+ struct usb_device *hop = udev;
+
+ if (hop->parent)
+ while (hop->parent->parent)
+ hop = hop->parent;
+
+ return _xhci_submit_control_msg(udev, pipe, buffer, length, setup,
+ hop->portnr);
+}
+
+int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
+ int length)
+{
+ return _xhci_submit_bulk_msg(udev, pipe, buffer, length);
+}
+
+int submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
+ int length, int interval)
+{
+ return _xhci_submit_int_msg(udev, pipe, buffer, length, interval);
+}
+
+/**
+ * Intialises the XHCI host controller
+ * and allocates the necessary data structures
+ *
+ * @param index index to the host controller data structure
+ * @return pointer to the intialised controller
+ */
+int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
+{
+ struct xhci_hccr *hccr;
+ struct xhci_hcor *hcor;
+ struct xhci_ctrl *ctrl;
+ int ret;
+
+ *controller = NULL;
+
+ if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0)
+ return -ENODEV;
+
+ if (xhci_reset(hcor) != 0)
+ return -ENODEV;
+
+ ctrl = &xhcic[index];
+
+ ctrl->hccr = hccr;
+ ctrl->hcor = hcor;
+
+ ret = xhci_lowlevel_init(ctrl);
+
+ if (ret) {
+ ctrl->hccr = NULL;
+ ctrl->hcor = NULL;
+ } else {
+ *controller = &xhcic[index];
+ }
+
+ return ret;
+}
+
/**
* Stops the XHCI host controller
* and cleans up all the related data structures
int usb_lowlevel_stop(int index)
{
struct xhci_ctrl *ctrl = (xhcic + index);
- u32 temp;
- xhci_reset(ctrl->hcor);
+ if (ctrl->hcor) {
+ xhci_lowlevel_stop(ctrl);
+ xhci_hcd_stop(index);
+ xhci_cleanup(ctrl);
+ }
- debug("// Disabling event ring interrupts\n");
- temp = xhci_readl(&ctrl->hcor->or_usbsts);
- xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT);
- temp = xhci_readl(&ctrl->ir_set->irq_pending);
- xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp));
+ return 0;
+}
+#endif /* CONFIG_DM_USB */
+
+#ifdef CONFIG_DM_USB
- xhci_hcd_stop(index);
+static int xhci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ struct devrequest *setup)
+{
+ struct usb_device *uhop;
+ struct udevice *hub;
+ int root_portnr = 0;
+
+ debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__,
+ dev->name, udev, udev->dev->name, udev->portnr);
+ hub = udev->dev;
+ if (device_get_uclass_id(hub) == UCLASS_USB_HUB) {
+ /* Figure out our port number on the root hub */
+ if (usb_hub_is_root_hub(hub)) {
+ root_portnr = udev->portnr;
+ } else {
+ while (!usb_hub_is_root_hub(hub->parent))
+ hub = hub->parent;
+ uhop = dev_get_parent_priv(hub);
+ root_portnr = uhop->portnr;
+ }
+ }
+/*
+ struct usb_device *hop = udev;
+
+ if (hop->parent)
+ while (hop->parent->parent)
+ hop = hop->parent;
+*/
+ return _xhci_submit_control_msg(udev, pipe, buffer, length, setup,
+ root_portnr);
+}
+static int xhci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _xhci_submit_bulk_msg(udev, pipe, buffer, length);
+}
+
+static int xhci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ int interval)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _xhci_submit_int_msg(udev, pipe, buffer, length, interval);
+}
+
+static int xhci_alloc_device(struct udevice *dev, struct usb_device *udev)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, 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)
+{
+ struct xhci_ctrl *ctrl = dev_get_priv(dev);
+ struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+ int ret;
+
+ debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p\n", __func__, dev->name,
+ ctrl, hccr, hcor);
+
+ ctrl->dev = dev;
+
+ /*
+ * XHCI needs to issue a Address device command to setup
+ * proper device context structures, before it can interact
+ * with the device. So a get_descriptor will fail before any
+ * of that is done for XHCI unlike EHCI.
+ */
+ priv->desc_before_addr = false;
+
+ ret = xhci_reset(hcor);
+ if (ret)
+ goto err;
+
+ ctrl->hccr = hccr;
+ ctrl->hcor = hcor;
+ ret = xhci_lowlevel_init(ctrl);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ free(ctrl);
+ debug("%s: failed, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+int xhci_deregister(struct udevice *dev)
+{
+ struct xhci_ctrl *ctrl = dev_get_priv(dev);
+
+ xhci_lowlevel_stop(ctrl);
xhci_cleanup(ctrl);
return 0;
}
+
+struct dm_usb_ops xhci_usb_ops = {
+ .control = xhci_submit_control_msg,
+ .bulk = xhci_submit_bulk_msg,
+ .interrupt = xhci_submit_int_msg,
+ .alloc_device = xhci_alloc_device,
+ .update_hub_device = xhci_update_hub_device,
+};
+
+#endif