diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-exynos.c | 117 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 128 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sunxi.c | 93 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 669 | ||||
-rw-r--r-- | drivers/usb/host/ohci-sunxi.c | 104 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 143 | ||||
-rw-r--r-- | drivers/usb/host/usb-uclass.c | 180 | ||||
-rw-r--r-- | drivers/usb/host/xhci-exynos5.c | 108 |
9 files changed, 950 insertions, 593 deletions
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 3b57e565533..4d35d3e5fe8 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o obj-$(CONFIG_USB_SL811HS) += sl811-hcd.o obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o obj-$(CONFIG_USB_OHCI_EP93XX) += ohci-ep93xx.o +obj-$(CONFIG_USB_OHCI_SUNXI) += ohci-sunxi.o # echi obj-$(CONFIG_USB_EHCI) += ehci-hcd.o diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 86cf6312feb..18e9251b64f 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -25,14 +25,12 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_DM_USB struct exynos_ehci_platdata { struct usb_platdata usb_plat; fdt_addr_t hcd_base; fdt_addr_t phy_base; struct gpio_desc vbus_gpio; }; -#endif /** * Contains pointers to register base addresses @@ -42,16 +40,8 @@ struct exynos_ehci { struct ehci_ctrl ctrl; struct exynos_usb_phy *usb; struct ehci_hccr *hcd; -#ifndef CONFIG_DM_USB - struct gpio_desc vbus_gpio; -#endif }; -#ifndef CONFIG_DM_USB -static struct exynos_ehci exynos; -#endif - -#ifdef CONFIG_DM_USB static int ehci_usb_ofdata_to_platdata(struct udevice *dev) { struct exynos_ehci_platdata *plat = dev_get_platdata(dev); @@ -91,55 +81,6 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev) return 0; } -#else -static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) -{ - fdt_addr_t addr; - unsigned int node; - int depth; - - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_EHCI); - if (node <= 0) { - debug("EHCI: Can't get device node for ehci\n"); - return -ENODEV; - } - - /* - * Get the base address for EHCI controller from the device node - */ - addr = fdtdec_get_addr(blob, node, "reg"); - if (addr == FDT_ADDR_T_NONE) { - debug("Can't get the EHCI register address\n"); - return -ENXIO; - } - - exynos->hcd = (struct ehci_hccr *)addr; - - /* Vbus gpio */ - gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, - &exynos->vbus_gpio, GPIOD_IS_OUT); - - depth = 0; - node = fdtdec_next_compatible_subnode(blob, node, - COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); - if (node <= 0) { - debug("EHCI: Can't get device node for usb-phy controller\n"); - return -ENODEV; - } - - /* - * Get the base address for usbphy from the device node - */ - exynos->usb = (struct exynos_usb_phy *)fdtdec_get_addr(blob, node, - "reg"); - if (exynos->usb == NULL) { - debug("Can't get the usbphy register address\n"); - return -ENXIO; - } - - return 0; -} -#endif static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb) { @@ -270,63 +211,6 @@ static void reset_usb_phy(struct exynos_usb_phy *usb) set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); } -#ifndef CONFIG_DM_USB -/* - * EHCI-initialization - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(int index, enum usb_init_type init, - struct ehci_hccr **hccr, struct ehci_hcor **hcor) -{ - struct exynos_ehci *ctx = &exynos; - -#ifdef CONFIG_OF_CONTROL - if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { - debug("Unable to parse device tree for ehci-exynos\n"); - return -ENODEV; - } -#else - ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); - ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); -#endif - -#ifdef CONFIG_OF_CONTROL - /* setup the Vbus gpio here */ - if (dm_gpio_is_valid(&ctx->vbus_gpio)) - dm_gpio_set_value(&ctx->vbus_gpio, 1); -#endif - - setup_usb_phy(ctx->usb); - - board_usb_init(index, init); - - *hccr = ctx->hcd; - *hcor = (struct ehci_hcor *)((uint32_t) *hccr - + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - - debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the EHCI host controller. - */ -int ehci_hcd_stop(int index) -{ - struct exynos_ehci *ctx = &exynos; - - reset_usb_phy(ctx->usb); - - return 0; -} -#endif - -#ifdef CONFIG_DM_USB static int ehci_usb_probe(struct udevice *dev) { struct exynos_ehci_platdata *plat = dev_get_platdata(dev); @@ -377,4 +261,3 @@ U_BOOT_DRIVER(usb_ehci) = { .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bd9861dd68e..1e5a6e2b20c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -125,14 +125,7 @@ static struct descriptor { static struct ehci_ctrl *ehci_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); + return dev_get_priv(usb_get_bus(udev->dev)); #else return udev->controller; #endif @@ -310,23 +303,33 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev, * in the tree before that one! */ #ifdef CONFIG_DM_USB + /* + * When called from usb-uclass.c: usb_scan_device() udev->dev points + * to the parent udevice, not the actual udevice belonging to the + * udev as the device is not instantiated yet. So when searching + * for the first usb-2 parent start with udev->dev not + * udev->dev->parent . + */ struct udevice *parent; + struct usb_device *uparent; + + ttdev = udev; + parent = udev->dev; + uparent = dev_get_parentdata(parent); - for (ttdev = udev; ; ) { - struct udevice *dev = ttdev->dev; + while (uparent->speed != USB_SPEED_HIGH) { + struct udevice *dev = parent; - if (dev->parent && - device_get_uclass_id(dev->parent) == UCLASS_USB_HUB) - parent = dev->parent; - else - parent = NULL; - if (!parent) + if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) { + printf("ehci: Error cannot find high speed parent of usb-1 device\n"); return; - ttdev = dev_get_parentdata(parent); - if (!ttdev->speed != USB_SPEED_HIGH) - break; + } + + ttdev = dev_get_parentdata(dev); + parent = dev->parent; + uparent = dev_get_parentdata(parent); } - parent_devnum = ttdev->devnum; + parent_devnum = uparent->devnum; #else ttdev = udev; while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) @@ -872,7 +875,7 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, port - 1); reg |= EHCI_PS_PO; ehci_writel(status_reg, reg); - break; + return -ENXIO; } else { int ret; @@ -894,11 +897,22 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, */ ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); - if (!ret) - ctrl->portreset |= 1 << port; - else + if (!ret) { + reg = ehci_readl(status_reg); + if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) + == EHCI_PS_CS && !ehci_is_TDI()) { + debug("port %d full speed --> companion\n", port - 1); + reg &= ~EHCI_PS_CLEAR; + reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); + return -ENXIO; + } else { + ctrl->portreset |= 1 << port; + } + } else { printf("port(%d) reset error\n", port - 1); + } } break; case USB_PORT_FEAT_TEST: @@ -1249,9 +1263,9 @@ disable_periodic(struct ehci_ctrl *ctrl) return 0; } -struct int_queue * -create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, - int elementsize, void *buffer, int interval) +static struct int_queue *_ehci_create_int_queue(struct usb_device *dev, + unsigned long pipe, int queuesize, int elementsize, + void *buffer, int interval) { struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); struct int_queue *result = NULL; @@ -1407,7 +1421,8 @@ fail1: return NULL; } -void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +static void *_ehci_poll_int_queue(struct usb_device *dev, + struct int_queue *queue) { struct QH *cur = queue->current; struct qTD *cur_td; @@ -1442,8 +1457,8 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) } /* Do not free buffers associated with QHs, they're owned by someone else */ -int -destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +static int _ehci_destroy_int_queue(struct usb_device *dev, + struct int_queue *queue) { struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); int result = -1; @@ -1500,12 +1515,12 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe, debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - queue = create_int_queue(dev, pipe, 1, length, buffer, interval); + queue = _ehci_create_int_queue(dev, pipe, 1, length, buffer, interval); if (!queue) return -1; timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); - while ((backbuffer = poll_int_queue(dev, queue)) == NULL) + while ((backbuffer = _ehci_poll_int_queue(dev, queue)) == NULL) if (get_timer(0) > timeout) { printf("Timeout poll on interrupt endpoint\n"); result = -ETIMEDOUT; @@ -1518,7 +1533,7 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe, return -EINVAL; } - ret = destroy_int_queue(dev, queue); + ret = _ehci_destroy_int_queue(dev, queue); if (ret < 0) return ret; @@ -1544,6 +1559,24 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, { return _ehci_submit_int_msg(dev, pipe, buffer, length, interval); } + +struct int_queue *create_int_queue(struct usb_device *dev, + unsigned long pipe, int queuesize, int elementsize, + void *buffer, int interval) +{ + return _ehci_create_int_queue(dev, pipe, queuesize, elementsize, + buffer, interval); +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + return _ehci_poll_int_queue(dev, queue); +} + +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + return _ehci_destroy_int_queue(dev, queue); +} #endif #ifdef CONFIG_DM_USB @@ -1572,16 +1605,42 @@ static int ehci_submit_int_msg(struct udevice *dev, struct usb_device *udev, return _ehci_submit_int_msg(udev, pipe, buffer, length, interval); } +static struct int_queue *ehci_create_int_queue(struct udevice *dev, + struct usb_device *udev, unsigned long pipe, int queuesize, + int elementsize, void *buffer, int interval) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _ehci_create_int_queue(udev, pipe, queuesize, elementsize, + buffer, interval); +} + +static void *ehci_poll_int_queue(struct udevice *dev, struct usb_device *udev, + struct int_queue *queue) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _ehci_poll_int_queue(udev, queue); +} + +static int ehci_destroy_int_queue(struct udevice *dev, struct usb_device *udev, + struct int_queue *queue) +{ + debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); + return _ehci_destroy_int_queue(udev, queue); +} + 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) { + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); struct ehci_ctrl *ctrl = dev_get_priv(dev); int ret; debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__, dev->name, ctrl, hccr, hcor, init); + priv->desc_before_addr = true; + ehci_setup_ops(ctrl, ops); ctrl->hccr = hccr; ctrl->hcor = hcor; @@ -1617,6 +1676,9 @@ struct dm_usb_ops ehci_usb_ops = { .control = ehci_submit_control_msg, .bulk = ehci_submit_bulk_msg, .interrupt = ehci_submit_int_msg, + .create_int_queue = ehci_create_int_queue, + .poll_int_queue = ehci_poll_int_queue, + .destroy_int_queue = ehci_destroy_int_queue, }; #endif diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 0edb6438cbd..34130f8443c 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -14,53 +14,88 @@ #include <asm/arch/clock.h> #include <asm/arch/usb_phy.h> #include <asm/io.h> +#include <dm.h> #include "ehci.h" -int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr, - struct ehci_hcor **hcor) +struct ehci_sunxi_priv { + struct ehci_ctrl ehci; + int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ + int phy_index; /* Index of the usb-phy attached to this hcd */ +}; + +static int ehci_usb_probe(struct udevice *dev) { struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - int ahb_gate_offset; + struct usb_platdata *plat = dev_get_platdata(dev); + struct ehci_sunxi_priv *priv = dev_get_priv(dev); + struct ehci_hccr *hccr = (struct ehci_hccr *)dev_get_addr(dev); + struct ehci_hcor *hcor; + + /* + * This should go away once we've moved to the driver model for + * clocks resp. phys. + */ + if (hccr == (void *)SUNXI_USB1_BASE) { + priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0; + priv->phy_index = 1; + } else { + priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1; + priv->phy_index = 2; + } - ahb_gate_offset = index ? AHB_GATE_OFFSET_USB_EHCI1 : - AHB_GATE_OFFSET_USB_EHCI0; - setbits_le32(&ccm->ahb_gate0, 1 << ahb_gate_offset); + setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); #ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, 1 << ahb_gate_offset); + setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); #endif - sunxi_usb_phy_init(index + 1); - sunxi_usb_phy_power_on(index + 1); - - if (index == 0) - *hccr = (void *)SUNXI_USB1_BASE; - else - *hccr = (void *)SUNXI_USB2_BASE; - - *hcor = (struct ehci_hcor *)((uint32_t) *hccr - + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + sunxi_usb_phy_init(priv->phy_index); + sunxi_usb_phy_power_on(priv->phy_index); - debug("sunxi-ehci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + hcor = (struct ehci_hcor *)((uint32_t)hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - return 0; + return ehci_register(dev, hccr, hcor, NULL, 0, plat->init_type); } -int ehci_hcd_stop(int index) +static int ehci_usb_remove(struct udevice *dev) { struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - int ahb_gate_offset; + struct ehci_sunxi_priv *priv = dev_get_priv(dev); + int ret; + + ret = ehci_deregister(dev); + if (ret) + return ret; - sunxi_usb_phy_power_off(index + 1); - sunxi_usb_phy_exit(index + 1); + sunxi_usb_phy_power_off(priv->phy_index); + sunxi_usb_phy_exit(priv->phy_index); - ahb_gate_offset = index ? AHB_GATE_OFFSET_USB_EHCI1 : - AHB_GATE_OFFSET_USB_EHCI0; #ifdef CONFIG_SUNXI_GEN_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, 1 << ahb_gate_offset); + clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); #endif - clrbits_le32(&ccm->ahb_gate0, 1 << ahb_gate_offset); + clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); return 0; } + +static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "allwinner,sun4i-a10-ehci", }, + { .compatible = "allwinner,sun5i-a13-ehci", }, + { .compatible = "allwinner,sun6i-a31-ehci", }, + { .compatible = "allwinner,sun7i-a20-ehci", }, + { .compatible = "allwinner,sun8i-a23-ehci", }, + { .compatible = "allwinner,sun9i-a80-ehci", }, + { } +}; + +U_BOOT_DRIVER(usb_ehci) = { + .name = "ehci_sunxi", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .probe = ehci_usb_probe, + .remove = ehci_usb_remove, + .ops = &ehci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct ehci_sunxi_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 97a7edeb539..691ed1c900b 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -30,6 +30,8 @@ #include <common.h> #include <asm/byteorder.h> +#include <dm.h> +#include <errno.h> #if defined(CONFIG_PCI_OHCI) # include <pci.h> @@ -103,16 +105,104 @@ static struct pci_device_id ehci_pci_ids[] = { # define m32_swap(x) cpu_to_le32(x) #endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ +#ifdef CONFIG_DM_USB +/* + * We really should do proper cache flushing everywhere, but for now we only + * do it for new (driver-model) usb code to avoid regressions. + */ +#define flush_dcache_buffer(addr, size) \ + flush_dcache_range((unsigned long)(addr), \ + ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN)) +#define invalidate_dcache_buffer(addr, size) \ + invalidate_dcache_range((unsigned long)(addr), \ + ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN)) +#else +#define flush_dcache_buffer(addr, size) +#define invalidate_dcache_buffer(addr, size) +#endif + +/* Do not use sizeof(ed / td) as our ed / td structs contain extra members */ +#define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16) +#define flush_dcache_td(addr) flush_dcache_buffer(addr, 16) +#define flush_dcache_iso_td(addr) flush_dcache_buffer(addr, 32) +#define flush_dcache_hcca(addr) flush_dcache_buffer(addr, 256) +#define invalidate_dcache_ed(addr) invalidate_dcache_buffer(addr, 16) +#define invalidate_dcache_td(addr) invalidate_dcache_buffer(addr, 16) +#define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32) +#define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256) + +#ifdef CONFIG_DM_USB +/* + * The various ohci_mdelay(1) calls in the code seem unnecessary. We keep + * them around when building for older boards not yet converted to the dm + * just in case (to avoid regressions), for dm this turns them into nops. + */ +#define ohci_mdelay(x) +#else +#define ohci_mdelay(x) mdelay(x) +#endif + +#ifndef CONFIG_DM_USB /* global ohci_t */ static ohci_t gohci; /* this must be aligned to a 256 byte boundary */ struct ohci_hcca ghcca[1]; -/* a pointer to the aligned storage */ -struct ohci_hcca *phcca; -/* this allocates EDs for all possible endpoints */ -struct ohci_device ohci_dev; -/* device which was disconnected */ -struct usb_device *devgone; +#endif + +/* mapping of the OHCI CC status to error codes */ +static int cc_to_error[16] = { + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +static const char *cc_to_string[16] = { + "No Error", + "CRC: Last data packet from endpoint contained a CRC error.", + "BITSTUFFING: Last data packet from endpoint contained a bit " \ + "stuffing violation", + "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ + "that did not match the expected value.", + "STALL: TD was moved to the Done Queue because the endpoint returned" \ + " a STALL PID", + "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ + "not provide a handshake (OUT)", + "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ + "(IN) or handshake (OUT)", + "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ + "value is not defined.", + "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ + "either the size of the maximum data packet allowed\n" \ + "from the endpoint (found in MaximumPacketSize field\n" \ + "of ED) or the remaining buffer size.", + "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ + "and that amount was not sufficient to fill the\n" \ + "specified buffer", + "reserved1", + "reserved2", + "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ + "than it could be written to system memory", + "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ + "system memory fast enough to keep up with data USB " \ + "data rate.", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(1)", + "NOT ACCESSED: This code is set by software before the TD is placed" \ + "on a list to be processed by the HC.(2)", +}; static inline u32 roothub_a(struct ohci *hc) { return ohci_readl(&hc->regs->roothub.a); } @@ -124,11 +214,42 @@ static inline u32 roothub_portstatus(struct ohci *hc, int i) { return ohci_readl(&hc->regs->roothub.portstatus[i]); } /* forward declaration */ -static int hc_interrupt(void); -static void td_submit_job(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, +static int hc_interrupt(ohci_t *ohci); +static void td_submit_job(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval); +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev, + unsigned long pipe, int interval, int load); + +/*-------------------------------------------------------------------------*/ + +/* TDs ... */ +static struct td *td_alloc(ohci_dev_t *ohci_dev, struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ohci_dev->tds[i].usb_dev == NULL) + { + td = &ohci_dev->tds[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void ed_free(struct ed *ed) +{ + ed->usb_dev = NULL; +} /*-------------------------------------------------------------------------* * URB support functions @@ -158,18 +279,18 @@ static void urb_free_priv(urb_priv_t *urb) /*-------------------------------------------------------------------------*/ #ifdef DEBUG -static int sohci_get_current_frame_number(struct usb_device *dev); +static int sohci_get_current_frame_number(ohci_t *ohci); /* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ -static void pkt_print(urb_priv_t *purb, struct usb_device *dev, +static void pkt_print(ohci_t *ohci, urb_priv_t *purb, struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, char *str, int small) { dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", str, - sohci_get_current_frame_number(dev), + sohci_get_current_frame_number(ohci), usb_pipedevice(pipe), usb_pipeendpoint(pipe), usb_pipeout(pipe)? 'O': 'I', @@ -213,9 +334,11 @@ void ep_print_int_eds(ohci_t *ohci, char *str) ed_p = &(ohci->hcca->int_table [i]); if (*ed_p == 0) continue; + invalidate_dcache_ed(ed_p); printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); while (*ed_p != 0 && j--) { ed_t *ed = (ed_t *)m32_swap(ed_p); + invalidate_dcache_ed(ed); printf(" ed: %4x;", ed->hwINFO); ed_p = &ed->hwNextED; } @@ -246,6 +369,7 @@ static void maybe_print_eds(char *label, __u32 value) if (value) { dbg("%s %08x", label, value); + invalidate_dcache_ed(edp); dbg("%08x", edp->hwINFO); dbg("%08x", edp->hwTailP); dbg("%08x", edp->hwHeadP); @@ -380,6 +504,7 @@ static void ohci_dump(ohci_t *controller, int verbose) ohci_dump_status(controller); if (verbose) ep_print_int_eds(controller, "hcca"); + invalidate_dcache_hcca(controller->hcca); dbg("hcca frame #%04x", controller->hcca->frame_no); ohci_dump_roothub(controller, 1); } @@ -391,9 +516,9 @@ static void ohci_dump(ohci_t *controller, int verbose) /* get a transfer request */ -int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) +int sohci_submit_job(ohci_t *ohci, ohci_dev_t *ohci_dev, urb_priv_t *urb, + struct devrequest *setup) { - ohci_t *ohci; ed_t *ed; urb_priv_t *purb_priv = urb; int i, size = 0; @@ -403,8 +528,6 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) int transfer_len = urb->transfer_buffer_length; int interval = urb->interval; - ohci = &gohci; - /* when controller's hung, permit only roothub cleanup attempts * such as powering down ports */ if (ohci->disabled) { @@ -417,7 +540,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) urb->finished = 0; /* every endpoint has a ed, locate and fill it */ - ed = ep_add_ed(dev, pipe, interval, 1); + ed = ep_add_ed(ohci_dev, dev, pipe, interval, 1); if (!ed) { err("sohci_submit_job: ENOMEM"); return -1; @@ -453,7 +576,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) /* allocate the TDs */ /* note that td[0] was allocated in ep_add_ed */ for (i = 0; i < size; i++) { - purb_priv->td[i] = td_alloc(dev); + purb_priv->td[i] = td_alloc(ohci_dev, dev); if (!purb_priv->td[i]) { purb_priv->length = i; urb_free_priv(purb_priv); @@ -473,55 +596,19 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) ep_link(ohci, ed); /* fill the TDs and link it to the ed */ - td_submit_job(dev, pipe, buffer, transfer_len, + td_submit_job(ohci, dev, pipe, buffer, transfer_len, setup, purb_priv, interval); return 0; } -static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) -{ - struct ohci_regs *regs = hc->regs; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - /* implicitly requeued */ - if (urb->dev->irq_handle && - (urb->dev->irq_act_len = urb->actual_length)) { - ohci_writel(OHCI_INTR_WDH, ®s->intrenable); - ohci_readl(®s->intrenable); /* PCI posting flush */ - urb->dev->irq_handle(urb->dev); - ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); - ohci_readl(®s->intrdisable); /* PCI posting flush */ - } - urb->actual_length = 0; - td_submit_job( - urb->dev, - urb->pipe, - urb->transfer_buffer, - urb->transfer_buffer_length, - NULL, - urb, - urb->interval); - break; - case PIPE_CONTROL: - case PIPE_BULK: - break; - default: - return 0; - } - return 1; -} - /*-------------------------------------------------------------------------*/ #ifdef DEBUG /* tell us the current USB frame number */ - -static int sohci_get_current_frame_number(struct usb_device *usb_dev) +static int sohci_get_current_frame_number(ohci_t *ohci) { - ohci_t *ohci = &gohci; - + invalidate_dcache_hcca(ohci->hcca); return m16_swap(ohci->hcca->frame_no); } #endif @@ -600,6 +687,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi) switch (ed->type) { case PIPE_CONTROL: ed->hwNextED = 0; + flush_dcache_ed(ed); if (ohci->ed_controltail == NULL) ohci_writel(ed, &ohci->regs->ed_controlhead); else @@ -617,6 +705,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi) case PIPE_BULK: ed->hwNextED = 0; + flush_dcache_ed(ed); if (ohci->ed_bulktail == NULL) ohci_writel(ed, &ohci->regs->ed_bulkhead); else @@ -649,7 +738,9 @@ static int ep_link(ohci_t *ohci, ed_t *edi) inter = ep_rev(6, ((ed_t *)ed_p)->int_interval); ed->hwNextED = *ed_p; + flush_dcache_ed(ed); *ed_p = m32_swap((unsigned long)ed); + flush_dcache_hcca(ohci->hcca); } break; } @@ -662,6 +753,8 @@ static int ep_link(ohci_t *ohci, ed_t *edi) static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, unsigned index, unsigned period) { + __maybe_unused unsigned long aligned_ed_p; + for (; index < NUM_INTS; index += period) { __u32 *ed_p = &ohci->hcca->int_table [index]; @@ -670,6 +763,12 @@ static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, if (((struct ed *) m32_swap((unsigned long)ed_p)) == ed) { *ed_p = ed->hwNextED; +#ifdef CONFIG_DM_USB + aligned_ed_p = (unsigned long)ed_p; + aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1); + flush_dcache_range(aligned_ed_p, + aligned_ed_p + ARCH_DMA_MINALIGN); +#endif break; } ed_p = &(((struct ed *) @@ -689,6 +788,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) int i; ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + flush_dcache_ed(ed); switch (ed->type) { case PIPE_CONTROL: @@ -702,6 +802,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) &ohci->regs->ed_controlhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; + flush_dcache_ed(ed->ed_prev); } if (ohci->ed_controltail == ed) { ohci->ed_controltail = ed->ed_prev; @@ -722,6 +823,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) &ohci->regs->ed_bulkhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; + flush_dcache_ed(ed->ed_prev); } if (ohci->ed_bulktail == ed) { ohci->ed_bulktail = ed->ed_prev; @@ -751,14 +853,14 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi) * info fields are setted anyway even though most of them should not * change */ -static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, - int interval, int load) +static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev, + unsigned long pipe, int interval, int load) { td_t *td; ed_t *ed_ret; volatile ed_t *ed; - ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | + ed = ed_ret = &ohci_dev->ed[(usb_pipeendpoint(pipe) << 1) | (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { @@ -769,12 +871,12 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, if (ed->state == ED_NEW) { /* dummy td; end of td list for ed */ - td = td_alloc(usb_dev); + td = td_alloc(ohci_dev, usb_dev); ed->hwTailP = m32_swap((unsigned long)td); ed->hwHeadP = ed->hwTailP; ed->state = ED_UNLINK; ed->type = usb_pipetype(pipe); - ohci_dev.ed_cnt++; + ohci_dev->ed_cnt++; } ed->hwINFO = m32_swap(usb_pipedevice(pipe) @@ -790,6 +892,8 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, ed->int_load = load; } + flush_dcache_ed(ed); + return ed_ret; } @@ -815,6 +919,7 @@ static void td_fill(ohci_t *ohci, unsigned int info, /* use this td as the next dummy */ td_pt = urb_priv->td [index]; td_pt->hwNextTD = 0; + flush_dcache_td(td_pt); /* fill the old dummy TD */ td = urb_priv->td [index] = @@ -842,27 +947,30 @@ static void td_fill(ohci_t *ohci, unsigned int info, td->hwBE = 0; td->hwNextTD = m32_swap((unsigned long)td_pt); + flush_dcache_td(td); /* append to queue */ td->ed->hwTailP = td->hwNextTD; + flush_dcache_ed(td->ed); } /*-------------------------------------------------------------------------*/ /* prepare all TDs of a transfer */ -static void td_submit_job(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, +static void td_submit_job(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) { - ohci_t *ohci = &gohci; int data_len = transfer_len; void *data; int cnt = 0; __u32 info = 0; unsigned int toggle = 0; + flush_dcache_buffer(buffer, data_len); + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle * bits for reseting */ if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { @@ -902,6 +1010,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe, case PIPE_CONTROL: /* Setup phase */ info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + flush_dcache_buffer(setup, 8); td_fill(ohci, info, setup, 8, dev, cnt++, urb); /* Optional Data phase */ @@ -914,7 +1023,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe, } /* Status phase */ - info = usb_pipeout(pipe)? + info = (usb_pipeout(pipe) || data_len == 0) ? TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; td_fill(ohci, info, data, 0, dev, cnt++, urb); @@ -973,6 +1082,7 @@ static void check_status(td_t *td_list) if (cc) { err(" USB-error: %s (%x)", cc_to_string[cc], cc); + invalidate_dcache_ed(td_list->ed); if (*phwHeadP & m32_swap(0x1)) { if (lurb_priv && ((td_list->index + 1) < urb_len)) { @@ -985,9 +1095,11 @@ static void check_status(td_t *td_list) td_list->index - 1; } else *phwHeadP &= m32_swap(0xfffffff2); + flush_dcache_ed(td_list->ed); } #ifdef CONFIG_MPC5200 td_list->hwNextTD = 0; + flush_dcache_td(td_list); #endif } } @@ -1000,11 +1112,14 @@ static td_t *dl_reverse_done_list(ohci_t *ohci) td_t *td_rev = NULL; td_t *td_list = NULL; + invalidate_dcache_hcca(ohci->hcca); td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; ohci->hcca->done_head = 0; + flush_dcache_hcca(ohci->hcca); while (td_list_hc) { td_list = (td_t *)td_list_hc; + invalidate_dcache_td(td_list); check_status(td_list); td_list->next_dl_td = td_rev; td_rev = td_list; @@ -1019,7 +1134,7 @@ static td_t *dl_reverse_done_list(ohci_t *ohci) static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) { if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) - urb->finished = sohci_return_job(ohci, urb); + urb->finished = 1; else dbg("finish_urb: strange.., ED state %x, \n", status); } @@ -1039,6 +1154,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list) urb_priv_t *lurb_priv; __u32 tdINFO, edHeadP, edTailP; + invalidate_dcache_td(td_list); tdINFO = m32_swap(td_list->hwINFO); ed = td_list->ed; @@ -1064,6 +1180,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list) lurb_priv->td_cnt, lurb_priv->length); if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { + invalidate_dcache_ed(ed); edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; edTailP = m32_swap(ed->hwTailP); @@ -1100,16 +1217,16 @@ static int dl_done_list(ohci_t *ohci) #define OK(x) len = (x); break #ifdef DEBUG #define WR_RH_STAT(x) {info("WR:status %#8x", (x)); ohci_writel((x), \ - &gohci.regs->roothub.status); } + &ohci->regs->roothub.status); } #define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \ - (x)); ohci_writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); } + (x)); ohci_writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); } #else -#define WR_RH_STAT(x) ohci_writel((x), &gohci.regs->roothub.status) +#define WR_RH_STAT(x) ohci_writel((x), &ohci->regs->roothub.status) #define WR_RH_PORTSTAT(x) ohci_writel((x), \ - &gohci.regs->roothub.portstatus[wIndex-1]) + &ohci->regs->roothub.portstatus[wIndex-1]) #endif -#define RD_RH_STAT roothub_status(&gohci) -#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1) +#define RD_RH_STAT roothub_status(ohci) +#define RD_RH_PORTSTAT roothub_portstatus(ohci, wIndex-1) /* request to virtual root hub */ @@ -1137,8 +1254,9 @@ int rh_check_port_status(ohci_t *controller) return res; } -static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) +static int ohci_submit_rh_msg(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *cmd) { void *data = buffer; int leni = transfer_len; @@ -1151,10 +1269,10 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32)); #ifdef DEBUG -pkt_print(NULL, dev, pipe, buffer, transfer_len, +pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); #else - mdelay(1); + ohci_mdelay(1); #endif if (usb_pipeint(pipe)) { info("Root-Hub submit IRQ: NOT implemented"); @@ -1235,7 +1353,6 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, OK(0); case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_PPS); - mdelay(100); OK(0); case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ if (RD_RH_PORTSTAT & RH_PS_CCS) @@ -1245,7 +1362,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, break; case RH_SET_ADDRESS: - gohci.rh.devnum = wValue; + ohci->rh.devnum = wValue; OK(0); case RH_GET_DESCRIPTOR: @@ -1290,7 +1407,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, case RH_GET_DESCRIPTOR | RH_CLASS: { - __u32 temp = roothub_a(&gohci); + __u32 temp = roothub_a(ohci); databuf[0] = 9; /* min length; */ databuf[1] = 0x29; @@ -1309,7 +1426,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, databuf[4] = 0; databuf[5] = (temp & RH_A_POTPGT) >> 24; databuf[6] = 0; - temp = roothub_b(&gohci); + temp = roothub_b(ohci); databuf[7] = temp & RH_B_DR; if (databuf[2] < 7) { databuf[8] = 0xff; @@ -1338,9 +1455,9 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, } #ifdef DEBUG - ohci_dump_roothub(&gohci, 1); + ohci_dump_roothub(ohci, 1); #else - mdelay(1); + ohci_mdelay(1); #endif len = min_t(int, len, leni); @@ -1350,10 +1467,10 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, dev->status = stat; #ifdef DEBUG - pkt_print(NULL, dev, pipe, buffer, + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); #else - mdelay(1); + ohci_mdelay(1); #endif return stat; @@ -1361,18 +1478,43 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, /*-------------------------------------------------------------------------*/ +static ohci_dev_t *ohci_get_ohci_dev(ohci_t *ohci, int devnum, int intr) +{ + int i; + + if (!intr) + return &ohci->ohci_dev; + + /* First see if we already have an ohci_dev for this dev. */ + for (i = 0; i < NUM_INT_DEVS; i++) { + if (ohci->int_dev[i].devnum == devnum) + return &ohci->int_dev[i]; + } + + /* If not then find a free one. */ + for (i = 0; i < NUM_INT_DEVS; i++) { + if (ohci->int_dev[i].devnum == -1) { + ohci->int_dev[i].devnum = devnum; + return &ohci->int_dev[i]; + } + } + + printf("ohci: Error out of ohci_devs for interrupt endpoints\n"); + return NULL; +} + /* common code for handling submit messages - used for all but root hub */ /* accesses. */ -int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) +static urb_priv_t *ohci_alloc_urb(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, int interval) { - int stat = 0; - int maxsize = usb_maxpacket(dev, pipe); - int timeout; urb_priv_t *urb; - urb = malloc(sizeof(urb_priv_t)); - memset(urb, 0, sizeof(urb_priv_t)); + urb = calloc(1, sizeof(urb_priv_t)); + if (!urb) { + printf("ohci: Error out of memory allocating urb\n"); + return NULL; + } urb->dev = dev; urb->pipe = pipe; @@ -1380,18 +1522,29 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, urb->transfer_buffer_length = transfer_len; urb->interval = interval; - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - dev->status = USB_ST_CRC_ERR; - return 0; - } + return urb; +} + +static int submit_common_msg(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + urb_priv_t *urb; + ohci_dev_t *ohci_dev; + + urb = ohci_alloc_urb(dev, pipe, buffer, transfer_len, interval); + if (!urb) + return -ENOMEM; #ifdef DEBUG urb->actual_length = 0; - pkt_print(urb, dev, pipe, buffer, transfer_len, + pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else - mdelay(1); + ohci_mdelay(1); #endif if (!maxsize) { err("submit_common_message: pipesize for pipe %lx is zero", @@ -1399,14 +1552,18 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } - if (sohci_submit_job(urb, setup) < 0) { + ohci_dev = ohci_get_ohci_dev(ohci, dev->devnum, usb_pipeint(pipe)); + if (!ohci_dev) + return -ENOMEM; + + if (sohci_submit_job(ohci, ohci_dev, urb, setup) < 0) { err("sohci_submit_job failed"); return -1; } #if 0 mdelay(10); - /* ohci_dump_status(&gohci); */ + /* ohci_dump_status(ohci); */ #endif timeout = USB_TIMEOUT_MS(pipe); @@ -1414,7 +1571,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, /* wait for it to complete */ for (;;) { /* check whether the controller is done */ - stat = hc_interrupt(); + stat = hc_interrupt(ohci); if (stat < 0) { stat = USB_ST_CRC_ERR; break; @@ -1440,7 +1597,8 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dbg("*"); } else { - err("CTL:TIMEOUT "); + if (!usb_pipeint(pipe)) + err("CTL:TIMEOUT "); dbg("submit_common_msg: TO status %x\n", stat); urb->finished = 1; stat = USB_ST_CRC_ERR; @@ -1451,60 +1609,168 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dev->status = stat; dev->act_len = urb->actual_length; + if (usb_pipein(pipe) && dev->status == 0 && dev->act_len) + invalidate_dcache_buffer(buffer, dev->act_len); + #ifdef DEBUG - pkt_print(urb, dev, pipe, buffer, transfer_len, + pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); #else - mdelay(1); + ohci_mdelay(1); #endif + urb_free_priv(urb); + return 0; +} + +#define MAX_INT_QUEUESIZE 8 + +struct int_queue { + int queuesize; + int curr_urb; + urb_priv_t *urb[MAX_INT_QUEUESIZE]; +}; + +static struct int_queue *_ohci_create_int_queue(ohci_t *ohci, + struct usb_device *udev, unsigned long pipe, int queuesize, + int elementsize, void *buffer, int interval) +{ + struct int_queue *queue; + ohci_dev_t *ohci_dev; + int i; + + if (queuesize > MAX_INT_QUEUESIZE) + return NULL; + + ohci_dev = ohci_get_ohci_dev(ohci, udev->devnum, 1); + if (!ohci_dev) + return NULL; + + queue = malloc(sizeof(*queue)); + if (!queue) { + printf("ohci: Error out of memory allocating int queue\n"); + return NULL; + } + + for (i = 0; i < queuesize; i++) { + queue->urb[i] = ohci_alloc_urb(udev, pipe, + buffer + i * elementsize, + elementsize, interval); + if (!queue->urb[i]) + break; + + if (sohci_submit_job(ohci, ohci_dev, queue->urb[i], NULL)) { + printf("ohci: Error submitting int queue job\n"); + urb_free_priv(queue->urb[i]); + break; + } + } + if (i == 0) { + /* We did not succeed in submitting even 1 urb */ + free(queue); + return NULL; + } + + queue->queuesize = i; + queue->curr_urb = 0; + + return queue; +} + +static void *_ohci_poll_int_queue(ohci_t *ohci, struct usb_device *udev, + struct int_queue *queue) +{ + if (queue->curr_urb == queue->queuesize) + return NULL; /* Queue depleted */ + + if (hc_interrupt(ohci) < 0) + return NULL; + + if (queue->urb[queue->curr_urb]->finished) { + void *ret = queue->urb[queue->curr_urb]->transfer_buffer; + queue->curr_urb++; + return ret; + } + + return NULL; +} + +static int _ohci_destroy_int_queue(ohci_t *ohci, struct usb_device *dev, + struct int_queue *queue) +{ + int i; + + for (i = 0; i < queue->queuesize; i++) + urb_free_priv(queue->urb[i]); + + free(queue); - /* free TDs in urb_priv */ - if (!usb_pipeint(pipe)) - urb_free_priv(urb); return 0; } +#ifndef CONFIG_DM_USB /* submit routines called from usb.c */ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len) { info("submit_bulk_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); + return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, + NULL, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, NULL, + interval); +} + +struct int_queue *create_int_queue(struct usb_device *dev, + unsigned long pipe, int queuesize, int elementsize, + void *buffer, int interval) +{ + return _ohci_create_int_queue(&gohci, dev, pipe, queuesize, + elementsize, buffer, interval); } -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup) +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + return _ohci_poll_int_queue(&gohci, dev, queue); +} + +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + return _ohci_destroy_int_queue(&gohci, dev, queue); +} +#endif + +static int _ohci_submit_control_msg(ohci_t *ohci, struct usb_device *dev, + unsigned long pipe, void *buffer, int transfer_len, + struct devrequest *setup) { int maxsize = usb_maxpacket(dev, pipe); info("submit_control_msg"); #ifdef DEBUG - pkt_print(NULL, dev, pipe, buffer, transfer_len, + pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else - mdelay(1); + ohci_mdelay(1); #endif if (!maxsize) { err("submit_control_message: pipesize for pipe %lx is zero", pipe); return -1; } - if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { - gohci.rh.dev = dev; + if (((pipe >> 8) & 0x7f) == ohci->rh.devnum) { + ohci->rh.dev = dev; /* root hub - redirect */ - return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); + return ohci_submit_rh_msg(ohci, dev, pipe, buffer, + transfer_len, setup); } - return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - info("submit_int_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, - interval); + return submit_common_msg(ohci, dev, pipe, buffer, transfer_len, + setup, 0); } /*-------------------------------------------------------------------------* @@ -1593,8 +1859,11 @@ static int hc_start(ohci_t *ohci) { __u32 mask; unsigned int fminterval; + int i; ohci->disabled = 1; + for (i = 0; i < NUM_INT_DEVS; i++) + ohci->int_dev[i].devnum = -1; /* Tell the controller where the control and bulk lists are * The lists are empty now. */ @@ -1635,9 +1904,6 @@ static int hc_start(ohci_t *ohci) ohci_writel(RH_HS_LPSC, &ohci->regs->roothub.status); #endif /* OHCI_USE_NPS */ - /* POTPGT delay is bits 24-31, in 2 ms units. */ - mdelay((roothub_a(ohci) >> 23) & 0x1fe); - /* connect the virtual root hub */ ohci->rh.devnum = 0; @@ -1648,13 +1914,14 @@ static int hc_start(ohci_t *ohci) /* an interrupt happens */ -static int hc_interrupt(void) +static int hc_interrupt(ohci_t *ohci) { - ohci_t *ohci = &gohci; struct ohci_regs *regs = ohci->regs; int ints; int stat = -1; + invalidate_dcache_hcca(ohci->hcca); + if ((ohci->hcca->done_head != 0) && !(m32_swap(ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; @@ -1688,7 +1955,7 @@ static int hc_interrupt(void) #ifdef DEBUG ohci_dump(ohci, 1); #else - mdelay(1); + ohci_mdelay(1); #endif /* FIXME: be optimistic, hope that bug won't repeat often. */ /* Make some non-interrupt context restart the controller. */ @@ -1699,10 +1966,10 @@ static int hc_interrupt(void) } if (ints & OHCI_INTR_WDH) { - mdelay(1); + ohci_mdelay(1); ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); (void)ohci_readl(®s->intrdisable); /* flush */ - stat = dl_done_list(&gohci); + stat = dl_done_list(ohci); ohci_writel(OHCI_INTR_WDH, ®s->intrenable); (void)ohci_readl(®s->intrdisable); /* flush */ } @@ -1729,6 +1996,8 @@ static int hc_interrupt(void) /*-------------------------------------------------------------------------*/ +#ifndef CONFIG_DM_USB + /*-------------------------------------------------------------------------*/ /* De-allocate all resources.. */ @@ -1772,21 +2041,9 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) err("HCCA not aligned!!"); return -1; } - phcca = &ghcca[0]; - info("aligned ghcca %p", phcca); - memset(&ohci_dev, 0, sizeof(struct ohci_device)); - if ((__u32)&ohci_dev.ed[0] & 0x7) { - err("EDs not aligned!!"); - return -1; - } - memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); - if ((__u32)gtd & 0x7) { - err("TDs not aligned!!"); - return -1; - } - ptd = gtd; - gohci.hcca = phcca; - memset(phcca, 0, sizeof(struct ohci_hcca)); + gohci.hcca = &ghcca[0]; + info("aligned ghcca %p", gohci.hcca); + memset(gohci.hcca, 0, sizeof(struct ohci_hcca)); gohci.disabled = 1; gohci.sleeping = 0; @@ -1848,7 +2105,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) #ifdef DEBUG ohci_dump(&gohci, 1); #else - mdelay(1); + ohci_mdelay(1); #endif ohci_inited = 1; return 0; @@ -1880,3 +2137,115 @@ int usb_lowlevel_stop(int index) ohci_inited = 0; return 0; } + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *setup) +{ + return _ohci_submit_control_msg(&gohci, dev, pipe, buffer, + transfer_len, setup); +} +#endif + +#ifdef CONFIG_DM_USB +static int ohci_submit_control_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); + + return _ohci_submit_control_msg(ohci, udev, pipe, buffer, + length, setup); +} + +static int ohci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); + + return submit_common_msg(ohci, udev, pipe, buffer, length, NULL, 0); +} + +static int ohci_submit_int_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval) +{ + ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); + + return submit_common_msg(ohci, udev, pipe, buffer, length, + NULL, interval); +} + +static struct int_queue *ohci_create_int_queue(struct udevice *dev, + struct usb_device *udev, unsigned long pipe, int queuesize, + int elementsize, void *buffer, int interval) +{ + ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); + + return _ohci_create_int_queue(ohci, udev, pipe, queuesize, elementsize, + buffer, interval); +} + +static void *ohci_poll_int_queue(struct udevice *dev, struct usb_device *udev, + struct int_queue *queue) +{ + ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); + + return _ohci_poll_int_queue(ohci, udev, queue); +} + +static int ohci_destroy_int_queue(struct udevice *dev, struct usb_device *udev, + struct int_queue *queue) +{ + ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); + + return _ohci_destroy_int_queue(ohci, udev, queue); +} + +int ohci_register(struct udevice *dev, struct ohci_regs *regs) +{ + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + ohci_t *ohci = dev_get_priv(dev); + u32 reg; + + priv->desc_before_addr = true; + + ohci->regs = regs; + ohci->hcca = memalign(256, sizeof(struct ohci_hcca)); + if (!ohci->hcca) + return -ENOMEM; + memset(ohci->hcca, 0, sizeof(struct ohci_hcca)); + + if (hc_reset(ohci) < 0) + return -EIO; + + if (hc_start(ohci) < 0) + return -EIO; + + reg = ohci_readl(®s->revision); + printf("USB OHCI %x.%x\n", (reg >> 4) & 0xf, reg & 0xf); + + return 0; +} + +int ohci_deregister(struct udevice *dev) +{ + ohci_t *ohci = dev_get_priv(dev); + + if (hc_reset(ohci) < 0) + return -EIO; + + free(ohci->hcca); + + return 0; +} + +struct dm_usb_ops ohci_usb_ops = { + .control = ohci_submit_control_msg, + .bulk = ohci_submit_bulk_msg, + .interrupt = ohci_submit_int_msg, + .create_int_queue = ohci_create_int_queue, + .poll_int_queue = ohci_poll_int_queue, + .destroy_int_queue = ohci_destroy_int_queue, +}; + +#endif diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c new file mode 100644 index 00000000000..e33a8f7ec2e --- /dev/null +++ b/drivers/usb/host/ohci-sunxi.c @@ -0,0 +1,104 @@ +/* + * Sunxi ohci glue + * + * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/usb_phy.h> +#include <asm/io.h> +#include <dm.h> +#include <usb.h> +#include "ohci.h" + +struct ohci_sunxi_priv { + ohci_t ohci; + int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ + int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */ + int phy_index; /* Index of the usb-phy attached to this hcd */ +}; + +static int ohci_usb_probe(struct udevice *dev) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); + struct ohci_sunxi_priv *priv = dev_get_priv(dev); + struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev); + + bus_priv->companion = true; + + /* + * This should go away once we've moved to the driver model for + * clocks resp. phys. + */ + if (regs == (void *)(SUNXI_USB1_BASE + 0x400)) { + priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0; + priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK; + priv->phy_index = 1; + } else { + priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI1; + priv->usb_gate_mask = CCM_USB_CTRL_OHCI1_CLK; + priv->phy_index = 2; + } + + setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); + setbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask); +#ifdef CONFIG_SUNXI_GEN_SUN6I + setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); +#endif + + sunxi_usb_phy_init(priv->phy_index); + sunxi_usb_phy_power_on(priv->phy_index); + + return ohci_register(dev, regs); +} + +static int ohci_usb_remove(struct udevice *dev) +{ + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct ohci_sunxi_priv *priv = dev_get_priv(dev); + int ret; + + ret = ohci_deregister(dev); + if (ret) + return ret; + + sunxi_usb_phy_power_off(priv->phy_index); + sunxi_usb_phy_exit(priv->phy_index); + +#ifdef CONFIG_SUNXI_GEN_SUN6I + clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); +#endif + clrbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask); + clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); + + return 0; +} + +static const struct udevice_id ohci_usb_ids[] = { + { .compatible = "allwinner,sun4i-a10-ohci", }, + { .compatible = "allwinner,sun5i-a13-ohci", }, + { .compatible = "allwinner,sun6i-a31-ohci", }, + { .compatible = "allwinner,sun7i-a20-ohci", }, + { .compatible = "allwinner,sun8i-a23-ohci", }, + { .compatible = "allwinner,sun9i-a80-ohci", }, + { } +}; + +U_BOOT_DRIVER(usb_ohci) = { + .name = "ohci_sunxi", + .id = UCLASS_USB, + .of_match = ohci_usb_ids, + .probe = ohci_usb_probe, + .remove = ohci_usb_remove, + .ops = &ohci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 9a4a2c24759..f1526d49004 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -18,6 +18,18 @@ # define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) #endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ +#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 16 +#define ED_ALIGNMENT ARCH_DMA_MINALIGN +#else +#define ED_ALIGNMENT 16 +#endif + +#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 32 +#define TD_ALIGNMENT ARCH_DMA_MINALIGN +#else +#define TD_ALIGNMENT 32 +#endif + /* functions for doing board or CPU specific setup/cleanup */ int usb_board_stop(void); @@ -25,64 +37,7 @@ int usb_cpu_init(void); int usb_cpu_stop(void); int usb_cpu_init_fail(void); -static int cc_to_error[16] = { - -/* mapping of the OHCI CC status to error codes */ - /* No Error */ 0, - /* CRC Error */ USB_ST_CRC_ERR, - /* Bit Stuff */ USB_ST_BIT_ERR, - /* Data Togg */ USB_ST_CRC_ERR, - /* Stall */ USB_ST_STALLED, - /* DevNotResp */ -1, - /* PIDCheck */ USB_ST_BIT_ERR, - /* UnExpPID */ USB_ST_BIT_ERR, - /* DataOver */ USB_ST_BUF_ERR, - /* DataUnder */ USB_ST_BUF_ERR, - /* reservd */ -1, - /* reservd */ -1, - /* BufferOver */ USB_ST_BUF_ERR, - /* BuffUnder */ USB_ST_BUF_ERR, - /* Not Access */ -1, - /* Not Access */ -1 -}; - -static const char *cc_to_string[16] = { - "No Error", - "CRC: Last data packet from endpoint contained a CRC error.", - "BITSTUFFING: Last data packet from endpoint contained a bit " \ - "stuffing violation", - "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ - "that did not match the expected value.", - "STALL: TD was moved to the Done Queue because the endpoint returned" \ - " a STALL PID", - "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ - "not provide a handshake (OUT)", - "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ - "(IN) or handshake (OUT)", - "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ - "value is not defined.", - "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ - "either the size of the maximum data packet allowed\n" \ - "from the endpoint (found in MaximumPacketSize field\n" \ - "of ED) or the remaining buffer size.", - "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ - "and that amount was not sufficient to fill the\n" \ - "specified buffer", - "reserved1", - "reserved2", - "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ - "than it could be written to system memory", - "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ - "system memory fast enough to keep up with data USB " \ - "data rate.", - "NOT ACCESSED: This code is set by software before the TD is placed" \ - "on a list to be processed by the HC.(1)", - "NOT ACCESSED: This code is set by software before the TD is placed" \ - "on a list to be processed by the HC.(2)", -}; - /* ED States */ - #define ED_NEW 0x00 #define ED_UNLINK 0x01 #define ED_OPER 0x02 @@ -109,7 +64,7 @@ struct ed { struct usb_device *usb_dev; void *purb; __u32 unused[2]; -} __attribute__((aligned(16))); +} __attribute__((aligned(ED_ALIGNMENT))); typedef struct ed ed_t; @@ -169,7 +124,7 @@ struct td { __u32 data; __u32 unused2[2]; -} __attribute__((aligned(32))); +} __attribute__((aligned(TD_ALIGNMENT))); typedef struct td td_t; #define OHCI_ED_SKIP (1 << 14) @@ -408,6 +363,19 @@ typedef struct } urb_priv_t; #define URB_DEL 1 +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +#define NUM_TD 64 /* we need more TDs than EDs */ + +#define NUM_INT_DEVS 8 /* num of ohci_dev structs for int endpoints */ + +typedef struct ohci_device { + ed_t ed[NUM_EDS] __aligned(ED_ALIGNMENT); + td_t tds[NUM_TD] __aligned(TD_ALIGNMENT); + int ed_cnt; + int devnum; +} ohci_dev_t; + /* * This is the full ohci controller description * @@ -417,6 +385,9 @@ typedef struct typedef struct ohci { + /* this allocates EDs for all possible endpoints */ + struct ohci_device ohci_dev __aligned(TD_ALIGNMENT); + struct ohci_device int_dev[NUM_INT_DEVS] __aligned(TD_ALIGNMENT); struct ohci_hcca *hcca; /* hcca */ /*dma_addr_t hcca_dma;*/ @@ -439,53 +410,9 @@ typedef struct ohci { const char *slot_name; } ohci_t; -#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ - -struct ohci_device { - ed_t ed[NUM_EDS]; - int ed_cnt; -}; - -/* hcd */ -/* endpoint */ -static int ep_link(ohci_t * ohci, ed_t * ed); -static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, - int interval, int load); - -/*-------------------------------------------------------------------------*/ - -/* we need more TDs than EDs */ -#define NUM_TD 64 - -/* +1 so we can align the storage */ -td_t gtd[NUM_TD+1]; -/* pointers to aligned storage */ -td_t *ptd; +#ifdef CONFIG_DM_USB +extern struct dm_usb_ops ohci_usb_ops; -/* TDs ... */ -static inline struct td * -td_alloc (struct usb_device *usb_dev) -{ - int i; - struct td *td; - - td = NULL; - for (i = 0; i < NUM_TD; i++) - { - if (ptd[i].usb_dev == NULL) - { - td = &ptd[i]; - td->usb_dev = usb_dev; - break; - } - } - - return td; -} - -static inline void -ed_free (struct ed *ed) -{ - ed->usb_dev = NULL; -} +int ohci_register(struct udevice *dev, struct ohci_regs *regs); +int ohci_deregister(struct udevice *dev); +#endif diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 714bc0e9588..963464cff94 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -21,6 +21,10 @@ DECLARE_GLOBAL_DATA_PTR; extern bool usb_started; /* flag for the started/stopped USB status */ static bool asynch_allowed; +struct usb_uclass_priv { + int companion_device_count; +}; + int usb_disable_asynch(int disable) { int old_value = asynch_allowed; @@ -46,11 +50,22 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe, { struct udevice *bus = udev->controller_dev; struct dm_usb_ops *ops = usb_get_ops(bus); + struct usb_uclass_priv *uc_priv = bus->uclass->priv; + int err; if (!ops->control) return -ENOSYS; - return ops->control(bus, udev, pipe, buffer, length, setup); + err = ops->control(bus, udev, pipe, buffer, length, setup); + if (setup->request == USB_REQ_SET_FEATURE && + setup->requesttype == USB_RT_PORT && + setup->value == cpu_to_le16(USB_PORT_FEAT_RESET) && + err == -ENXIO) { + /* Device handed over to companion after port reset */ + uc_priv->companion_device_count++; + } + + return err; } int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, @@ -65,6 +80,42 @@ int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, return ops->bulk(bus, udev, pipe, buffer, length); } +struct int_queue *create_int_queue(struct usb_device *udev, + unsigned long pipe, int queuesize, int elementsize, + void *buffer, int interval) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->create_int_queue) + return NULL; + + return ops->create_int_queue(bus, udev, pipe, queuesize, elementsize, + buffer, interval); +} + +void *poll_int_queue(struct usb_device *udev, struct int_queue *queue) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->poll_int_queue) + return NULL; + + return ops->poll_int_queue(bus, udev, queue); +} + +int destroy_int_queue(struct usb_device *udev, struct int_queue *queue) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->destroy_int_queue) + return -ENOSYS; + + return ops->destroy_int_queue(bus, udev, queue); +} + int usb_alloc_device(struct usb_device *udev) { struct udevice *bus = udev->controller_dev; @@ -81,12 +132,16 @@ int usb_stop(void) { struct udevice *bus; struct uclass *uc; + struct usb_uclass_priv *uc_priv; int err = 0, ret; /* De-activate any devices that have been activated */ ret = uclass_get(UCLASS_USB, &uc); if (ret) return ret; + + uc_priv = uc->priv; + uclass_foreach_dev(bus, uc) { ret = device_remove(bus); if (ret && !err) @@ -106,12 +161,13 @@ int usb_stop(void) #endif usb_stor_reset(); usb_hub_reset(); + uc_priv->companion_device_count = 0; usb_started = 0; return err; } -static int usb_scan_bus(struct udevice *bus, bool recurse) +static void usb_scan_bus(struct udevice *bus, bool recurse) { struct usb_bus_priv *priv; struct udevice *dev; @@ -121,16 +177,22 @@ static int usb_scan_bus(struct udevice *bus, bool recurse) assert(recurse); /* TODO: Support non-recusive */ + printf("scanning bus %d for devices... ", bus->seq); + debug("\n"); ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev); if (ret) - return ret; - - return priv->next_addr; + printf("failed, error %d\n", ret); + else if (priv->next_addr == 0) + printf("No USB Device found\n"); + else + printf("%d USB Device(s) found\n", priv->next_addr); } int usb_init(void) { int controllers_initialized = 0; + struct usb_uclass_priv *uc_priv; + struct usb_bus_priv *priv; struct udevice *bus; struct uclass *uc; int count = 0; @@ -143,11 +205,12 @@ int usb_init(void) if (ret) return ret; + uc_priv = uc->priv; + uclass_foreach_dev(bus, uc) { /* init low_level USB */ + printf("USB%d: ", count); count++; - printf("USB"); - printf("%d: ", bus->seq); ret = device_probe(bus); if (ret == -ENODEV) { /* No such device. */ puts("Port not available.\n"); @@ -159,23 +222,39 @@ int usb_init(void) printf("probe failed, error %d\n", ret); continue; } - /* - * lowlevel init is OK, now scan the bus for devices - * i.e. search HUBs and configure them - */ controllers_initialized++; - printf("scanning bus %d for devices... ", bus->seq); - debug("\n"); - ret = usb_scan_bus(bus, true); - if (ret < 0) - printf("failed, error %d\n", ret); - else if (!ret) - printf("No USB Device found\n"); - else - printf("%d USB Device(s) found\n", ret); usb_started = true; } + /* + * lowlevel init done, now scan the bus for devices i.e. search HUBs + * and configure them, first scan primary controllers. + */ + uclass_foreach_dev(bus, uc) { + if (!device_active(bus)) + continue; + + priv = dev_get_uclass_priv(bus); + if (!priv->companion) + usb_scan_bus(bus, true); + } + + /* + * Now that the primary controllers have been scanned and have handed + * over any devices they do not understand to their companions, scan + * the companions if necessary. + */ + if (uc_priv->companion_device_count) { + uclass_foreach_dev(bus, uc) { + if (!device_active(bus)) + continue; + + priv = dev_get_uclass_priv(bus); + if (priv->companion) + usb_scan_bus(bus, true); + } + } + debug("scan end\n"); /* if we were not able to find at least one working bus, bail out */ if (!count) @@ -477,9 +556,7 @@ int usb_scan_device(struct udevice *parent, int port, *devp = NULL; memset(udev, '\0', sizeof(*udev)); - ret = usb_get_bus(parent, &udev->controller_dev); - if (ret) - return ret; + udev->controller_dev = usb_get_bus(parent); priv = dev_get_uclass_priv(udev->controller_dev); /* @@ -536,11 +613,7 @@ int usb_scan_device(struct udevice *parent, int port, plat = dev_get_parent_platdata(dev); debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat); plat->devnum = udev->devnum; - plat->speed = udev->speed; - plat->slot_id = udev->slot_id; - plat->portnr = port; - debug("** device '%s': stashing slot_id=%d\n", dev->name, - plat->slot_id); + plat->udev = udev; priv->next_addr++; ret = device_probe(dev); if (ret) { @@ -579,45 +652,55 @@ int usb_child_post_bind(struct udevice *dev) return 0; } -int usb_get_bus(struct udevice *dev, struct udevice **busp) +struct udevice *usb_get_bus(struct udevice *dev) { struct udevice *bus; - *busp = NULL; for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; ) bus = bus->parent; if (!bus) { /* By design this cannot happen */ assert(bus); debug("USB HUB '%s' does not have a controller\n", dev->name); - return -EXDEV; } - *busp = bus; - return 0; + return bus; } int usb_child_pre_probe(struct udevice *dev) { - struct udevice *bus; struct usb_device *udev = dev_get_parentdata(dev); struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); int ret; - ret = usb_get_bus(dev, &bus); - if (ret) - return ret; - udev->controller_dev = bus; - udev->dev = dev; - udev->devnum = plat->devnum; - udev->slot_id = plat->slot_id; - udev->portnr = plat->portnr; - udev->speed = plat->speed; - debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id); - - ret = usb_select_config(udev); - if (ret) - return ret; + if (plat->udev) { + /* + * Copy over all the values set in the on stack struct + * usb_device in usb_scan_device() to our final struct + * usb_device for this dev. + */ + *udev = *(plat->udev); + /* And clear plat->udev as it will not be valid for long */ + plat->udev = NULL; + udev->dev = dev; + } else { + /* + * This happens with devices which are explicitly bound + * instead of being discovered through usb_scan_device() + * such as sandbox emul devices. + */ + udev->dev = dev; + udev->controller_dev = usb_get_bus(dev); + udev->devnum = plat->devnum; + + /* + * udev did not go through usb_scan_device(), so we need to + * select the config and read the config descriptors. + */ + ret = usb_select_config(udev); + if (ret) + return ret; + } return 0; } @@ -627,6 +710,7 @@ UCLASS_DRIVER(usb) = { .name = "usb", .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = usb_post_bind, + .priv_auto_alloc_size = sizeof(struct usb_uclass_priv), .per_child_auto_alloc_size = sizeof(struct usb_device), .per_device_auto_alloc_size = sizeof(struct usb_bus_priv), .child_post_bind = usb_child_post_bind, diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c index 23c7ecc5d86..a27a79632b8 100644 --- a/drivers/usb/host/xhci-exynos5.c +++ b/drivers/usb/host/xhci-exynos5.c @@ -33,36 +33,24 @@ /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; -#ifdef CONFIG_DM_USB struct exynos_xhci_platdata { fdt_addr_t hcd_base; fdt_addr_t phy_base; struct gpio_desc vbus_gpio; }; -#endif /** * Contains pointers to register base addresses * for the usb controller. */ struct exynos_xhci { -#ifdef CONFIG_DM_USB struct usb_platdata usb_plat; -#endif struct xhci_ctrl ctrl; struct exynos_usb3_phy *usb3_phy; struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; -#ifndef CONFIG_DM_USB - struct gpio_desc vbus_gpio; -#endif }; -#ifndef CONFIG_DM_USB -static struct exynos_xhci exynos; -#endif - -#ifdef CONFIG_DM_USB static int xhci_usb_ofdata_to_platdata(struct udevice *dev) { struct exynos_xhci_platdata *plat = dev_get_platdata(dev); @@ -102,54 +90,6 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev) return 0; } -#else -static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) -{ - fdt_addr_t addr; - unsigned int node; - int depth; - - node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI); - if (node <= 0) { - debug("XHCI: Can't get device node for xhci\n"); - return -ENODEV; - } - - /* - * Get the base address for XHCI controller from the device node - */ - addr = fdtdec_get_addr(blob, node, "reg"); - if (addr == FDT_ADDR_T_NONE) { - debug("Can't get the XHCI register base address\n"); - return -ENXIO; - } - exynos->hcd = (struct xhci_hccr *)addr; - - /* Vbus gpio */ - gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0, - &exynos->vbus_gpio, GPIOD_IS_OUT); - - depth = 0; - node = fdtdec_next_compatible_subnode(blob, node, - COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); - if (node <= 0) { - debug("XHCI: Can't get device node for usb3-phy controller\n"); - return -ENODEV; - } - - /* - * Get the base address for usbphy from the device node - */ - exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node, - "reg"); - if (exynos->usb3_phy == NULL) { - debug("Can't get the usbphy register address\n"); - return -ENXIO; - } - - return 0; -} -#endif static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) { @@ -340,53 +280,6 @@ static void exynos_xhci_core_exit(struct exynos_xhci *exynos) exynos5_usb3_phy_exit(exynos->usb3_phy); } -#ifndef CONFIG_DM_USB -int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) -{ - struct exynos_xhci *ctx = &exynos; - int ret; - -#ifdef CONFIG_OF_CONTROL - exynos_usb3_parse_dt(gd->fdt_blob, ctx); -#else - ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy(); - ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci(); -#endif - - ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); - -#ifdef CONFIG_OF_CONTROL - /* setup the Vbus gpio here */ - if (dm_gpio_is_valid(&ctx->vbus_gpio)) - dm_gpio_set_value(&ctx->vbus_gpio, 1); -#endif - - ret = exynos_xhci_core_init(ctx); - if (ret) { - puts("XHCI: failed to initialize controller\n"); - return -EINVAL; - } - - *hccr = (ctx->hcd); - *hcor = (struct xhci_hcor *)((uint32_t) *hccr - + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); - - debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n", - (uint32_t)*hccr, (uint32_t)*hcor, - (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); - - return 0; -} - -void xhci_hcd_stop(int index) -{ - struct exynos_xhci *ctx = &exynos; - - exynos_xhci_core_exit(ctx); -} -#endif - -#ifdef CONFIG_DM_USB static int xhci_usb_probe(struct udevice *dev) { struct exynos_xhci_platdata *plat = dev_get_platdata(dev); @@ -443,4 +336,3 @@ U_BOOT_DRIVER(usb_xhci) = { .priv_auto_alloc_size = sizeof(struct exynos_xhci), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; -#endif |