From 181479dd8a64138dc1a35f3bf0b940b9501ffb4e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:06 -0800 Subject: usb: xhci-hcd: Tabify the file Replace occasional all-whiltespace indentation with tabs for consistency. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 136 ++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 2b808cc875..7cfd74de41 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -29,10 +29,10 @@ static int xhci_ring_is_last_trb(struct xhci_ring *ring, union xhci_trb *trb) { - if (ring->type == TYPE_EVENT) - return trb == &ring->trbs[NUM_EVENT_TRBS]; - else - return TRB_TYPE_LINK(le32_to_cpu(trb->link.control)); + if (ring->type == TYPE_EVENT) + return trb == &ring->trbs[NUM_EVENT_TRBS]; + else + return TRB_TYPE_LINK(le32_to_cpu(trb->link.control)); } static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue) @@ -41,7 +41,7 @@ static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue) (*queue)++; - if (!xhci_ring_is_last_trb(ring, *queue)) + if (!xhci_ring_is_last_trb(ring, *queue)) return 0; if (ring->type == TYPE_EVENT) { @@ -139,15 +139,15 @@ static unsigned int xhci_get_endpoint_index(u8 epaddress, u8 epattributes) { u8 epnum = epaddress & USB_ENDPOINT_NUMBER_MASK; u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK; - unsigned int index; + unsigned int index; - if (xfer == USB_ENDPOINT_XFER_CONTROL) - index = (unsigned int)(epnum * 2); - else - index = (unsigned int)(epnum * 2) + - ((epaddress & USB_DIR_IN) ? 1 : 0) - 1; + if (xfer == USB_ENDPOINT_XFER_CONTROL) + index = (unsigned int)(epnum * 2); + else + index = (unsigned int)(epnum * 2) + + ((epaddress & USB_DIR_IN) ? 1 : 0) - 1; - return index; + return index; } static u8 xhci_get_endpoint_type(u8 epaddress, u8 epattributes) @@ -341,15 +341,15 @@ int xhci_issue_command(struct xhci_hcd *xhci, union xhci_trb *trb) static void xhci_set_event_dequeue(struct xhci_hcd *xhci) { - u64 reg64; + u64 reg64; - reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue); - reg64 &= ERST_PTR_MASK; - /* + reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue); + reg64 &= ERST_PTR_MASK; + /* * Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - reg64 &= ~ERST_EHB; + * there might be more events to service. + */ + reg64 &= ~ERST_EHB; reg64 |= (dma_addr_t)xhci->event_ring.dequeue & ~(dma_addr_t)ERST_PTR_MASK; @@ -489,16 +489,16 @@ static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev) { int i; - /* When a device's add flag and drop flag are zero, any subsequent - * configure endpoint command will leave that endpoint's state - * untouched. Make sure we don't leave any old state in the input - * endpoint contexts. - */ - vdev->in_ctx->icc.drop_flags = 0; - vdev->in_ctx->icc.add_flags = 0; - vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); - /* Endpoint 0 is always valid */ - vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(1)); + /* When a device's add flag and drop flag are zero, any subsequent + * configure endpoint command will leave that endpoint's state + * untouched. Make sure we don't leave any old state in the input + * endpoint contexts. + */ + vdev->in_ctx->icc.drop_flags = 0; + vdev->in_ctx->icc.add_flags = 0; + vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); + /* Endpoint 0 is always valid */ + vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(1)); for (i = 1; i < 31; i++) { vdev->in_ctx->ep[i].ep_info = 0; vdev->in_ctx->ep[i].ep_info2 = 0; @@ -522,7 +522,7 @@ static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev) ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); xhci_print_trb(xhci, &trb, "Response DisableSlot"); - /* Clear Device Context Base Address Array */ + /* Clear Device Context Base Address Array */ xhci->dcbaa[vdev->slot_id] = 0; return ret; @@ -565,7 +565,7 @@ int xhci_virtdev_reset(struct xhci_virtual_device *vdev) /* If device is not setup, there is no point in resetting it */ if (GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)) == SLOT_STATE_DISABLED) - return 0; + return 0; memset(&trb, 0, sizeof(union xhci_trb)); trb.event_cmd.flags = TRB_TYPE(TRB_RESET_DEV) | @@ -575,26 +575,26 @@ int xhci_virtdev_reset(struct xhci_virtual_device *vdev) ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); xhci_print_trb(xhci, &trb, "Response Reset"); - /* + /* * The Reset Device command can't fail, according to the 0.95/0.96 spec, - * unless we tried to reset a slot ID that wasn't enabled, - * or the device wasn't in the addressed or configured state. - */ - switch (GET_COMP_CODE(trb.event_cmd.status)) { - case COMP_CMD_ABORT: - case COMP_CMD_STOP: - dev_warn(xhci->dev, "Timeout waiting for reset device command\n"); - ret = -ETIMEDOUT; + * unless we tried to reset a slot ID that wasn't enabled, + * or the device wasn't in the addressed or configured state. + */ + switch (GET_COMP_CODE(trb.event_cmd.status)) { + case COMP_CMD_ABORT: + case COMP_CMD_STOP: + dev_warn(xhci->dev, "Timeout waiting for reset device command\n"); + ret = -ETIMEDOUT; + break; + case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ + case COMP_CTX_STATE: /* 0.96 completion code for same thing */ + /* Don't treat this as an error. May change my mind later. */ + ret = 0; + case COMP_SUCCESS: break; - case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ - case COMP_CTX_STATE: /* 0.96 completion code for same thing */ - /* Don't treat this as an error. May change my mind later. */ - ret = 0; - case COMP_SUCCESS: - break; - default: - ret = -EINVAL; - } + default: + ret = -EINVAL; + } return ret; } @@ -651,14 +651,14 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, if (xhci->hci_version < 0x100 || vdev->udev->speed == USB_SPEED_HIGH) { u32 think_time = (hubchar & HUB_CHAR_TTTT) >> 5; - tt_info |= TT_THINK_TIME(think_time); + tt_info |= TT_THINK_TIME(think_time); } - } + } vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); - /* Issue Configure Endpoint or Evaluate Context Command */ + /* Issue Configure Endpoint or Evaluate Context Command */ memset(&trb, 0, sizeof(union xhci_trb)); xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = SLOT_ID_FOR_TRB(vdev->slot_id); @@ -782,21 +782,21 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) last_ctx = fls(add_flags) - 1; - /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ + /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ vdev->in_ctx->icc.add_flags = cpu_to_le32(add_flags); - vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); - vdev->in_ctx->icc.add_flags &= cpu_to_le32(~EP0_FLAG); - vdev->in_ctx->icc.drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); + vdev->in_ctx->icc.add_flags &= cpu_to_le32(~EP0_FLAG); + vdev->in_ctx->icc.drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); /* Don't issue the command if there's no endpoints to update. */ - if (vdev->in_ctx->icc.add_flags == cpu_to_le32(SLOT_FLAG) && - vdev->in_ctx->icc.drop_flags == 0) + if (vdev->in_ctx->icc.add_flags == cpu_to_le32(SLOT_FLAG) && + vdev->in_ctx->icc.drop_flags == 0) return 0; vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - /* Issue Configure Endpoint Command */ + /* Issue Configure Endpoint Command */ memset(&trb, 0, sizeof(union xhci_trb)); xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | @@ -816,7 +816,7 @@ static int xhci_virtdev_deconfigure(struct xhci_virtual_device *vdev) union xhci_trb trb; int ret; - /* Issue Deconfigure Endpoint Command */ + /* Issue Deconfigure Endpoint Command */ memset(&trb, 0, sizeof(union xhci_trb)); xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | @@ -912,7 +912,7 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev) vdev->in_ctx->ep[0].deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | vdev->ep[0]->cycle_state); - /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ + /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ xhci->dcbaa[vdev->slot_id] = cpu_to_le64((dma_addr_t)vdev->out_ctx); return 0; @@ -952,7 +952,7 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, vdev->in_ctx->icc.add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); vdev->in_ctx->icc.drop_flags = 0; - /* Issue Address Device Command */ + /* Issue Address Device Command */ memset(&trb, 0, sizeof(union xhci_trb)); xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = TRB_TYPE(TRB_ADDR_DEV) | @@ -970,12 +970,12 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, static int xhci_virtdev_set_address(struct xhci_virtual_device *vdev) { - return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS); + return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS); } static int xhci_virtdev_enable(struct xhci_virtual_device *vdev) { - return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY); + return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY); } static int xhci_virtdev_attach(struct xhci_hcd *xhci, struct usb_device *udev) @@ -1291,7 +1291,7 @@ static int xhci_reset(struct xhci_hcd *xhci) return ret; } - return 0; + return 0; } static int xhci_start(struct xhci_hcd *xhci) @@ -1305,8 +1305,8 @@ static int xhci_start(struct xhci_hcd *xhci) ret = xhci_handshake(&xhci->op_regs->status, STS_HALT, 0, XHCI_MAX_HALT_USEC); - if (ret) { - dev_err(xhci->dev, "failed to start\n"); + if (ret) { + dev_err(xhci->dev, "failed to start\n"); return ret; } @@ -1314,7 +1314,7 @@ static int xhci_start(struct xhci_hcd *xhci) for (i = 0; i < xhci->num_usb_ports; i++) xhci_hub_port_power(xhci, i, false); - return 0; + return 0; } static int xhci_init(struct usb_host *host) -- cgit v1.2.3 From fa42474239d7bf5370aac3672cfb6b6b37d7f2dc Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:07 -0800 Subject: usb: xhci-hcd: Do not zero out DMA coherent memory Memory returned by dma_alloc_coherent() should already be zeroed out, so there's no need to do this explicitly. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 7cfd74de41..7106a56371 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -445,7 +445,6 @@ static struct xhci_virtual_device *xhci_alloc_virtdev(struct xhci_hcd *xhci, vdev->dma_size = sz_ictx + sz_dctx; p = vdev->dma = dma_alloc_coherent(vdev->dma_size, DMA_ADDRESS_BROKEN); - memset(vdev->dma, 0, vdev->dma_size); vdev->out_ctx = p; p += sz_dctx; vdev->in_ctx = p; p += sz_ictx; @@ -1225,7 +1224,6 @@ static void xhci_dma_alloc(struct xhci_hcd *xhci) xhci->dma_size += num_ep * sz_ep; p = xhci->dma = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN); - memset(xhci->dma, 0, xhci->dma_size); xhci->sp = p; p += sz_sp; xhci->dcbaa = p; p += sz_dca; -- cgit v1.2.3 From 1b94263898b895760dfcde86abc586a2b2075224 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:08 -0800 Subject: usb: xhci-hcd: Cast ~XHCI_IRQS to u32 Cast ~XHCI_IRQS to u32 to avoid getting a warning on 64-bit builds. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 7106a56371..a01f9fe38a 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1259,7 +1259,7 @@ static void xhci_dma_alloc(struct xhci_hcd *xhci) static int xhci_halt(struct xhci_hcd *xhci) { u32 reg = readl(&xhci->op_regs->status); - u32 mask = ~XHCI_IRQS; + u32 mask = (u32)~XHCI_IRQS; if (!(reg & STS_HALT)) mask &= ~CMD_RUN; -- cgit v1.2.3 From e4193e0a3b7070d91f127e31fd978c87243656a1 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:09 -0800 Subject: usb: xhci-hcd: Make sure to initialize xhci->rings_list Make sure to initialize xhci->rings_list, otherwise any use of the driver will result in a crash. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index a01f9fe38a..cdce0d5ecb 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1470,6 +1470,7 @@ int xhci_register(struct device_d *dev, struct xhci_data *data) xhci = xzalloc(sizeof(*xhci)); host = &xhci->host; INIT_LIST_HEAD(&xhci->vdev_list); + INIT_LIST_HEAD(&xhci->rings_list); xhci->dev = dev; xhci->cap_regs = data->regs; xhci->op_regs = (void __iomem *)xhci->cap_regs + -- cgit v1.2.3 From 0fb81bd2b8d7538d78b0b37e72df822da8c49f1a Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:10 -0800 Subject: usb: xhci-hcd: Drop pointless bitwise or On_hs_hub is a boolean, not a bitfiled, so usage of bitwise or is unnecessary. Replace it with a regular assignement operator. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index cdce0d5ecb..f97a334f01 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -849,7 +849,7 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev) if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB && top_dev->parent->speed != USB_SPEED_LOW && top_dev->parent->speed != USB_SPEED_FULL) { - on_hs_hub |= true; + on_hs_hub = true; if (!hs_slot_id) { struct xhci_virtual_device *vhub = xhci_find_virtdev(xhci, top_dev->parent); -- cgit v1.2.3 From cd6dcd5cc16ff12abc03bda614e2a13fd349777f Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:11 -0800 Subject: usb: xhci-hcd: Add support for 64-byte context size xHCI spec allows for various context to be either 32 or 64 bytes in size, so we can't rely on struct definitions to correctly reflect data layout. Port varios context accessors from Linux kernel and convert the rest of the code to use them in order to be able to support both context sizes. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 233 +++++++++++++++++++++++++++++++------------- drivers/usb/host/xhci.h | 36 ++++--- 2 files changed, 188 insertions(+), 81 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index f97a334f01..91ddcbb303 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -23,6 +23,39 @@ #include "xhci.h" + +static struct xhci_input_control_ctx * +xhci_get_input_control_ctx(struct xhci_container_ctx *ctx) +{ + if (ctx->type != XHCI_CTX_TYPE_INPUT) + return NULL; + + return (struct xhci_input_control_ctx *)ctx->bytes; +} + +static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_DEVICE) + return (struct xhci_slot_ctx *)ctx->bytes; + + return (struct xhci_slot_ctx *) + (ctx->bytes + HCC_CTX_SIZE(xhci->hcc_params)); +} + +static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx, + unsigned int ep_index) +{ + /* increment ep index by offset of start of ep ctx array */ + ep_index++; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + ep_index++; + + return (struct xhci_ep_ctx *) + (ctx->bytes + (ep_index * HCC_CTX_SIZE(xhci->hcc_params))); +} + /* * xHCI ring handling */ @@ -426,28 +459,48 @@ static struct xhci_virtual_device *xhci_find_virtdev(struct xhci_hcd *xhci, return NULL; } +static struct xhci_container_ctx * +xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type) +{ + struct xhci_container_ctx *ctx; + + if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)) + return NULL; + + ctx = xzalloc(sizeof(*ctx)); + ctx->type = type; + ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + if (type == XHCI_CTX_TYPE_INPUT) + ctx->size += HCC_CTX_SIZE(xhci->hcc_params); + + ctx->bytes = dma_alloc_coherent(ctx->size, &ctx->dma); + if (WARN_ON(!ctx->bytes)) { + kfree(ctx); + return NULL; + } + return ctx; +} + +static void xhci_free_container_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + if (!ctx) + return; + dma_free_coherent(ctx->bytes, ctx->dma, ctx->size); + kfree(ctx); +} + static struct xhci_virtual_device *xhci_alloc_virtdev(struct xhci_hcd *xhci, struct usb_device *udev) { struct xhci_virtual_device *vdev; - size_t sz_ctx, sz_ictx, sz_dctx; - void *p; vdev = xzalloc(sizeof(*vdev)); vdev->udev = udev; list_add_tail(&vdev->list, &xhci->vdev_list); - sz_ctx = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; - /* Device Context: 64B aligned */ - sz_dctx = ALIGN(sz_ctx, 64); - /* Input Control Context: 64B aligned */ - sz_ictx = ALIGN(sz_ctx + HCC_CTX_SIZE(xhci->hcc_params), 64); - - vdev->dma_size = sz_ictx + sz_dctx; - p = vdev->dma = dma_alloc_coherent(vdev->dma_size, DMA_ADDRESS_BROKEN); - - vdev->out_ctx = p; p += sz_dctx; - vdev->in_ctx = p; p += sz_ictx; + vdev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE); + vdev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT); return vdev; } @@ -462,7 +515,8 @@ static void xhci_free_virtdev(struct xhci_virtual_device *vdev) xhci_put_endpoint_ring(xhci, vdev->ep[i]); list_del(&vdev->list); - dma_free_coherent(vdev->dma, 0, vdev->dma_size); + xhci_free_container_ctx(xhci, vdev->out_ctx); + xhci_free_container_ctx(xhci, vdev->in_ctx); free(vdev); } @@ -486,23 +540,32 @@ static int xhci_virtdev_issue_transfer(struct xhci_virtual_device *vdev, static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev) { + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot; + struct xhci_ep_ctx *in_ep; int i; + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + /* When a device's add flag and drop flag are zero, any subsequent * configure endpoint command will leave that endpoint's state * untouched. Make sure we don't leave any old state in the input * endpoint contexts. */ - vdev->in_ctx->icc.drop_flags = 0; - vdev->in_ctx->icc.add_flags = 0; - vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); + in_icc->drop_flags = 0; + in_icc->add_flags = 0; + in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK); /* Endpoint 0 is always valid */ - vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(1)); + in_slot->dev_info |= cpu_to_le32(LAST_CTX(1)); for (i = 1; i < 31; i++) { - vdev->in_ctx->ep[i].ep_info = 0; - vdev->in_ctx->ep[i].ep_info2 = 0; - vdev->in_ctx->ep[i].deq = 0; - vdev->in_ctx->ep[i].tx_info = 0; + in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, i); + + in_ep->ep_info = 0; + in_ep->ep_info2 = 0; + in_ep->deq = 0; + in_ep->tx_info = 0; } } @@ -558,11 +621,14 @@ static int xhci_virtdev_enable_slot(struct xhci_virtual_device *vdev) int xhci_virtdev_reset(struct xhci_virtual_device *vdev) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_slot_ctx *out_slot; union xhci_trb trb; int ret; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + /* If device is not setup, there is no point in resetting it */ - if (GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)) == + if (GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)) == SLOT_STATE_DISABLED) return 0; @@ -607,31 +673,37 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); struct usb_hub_descriptor *desc = buffer; + struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot, *out_slot; union xhci_trb trb; u32 dev_info, dev_info2, tt_info; u8 maxchild; u16 hubchar; int ret; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + /* Need at least first byte of wHubCharacteristics */ if (length < 4) return 0; /* Skip already configured hub device */ - if (vdev->out_ctx->slot.dev_info & DEV_HUB) + if (out_slot->dev_info & DEV_HUB) return 0; maxchild = desc->bNbrPorts; hubchar = le16_to_cpu(desc->wHubCharacteristics); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + /* update slot context */ - memcpy(&vdev->in_ctx->slot, &vdev->out_ctx->slot, - sizeof(struct xhci_slot_ctx)); - vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); - vdev->in_ctx->icc.drop_flags = 0; - vdev->in_ctx->slot.dev_state = 0; - dev_info = le32_to_cpu(vdev->in_ctx->slot.dev_info); - dev_info2 = le32_to_cpu(vdev->in_ctx->slot.dev_info2); - tt_info = le32_to_cpu(vdev->in_ctx->slot.tt_info); + memcpy(in_slot, out_slot, sizeof(struct xhci_slot_ctx)); + in_icc->add_flags |= cpu_to_le32(SLOT_FLAG); + in_icc->drop_flags = 0; + in_slot->dev_state = 0; + dev_info = le32_to_cpu(in_slot->dev_info); + dev_info2 = le32_to_cpu(in_slot->dev_info2); + tt_info = le32_to_cpu(in_slot->tt_info); dev_info |= DEV_HUB; /* HS Multi-TT in bDeviceProtocol */ @@ -653,13 +725,13 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, tt_info |= TT_THINK_TIME(think_time); } } - vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); - vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); - vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); + in_slot->dev_info = cpu_to_le32(dev_info); + in_slot->dev_info2 = cpu_to_le32(dev_info2); + in_slot->tt_info = cpu_to_le32(tt_info); /* Issue Configure Endpoint or Evaluate Context Command */ memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = SLOT_ID_FOR_TRB(vdev->slot_id); if (xhci->hci_version > 0x95) trb.event_cmd.flags |= TRB_TYPE(TRB_CONFIG_EP); @@ -695,6 +767,8 @@ static int xhci_virtdev_update_hub_status(struct xhci_virtual_device *vhub, static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot; struct usb_device *udev = vdev->udev; union xhci_trb trb; u32 add_flags = 0, last_ctx; @@ -711,10 +785,12 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) ep->bmAttributes); u8 epi = xhci_get_endpoint_index(ep->bEndpointAddress, ep->bmAttributes); - struct xhci_ep_ctx *ctx = &vdev->in_ctx->ep[epi]; + struct xhci_ep_ctx *ctx; u32 mps, interval, mult, esit, max_packet, max_burst; u32 ep_info, ep_info2, tx_info; + ctx = xhci_get_ep_ctx(xhci, vdev->in_ctx, epi); + vdev->ep[epi] = xhci_get_endpoint_ring(xhci); if (!vdev->ep[epi]) return -ENOMEM; @@ -781,23 +857,26 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) last_ctx = fls(add_flags) - 1; + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ - vdev->in_ctx->icc.add_flags = cpu_to_le32(add_flags); - vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); - vdev->in_ctx->icc.add_flags &= cpu_to_le32(~EP0_FLAG); - vdev->in_ctx->icc.drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + in_icc->add_flags = cpu_to_le32(add_flags); + in_icc->add_flags |= cpu_to_le32(SLOT_FLAG); + in_icc->add_flags &= cpu_to_le32(~EP0_FLAG); + in_icc->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); /* Don't issue the command if there's no endpoints to update. */ - if (vdev->in_ctx->icc.add_flags == cpu_to_le32(SLOT_FLAG) && - vdev->in_ctx->icc.drop_flags == 0) + if (in_icc->add_flags == cpu_to_le32(SLOT_FLAG) && + in_icc->drop_flags == 0) return 0; - vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); - vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); + in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + in_slot->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); /* Issue Configure Endpoint Command */ memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(vdev->slot_id); xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); @@ -817,7 +896,7 @@ static int xhci_virtdev_deconfigure(struct xhci_virtual_device *vdev) /* Issue Deconfigure Endpoint Command */ memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | SLOT_ID_FOR_TRB(vdev->slot_id); xhci_print_trb(xhci, &trb, "Request DeconfigureEndpoint"); @@ -833,11 +912,16 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); struct usb_device *top_dev; + struct xhci_slot_ctx *in_slot; + struct xhci_ep_ctx *in_ep; int max_packets; u32 route = 0, dev_info, dev_info2, tt_info, ep_info2, tx_info; bool on_hs_hub = false; int hs_slot_id = 0; + in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + /* * Find the root hub port this device is under, also determine SlotID * of possible external HS hub a LS/FS device could be connected to. @@ -892,9 +976,9 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev) tt_info |= (top_dev->portnr << 8) | hs_slot_id; } - vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); - vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); - vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); + in_slot->dev_info = cpu_to_le32(dev_info); + in_slot->dev_info2 = cpu_to_le32(dev_info2); + in_slot->tt_info = cpu_to_le32(tt_info); /* 4.3.3 4) Initalize Transfer Ring */ vdev->ep[0] = xhci_get_endpoint_ring(xhci); @@ -906,13 +990,13 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev) ep_info2 = EP_TYPE(CTRL_EP) | MAX_BURST(0) | ERROR_COUNT(3); ep_info2 |= MAX_PACKET(max_packets); tx_info = AVG_TRB_LENGTH_FOR_EP(8); - vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(ep_info2); - vdev->in_ctx->ep[0].tx_info = cpu_to_le32(tx_info); - vdev->in_ctx->ep[0].deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | - vdev->ep[0]->cycle_state); + in_ep->ep_info2 = cpu_to_le32(ep_info2); + in_ep->tx_info = cpu_to_le32(tx_info); + in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | + vdev->ep[0]->cycle_state); /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ - xhci->dcbaa[vdev->slot_id] = cpu_to_le64((dma_addr_t)vdev->out_ctx); + xhci->dcbaa[vdev->slot_id] = cpu_to_le64(vdev->out_ctx->dma); return 0; } @@ -921,39 +1005,46 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, enum xhci_setup_dev setup) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + static struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot; + struct xhci_ep_ctx *in_ep; union xhci_trb trb; int ret; + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + /* * If this is the first Set Address since device * plug-in then initialize Slot Context */ - if (!vdev->in_ctx->slot.dev_info) + if (!in_slot->dev_info) xhci_virtdev_init(vdev); else { + in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0); + /* Otherwise, update Control Ring Dequeue pointer */ - vdev->in_ctx->ep[0].deq = - cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | - vdev->ep[0]->cycle_state); + in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | + vdev->ep[0]->cycle_state); /* * FS devices have MaxPacketSize0 of 8 or 64, we start * with 64. If assumtion was wrong, fix it up here. */ if (vdev->udev->speed == USB_SPEED_FULL && vdev->udev->maxpacketsize == PACKET_SIZE_8) { - u32 info = le32_to_cpu(vdev->in_ctx->ep[0].ep_info2); + u32 info = le32_to_cpu(in_ep->ep_info2); info &= ~MAX_PACKET_MASK; info |= MAX_PACKET(8); - vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(info); + in_ep->ep_info2 = cpu_to_le32(info); } } - vdev->in_ctx->icc.add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); - vdev->in_ctx->icc.drop_flags = 0; + in_icc->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); + in_icc->drop_flags = 0; /* Issue Address Device Command */ memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); trb.event_cmd.flags = TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(vdev->slot_id); if (setup == SETUP_CONTEXT_ONLY) @@ -1008,6 +1099,7 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, struct usb_host *host = udev->host; struct xhci_hcd *xhci = to_xhci_hcd(host); struct xhci_virtual_device *vdev; + struct xhci_slot_ctx *out_slot; union xhci_trb trb; u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) | usb_pipeendpoint(pipe); @@ -1018,10 +1110,12 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, if (!vdev) return -ENODEV; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", __func__, udev, vdev, vdev->slot_id, - GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), epi, - vdev->in_ctx, vdev->out_ctx); + GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), epi, + vdev->in_ctx->bytes, vdev->out_ctx->bytes); /* pass ownership of data buffer to device */ dma_sync_single_for_device((unsigned long)buffer, length, @@ -1069,6 +1163,7 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, struct usb_host *host = udev->host; struct xhci_hcd *xhci = to_xhci_hcd(host); struct xhci_virtual_device *vdev; + struct xhci_slot_ctx *out_slot; union xhci_trb trb; u16 typeReq = (req->requesttype << 8) | req->request; int ret; @@ -1090,10 +1185,12 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, if (!vdev) return -ENODEV; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", __func__, udev, vdev, vdev->slot_id, - GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), 0, - vdev->in_ctx, vdev->out_ctx); + GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), 0, + vdev->in_ctx->bytes, vdev->out_ctx->bytes); if (req->request == USB_REQ_SET_ADDRESS) return xhci_virtdev_set_address(vdev); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 078f881183..9f2d576756 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -503,6 +503,27 @@ struct xhci_doorbell_array { #define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16)) #define DB_VALUE_HOST 0x00000000 +/** + * struct xhci_container_ctx + * @type: Type of context. Used to calculated offsets to contained contexts. + * @size: Size of the context data + * @bytes: The raw context data given to HW + * @dma: dma address of the bytes + * + * Represents either a Device or Input context. Holds a pointer to the raw + * memory used for the context (bytes) and dma address of it (dma). + */ +struct xhci_container_ctx { + unsigned type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + + int size; + + u8 *bytes; + dma_addr_t dma; +}; + /** * struct xhci_slot_ctx * @dev_info: Route string, device speed, hub info, and last valid endpoint @@ -1183,17 +1204,6 @@ struct xhci_ring { int cycle_state; }; -struct xhci_device_context { - struct xhci_slot_ctx slot; - struct xhci_ep_ctx ep[31]; -}; - -struct xhci_input_context { - struct xhci_input_control_ctx icc; - struct xhci_slot_ctx slot; - struct xhci_ep_ctx ep[31]; -}; - struct xhci_virtual_device { struct list_head list; struct usb_device *udev; @@ -1201,8 +1211,8 @@ struct xhci_virtual_device { size_t dma_size; int slot_id; struct xhci_ring *ep[USB_MAXENDPOINTS]; - struct xhci_input_context *in_ctx; - struct xhci_device_context *out_ctx; + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; }; struct usb_root_hub_info { -- cgit v1.2.3 From e77f11086a5616adfafa6c4c784c4be969eb8570 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:13 -0800 Subject: usb: xhci-hcd: Always wait for "Response Data" completion Xhci_submit_control() submits TRB_DATA with TRB_IOC flag regardless of the vlaue of req->requesttype, so we shouldn't gate waiting for the event that will result from it with "req->requesttype & USB_DIR_IN". Failing to do this will result in unstable USB performance and will eventually cause the controller to fail completely. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 91ddcbb303..855dc47c52 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1246,7 +1246,7 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, xhci_print_trb(xhci, &trb, "Request Status"); xhci_virtdev_issue_transfer(vdev, 0, &trb, true); - if (length > 0 && req->requesttype & USB_DIR_IN) { + if (length > 0) { ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); xhci_print_trb(xhci, &trb, "Response Data "); if (ret == -COMP_SHORT_TX) -- cgit v1.2.3 From 882e9108bb09eb1fb4287f0b2241675eecac9db4 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:12 -0800 Subject: usb: xhci-hcd: Don't try to DMA sync if buffer is NULL Driver's .submit_control() callback can and will be called with buffer set to NULL (and length set to 0), so we need to make sure that we don't try to DMA sync the buffer in that case. Add appropriate gurads to make sure that doesn't happen. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 855dc47c52..8a00b8854c 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1200,10 +1200,12 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, return ret; } - /* Pass ownership of data buffer to device */ - dma_sync_single_for_device((unsigned long)buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (length > 0) { + /* Pass ownership of data buffer to device */ + dma_sync_single_for_device((unsigned long)buffer, length, + (req->requesttype & USB_DIR_IN) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } /* Setup TRB */ memset(&trb, 0, sizeof(union xhci_trb)); @@ -1259,10 +1261,13 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, xhci_print_trb(xhci, &trb, "Response Status"); dma_regain: - /* Regain ownership of data buffer from device */ - dma_sync_single_for_cpu((unsigned long)buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (length > 0) { + /* Regain ownership of data buffer from device */ + dma_sync_single_for_cpu((unsigned long)buffer, length, + (req->requesttype & USB_DIR_IN) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + if (ret < 0) return ret; -- cgit v1.2.3 From f5b4df6d55a4b5fdea935ecc0a01b4c0450bf2e0 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:14 -0800 Subject: usb: xhci-hcd: Convert xhci_submit_normal() to use dma_map_single() Convert xhci_submit_normal() to use dma_(un)map_single(). These functions both allow us to handle potential physical/virtual address differences as well as allowing to drop a number of typecasts. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 8a00b8854c..d0b73041d8 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1100,6 +1100,7 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, struct xhci_hcd *xhci = to_xhci_hcd(host); struct xhci_virtual_device *vdev; struct xhci_slot_ctx *out_slot; + dma_addr_t buffer_dma; union xhci_trb trb; u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) | usb_pipeendpoint(pipe); @@ -1118,13 +1119,15 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, vdev->in_ctx->bytes, vdev->out_ctx->bytes); /* pass ownership of data buffer to device */ - dma_sync_single_for_device((unsigned long)buffer, length, - usb_pipein(pipe) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + buffer_dma = dma_map_single(xhci->dev, buffer, length, + usb_pipein(pipe) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(xhci->dev, buffer_dma)) + return -EFAULT; /* Normal TRB */ memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer); + trb.event_cmd.cmd_trb = cpu_to_le64(buffer_dma); /* FIXME: TD remainder */ trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); trb.event_cmd.flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; @@ -1136,9 +1139,9 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, xhci_print_trb(xhci, &trb, "Response Normal"); /* Regain ownership of data buffer from device */ - dma_sync_single_for_cpu((unsigned long)buffer, length, - usb_pipein(pipe) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + dma_unmap_single(xhci->dev, buffer_dma, length, + usb_pipein(pipe) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); switch (ret) { case -COMP_SHORT_TX: -- cgit v1.2.3 From 7a0e0c3bf073fb876fc9bc2698e4e47946371f0b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:15 -0800 Subject: usb: xhci-hcd: Convert xhci_submit_control() to use dma_map_single() Convert xhci_submit_control() to use dma_(un)map_single(). These functions both allow us to handle potential physical/virtual address differences as well as allowing to drop a number of typecasts. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index d0b73041d8..4e28e24c47 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1167,6 +1167,7 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, struct xhci_hcd *xhci = to_xhci_hcd(host); struct xhci_virtual_device *vdev; struct xhci_slot_ctx *out_slot; + dma_addr_t buffer_dma = 0; union xhci_trb trb; u16 typeReq = (req->requesttype << 8) | req->request; int ret; @@ -1205,9 +1206,11 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, if (length > 0) { /* Pass ownership of data buffer to device */ - dma_sync_single_for_device((unsigned long)buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + buffer_dma = dma_map_single(xhci->dev, buffer, length, + (req->requesttype & USB_DIR_IN) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(xhci->dev, buffer_dma)) + return -EFAULT; } /* Setup TRB */ @@ -1230,7 +1233,7 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, /* Data TRB */ if (length > 0) { memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer); + trb.event_cmd.cmd_trb = cpu_to_le64(buffer_dma); /* FIXME: TD remainder */ trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); trb.event_cmd.flags = TRB_TYPE(TRB_DATA) | TRB_IOC; @@ -1266,9 +1269,9 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, dma_regain: if (length > 0) { /* Regain ownership of data buffer from device */ - dma_sync_single_for_cpu((unsigned long)buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + dma_unmap_single(xhci->dev, buffer_dma, length, + (req->requesttype & USB_DIR_IN) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); } if (ret < 0) -- cgit v1.2.3 From f926ddbc4b29e1be3dd5924092676097434785df Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:16 -0800 Subject: usb: xhci-hcd: Simplify TRB initialization code Current TRB initialization code in xHCI driver is: a) Somewhat inconsistent in endianness correctness: LE is insured in some places whereas in some places it isn't b) Somewhat inconsistent in how DMA buffer address is being written: some places utilize xhci_write_64, whereas others just assing cmd_trb field directly c) A bit wasetful since in a number of codepaths where TRB is used it is memset to zero first only to have 80+% of its fields changed to something else right after. To fix all of the above introduce xhci_init_event_cmd_trb(), that will initialize all of the fileds to desired values only once as well as taking care of endianness. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 128 +++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 50 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 4e28e24c47..b0e9a0c563 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -569,6 +569,14 @@ static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev) } } +static void xhci_init_event_cmd_trb(union xhci_trb *trb, + u64 cmd_trb, u32 status, u32 flags) +{ + xhci_write_64(cmd_trb, &trb->event_cmd.cmd_trb); + writel(status, &trb->event_cmd.status); + writel(flags, &trb->event_cmd.flags); +} + static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); @@ -576,9 +584,11 @@ static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev) int ret; /* Issue Disable Slot Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.flags = TRB_TYPE(TRB_DISABLE_SLOT) | - SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_init_event_cmd_trb(&trb, + 0, + 0, + TRB_TYPE(TRB_DISABLE_SLOT) | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request DisableSlot"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -598,8 +608,10 @@ static int xhci_virtdev_enable_slot(struct xhci_virtual_device *vdev) int ret; /* Issue Enable Slot Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.flags = TRB_TYPE(TRB_ENABLE_SLOT); + xhci_init_event_cmd_trb(&trb, + 0, + 0, + TRB_TYPE(TRB_ENABLE_SLOT)); xhci_print_trb(xhci, &trb, "Request EnableSlot"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -632,9 +644,11 @@ int xhci_virtdev_reset(struct xhci_virtual_device *vdev) SLOT_STATE_DISABLED) return 0; - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.flags = TRB_TYPE(TRB_RESET_DEV) | - SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_init_event_cmd_trb(&trb, + 0, + 0, + TRB_TYPE(TRB_RESET_DEV) | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request Reset"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -679,6 +693,7 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, u32 dev_info, dev_info2, tt_info; u8 maxchild; u16 hubchar; + u32 flags; int ret; out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); @@ -730,13 +745,15 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, in_slot->tt_info = cpu_to_le32(tt_info); /* Issue Configure Endpoint or Evaluate Context Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = SLOT_ID_FOR_TRB(vdev->slot_id); + flags = SLOT_ID_FOR_TRB(vdev->slot_id); if (xhci->hci_version > 0x95) - trb.event_cmd.flags |= TRB_TYPE(TRB_CONFIG_EP); + flags |= TRB_TYPE(TRB_CONFIG_EP); else - trb.event_cmd.flags |= TRB_TYPE(TRB_EVAL_CONTEXT); + flags |= TRB_TYPE(TRB_EVAL_CONTEXT); + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + flags); xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -875,10 +892,11 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) in_slot->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); /* Issue Configure Endpoint Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | - SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + TRB_TYPE(TRB_CONFIG_EP) | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -895,10 +913,11 @@ static int xhci_virtdev_deconfigure(struct xhci_virtual_device *vdev) int ret; /* Issue Deconfigure Endpoint Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | - SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request DeconfigureEndpoint"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -1009,6 +1028,7 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, struct xhci_slot_ctx *in_slot; struct xhci_ep_ctx *in_ep; union xhci_trb trb; + u32 flags; int ret; in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); @@ -1043,12 +1063,14 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, in_icc->drop_flags = 0; /* Issue Address Device Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64(vdev->in_ctx->dma, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = TRB_TYPE(TRB_ADDR_DEV) | + flags = TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(vdev->slot_id); if (setup == SETUP_CONTEXT_ONLY) - trb.event_cmd.flags |= TRB_BSR; + flags |= TRB_BSR; + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + flags); xhci_print_trb(xhci, &trb, "Request AddressDevice"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -1105,6 +1127,7 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) | usb_pipeendpoint(pipe); u8 epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); + u32 flags; int ret; vdev = xhci_find_virtdev(xhci, udev); @@ -1126,13 +1149,14 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, return -EFAULT; /* Normal TRB */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.cmd_trb = cpu_to_le64(buffer_dma); /* FIXME: TD remainder */ - trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); - trb.event_cmd.flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; + flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; if (usb_pipein(pipe)) - trb.event_cmd.flags |= TRB_ISP; + flags |= TRB_ISP; + xhci_init_event_cmd_trb(&trb, + buffer_dma, + TRB_LEN(length) | TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Normal"); xhci_virtdev_issue_transfer(vdev, epi, &trb, true); ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); @@ -1170,6 +1194,8 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, dma_addr_t buffer_dma = 0; union xhci_trb trb; u16 typeReq = (req->requesttype << 8) | req->request; + u64 field[2]; + u32 flags; int ret; dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n", @@ -1212,45 +1238,47 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, if (dma_mapping_error(xhci->dev, buffer_dma)) return -EFAULT; } - /* Setup TRB */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.generic.field[0] = le16_to_cpu(req->value) << 16 | + field[0] = le16_to_cpu(req->value) << 16 | req->request << 8 | req->requesttype; - trb.generic.field[1] = le16_to_cpu(req->length) << 16 | + field[1] = le16_to_cpu(req->length) << 16 | le16_to_cpu(req->index); - trb.event_cmd.status = TRB_LEN(8) | TRB_INTR_TARGET(0); - trb.event_cmd.flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; + flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; if (xhci->hci_version == 0x100 && length > 0) { if (req->requesttype & USB_DIR_IN) - trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_IN); + flags |= TRB_TX_TYPE(TRB_DATA_IN); else - trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_OUT); + flags |= TRB_TX_TYPE(TRB_DATA_OUT); } + xhci_init_event_cmd_trb(&trb, + field[1] << 32 | field[0], + TRB_LEN(8) | TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Setup "); xhci_virtdev_issue_transfer(vdev, 0, &trb, false); /* Data TRB */ if (length > 0) { - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.cmd_trb = cpu_to_le64(buffer_dma); /* FIXME: TD remainder */ - trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); - trb.event_cmd.flags = TRB_TYPE(TRB_DATA) | TRB_IOC; + flags = TRB_TYPE(TRB_DATA) | TRB_IOC; if (req->requesttype & USB_DIR_IN) - trb.event_cmd.flags |= TRB_ISP | TRB_DIR_IN; + flags |= TRB_ISP | TRB_DIR_IN; + xhci_init_event_cmd_trb(&trb, + buffer_dma, + TRB_LEN(length) | TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Data "); xhci_virtdev_issue_transfer(vdev, 0, &trb, false); } /* Status TRB */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.status = TRB_INTR_TARGET(0); - if (length > 0 && req->requesttype & USB_DIR_IN) - trb.event_cmd.flags = 0; - else - trb.event_cmd.flags = TRB_DIR_IN; - trb.event_cmd.flags |= TRB_TYPE(TRB_STATUS) | TRB_IOC; + flags = TRB_TYPE(TRB_STATUS) | TRB_IOC; + if (!(length > 0 && req->requesttype & USB_DIR_IN)) + flags |= TRB_DIR_IN; + xhci_init_event_cmd_trb(&trb, + 0, + TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Status"); xhci_virtdev_issue_transfer(vdev, 0, &trb, true); -- cgit v1.2.3 From 8e216ea635ebe88334136595dd4a7a89328442b6 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:17 -0800 Subject: usb: xhci-hcd: Drop 'dma' field from struct xhci_hcd Drop 'dma' field from struct xhci_hcd since it is not really used anywhere. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 2 +- drivers/usb/host/xhci.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index b0e9a0c563..6a62916207 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1359,7 +1359,7 @@ static void xhci_dma_alloc(struct xhci_hcd *xhci) num_ep = max(MAX_EP_RINGS, MIN_EP_RINGS + num_ep); xhci->dma_size += num_ep * sz_ep; - p = xhci->dma = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN); + p = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN); xhci->sp = p; p += sz_sp; xhci->dcbaa = p; p += sz_dca; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9f2d576756..dfbfe579be 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1242,7 +1242,6 @@ struct xhci_hcd { int num_sp; int page_size; int page_shift; - void *dma; size_t dma_size; __le64 *dcbaa; void *sp; -- cgit v1.2.3 From e9fb81b8b518c52c9c472c30a80c9de018d6ee35 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:18 -0800 Subject: usb: xhci-hcd: Check usb_pipein(pipe) only once in xhci_submit_normal() Simplfy xhci_submit_normal()'s logic a bit by using helper variables and making sure that usb_pipein(pipe) is only called once. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 6a62916207..c8327c88f8 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1120,16 +1120,26 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, { struct usb_host *host = udev->host; struct xhci_hcd *xhci = to_xhci_hcd(host); + enum dma_data_direction dma_direction; struct xhci_virtual_device *vdev; struct xhci_slot_ctx *out_slot; dma_addr_t buffer_dma; union xhci_trb trb; - u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) | - usb_pipeendpoint(pipe); - u8 epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); - u32 flags; + u8 epaddr = usb_pipeendpoint(pipe); + u8 epi; + u32 flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; int ret; + if (usb_pipein(pipe)) { + epaddr |= USB_DIR_IN; + flags |= TRB_ISP; + dma_direction = DMA_FROM_DEVICE; + } else { + epaddr |= USB_DIR_OUT; + dma_direction = DMA_TO_DEVICE; + } + + epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); vdev = xhci_find_virtdev(xhci, udev); if (!vdev) return -ENODEV; @@ -1143,16 +1153,12 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, /* pass ownership of data buffer to device */ buffer_dma = dma_map_single(xhci->dev, buffer, length, - usb_pipein(pipe) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + dma_direction); if (dma_mapping_error(xhci->dev, buffer_dma)) return -EFAULT; /* Normal TRB */ /* FIXME: TD remainder */ - flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; - if (usb_pipein(pipe)) - flags |= TRB_ISP; xhci_init_event_cmd_trb(&trb, buffer_dma, TRB_LEN(length) | TRB_INTR_TARGET(0), @@ -1164,9 +1170,7 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, /* Regain ownership of data buffer from device */ dma_unmap_single(xhci->dev, buffer_dma, length, - usb_pipein(pipe) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - + dma_direction); switch (ret) { case -COMP_SHORT_TX: udev->status = 0; -- cgit v1.2.3 From afaeb00ca1b9292d892c6bc5e481dfa34722e400 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:19 -0800 Subject: usb: xhci-hcd: Initialize TRT flag for xHCI >= 1.0 Initialize TRT flag for xHCI >= 1.0, not just == 1.0. This is what Linux xHCI driver does. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index c8327c88f8..5174463793 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1248,7 +1248,7 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, field[1] = le16_to_cpu(req->length) << 16 | le16_to_cpu(req->index); flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; - if (xhci->hci_version == 0x100 && length > 0) { + if (xhci->hci_version >= 0x100 && length > 0) { if (req->requesttype & USB_DIR_IN) flags |= TRB_TX_TYPE(TRB_DATA_IN); else -- cgit v1.2.3 From 956fc9cc056abc878256298e05745fc76732c82c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:20 -0800 Subject: usb: xhci-hcd: Simplify route string building loop Simplify route string building loop by avoiding checking top_dev->parent->descriptor->bDeviceClass twice. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci-hcd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 5174463793..32a6ccd5cd 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -947,10 +947,12 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev) */ for (top_dev = vdev->udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) { - if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB) - route = (route << 4) | (top_dev->portnr & 0xf); - if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB && - top_dev->parent->speed != USB_SPEED_LOW && + if (top_dev->parent->descriptor->bDeviceClass != USB_CLASS_HUB) + continue; + + route = (route << 4) | (top_dev->portnr & 0xf); + + if (top_dev->parent->speed != USB_SPEED_LOW && top_dev->parent->speed != USB_SPEED_FULL) { on_hs_hub = true; if (!hs_slot_id) { -- cgit v1.2.3 From 3c5317e9046c86aa0bbba402b2b1fd173fd4774e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:21 -0800 Subject: usb: xhci-hcd: Make use of lo_hi_readq/writeq() Make use of lo_hi_readq/writeq() to implement xhci_read/write_64() helpers, same as it is done in the Linux kernel. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/host/xhci.h | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index dfbfe579be..84a14dd1fc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -24,6 +24,8 @@ #ifndef __XHCI_H #define __XHCI_H +#include + #define NUM_COMMAND_TRBS 8 #define NUM_TRANSFER_TRBS 8 #define NUM_EVENT_SEGM 1 /* only one supported */ @@ -1165,19 +1167,11 @@ struct xhci_erst_entry { */ static inline u64 xhci_read_64(__le64 __iomem *regs) { - __u32 __iomem *ptr = (__u32 __iomem *)regs; - u64 val_lo = readl(ptr); - u64 val_hi = readl(ptr + 1); - return val_lo + (val_hi << 32); + return lo_hi_readq(regs); } static inline void xhci_write_64(const u64 val, __le64 __iomem *regs) { - __u32 __iomem *ptr = (__u32 __iomem *)regs; - u32 val_lo = lower_32_bits(val); - u32 val_hi = upper_32_bits(val); - - writel(val_lo, ptr); - writel(val_hi, ptr + 1); + lo_hi_writeq(val, regs); } /* -- cgit v1.2.3 From f30b6e70e956d3f7986a19bdfabb401142425777 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:22 -0800 Subject: phy: core: Assume EPROBE_DEFER in of_phy_provider_lookup() In order to support PHY driver probe deferral change the code to assume EPROBE_DEFER instead of ENODEV when requested PHY is missing from phy_provider_list. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/phy/phy-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 9d6288fc07..066a887a22 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -227,7 +227,7 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node) return phy_provider; } - return ERR_PTR(-ENODEV); + return ERR_PTR(-EPROBE_DEFER); } /** @@ -254,7 +254,7 @@ static struct phy *_of_phy_get(struct device_node *np, int index) phy_provider = of_phy_provider_lookup(args.np); if (IS_ERR(phy_provider)) { - return ERR_PTR(-ENODEV); + return ERR_CAST(phy_provider); } return phy_provider->of_xlate(phy_provider->dev, &args); -- cgit v1.2.3 From 127a3cd41acec88051b574a4c405e3369bb4462c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:23 -0800 Subject: phy: Port i.MX8MQ USB PHY driver from Linux Port i.MX8MQ USB PHY driver from Linux. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/phy/Kconfig | 2 + drivers/phy/Makefile | 1 + drivers/phy/freescale/Kconfig | 4 + drivers/phy/freescale/Makefile | 1 + drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 130 +++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 drivers/phy/freescale/Kconfig create mode 100644 drivers/phy/freescale/Makefile create mode 100644 drivers/phy/freescale/phy-fsl-imx8mq-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b0c8b9bf0e..b5cefb2ff3 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -22,4 +22,6 @@ config USB_NOP_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. +source "drivers/phy/freescale/Kconfig" + endif diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 8fc85953b3..179c55e60a 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_USB_NOP_XCEIV) += usb-nop-xceiv.o +obj-y += freescale/ diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig new file mode 100644 index 0000000000..8e56dd7e79 --- /dev/null +++ b/drivers/phy/freescale/Kconfig @@ -0,0 +1,4 @@ +config PHY_FSL_IMX8MQ_USB + bool "Freescale i.MX8M USB3 PHY" + default SOC_IMX8MQ + diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile new file mode 100644 index 0000000000..dc2b3f1f2f --- /dev/null +++ b/drivers/phy/freescale/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c new file mode 100644 index 0000000000..1aef2b3004 --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2017 NXP. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PHY_CTRL0 0x0 +#define PHY_CTRL0_REF_SSP_EN BIT(2) + +#define PHY_CTRL1 0x4 +#define PHY_CTRL1_RESET BIT(0) +#define PHY_CTRL1_COMMONONN BIT(1) +#define PHY_CTRL1_ATERESET BIT(3) +#define PHY_CTRL1_VDATSRCENB0 BIT(19) +#define PHY_CTRL1_VDATDETENB0 BIT(20) + +#define PHY_CTRL2 0x8 +#define PHY_CTRL2_TXENABLEN0 BIT(8) + +struct imx8mq_usb_phy { + struct phy *phy; + struct clk *clk; + void __iomem *base; +}; + +static int imx8mq_usb_phy_init(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + u32 value; + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | + PHY_CTRL1_COMMONONN); + value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; + writel(value, imx_phy->base + PHY_CTRL1); + + value = readl(imx_phy->base + PHY_CTRL0); + value |= PHY_CTRL0_REF_SSP_EN; + writel(value, imx_phy->base + PHY_CTRL0); + + value = readl(imx_phy->base + PHY_CTRL2); + value |= PHY_CTRL2_TXENABLEN0; + writel(value, imx_phy->base + PHY_CTRL2); + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); + writel(value, imx_phy->base + PHY_CTRL1); + + return 0; +} + +static int imx8mq_phy_power_on(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + + return clk_enable(imx_phy->clk); +} + +static int imx8mq_phy_power_off(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + + clk_disable(imx_phy->clk); + + return 0; +} + +static struct phy_ops imx8mq_usb_phy_ops = { + .init = imx8mq_usb_phy_init, + .power_on = imx8mq_phy_power_on, + .power_off = imx8mq_phy_power_off, +}; + +static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev, + struct of_phandle_args *args) +{ + struct imx8mq_usb_phy *imx_phy = dev->priv; + + return imx_phy->phy; +} + +static int imx8mq_usb_phy_probe(struct device_d *dev) +{ + struct phy_provider *phy_provider; + struct imx8mq_usb_phy *imx_phy; + + imx_phy = xzalloc(sizeof(*imx_phy)); + + dev->priv = imx_phy; + + imx_phy->clk = clk_get(dev, "phy"); + if (IS_ERR(imx_phy->clk)) + return PTR_ERR(imx_phy->clk); + + imx_phy->base = dev_get_mem_region(dev, 0); + if (IS_ERR(imx_phy->base)) + return PTR_ERR(imx_phy->base); + + imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops, NULL); + if (IS_ERR(imx_phy->phy)) + return PTR_ERR(imx_phy->phy); + + phy_set_drvdata(imx_phy->phy, imx_phy); + + phy_provider = of_phy_provider_register(dev, imx8mq_usb_phy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id imx8mq_usb_phy_of_match[] = { + {.compatible = "fsl,imx8mq-usb-phy",}, + { }, +}; + +static struct driver_d imx8mq_usb_phy_driver = { + .name = "imx8mq-usb-phy", + .probe = imx8mq_usb_phy_probe, + .of_compatible = DRV_OF_COMPAT(imx8mq_usb_phy_of_match), +}; +device_platform_driver(imx8mq_usb_phy_driver); -- cgit v1.2.3 From 2614cb18799ef24fdebe54d04c3c8efb8f9c3b78 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:24 -0800 Subject: clk: Drop separate definitions of clk_put() Regardless of the value of CONFIG_HAVE_CLK, clk_put() implementation is always a no-op. Move the definition to linux/clk.h and drop the rest of the code implementing it. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/clk/clkdev.c | 5 ----- include/linux/clk.h | 21 ++++----------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index abdc415272..f67a5c4d98 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -189,11 +189,6 @@ struct clk *clk_get(struct device_d *dev, const char *con_id) } EXPORT_SYMBOL(clk_get); -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_put); - void clkdev_add(struct clk_lookup *cl) { if (cl->dev_id) diff --git a/include/linux/clk.h b/include/linux/clk.h index 978a0a8a9a..38c86e0b47 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -75,19 +75,6 @@ void clk_disable(struct clk *clk); */ unsigned long clk_get_rate(struct clk *clk); -/** - * clk_put - "free" the clock source - * @clk: clock source - * - * Note: drivers must ensure that all clk_enable calls made on this - * clock source are balanced by clk_disable calls prior to calling - * this function. - * - * clk_put should not be called from within interrupt context. - */ -void clk_put(struct clk *clk); - - /* * The remaining APIs are optional for machine class support. */ @@ -180,10 +167,6 @@ static inline unsigned long clk_get_rate(struct clk *clk) return 0; } -static inline void clk_put(struct clk *clk) -{ -} - static inline long clk_round_rate(struct clk *clk, unsigned long rate) { return 0; @@ -195,6 +178,10 @@ static inline int clk_set_rate(struct clk *clk, unsigned long rate) } #endif +static inline void clk_put(struct clk *clk) +{ +} + #ifdef CONFIG_COMMON_CLK #include -- cgit v1.2.3 From 2916bfffbdee5f1ba1a74d65b548635c4fe5e174 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:25 -0800 Subject: include/usb: Import USB_SPEED_SUPER_PLUS from Linux Import USB_SPEED_SUPER_PLUS constant from Linux to support porting kernel code that uses it. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- include/usb/ch9.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/usb/ch9.h b/include/usb/ch9.h index b44d41e85f..89d83e0d09 100644 --- a/include/usb/ch9.h +++ b/include/usb/ch9.h @@ -913,6 +913,7 @@ enum usb_device_speed { USB_SPEED_HIGH, /* usb 2.0 */ USB_SPEED_WIRELESS, /* wireless (usb 2.5) */ USB_SPEED_SUPER, /* usb 3.0 */ + USB_SPEED_SUPER_PLUS, /* usb 3.1 */ }; -- cgit v1.2.3 From 7817ca90abcc7807152cb4339065cfd8db565d68 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:26 -0800 Subject: clk: Import a subset of clk_bulk API from Linux Import a subset of clk_bulk API from Linux to support porting kernel code that uses it. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/clk/Makefile | 3 +- drivers/clk/clk-bulk.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-bulk.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cd4026c944..34c44fff9b 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \ clk-mux.o clk-gate.o clk-composite.o \ clk-fractional-divider.o clk-conf.o \ - clk-gate-shared.o clk-gpio.o + clk-gate-shared.o clk-gpio.o \ + clk-bulk.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_ARCH_MVEBU) += mvebu/ diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c new file mode 100644 index 0000000000..ddbe32f9c2 --- /dev/null +++ b/drivers/clk/clk-bulk.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017 NXP + * + * Dong Aisheng + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) +{ + while (--num_clks >= 0) { + clk_put(clks[num_clks].clk); + clks[num_clks].clk = NULL; + } +} +EXPORT_SYMBOL_GPL(clk_bulk_put); + +int __must_check clk_bulk_get(struct device_d *dev, int num_clks, + struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) + clks[i].clk = NULL; + + for (i = 0; i < num_clks; i++) { + clks[i].clk = clk_get(dev, clks[i].id); + if (IS_ERR(clks[i].clk)) { + ret = PTR_ERR(clks[i].clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get clk '%s': %d\n", + clks[i].id, ret); + clks[i].clk = NULL; + goto err; + } + } + + return 0; + +err: + clk_bulk_put(i, clks); + + return ret; +} +EXPORT_SYMBOL(clk_bulk_get); + +/** + * clk_bulk_disable - gate a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being gated + * + * clk_bulk_disable must not sleep, which differentiates it from + * clk_bulk_unprepare. clk_bulk_disable must be called before + * clk_bulk_unprepare. + */ +void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks) +{ + + while (--num_clks >= 0) + clk_disable(clks[num_clks].clk); +} +EXPORT_SYMBOL_GPL(clk_bulk_disable); + +/** + * clk_bulk_enable - ungate a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being ungated + * + * clk_bulk_enable must not sleep + * Returns 0 on success, -EERROR otherwise. + */ +int __must_check clk_bulk_enable(int num_clks, const struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) { + ret = clk_enable(clks[i].clk); + if (ret) { + pr_err("Failed to enable clk '%s': %d\n", + clks[i].id, ret); + goto err; + } + } + + return 0; + +err: + clk_bulk_disable(i, clks); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_bulk_enable); diff --git a/include/linux/clk.h b/include/linux/clk.h index 38c86e0b47..961fc89ba7 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -25,6 +25,22 @@ struct device_d; */ struct clk; +/** + * struct clk_bulk_data - Data used for bulk clk operations. + * + * @id: clock consumer ID + * @clk: struct clk * to store the associated clock + * + * The CLK APIs provide a series of clk_bulk_() API calls as + * a convenience to consumers which require multiple clks. This + * structure is used to manage data for these calls. + */ +struct clk_bulk_data { + const char *id; + struct clk *clk; +}; + + #ifdef CONFIG_HAVE_CLK /** @@ -44,6 +60,29 @@ struct clk; */ struct clk *clk_get(struct device_d *dev, const char *id); +/** + * clk_bulk_get - lookup and obtain a number of references to clock producer. + * @dev: device for clock "consumer" + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * This helper function allows drivers to get several clk consumers in one + * operation. If any of the clk cannot be acquired then any clks + * that were obtained will be freed before returning to the caller. + * + * Returns 0 if all clocks specified in clk_bulk_data table are obtained + * successfully, or valid IS_ERR() condition containing errno. + * The implementation uses @dev and @clk_bulk_data.id to determine the + * clock consumer, and thereby the clock producer. + * The clock returned is stored in each @clk_bulk_data.clk field. + * + * Drivers must assume that the clock source is not enabled. + * + * clk_bulk_get should not be called from within interrupt context. + */ +int __must_check clk_bulk_get(struct device_d *dev, int num_clks, + struct clk_bulk_data *clks); + /** * clk_enable - inform the system when the clock source should be running. * @clk: clock source @@ -54,6 +93,18 @@ struct clk *clk_get(struct device_d *dev, const char *id); */ int clk_enable(struct clk *clk); +/** + * clk_bulk_enable - inform the system when the set of clks should be running. + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * May be called from atomic contexts. + * + * Returns success (0) or negative errno. + */ +int __must_check clk_bulk_enable(int num_clks, + const struct clk_bulk_data *clks); + /** * clk_disable - inform the system when the clock source is no longer required. * @clk: clock source @@ -68,6 +119,24 @@ int clk_enable(struct clk *clk); */ void clk_disable(struct clk *clk); +/** + * clk_bulk_disable - inform the system when the set of clks is no + * longer required. + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * Inform the system that a set of clks is no longer required by + * a driver and may be shut down. + * + * May be called from atomic contexts. + * + * Implementation detail: if the set of clks is shared between + * multiple drivers, clk_bulk_enable() calls must be balanced by the + * same number of clk_bulk_disable() calls for the clock source to be + * disabled. + */ +void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks); + /** * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. * This is only valid once the clock source has been enabled. @@ -75,6 +144,19 @@ void clk_disable(struct clk *clk); */ unsigned long clk_get_rate(struct clk *clk); +/** + * clk_bulk_put - "free" the clock source + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * Note: drivers must ensure that all clk_bulk_enable calls made on this + * clock source are balanced by clk_bulk_disable calls prior to calling + * this function. + * + * clk_bulk_put should not be called from within interrupt context. + */ +void clk_bulk_put(int num_clks, struct clk_bulk_data *clks); + /* * The remaining APIs are optional for machine class support. */ @@ -153,15 +235,31 @@ static inline struct clk *clk_get(struct device_d *dev, const char *id) return NULL; } +static inline int __must_check clk_bulk_get(struct device_d *dev, int num_clks, + struct clk_bulk_data *clks) +{ + return 0; +} + +static inline void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) {} + static inline int clk_enable(struct clk *clk) { return 0; } +static inline int __must_check clk_bulk_enable(int num_clks, struct clk_bulk_data *clks) +{ + return 0; +} + static inline void clk_disable(struct clk *clk) { } +static inline void clk_bulk_disable(int num_clks, + struct clk_bulk_data *clks) {} + static inline unsigned long clk_get_rate(struct clk *clk) { return 0; -- cgit v1.2.3 From de90d053e79146361f99eb6f7d2aba555c37e902 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:27 -0800 Subject: usb: Import DWC3 USB controller driver from Linux Import DWC3 USB controller driver from Linux. This is a bare minimum port of the code needed to support USB host functionality on i.MX8MQ. No other use-case is explicitly supported. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 1 + drivers/usb/dwc3/Kconfig | 22 + drivers/usb/dwc3/Makefile | 10 + drivers/usb/dwc3/core.c | 740 ++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 1267 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/debug.h | 664 ++++++++++++++++++++++++ drivers/usb/dwc3/host.c | 36 ++ drivers/usb/dwc3/io.h | 41 ++ 9 files changed, 2783 insertions(+) create mode 100644 drivers/usb/dwc3/Kconfig create mode 100644 drivers/usb/dwc3/Makefile create mode 100644 drivers/usb/dwc3/core.c create mode 100644 drivers/usb/dwc3/core.h create mode 100644 drivers/usb/dwc3/debug.h create mode 100644 drivers/usb/dwc3/host.c create mode 100644 drivers/usb/dwc3/io.h diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 8520a2fd9b..ee33e0f28c 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -9,6 +9,8 @@ if USB_HOST source drivers/usb/imx/Kconfig +source "drivers/usb/dwc3/Kconfig" + source drivers/usb/host/Kconfig source drivers/usb/otg/Kconfig diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 047f184800..13fca2cbdf 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/ +obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MUSB) += musb/ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_STORAGE) += storage/ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig new file mode 100644 index 0000000000..c91fd36a95 --- /dev/null +++ b/drivers/usb/dwc3/Kconfig @@ -0,0 +1,22 @@ +config USB_DWC3 + tristate "DesignWare USB3 DRD Core Support" + depends on USB && HAS_DMA + select USB_XHCI + select USB_DWC3_HOST # Remove this once we support more + # than USB host + help + Say Y or M here if your system has a Dual Role SuperSpeed + USB controller based on the DesignWare USB3 IP Core. + + If you choose to build this driver is a dynamically linked + module, the module will be called dwc3.ko. + +if USB_DWC3 + +config USB_DWC3_HOST + bool "Host only mode" + help + Select this when you want to use DWC3 in host mode only, + thereby the gadget feature will be regressed. + +endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile new file mode 100644 index 0000000000..d43b23eb2d --- /dev/null +++ b/drivers/usb/dwc3/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_DWC3) += dwc3.o + +dwc3-y := core.o + +ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST)),) + dwc3-y += host.o +endif + diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c new file mode 100644 index 0000000000..2e7031a348 --- /dev/null +++ b/drivers/usb/dwc3/core.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * core.c - DesignWare USB3 DRD Controller Core file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#include +#include +#include +#include +#include + +#include "core.h" +#include "io.h" + + +#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ + +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); + reg |= DWC3_GCTL_PRTCAPDIR(mode); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + dwc->current_dr_role = mode; +} + +/** + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static int dwc3_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + int retries = 1000; + int ret; + + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + return ret; + } + + /* + * We're resetting only the device side because, if we're in host mode, + * XHCI driver will reset the host block. If dwc3 was configured for + * host-only mode, then we can return early. + */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) + return 0; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= DWC3_DCTL_CSFTRST; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + goto done; + + udelay(1); + } while (--retries); + + phy_exit(dwc->usb3_generic_phy); + phy_exit(dwc->usb2_generic_phy); + + return -ETIMEDOUT; + +done: + /* + * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared, + * we must wait at least 50ms before accessing the PHY domain + * (synchronization delay). DWC_usb31 programming guide section 1.3.2. + */ + if (dwc3_is_usb31(dwc)) + mdelay(50); + + return 0; +} + +static const struct clk_bulk_data dwc3_core_clks[] = { + { .id = "ref" }, + { .id = "bus_early" }, + { .id = "suspend" }, +}; + +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc) +{ + u32 reg; + u32 dft; + + if (dwc->revision < DWC3_REVISION_250A) + return; + + if (dwc->fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + if (!WARN(dft == dwc->fladj, + "request value same as default, ignoring\n")) { + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + } +} + +static void dwc3_core_num_eps(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + dwc->num_eps = DWC3_NUM_EPS(parms); +} + +static void dwc3_cache_hwparams(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); +} + +/** + * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. + */ +static int dwc3_phy_setup(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + /* + * Make sure UX_EXIT_PX is cleared as that causes issues with some + * PHYs. Also, this bit is not supposed to be used in normal operation. + */ + reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX; + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY + * to '0' during coreConsultant configuration. So default value + * will be '0' when the core is reset. Application needs to set it + * to '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + if (dwc->u2ss_inp3_quirk) + reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; + + if (dwc->dis_rxdet_inp3_quirk) + reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3; + + if (dwc->req_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_REQP1P2P3; + + if (dwc->del_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN; + + if (dwc->del_phy_power_chg_quirk) + reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE; + + if (dwc->lfps_filter_quirk) + reg |= DWC3_GUSB3PIPECTL_LFPSFILT; + + if (dwc->rx_detect_poll_quirk) + reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL; + + if (dwc->tx_de_emphasis_quirk) + reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis); + + if (dwc->dis_u3_susphy_quirk) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + + if (dwc->dis_del_phy_power_chg_quirk) + reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + /* Select the HS PHY interface */ + switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { + case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: + if (dwc->hsphy_interface && + !strncmp(dwc->hsphy_interface, "utmi", 4)) { + reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + break; + } else if (dwc->hsphy_interface && + !strncmp(dwc->hsphy_interface, "ulpi", 4)) { + reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } else { + /* Relying on default value. */ + if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) + break; + } + /* FALLTHROUGH */ + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: + /* FALLTHROUGH */ + default: + break; + } + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to + * '0' during coreConsultant configuration. So default value will + * be '0' when the core is reset. Application needs to set it to + * '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + if (dwc->dis_u2_susphy_quirk) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + + if (dwc->dis_enblslpm_quirk) + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + else + reg |= DWC3_GUSB2PHYCFG_ENBLSLPM; + + if (dwc->dis_u2_freeclk_exists_quirk) + reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + return 0; +} + +static void dwc3_core_exit(struct dwc3 *dwc) +{ + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); + clk_bulk_disable(dwc->num_clks, dwc->clks); +} + +static bool dwc3_core_is_valid(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { + /* Detected DWC_usb3 IP */ + dwc->revision = reg; + } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) { + /* Detected DWC_usb31 IP */ + dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); + dwc->revision |= DWC3_REVISION_IS_DWC31; + dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); + } else { + return false; + } + + return true; +} + +static void dwc3_core_setup_global_control(struct dwc3 *dwc) +{ + u32 hwparams4 = dwc->hwparams.hwparams4; + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_SCALEDOWN_MASK; + + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + /** + * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an + * issue which would cause xHCI compliance tests to fail. + * + * Because of that we cannot enable clock gating on such + * configurations. + * + * Refers to: + * + * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based + * SOF/ITP Mode Used + */ + if ((dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) && + (dwc->revision >= DWC3_REVISION_210A && + dwc->revision <= DWC3_REVISION_250A)) + reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; + else + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + /* enable hibernation here */ + dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); + + /* + * REVISIT Enabling this bit so that host-mode hibernation + * will work. Device-mode hibernation is not yet implemented. + */ + reg |= DWC3_GCTL_GBLHIBERNATIONEN; + break; + default: + /* nothing */ + break; + } + + /* check if current dwc3 is on simulation board */ + if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { + dev_info(dwc->dev, "Running with FPGA optimizations\n"); + dwc->is_fpga = true; + } + + WARN(dwc->disable_scramble_quirk && !dwc->is_fpga, + "disable_scramble cannot be used on non-FPGA builds\n"); + + if (dwc->disable_scramble_quirk && dwc->is_fpga) + reg |= DWC3_GCTL_DISSCRAMBLE; + else + reg &= ~DWC3_GCTL_DISSCRAMBLE; + + if (dwc->u2exit_lfps_quirk) + reg |= DWC3_GCTL_U2EXIT_LFPS; + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * where the device can fail to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter a Connect/Disconnect loop + */ + if (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +static int dwc3_core_get_phy(struct dwc3 *dwc); + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int dwc3_core_init(struct dwc3 *dwc) +{ + u32 reg; + int ret; + + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + + /* + * Write Linux Version Code to our GUID register so it's easy to figure + * out which kernel version a bug was found. + */ + dwc3_writel(dwc->regs, DWC3_GUID, 0xdeadbeef); + + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + + if (!dwc->phys_ready) { + ret = dwc3_core_get_phy(dwc); + if (ret) + goto err0a; + dwc->phys_ready = true; + } + + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0a; + + dwc3_core_setup_global_control(dwc); + dwc3_core_num_eps(dwc); + + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc); + + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) + goto err2; + + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err3; + /* + * ENDXFER polling is available on version 3.10a and later of + * the DWC_usb3 controller. It is NOT available in the + * DWC_usb31 controller. + */ + if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); + reg |= DWC3_GUCTL2_RST_ACTBITLATER; + dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); + } + + if (dwc->revision >= DWC3_REVISION_250A) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + + /* + * Enable hardware control of sending remote wakeup + * in HS when the device is in the L1 state. + */ + if (dwc->revision >= DWC3_REVISION_290A) + reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW; + + if (dwc->dis_tx_ipgap_linecheck_quirk) + reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + } + + if (dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL); + + /* + * Enable Auto retry Feature to make the controller operating in + * Host mode on seeing transaction errors(CRC errors or internal + * overrun scenerios) on IN transfers to reply to the device + * with a non-terminating retry ACK (i.e, an ACK transcation + * packet with Retry=1 & Nump != 0) + */ + reg |= DWC3_GUCTL_HSTINAUTORETRY; + + dwc3_writel(dwc->regs, DWC3_GUCTL, reg); + } + + /* + * Must config both number of packets and max burst settings to enable + * RX and/or TX threshold. + */ + if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) { + u8 rx_thr_num = dwc->rx_thr_num_pkt_prd; + u8 rx_maxburst = dwc->rx_max_burst_prd; + u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; + u8 tx_maxburst = dwc->tx_max_burst_prd; + + if (rx_thr_num && rx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg |= DWC31_RXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_RXTHRNUMPKT_PRD(~0); + reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num); + + reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + } + + if (tx_thr_num && tx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg |= DWC31_TXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_TXTHRNUMPKT_PRD(~0); + reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num); + + reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + } + } + + return 0; + +/* err4: */ + /* phy_power_off(dwc->usb3_generic_phy); */ +err3: + phy_power_off(dwc->usb2_generic_phy); +err2: +/* err1: */ + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); +err0a: +err0: + return ret; +} + +static int dwc3_core_get_phy(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + int ret; + + dwc->usb2_generic_phy = phy_get(dev, "usb2-phy"); + if (IS_ERR(dwc->usb2_generic_phy)) { + ret = PTR_ERR(dwc->usb2_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dev_err(dev, "no usb2 phy configured\n"); + dwc->usb2_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } + } + + dwc->usb3_generic_phy = phy_get(dev, "usb3-phy"); + if (IS_ERR(dwc->usb3_generic_phy)) { + ret = PTR_ERR(dwc->usb3_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dev_err(dev, "no usb2 phy configured\n"); + dwc->usb3_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } + } + + return 0; +} + +static int dwc3_core_init_mode(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + int ret; + + switch (dwc->dr_mode) { + case USB_DR_MODE_HOST: + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + + ret = dwc3_host_init(dwc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize host\n"); + return ret; + } + break; + default: + dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); + return -EINVAL; + } + + return 0; +} + +static void dwc3_get_properties(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + u8 lpm_nyet_threshold; + u8 tx_de_emphasis; + u8 hird_threshold; + + /* default to highest possible threshold */ + lpm_nyet_threshold = 0xff; + + /* default to -3.5dB de-emphasis */ + tx_de_emphasis = 1; + + /* + * default to assert utmi_sleep_n and use maximum allowed HIRD + * threshold value of 0b1100 + */ + hird_threshold = 12; + + dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL); + dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL); + + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + + dwc->hird_threshold = hird_threshold + | (dwc->is_utmi_l1_suspend << 4); + + dwc->imod_interval = 0; +} + +/* check whether the core supports IMOD */ +bool dwc3_has_imod(struct dwc3 *dwc) +{ + return ((dwc3_is_usb3(dwc) && + dwc->revision >= DWC3_REVISION_300A) || + (dwc3_is_usb31(dwc) && + dwc->revision >= DWC3_USB31_REVISION_120A)); +} + +static void dwc3_check_params(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + + /* Check for proper value of imod_interval */ + if (dwc->imod_interval && !dwc3_has_imod(dwc)) { + dev_warn(dwc->dev, "Interrupt moderation not supported\n"); + dwc->imod_interval = 0; + } + + /* + * Workaround for STAR 9000961433 which affects only version + * 3.00a of the DWC_usb3 core. This prevents the controller + * interrupt from being masked while handling events. IMOD + * allows us to work around this issue. Enable it for the + * affected version. + */ + if (!dwc->imod_interval && + (dwc->revision == DWC3_REVISION_300A)) + dwc->imod_interval = 1; + + /* Check the maximum_speed parameter */ + switch (dwc->maximum_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + break; + default: + dev_err(dev, "invalid maximum_speed parameter %d\n", + dwc->maximum_speed); + /* fall through */ + case USB_SPEED_UNKNOWN: + /* default to superspeed */ + dwc->maximum_speed = USB_SPEED_SUPER; + + /* + * default to superspeed plus if we are capable. + */ + if (dwc3_is_usb31(dwc) && + (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; + + break; + } +} + +static int dwc3_probe(struct device_d *dev) +{ + struct dwc3 *dwc; + int ret; + + dwc = xzalloc(sizeof(*dwc)); + dev->priv = dwc; + + dwc->clks = xmemdup(dwc3_core_clks, sizeof(dwc3_core_clks)); + dwc->dev = dev; + dwc->regs = dev_get_mem_region(dwc->dev, 0) + DWC3_GLOBALS_REGS_START; + + dwc3_get_properties(dwc); + + if (dev->device_node) { + dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); + + ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); + if (ret == -EPROBE_DEFER) + return ret; + /* + * Clocks are optional, but new DT platforms should support all + * clocks as required by the DT-binding. + */ + if (ret) + dwc->num_clks = 0; + } + + ret = clk_bulk_enable(dwc->num_clks, dwc->clks); + if (ret) + return ret; + + dwc3_cache_hwparams(dwc); + + ret = dwc3_core_init(dwc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize core: %d\n", ret); + return ret; + } + + dwc3_check_params(dwc); + + ret = dwc3_core_init_mode(dwc); + if (ret) + return ret; + + return 0; +} + +static void dwc3_remove(struct device_d *dev) +{ + struct dwc3 *dwc = dev->priv; + + dwc3_core_exit(dwc); + clk_bulk_put(dwc->num_clks, dwc->clks); +} + +static const struct of_device_id of_dwc3_match[] = { + { + .compatible = "snps,dwc3" + }, + { + .compatible = "synopsys,dwc3" + }, + { }, +}; + +static struct driver_d dwc3_driver = { + .probe = dwc3_probe, + .remove = dwc3_remove, + .name = "dwc3", + .of_compatible = DRV_OF_COMPAT(of_dwc3_match), +}; +device_platform_driver(dwc3_driver); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h new file mode 100644 index 0000000000..a404e4cd6a --- /dev/null +++ b/drivers/usb/dwc3/core.h @@ -0,0 +1,1267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * core.h - DesignWare USB3 DRD Core Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#ifndef __DRIVERS_USB_DWC3_CORE_H +#define __DRIVERS_USB_DWC3_CORE_H + +#include + +#define DWC3_MSG_MAX 500 + +/* Global constants */ +#define DWC3_PULL_UP_TIMEOUT 500 /* ms */ +#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */ +#define DWC3_EP0_SETUP_SIZE 512 +#define DWC3_ENDPOINTS_NUM 32 +#define DWC3_XHCI_RESOURCES_NUM 2 +#define DWC3_ISOC_MAX_RETRIES 5 + +#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ +#define DWC3_EVENT_BUFFERS_SIZE 4096 +#define DWC3_EVENT_TYPE_MASK 0xfe + +#define DWC3_EVENT_TYPE_DEV 0 +#define DWC3_EVENT_TYPE_CARKIT 3 +#define DWC3_EVENT_TYPE_I2C 4 + +#define DWC3_DEVICE_EVENT_DISCONNECT 0 +#define DWC3_DEVICE_EVENT_RESET 1 +#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 +#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 +#define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_HIBER_REQ 5 +#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SOF 7 +#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 +#define DWC3_DEVICE_EVENT_CMD_CMPL 10 +#define DWC3_DEVICE_EVENT_OVERFLOW 11 + +/* Controller's role while using the OTG block */ +#define DWC3_OTG_ROLE_IDLE 0 +#define DWC3_OTG_ROLE_HOST 1 +#define DWC3_OTG_ROLE_DEVICE 2 + +#define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GEVNTCOUNT_EHB BIT(31) +#define DWC3_GSNPSID_MASK 0xffff0000 +#define DWC3_GSNPSREV_MASK 0xffff + +/* DWC3 registers memory space boundries */ +#define DWC3_XHCI_REGS_START 0x0 +#define DWC3_XHCI_REGS_END 0x7fff +#define DWC3_GLOBALS_REGS_START 0xc100 +#define DWC3_GLOBALS_REGS_END 0xc6ff +#define DWC3_DEVICE_REGS_START 0xc700 +#define DWC3_DEVICE_REGS_END 0xcbff +#define DWC3_OTG_REGS_START 0xcc00 +#define DWC3_OTG_REGS_END 0xccff + +/* Global Registers */ +#define DWC3_GSBUSCFG0 0xc100 +#define DWC3_GSBUSCFG1 0xc104 +#define DWC3_GTXTHRCFG 0xc108 +#define DWC3_GRXTHRCFG 0xc10c +#define DWC3_GCTL 0xc110 +#define DWC3_GEVTEN 0xc114 +#define DWC3_GSTS 0xc118 +#define DWC3_GUCTL1 0xc11c +#define DWC3_GSNPSID 0xc120 +#define DWC3_GGPIO 0xc124 +#define DWC3_GUID 0xc128 +#define DWC3_GUCTL 0xc12c +#define DWC3_GBUSERRADDR0 0xc130 +#define DWC3_GBUSERRADDR1 0xc134 +#define DWC3_GPRTBIMAP0 0xc138 +#define DWC3_GPRTBIMAP1 0xc13c +#define DWC3_GHWPARAMS0 0xc140 +#define DWC3_GHWPARAMS1 0xc144 +#define DWC3_GHWPARAMS2 0xc148 +#define DWC3_GHWPARAMS3 0xc14c +#define DWC3_GHWPARAMS4 0xc150 +#define DWC3_GHWPARAMS5 0xc154 +#define DWC3_GHWPARAMS6 0xc158 +#define DWC3_GHWPARAMS7 0xc15c +#define DWC3_GDBGFIFOSPACE 0xc160 +#define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GDBGBMU 0xc16c +#define DWC3_GDBGLSPMUX 0xc170 +#define DWC3_GDBGLSP 0xc174 +#define DWC3_GDBGEPINFO0 0xc178 +#define DWC3_GDBGEPINFO1 0xc17c +#define DWC3_GPRTBIMAP_HS0 0xc180 +#define DWC3_GPRTBIMAP_HS1 0xc184 +#define DWC3_GPRTBIMAP_FS0 0xc188 +#define DWC3_GPRTBIMAP_FS1 0xc18c +#define DWC3_GUCTL2 0xc19c + +#define DWC3_VER_NUMBER 0xc1a0 +#define DWC3_VER_TYPE 0xc1a4 + +#define DWC3_GUSB2PHYCFG(n) (0xc200 + ((n) * 0x04)) +#define DWC3_GUSB2I2CCTL(n) (0xc240 + ((n) * 0x04)) + +#define DWC3_GUSB2PHYACC(n) (0xc280 + ((n) * 0x04)) + +#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + ((n) * 0x04)) + +#define DWC3_GTXFIFOSIZ(n) (0xc300 + ((n) * 0x04)) +#define DWC3_GRXFIFOSIZ(n) (0xc380 + ((n) * 0x04)) + +#define DWC3_GEVNTADRLO(n) (0xc400 + ((n) * 0x10)) +#define DWC3_GEVNTADRHI(n) (0xc404 + ((n) * 0x10)) +#define DWC3_GEVNTSIZ(n) (0xc408 + ((n) * 0x10)) +#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10)) + +#define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GFLADJ 0xc630 + +/* Device Registers */ +#define DWC3_DCFG 0xc700 +#define DWC3_DCTL 0xc704 +#define DWC3_DEVTEN 0xc708 +#define DWC3_DSTS 0xc70c +#define DWC3_DGCMDPAR 0xc710 +#define DWC3_DGCMD 0xc714 +#define DWC3_DALEPENA 0xc720 + +#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10)) +#define DWC3_DEPCMDPAR2 0x00 +#define DWC3_DEPCMDPAR1 0x04 +#define DWC3_DEPCMDPAR0 0x08 +#define DWC3_DEPCMD 0x0c + +#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4)) + +/* OTG Registers */ +#define DWC3_OCFG 0xcc00 +#define DWC3_OCTL 0xcc04 +#define DWC3_OEVT 0xcc08 +#define DWC3_OEVTEN 0xcc0C +#define DWC3_OSTS 0xcc10 + +/* Bit fields */ + +/* Global SoC Bus Configuration INCRx Register 0 */ +#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */ +#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */ +#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */ +#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */ +#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */ +#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */ +#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */ +#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ +#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff + +/* Global Debug LSP MUX Select */ +#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ +#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) +#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4) +#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf) + +/* Global Debug Queue/FIFO Space Available Register */ +#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f) +#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) +#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff) + +#define DWC3_TXFIFO 0 +#define DWC3_RXFIFO 1 +#define DWC3_TXREQQ 2 +#define DWC3_RXREQQ 3 +#define DWC3_RXINFOQ 4 +#define DWC3_PSTATQ 5 +#define DWC3_DESCFETCHQ 6 +#define DWC3_EVENTQ 7 +#define DWC3_AUXEVENTQ 8 + +/* Global RX Threshold Configuration Register */ +#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19) +#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24) +#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29) + +/* Global RX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f) + +/* Global TX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f) + +/* Global Configuration Register */ +#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) +#define DWC3_GCTL_U2RSTECN BIT(16) +#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6) +#define DWC3_GCTL_CLK_BUS (0) +#define DWC3_GCTL_CLK_PIPE (1) +#define DWC3_GCTL_CLK_PIPEHALF (2) +#define DWC3_GCTL_CLK_MASK (3) + +#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) +#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12) +#define DWC3_GCTL_PRTCAP_HOST 1 +#define DWC3_GCTL_PRTCAP_DEVICE 2 +#define DWC3_GCTL_PRTCAP_OTG 3 + +#define DWC3_GCTL_CORESOFTRESET BIT(11) +#define DWC3_GCTL_SOFITPSYNC BIT(10) +#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) +#define DWC3_GCTL_DISSCRAMBLE BIT(3) +#define DWC3_GCTL_U2EXIT_LFPS BIT(2) +#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1) +#define DWC3_GCTL_DSBLCLKGTNG BIT(0) + +/* Global User Control Register */ +#define DWC3_GUCTL_HSTINAUTORETRY BIT(14) + +/* Global User Control 1 Register */ +#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) +#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) + +/* Global Status Register */ +#define DWC3_GSTS_OTG_IP BIT(10) +#define DWC3_GSTS_BC_IP BIT(9) +#define DWC3_GSTS_ADP_IP BIT(8) +#define DWC3_GSTS_HOST_IP BIT(7) +#define DWC3_GSTS_DEVICE_IP BIT(6) +#define DWC3_GSTS_CSR_TIMEOUT BIT(5) +#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) +#define DWC3_GSTS_CURMOD(n) ((n) & 0x3) +#define DWC3_GSTS_CURMOD_DEVICE 0 +#define DWC3_GSTS_CURMOD_HOST 1 + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) +#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30) +#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6) +#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4) +#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8) +#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) +#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) +#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) +#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf) +#define USBTRDTIM_UTMI_8_BIT 9 +#define USBTRDTIM_UTMI_16_BIT 5 +#define UTMI_PHYIF_16_BIT 1 +#define UTMI_PHYIF_8_BIT 0 + +/* Global USB2 PHY Vendor Control Register */ +#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25) +#define DWC3_GUSB2PHYACC_BUSY BIT(23) +#define DWC3_GUSB2PHYACC_WRITE BIT(22) +#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) +#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8) +#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff) + +/* Global USB3 PIPE Control Register */ +#define DWC3_GUSB3PIPECTL_PHYSOFTRST BIT(31) +#define DWC3_GUSB3PIPECTL_U2SSINP3OK BIT(29) +#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28) +#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27) +#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(24) +#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19) +#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7) +#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1) +#define DWC3_GUSB3PIPECTL_DEPOCHANGE BIT(18) +#define DWC3_GUSB3PIPECTL_SUSPHY BIT(17) +#define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9) +#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8) +#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3) +#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1) + +/* Global TX Fifo Size Register */ +#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ +#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ +#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) + +/* Global Event Size Registers */ +#define DWC3_GEVNTSIZ_INTMASK BIT(31) +#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) + +/* Global HWPARAMS0 Register */ +#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3) +#define DWC3_GHWPARAMS0_MODE_GADGET 0 +#define DWC3_GHWPARAMS0_MODE_HOST 1 +#define DWC3_GHWPARAMS0_MODE_DRD 2 +#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7) +#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3) +#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff) +#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff) +#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff) + +/* Global HWPARAMS1 Register */ +#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) +#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 +#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 +#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 +#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) +#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) +#define DWC3_GHWPARAMS1_ENDBC BIT(31) + +/* Global HWPARAMS3 Register */ +#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) +#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1 +#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */ +#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) +#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3 +#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4) +#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1 + +/* Global HWPARAMS4 Register */ +#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) +#define DWC3_MAX_HIBER_SCRATCHBUFS 15 + +/* Global HWPARAMS6 Register */ +#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14) +#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13) +#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12) +#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11) +#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10) +#define DWC3_GHWPARAMS6_EN_FPGA BIT(7) + +/* Global HWPARAMS7 Register */ +#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff) +#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff) + +/* Global Frame Length Adjustment Register */ +#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) +#define DWC3_GFLADJ_30MHZ_MASK 0x3f + +/* Global User Control Register 2 */ +#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) + +/* Device Configuration Register */ +#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) +#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + +#define DWC3_DCFG_SPEED_MASK (7 << 0) +#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ +#define DWC3_DCFG_SUPERSPEED (4 << 0) +#define DWC3_DCFG_HIGHSPEED (0 << 0) +#define DWC3_DCFG_FULLSPEED BIT(0) +#define DWC3_DCFG_LOWSPEED (2 << 0) + +#define DWC3_DCFG_NUMP_SHIFT 17 +#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) +#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT) +#define DWC3_DCFG_LPM_CAP BIT(22) + +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP BIT(31) +#define DWC3_DCTL_CSFTRST BIT(30) +#define DWC3_DCTL_LSFTRST BIT(29) + +#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) +#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24) + +#define DWC3_DCTL_APPL1RES BIT(23) + +/* These apply for core versions 1.87a and earlier */ +#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) +#define DWC3_DCTL_TRGTULST(n) ((n) << 17) +#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) +#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) +#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) +#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) +#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf) +#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20) + +#define DWC3_DCTL_KEEP_CONNECT BIT(19) +#define DWC3_DCTL_L1_HIBER_EN BIT(18) +#define DWC3_DCTL_CRS BIT(17) +#define DWC3_DCTL_CSS BIT(16) + +#define DWC3_DCTL_INITU2ENA BIT(12) +#define DWC3_DCTL_ACCEPTU2ENA BIT(11) +#define DWC3_DCTL_INITU1ENA BIT(10) +#define DWC3_DCTL_ACCEPTU1ENA BIT(9) +#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) + +#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) +#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) + +#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) +#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) +#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) +#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) +#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) +#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) +#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) + +/* Device Event Enable Register */ +#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN BIT(12) +#define DWC3_DEVTEN_EVNTOVERFLOWEN BIT(11) +#define DWC3_DEVTEN_CMDCMPLTEN BIT(10) +#define DWC3_DEVTEN_ERRTICERREN BIT(9) +#define DWC3_DEVTEN_SOFEN BIT(7) +#define DWC3_DEVTEN_EOPFEN BIT(6) +#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5) +#define DWC3_DEVTEN_WKUPEVTEN BIT(4) +#define DWC3_DEVTEN_ULSTCNGEN BIT(3) +#define DWC3_DEVTEN_CONNECTDONEEN BIT(2) +#define DWC3_DEVTEN_USBRSTEN BIT(1) +#define DWC3_DEVTEN_DISCONNEVTEN BIT(0) + +/* Device Status Register */ +#define DWC3_DSTS_DCNRD BIT(29) + +/* This applies for core versions 1.87a and earlier */ +#define DWC3_DSTS_PWRUPREQ BIT(24) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DSTS_RSS BIT(25) +#define DWC3_DSTS_SSS BIT(24) + +#define DWC3_DSTS_COREIDLE BIT(23) +#define DWC3_DSTS_DEVCTRLHLT BIT(22) + +#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) +#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) + +#define DWC3_DSTS_RXFIFOEMPTY BIT(17) + +#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3) +#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) + +#define DWC3_DSTS_CONNECTSPD (7 << 0) + +#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ +#define DWC3_DSTS_SUPERSPEED (4 << 0) +#define DWC3_DSTS_HIGHSPEED (0 << 0) +#define DWC3_DSTS_FULLSPEED BIT(0) +#define DWC3_DSTS_LOWSPEED (2 << 0) + +/* Device Generic Command Register */ +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 + +/* These apply for core versions 1.94a and later */ +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 + +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F) +#define DWC3_DGCMD_CMDACT BIT(10) +#define DWC3_DGCMD_CMDIOC BIT(8) + +/* Device Generic Command Parameter Register */ +#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT BIT(0) +#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0) +#define DWC3_DGCMDPAR_RX_FIFO (0 << 5) +#define DWC3_DGCMDPAR_TX_FIFO BIT(5) +#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0) +#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0) + +/* Device Endpoint Command Register */ +#define DWC3_DEPCMD_PARAM_SHIFT 16 +#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT) +#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F) +#define DWC3_DEPCMD_HIPRI_FORCERM BIT(11) +#define DWC3_DEPCMD_CLEARPENDIN BIT(11) +#define DWC3_DEPCMD_CMDACT BIT(10) +#define DWC3_DEPCMD_CMDIOC BIT(8) + +#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) +#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) +#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) +#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) +#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) +#define DWC3_DEPCMD_SETSTALL (0x04 << 0) +/* This applies for core versions 1.90a and earlier */ +#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +/* This applies for core versions 1.94a and later */ +#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0) +#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) +#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) + +#define DWC3_DEPCMD_CMD(x) ((x) & 0xf) + +/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ +#define DWC3_DALEPENA_EP(n) BIT(n) + +#define DWC3_DEPCMD_TYPE_CONTROL 0 +#define DWC3_DEPCMD_TYPE_ISOC 1 +#define DWC3_DEPCMD_TYPE_BULK 2 +#define DWC3_DEPCMD_TYPE_INTR 3 + +#define DWC3_DEV_IMOD_COUNT_SHIFT 16 +#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16) +#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0 +#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0) + +/* OTG Configuration Register */ +#define DWC3_OCFG_DISPWRCUTTOFF BIT(5) +#define DWC3_OCFG_HIBDISMASK BIT(4) +#define DWC3_OCFG_SFTRSTMASK BIT(3) +#define DWC3_OCFG_OTGVERSION BIT(2) +#define DWC3_OCFG_HNPCAP BIT(1) +#define DWC3_OCFG_SRPCAP BIT(0) + +/* OTG CTL Register */ +#define DWC3_OCTL_OTG3GOERR BIT(7) +#define DWC3_OCTL_PERIMODE BIT(6) +#define DWC3_OCTL_PRTPWRCTL BIT(5) +#define DWC3_OCTL_HNPREQ BIT(4) +#define DWC3_OCTL_SESREQ BIT(3) +#define DWC3_OCTL_TERMSELIDPULSE BIT(2) +#define DWC3_OCTL_DEVSETHNPEN BIT(1) +#define DWC3_OCTL_HSTSETHNPEN BIT(0) + +/* OTG Event Register */ +#define DWC3_OEVT_DEVICEMODE BIT(31) +#define DWC3_OEVT_XHCIRUNSTPSET BIT(27) +#define DWC3_OEVT_DEVRUNSTPSET BIT(26) +#define DWC3_OEVT_HIBENTRY BIT(25) +#define DWC3_OEVT_CONIDSTSCHNG BIT(24) +#define DWC3_OEVT_HRRCONFNOTIF BIT(23) +#define DWC3_OEVT_HRRINITNOTIF BIT(22) +#define DWC3_OEVT_ADEVIDLE BIT(21) +#define DWC3_OEVT_ADEVBHOSTEND BIT(20) +#define DWC3_OEVT_ADEVHOST BIT(19) +#define DWC3_OEVT_ADEVHNPCHNG BIT(18) +#define DWC3_OEVT_ADEVSRPDET BIT(17) +#define DWC3_OEVT_ADEVSESSENDDET BIT(16) +#define DWC3_OEVT_BDEVBHOSTEND BIT(11) +#define DWC3_OEVT_BDEVHNPCHNG BIT(10) +#define DWC3_OEVT_BDEVSESSVLDDET BIT(9) +#define DWC3_OEVT_BDEVVBUSCHNG BIT(8) +#define DWC3_OEVT_BSESSVLD BIT(3) +#define DWC3_OEVT_HSTNEGSTS BIT(2) +#define DWC3_OEVT_SESREQSTS BIT(1) +#define DWC3_OEVT_ERROR BIT(0) + +/* OTG Event Enable Register */ +#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27) +#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26) +#define DWC3_OEVTEN_HIBENTRYEN BIT(25) +#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24) +#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23) +#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22) +#define DWC3_OEVTEN_ADEVIDLEEN BIT(21) +#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20) +#define DWC3_OEVTEN_ADEVHOSTEN BIT(19) +#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18) +#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17) +#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16) +#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11) +#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10) +#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9) +#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8) + +/* OTG Status Register */ +#define DWC3_OSTS_DEVRUNSTP BIT(13) +#define DWC3_OSTS_XHCIRUNSTP BIT(12) +#define DWC3_OSTS_PERIPHERALSTATE BIT(4) +#define DWC3_OSTS_XHCIPRTPOWER BIT(3) +#define DWC3_OSTS_BSESVLD BIT(2) +#define DWC3_OSTS_VBUSVLD BIT(1) +#define DWC3_OSTS_CONIDSTS BIT(0) + +/* Structures */ + +struct dwc3_trb; + +#define DWC3_EP_FLAG_STALLED BIT(0) +#define DWC3_EP_FLAG_WEDGED BIT(1) + +#define DWC3_EP_DIRECTION_TX true +#define DWC3_EP_DIRECTION_RX false + +#define DWC3_TRB_NUM 256 + +enum dwc3_phy { + DWC3_PHY_UNKNOWN = 0, + DWC3_PHY_USB3, + DWC3_PHY_USB2, +}; + +enum dwc3_ep0_next { + DWC3_EP0_UNKNOWN = 0, + DWC3_EP0_COMPLETE, + DWC3_EP0_NRDY_DATA, + DWC3_EP0_NRDY_STATUS, +}; + +enum dwc3_ep0_state { + EP0_UNCONNECTED = 0, + EP0_SETUP_PHASE, + EP0_DATA_PHASE, + EP0_STATUS_PHASE, +}; + +enum dwc3_link_state { + /* In SuperSpeed */ + DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ + DWC3_LINK_STATE_U1 = 0x01, + DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ + DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ + DWC3_LINK_STATE_SS_DIS = 0x04, + DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ + DWC3_LINK_STATE_SS_INACT = 0x06, + DWC3_LINK_STATE_POLL = 0x07, + DWC3_LINK_STATE_RECOV = 0x08, + DWC3_LINK_STATE_HRESET = 0x09, + DWC3_LINK_STATE_CMPLY = 0x0a, + DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_RESET = 0x0e, + DWC3_LINK_STATE_RESUME = 0x0f, + DWC3_LINK_STATE_MASK = 0x0f, +}; + +/* TRB Length, PCM and Status */ +#define DWC3_TRB_SIZE_MASK (0x00ffffff) +#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) +#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24) +#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28) + +#define DWC3_TRBSTS_OK 0 +#define DWC3_TRBSTS_MISSED_ISOC 1 +#define DWC3_TRBSTS_SETUP_PENDING 2 +#define DWC3_TRB_STS_XFER_IN_PROG 4 + +/* TRB Control */ +#define DWC3_TRB_CTRL_HWO BIT(0) +#define DWC3_TRB_CTRL_LST BIT(1) +#define DWC3_TRB_CTRL_CHN BIT(2) +#define DWC3_TRB_CTRL_CSP BIT(3) +#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4) +#define DWC3_TRB_CTRL_ISP_IMI BIT(10) +#define DWC3_TRB_CTRL_IOC BIT(11) +#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14) +#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14) + +#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4)) +#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1) +#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2) +#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3) +#define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4) +#define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5) +#define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6) +#define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7) +#define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8) + +/** + * struct dwc3_trb - transfer request block (hw format) + * @bpl: DW0-3 + * @bph: DW4-7 + * @size: DW8-B + * @ctrl: DWC-F + */ +struct dwc3_trb { + u32 bpl; + u32 bph; + u32 size; + u32 ctrl; +} __packed; + +/** + * struct dwc3_hwparams - copy of HWPARAMS registers + * @hwparams0: GHWPARAMS0 + * @hwparams1: GHWPARAMS1 + * @hwparams2: GHWPARAMS2 + * @hwparams3: GHWPARAMS3 + * @hwparams4: GHWPARAMS4 + * @hwparams5: GHWPARAMS5 + * @hwparams6: GHWPARAMS6 + * @hwparams7: GHWPARAMS7 + * @hwparams8: GHWPARAMS8 + */ +struct dwc3_hwparams { + u32 hwparams0; + u32 hwparams1; + u32 hwparams2; + u32 hwparams3; + u32 hwparams4; + u32 hwparams5; + u32 hwparams6; + u32 hwparams7; + u32 hwparams8; +}; + +/* HWPARAMS0 */ +#define DWC3_MODE(n) ((n) & 0x7) + +#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) + +/* HWPARAMS1 */ +#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) + +/* HWPARAMS3 */ +#define DWC3_NUM_IN_EPS_MASK (0x1f << 18) +#define DWC3_NUM_EPS_MASK (0x3f << 12) +#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_EPS_MASK)) >> 12) +#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_IN_EPS_MASK)) >> 18) + +/* HWPARAMS7 */ +#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) + +/** + * struct dwc3 - representation of our controller + * @drd_work: workqueue used for role swapping + * @ep0_trb: trb which is used for the ctrl_req + * @bounce: address of bounce buffer + * @scratchbuf: address of scratch buffer + * @setup_buf: used while precessing STD USB requests + * @ep0_trb_addr: dma address of @ep0_trb + * @bounce_addr: dma address of @bounce + * @ep0_usb_req: dummy req used while handling STD USB requests + * @scratch_addr: dma address of scratchbuf + * @ep0_in_setup: one control transfer is completed and enter setup phase + * @lock: for synchronizing + * @dev: pointer to our struct device + * @sysdev: pointer to the DMA-capable device + * @xhci: pointer to our xHCI child + * @xhci_resources: struct resources for our @xhci child + * @ev_buf: struct dwc3_event_buffer pointer + * @eps: endpoint array + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @clks: array of clocks + * @num_clks: number of clocks + * @reset: reset control + * @regs: base address for our registers + * @regs_size: address space size + * @fladj: frame length adjustment + * @irq_gadget: peripheral controller's IRQ number + * @otg_irq: IRQ number for OTG IRQs + * @current_otg_role: current role of operation while using the OTG block + * @desired_otg_role: desired role of operation while using the OTG block + * @otg_restart_host: flag that OTG controller needs to restart host + * @nr_scratch: number of scratch buffers + * @u1u2: only used on revisions <1.83a for workaround + * @maximum_speed: maximum speed requested (mainly for testing purposes) + * @revision: revision register contents + * @version_type: VERSIONTYPE register contents, a sub release of a revision + * @dr_mode: requested mode of operation + * @current_dr_role: current role of operation when in dual-role mode + * @desired_dr_role: desired role of operation when in dual-role mode + * @edev: extcon handle + * @edev_nb: extcon notifier + * @hsphy_mode: UTMI phy mode, one of following: + * - USBPHY_INTERFACE_MODE_UTMI + * - USBPHY_INTERFACE_MODE_UTMIW + * @usb2_phy: pointer to USB2 PHY + * @usb3_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to USB2 PHY + * @usb3_generic_phy: pointer to USB3 PHY + * @phys_ready: flag to indicate that PHYs are ready + * @ulpi: pointer to ulpi interface + * @ulpi_ready: flag to indicate that ULPI is initialized + * @u2sel: parameter from Set SEL request. + * @u2pel: parameter from Set SEL request. + * @u1sel: parameter from Set SEL request. + * @u1pel: parameter from Set SEL request. + * @num_eps: number of endpoints + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state + * @speed: device speed (super, high, full, low) + * @hwparams: copy of hwparams registers + * @root: debugfs root folder pointer + * @regset: debugfs pointer to regdump file + * @dbg_lsp_select: current debug lsp mux register selection + * @test_mode: true when we're entering a USB test mode + * @test_mode_nr: test feature selector + * @lpm_nyet_threshold: LPM NYET response threshold + * @hird_threshold: HIRD threshold + * @rx_thr_num_pkt_prd: periodic ESS receive packet count + * @rx_max_burst_prd: max periodic ESS receive burst size + * @tx_thr_num_pkt_prd: periodic ESS transmit packet count + * @tx_max_burst_prd: max periodic ESS transmit burst size + * @hsphy_interface: "utmi" or "ulpi" + * @connected: true when we're connected to a host, false otherwise + * @delayed_status: true when gadget driver asks for delayed status + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @has_hibernation: true when dwc3 was configured with Hibernation + * @sysdev_is_parent: true when dwc3 device has a parent driver + * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that + * there's now way for software to detect this in runtime. + * @is_utmi_l1_suspend: the core asserts output signal + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n + * @is_fpga: true when we are using the FPGA board + * @pending_events: true when we have pending IRQs to be handled + * @pullups_connected: true when Run/Stop bit is set + * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @three_stage_setup: set if we perform a three phase setup + * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is + * not needed for DWC_usb31 version 1.70a-ea06 and below + * @usb3_lpm_capable: set if hadrware supports Link Power Management + * @usb2_lpm_disable: set to disable usb2 lpm + * @disable_scramble_quirk: set if we enable the disable scramble quirk + * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk + * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk + * @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk + * @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk + * @del_phy_power_chg_quirk: set if we enable delay phy power change quirk + * @lfps_filter_quirk: set if we enable LFPS filter quirk + * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk + * @dis_u3_susphy_quirk: set if we disable usb3 suspend phy + * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy + * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, + * disabling the suspend signal to the PHY. + * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3 + * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists + * in GUSB2PHYCFG, specify that USB2 PHY doesn't + * provide a free-running PHY clock. + * @dis_del_phy_power_chg_quirk: set if we disable delay phy power + * change quirk. + * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate + * check during HS transmit. + * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk + * @tx_de_emphasis: Tx de-emphasis value + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved + * @dis_metastability_quirk: set to disable metastability quirk. + * @imod_interval: set the interrupt moderation interval in 250ns + * increments or 0 to disable. + */ +struct dwc3 { + struct device_d *dev; + struct device_d *xhci; + + struct clk_bulk_data *clks; + int num_clks; + + struct phy *usb2_generic_phy; + struct phy *usb3_generic_phy; + + bool phys_ready; + + void __iomem *regs; + + enum usb_dr_mode dr_mode; + u32 current_dr_role; + u32 desired_dr_role; + + u32 fladj; + u32 irq_gadget; + u32 otg_irq; + u32 current_otg_role; + u32 desired_otg_role; + bool otg_restart_host; + u32 nr_scratch; + u32 u1u2; + u32 maximum_speed; + + /* + * All 3.1 IP version constants are greater than the 3.0 IP + * version constants. This works for most version checks in + * dwc3. However, in the future, this may not apply as + * features may be developed on newer versions of the 3.0 IP + * that are not in the 3.1 IP. + */ + u32 revision; + +#define DWC3_REVISION_173A 0x5533173a +#define DWC3_REVISION_175A 0x5533175a +#define DWC3_REVISION_180A 0x5533180a +#define DWC3_REVISION_183A 0x5533183a +#define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_187A 0x5533187a +#define DWC3_REVISION_188A 0x5533188a +#define DWC3_REVISION_190A 0x5533190a +#define DWC3_REVISION_194A 0x5533194a +#define DWC3_REVISION_200A 0x5533200a +#define DWC3_REVISION_202A 0x5533202a +#define DWC3_REVISION_210A 0x5533210a +#define DWC3_REVISION_220A 0x5533220a +#define DWC3_REVISION_230A 0x5533230a +#define DWC3_REVISION_240A 0x5533240a +#define DWC3_REVISION_250A 0x5533250a +#define DWC3_REVISION_260A 0x5533260a +#define DWC3_REVISION_270A 0x5533270a +#define DWC3_REVISION_280A 0x5533280a +#define DWC3_REVISION_290A 0x5533290a +#define DWC3_REVISION_300A 0x5533300a +#define DWC3_REVISION_310A 0x5533310a +#define DWC3_REVISION_330A 0x5533330a + +/* + * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really + * just so dwc31 revisions are always larger than dwc3. + */ +#define DWC3_REVISION_IS_DWC31 0x80000000 +#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31) + + u32 version_type; + +#define DWC31_VERSIONTYPE_EA01 0x65613031 +#define DWC31_VERSIONTYPE_EA02 0x65613032 +#define DWC31_VERSIONTYPE_EA03 0x65613033 +#define DWC31_VERSIONTYPE_EA04 0x65613034 +#define DWC31_VERSIONTYPE_EA05 0x65613035 +#define DWC31_VERSIONTYPE_EA06 0x65613036 + + u16 u2sel; + u16 u2pel; + u8 u1sel; + u8 u1pel; + + u8 speed; + + u8 num_eps; + + struct dwc3_hwparams hwparams; + + u32 dbg_lsp_select; + + u8 test_mode; + u8 test_mode_nr; + u8 lpm_nyet_threshold; + u8 hird_threshold; + u8 rx_thr_num_pkt_prd; + u8 rx_max_burst_prd; + u8 tx_thr_num_pkt_prd; + u8 tx_max_burst_prd; + + const char *hsphy_interface; + + unsigned connected:1; + unsigned delayed_status:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned has_hibernation:1; + unsigned sysdev_is_parent:1; + unsigned has_lpm_erratum:1; + unsigned is_utmi_l1_suspend:1; + unsigned is_fpga:1; + unsigned pending_events:1; + unsigned pullups_connected:1; + unsigned setup_packet_pending:1; + unsigned three_stage_setup:1; + unsigned dis_start_transfer_quirk:1; + unsigned usb3_lpm_capable:1; + unsigned usb2_lpm_disable:1; + + unsigned disable_scramble_quirk:1; + unsigned u2exit_lfps_quirk:1; + unsigned u2ss_inp3_quirk:1; + unsigned req_p1p2p3_quirk:1; + unsigned del_p1p2p3_quirk:1; + unsigned del_phy_power_chg_quirk:1; + unsigned lfps_filter_quirk:1; + unsigned rx_detect_poll_quirk:1; + unsigned dis_u3_susphy_quirk:1; + unsigned dis_u2_susphy_quirk:1; + unsigned dis_enblslpm_quirk:1; + unsigned dis_rxdet_inp3_quirk:1; + unsigned dis_u2_freeclk_exists_quirk:1; + unsigned dis_del_phy_power_chg_quirk:1; + unsigned dis_tx_ipgap_linecheck_quirk:1; + + unsigned tx_de_emphasis_quirk:1; + unsigned tx_de_emphasis:2; + + unsigned dis_metastability_quirk:1; + + u16 imod_interval; +}; + +#define INCRX_BURST_MODE 0 +#define INCRX_UNDEF_LENGTH_BURST_MODE 1 + +#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work)) + +/* -------------------------------------------------------------------------- */ + +struct dwc3_event_type { + u32 is_devspec:1; + u32 type:7; + u32 reserved8_31:24; +} __packed; + +#define DWC3_DEPEVT_XFERCOMPLETE 0x01 +#define DWC3_DEPEVT_XFERINPROGRESS 0x02 +#define DWC3_DEPEVT_XFERNOTREADY 0x03 +#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 +#define DWC3_DEPEVT_STREAMEVT 0x06 +#define DWC3_DEPEVT_EPCMDCMPLT 0x07 + +/** + * struct dwc3_event_depvt - Device Endpoint Events + * @one_bit: indicates this is an endpoint event (not used) + * @endpoint_number: number of the endpoint + * @endpoint_event: The event we have: + * 0x00 - Reserved + * 0x01 - XferComplete + * 0x02 - XferInProgress + * 0x03 - XferNotReady + * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) + * 0x05 - Reserved + * 0x06 - StreamEvt + * 0x07 - EPCmdCmplt + * @reserved11_10: Reserved, don't use. + * @status: Indicates the status of the event. Refer to databook for + * more information. + * @parameters: Parameters of the current event. Refer to databook for + * more information. + */ +struct dwc3_event_depevt { + u32 one_bit:1; + u32 endpoint_number:5; + u32 endpoint_event:4; + u32 reserved11_10:2; + u32 status:4; + +/* Within XferNotReady */ +#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3) + +/* Within XferComplete or XferInProgress */ +#define DEPEVT_STATUS_BUSERR BIT(0) +#define DEPEVT_STATUS_SHORT BIT(1) +#define DEPEVT_STATUS_IOC BIT(2) +#define DEPEVT_STATUS_LST BIT(3) /* XferComplete */ +#define DEPEVT_STATUS_MISSED_ISOC BIT(3) /* XferInProgress */ + +/* Stream event only */ +#define DEPEVT_STREAMEVT_FOUND 1 +#define DEPEVT_STREAMEVT_NOTFOUND 2 + +/* Control-only Status */ +#define DEPEVT_STATUS_CONTROL_DATA 1 +#define DEPEVT_STATUS_CONTROL_STATUS 2 +#define DEPEVT_STATUS_CONTROL_PHASE(n) ((n) & 3) + +/* In response to Start Transfer */ +#define DEPEVT_TRANSFER_NO_RESOURCE 1 +#define DEPEVT_TRANSFER_BUS_EXPIRY 2 + + u32 parameters:16; + +/* For Command Complete Events */ +#define DEPEVT_PARAMETER_CMD(n) (((n) & (0xf << 8)) >> 8) +} __packed; + +/** + * struct dwc3_event_devt - Device Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's a device event. Should read as 0x00 + * @type: indicates the type of device event. + * 0 - DisconnEvt + * 1 - USBRst + * 2 - ConnectDone + * 3 - ULStChng + * 4 - WkUpEvt + * 5 - Reserved + * 6 - EOPF + * 7 - SOF + * 8 - Reserved + * 9 - ErrticErr + * 10 - CmdCmplt + * 11 - EvntOverflow + * 12 - VndrDevTstRcved + * @reserved15_12: Reserved, not used + * @event_info: Information about this event + * @reserved31_25: Reserved, not used + */ +struct dwc3_event_devt { + u32 one_bit:1; + u32 device_event:7; + u32 type:4; + u32 reserved15_12:4; + u32 event_info:9; + u32 reserved31_25:7; +} __packed; + +/** + * struct dwc3_event_gevt - Other Core Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. + * @phy_port_number: self-explanatory + * @reserved31_12: Reserved, not used. + */ +struct dwc3_event_gevt { + u32 one_bit:1; + u32 device_event:7; + u32 phy_port_number:4; + u32 reserved31_12:20; +} __packed; + +/** + * union dwc3_event - representation of Event Buffer contents + * @raw: raw 32-bit event + * @type: the type of the event + * @depevt: Device Endpoint Event + * @devt: Device Event + * @gevt: Global Event + */ +union dwc3_event { + u32 raw; + struct dwc3_event_type type; + struct dwc3_event_depevt depevt; + struct dwc3_event_devt devt; + struct dwc3_event_gevt gevt; +}; + +/** + * struct dwc3_gadget_ep_cmd_params - representation of endpoint command + * parameters + * @param2: third parameter + * @param1: second parameter + * @param0: first parameter + */ +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + +/* + * DWC3 Features to be used as Driver Data + */ + +#define DWC3_HAS_PERIPHERAL BIT(0) +#define DWC3_HAS_XHCI BIT(1) +#define DWC3_HAS_OTG BIT(3) + +/* prototypes */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); +void dwc3_set_mode(struct dwc3 *dwc, u32 mode); + +/* check whether we are on the DWC_usb3 core */ +static inline bool dwc3_is_usb3(struct dwc3 *dwc) +{ + return !(dwc->revision & DWC3_REVISION_IS_DWC31); +} + +/* check whether we are on the DWC_usb31 core */ +static inline bool dwc3_is_usb31(struct dwc3 *dwc) +{ + return !!(dwc->revision & DWC3_REVISION_IS_DWC31); +} + +bool dwc3_has_imod(struct dwc3 *dwc); + +int dwc3_event_buffers_setup(struct dwc3 *dwc); +void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + +#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_host_init(struct dwc3 *dwc); +#else +static inline int dwc3_host_init(struct dwc3 *dwc) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, + struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) +{ } +static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ return 0; } +static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ return 0; } +static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, + enum dwc3_link_state state) +{ return 0; } + +static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, + int cmd, u32 param) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_drd_init(struct dwc3 *dwc); +void dwc3_drd_exit(struct dwc3 *dwc); +void dwc3_otg_init(struct dwc3 *dwc); +void dwc3_otg_exit(struct dwc3 *dwc); +void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus); +void dwc3_otg_host_init(struct dwc3 *dwc); +#else +static inline int dwc3_drd_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_drd_exit(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_init(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_exit(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) +{ } +static inline void dwc3_otg_host_init(struct dwc3 *dwc) +{ } +#endif + +/* power management interface */ +#if !IS_ENABLED(CONFIG_USB_DWC3_HOST) +int dwc3_gadget_suspend(struct dwc3 *dwc); +int dwc3_gadget_resume(struct dwc3 *dwc); +void dwc3_gadget_process_pending_events(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_suspend(struct dwc3 *dwc) +{ + return 0; +} + +static inline int dwc3_gadget_resume(struct dwc3 *dwc) +{ + return 0; +} + +static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc) +{ +} +#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ + +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI) +int dwc3_ulpi_init(struct dwc3 *dwc); +void dwc3_ulpi_exit(struct dwc3 *dwc); +#else +static inline int dwc3_ulpi_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_ulpi_exit(struct dwc3 *dwc) +{ } +#endif + +#endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h new file mode 100644 index 0000000000..4f75ab3505 --- /dev/null +++ b/drivers/usb/dwc3/debug.h @@ -0,0 +1,664 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * debug.h - DesignWare USB3 DRD Controller Debug Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#ifndef __DWC3_DEBUG_H +#define __DWC3_DEBUG_H + +#include "core.h" + +/** + * dwc3_gadget_ep_cmd_string - returns endpoint command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +/** + * dwc3_gadget_generic_cmd_string - returns generic command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_generic_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DGCMD_SET_LMP: + return "Set LMP"; + case DWC3_DGCMD_SET_PERIODIC_PAR: + return "Set Periodic Parameters"; + case DWC3_DGCMD_XMIT_FUNCTION: + return "Transmit Function Wake Device Notification"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: + return "Set Scratchpad Buffer Array Address Lo"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: + return "Set Scratchpad Buffer Array Address Hi"; + case DWC3_DGCMD_SELECTED_FIFO_FLUSH: + return "Selected FIFO Flush"; + case DWC3_DGCMD_ALL_FIFO_FLUSH: + return "All FIFO Flush"; + case DWC3_DGCMD_SET_ENDPOINT_NRDY: + return "Set Endpoint NRDY"; + case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: + return "Run SoC Bus Loopback Test"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_link_string - returns link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "U0"; + case DWC3_LINK_STATE_U1: + return "U1"; + case DWC3_LINK_STATE_U2: + return "U2"; + case DWC3_LINK_STATE_U3: + return "U3"; + case DWC3_LINK_STATE_SS_DIS: + return "SS.Disabled"; + case DWC3_LINK_STATE_RX_DET: + return "RX.Detect"; + case DWC3_LINK_STATE_SS_INACT: + return "SS.Inactive"; + case DWC3_LINK_STATE_POLL: + return "Polling"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_HRESET: + return "Hot Reset"; + case DWC3_LINK_STATE_CMPLY: + return "Compliance"; + case DWC3_LINK_STATE_LPBK: + return "Loopback"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_gadget_hs_link_string - returns highspeed and below link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_hs_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "On"; + case DWC3_LINK_STATE_U2: + return "Sleep"; + case DWC3_LINK_STATE_U3: + return "Suspend"; + case DWC3_LINK_STATE_SS_DIS: + return "Disconnected"; + case DWC3_LINK_STATE_RX_DET: + return "Early Suspend"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_trb_type_string - returns TRB type as a string + * @type: the type of the TRB + */ +static inline const char *dwc3_trb_type_string(unsigned int type) +{ + switch (type) { + case DWC3_TRBCTL_NORMAL: + return "normal"; + case DWC3_TRBCTL_CONTROL_SETUP: + return "setup"; + case DWC3_TRBCTL_CONTROL_STATUS2: + return "status2"; + case DWC3_TRBCTL_CONTROL_STATUS3: + return "status3"; + case DWC3_TRBCTL_CONTROL_DATA: + return "data"; + case DWC3_TRBCTL_ISOCHRONOUS_FIRST: + return "isoc-first"; + case DWC3_TRBCTL_ISOCHRONOUS: + return "isoc"; + case DWC3_TRBCTL_LINK_TRB: + return "link"; + default: + return "UNKNOWN"; + } +} + +static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char * +dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event) +{ + enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK; + + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + sprintf(str, "Disconnect: [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_RESET: + sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + sprintf(str, "Connection Done [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + sprintf(str, "Link Change [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_EOPF: + sprintf(str, "End-Of-Frame [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_SOF: + sprintf(str, "Start-Of-Frame [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + sprintf(str, "Erratic Error [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + sprintf(str, "Command Complete [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state)); + break; + default: + sprintf(str, "UNKNOWN"); + } + + return str; +} + +static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str) +{ + switch (t & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + sprintf(str, "Get Interface Status(Intf = %d, Length = %d)", + i, l); + break; + case USB_RECIP_ENDPOINT: + sprintf(str, "Get Endpoint Status(ep%d%s)", + i & ~USB_DIR_IN, + i & USB_DIR_IN ? "in" : "out"); + break; + } +} + +static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, + __u16 i, char *str) +{ + switch (t & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + sprintf(str, "%s Device Feature(%s%s)", + b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + ({char *s; + switch (v) { + case USB_DEVICE_SELF_POWERED: + s = "Self Powered"; + break; + case USB_DEVICE_REMOTE_WAKEUP: + s = "Remote Wakeup"; + break; + case USB_DEVICE_TEST_MODE: + s = "Test Mode"; + break; + case USB_DEVICE_U1_ENABLE: + s = "U1 Enable"; + break; + case USB_DEVICE_U2_ENABLE: + s = "U2 Enable"; + break; + case USB_DEVICE_LTM_ENABLE: + s = "LTM Enable"; + break; + default: + s = "UNKNOWN"; + } s; }), + v == USB_DEVICE_TEST_MODE ? + ({ char *s; + switch (i) { + case TEST_J: + s = ": TEST_J"; + break; + case TEST_K: + s = ": TEST_K"; + break; + case TEST_SE0_NAK: + s = ": TEST_SE0_NAK"; + break; + case TEST_PACKET: + s = ": TEST_PACKET"; + break; + case TEST_FORCE_EN: + s = ": TEST_FORCE_EN"; + break; + default: + s = ": UNKNOWN"; + } s; }) : ""); + break; + case USB_RECIP_INTERFACE: + sprintf(str, "%s Interface Feature(%s)", + b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + v == USB_INTRF_FUNC_SUSPEND ? + "Function Suspend" : "UNKNOWN"); + break; + case USB_RECIP_ENDPOINT: + sprintf(str, "%s Endpoint Feature(%s ep%d%s)", + b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN", + i & ~USB_DIR_IN, + i & USB_DIR_IN ? "in" : "out"); + break; + } +} + +static inline void dwc3_decode_set_address(__u16 v, char *str) +{ + sprintf(str, "Set Address(Addr = %02x)", v); +} + +static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v, + __u16 i, __u16 l, char *str) +{ + sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)", + b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", + ({ char *s; + switch (v >> 8) { + case USB_DT_DEVICE: + s = "Device"; + break; + case USB_DT_CONFIG: + s = "Configuration"; + break; + case USB_DT_STRING: + s = "String"; + break; + case USB_DT_INTERFACE: + s = "Interface"; + break; + case USB_DT_ENDPOINT: + s = "Endpoint"; + break; + case USB_DT_DEVICE_QUALIFIER: + s = "Device Qualifier"; + break; + case USB_DT_OTHER_SPEED_CONFIG: + s = "Other Speed Config"; + break; + case USB_DT_INTERFACE_POWER: + s = "Interface Power"; + break; + case USB_DT_OTG: + s = "OTG"; + break; + case USB_DT_DEBUG: + s = "Debug"; + break; + case USB_DT_INTERFACE_ASSOCIATION: + s = "Interface Association"; + break; + case USB_DT_BOS: + s = "BOS"; + break; + case USB_DT_DEVICE_CAPABILITY: + s = "Device Capability"; + break; + case USB_DT_PIPE_USAGE: + s = "Pipe Usage"; + break; + case USB_DT_SS_ENDPOINT_COMP: + s = "SS Endpoint Companion"; + break; + case USB_DT_SSP_ISOC_ENDPOINT_COMP: + s = "SSP Isochronous Endpoint Companion"; + break; + default: + s = "UNKNOWN"; + break; + } s; }), v & 0xff, l); +} + + +static inline void dwc3_decode_get_configuration(__u16 l, char *str) +{ + sprintf(str, "Get Configuration(Length = %d)", l); +} + +static inline void dwc3_decode_set_configuration(__u8 v, char *str) +{ + sprintf(str, "Set Configuration(Config = %d)", v); +} + +static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str) +{ + sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l); +} + +static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str) +{ + sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v); +} + +static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str) +{ + sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l); +} + +static inline void dwc3_decode_set_sel(__u16 l, char *str) +{ + sprintf(str, "Set SEL(Length = %d)", l); +} + +static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str) +{ + sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v); +} + +/** + * dwc3_decode_ctrl - returns a string represetion of ctrl request + */ +static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType, + __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength) +{ + switch (bRequest) { + case USB_REQ_GET_STATUS: + dwc3_decode_get_status(bRequestType, wIndex, wLength, str); + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue, + wIndex, str); + break; + case USB_REQ_SET_ADDRESS: + dwc3_decode_set_address(wValue, str); + break; + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_SET_DESCRIPTOR: + dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue, + wIndex, wLength, str); + break; + case USB_REQ_GET_CONFIGURATION: + dwc3_decode_get_configuration(wLength, str); + break; + case USB_REQ_SET_CONFIGURATION: + dwc3_decode_set_configuration(wValue, str); + break; + case USB_REQ_GET_INTERFACE: + dwc3_decode_get_intf(wIndex, wLength, str); + break; + case USB_REQ_SET_INTERFACE: + dwc3_decode_set_intf(wValue, wIndex, str); + break; + case USB_REQ_SYNCH_FRAME: + dwc3_decode_synch_frame(wIndex, wLength, str); + break; + case USB_REQ_SET_SEL: + dwc3_decode_set_sel(wLength, str); + break; + case USB_REQ_SET_ISOCH_DELAY: + dwc3_decode_set_isoch_delay(wValue, str); + break; + default: + sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x", + bRequestType, bRequest, + cpu_to_le16(wValue) & 0xff, + cpu_to_le16(wValue) >> 8, + cpu_to_le16(wIndex) & 0xff, + cpu_to_le16(wIndex) >> 8, + cpu_to_le16(wLength) & 0xff, + cpu_to_le16(wLength) >> 8); + } + + return str; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char * +dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, + u32 ep0state) +{ + u8 epnum = event->endpoint_number; + size_t len; + int status; + int ret; + + ret = sprintf(str, "ep%d%s: ", epnum >> 1, + (epnum & 1) ? "in" : "out"); + if (ret < 0) + return "UNKNOWN"; + + status = event->status; + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + len = strlen(str); + sprintf(str + len, "Transfer Complete (%c%c%c)", + status & DEPEVT_STATUS_SHORT ? 'S' : 's', + status & DEPEVT_STATUS_IOC ? 'I' : 'i', + status & DEPEVT_STATUS_LST ? 'L' : 'l'); + + len = strlen(str); + + if (epnum <= 1) + sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state)); + break; + case DWC3_DEPEVT_XFERINPROGRESS: + len = strlen(str); + + sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)", + event->parameters, + status & DEPEVT_STATUS_SHORT ? 'S' : 's', + status & DEPEVT_STATUS_IOC ? 'I' : 'i', + status & DEPEVT_STATUS_LST ? 'M' : 'm'); + break; + case DWC3_DEPEVT_XFERNOTREADY: + len = strlen(str); + + sprintf(str + len, "Transfer Not Ready [%d]%s", + event->parameters, + status & DEPEVT_STATUS_TRANSFER_ACTIVE ? + " (Active)" : " (Not Active)"); + + /* Control Endpoints */ + if (epnum <= 1) { + int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status); + + switch (phase) { + case DEPEVT_STATUS_CONTROL_DATA: + strcat(str, " [Data Phase]"); + break; + case DEPEVT_STATUS_CONTROL_STATUS: + strcat(str, " [Status Phase]"); + } + } + break; + case DWC3_DEPEVT_RXTXFIFOEVT: + strcat(str, "FIFO"); + break; + case DWC3_DEPEVT_STREAMEVT: + status = event->status; + + switch (status) { + case DEPEVT_STREAMEVT_FOUND: + sprintf(str + ret, " Stream %d Found", + event->parameters); + break; + case DEPEVT_STREAMEVT_NOTFOUND: + default: + strcat(str, " Stream Not Found"); + break; + } + + break; + case DWC3_DEPEVT_EPCMDCMPLT: + strcat(str, "Endpoint Command Complete"); + break; + default: + sprintf(str, "UNKNOWN"); + } + + return str; +} + +/** + * dwc3_gadget_event_type_string - return event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_type_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connect Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "Wake-Up"; + case DWC3_DEVICE_EVENT_HIBER_REQ: + return "Hibernation"; + case DWC3_DEVICE_EVENT_EOPF: + return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start of Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + default: + return "UNKNOWN"; + } +} + +static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state) +{ + const union dwc3_event evt = (union dwc3_event) event; + + if (evt.type.is_devspec) + return dwc3_gadget_event_string(str, &evt.devt); + else + return dwc3_ep_event_string(str, &evt.depevt, ep0state); +} + +static inline const char *dwc3_ep_cmd_status_string(int status) +{ + switch (status) { + case -ETIMEDOUT: + return "Timed Out"; + case 0: + return "Successful"; + case DEPEVT_TRANSFER_NO_RESOURCE: + return "No Resource"; + case DEPEVT_TRANSFER_BUS_EXPIRY: + return "Bus Expiry"; + default: + return "UNKNOWN"; + } +} + +static inline const char *dwc3_gadget_generic_cmd_status_string(int status) +{ + switch (status) { + case -ETIMEDOUT: + return "Timed Out"; + case 0: + return "Successful"; + case 1: + return "Error"; + default: + return "UNKNOWN"; + } +} + + +#ifdef CONFIG_DEBUG_FS +extern void dwc3_debugfs_init(struct dwc3 *); +extern void dwc3_debugfs_exit(struct dwc3 *); +#else +static inline void dwc3_debugfs_init(struct dwc3 *d) +{ } +static inline void dwc3_debugfs_exit(struct dwc3 *d) +{ } +#endif +#endif /* __DWC3_DEBUG_H */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c new file mode 100644 index 0000000000..d5daa7f19e --- /dev/null +++ b/drivers/usb/dwc3/host.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * host.c - DesignWare USB3 DRD Controller Host Glue + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + */ + +#include +#include +#include + +#include "core.h" + +int dwc3_host_init(struct dwc3 *dwc) +{ + struct resource *io; + struct device_d *dev = dwc->dev; + + io = dev_get_resource(dev, IORESOURCE_MEM, 0); + if (IS_ERR(io)) { + dev_err(dev, "Failed to get IORESOURCE_MEM\n"); + return PTR_ERR(io); + } + + dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL, + io->start, resource_size(io), + IORESOURCE_MEM, NULL); + if (!dwc->xhci) { + dev_err(dev, "Failed to register xHCI device\n"); + return -ENODEV; + } + + return 0; +} diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h new file mode 100644 index 0000000000..f87b173e90 --- /dev/null +++ b/drivers/usb/dwc3/io.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * io.h - DesignWare USB3 DRD IO Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#ifndef __DRIVERS_USB_DWC3_IO_H +#define __DRIVERS_USB_DWC3_IO_H + +#include +#include "core.h" + +static inline u32 dwc3_readl(void __iomem *base, u32 offset) +{ + u32 value; + + /* + * We requested the mem region starting from the Globals address + * space, see dwc3_probe in core.c. + * However, the offsets are given starting from xHCI address space. + */ + value = readl(base + offset - DWC3_GLOBALS_REGS_START); + + return value; +} + +static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + /* + * We requested the mem region starting from the Globals address + * space, see dwc3_probe in core.c. + * However, the offsets are given starting from xHCI address space. + */ + writel(value, base + offset - DWC3_GLOBALS_REGS_START); +} + +#endif /* __DRIVERS_USB_DWC3_IO_H */ -- cgit v1.2.3 From 9056a502470765edaff86eb17985972acc549ecd Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:28 -0800 Subject: lib: Port basic Linux kernel NLS functions Port basic Linux kernel NLS functions: utf8_to_utf32() and utf8s_to_utf16s() in order to support porting kernel code that uses them. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- include/linux/nls.h | 40 ++++++++++++++++ lib/Kconfig | 3 ++ lib/Makefile | 1 + lib/nls_base.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 include/linux/nls.h create mode 100644 lib/nls_base.c diff --git a/include/linux/nls.h b/include/linux/nls.h new file mode 100644 index 0000000000..62fb7b5a97 --- /dev/null +++ b/include/linux/nls.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_NLS_H +#define _LINUX_NLS_H + +/* Unicode has changed over the years. Unicode code points no longer + * fit into 16 bits; as of Unicode 5 valid code points range from 0 + * to 0x10ffff (17 planes, where each plane holds 65536 code points). + * + * The original decision to represent Unicode characters as 16-bit + * wchar_t values is now outdated. But plane 0 still includes the + * most commonly used characters, so we will retain it. The newer + * 32-bit unicode_t type can be used when it is necessary to + * represent the full Unicode character set. + */ + +/* Plane-0 Unicode character */ +typedef u16 wchar_t; +#define MAX_WCHAR_T 0xffff + +/* Arbitrary Unicode character */ +typedef u32 unicode_t; + +/* this value hold the maximum octet of charset */ +#define NLS_MAX_CHARSET_SIZE 6 /* for UTF-8 */ + +/* Byte order for UTF-16 strings */ +enum utf16_endian { + UTF16_HOST_ENDIAN, + UTF16_LITTLE_ENDIAN, + UTF16_BIG_ENDIAN +}; + +/* nls_base.c */ + +extern int utf8_to_utf32(const u8 *s, int len, unicode_t *pu); +extern int utf8s_to_utf16s(const u8 *s, int len, + enum utf16_endian endian, wchar_t *pwcs, int maxlen); + +#endif /* _LINUX_NLS_H */ + diff --git a/lib/Kconfig b/lib/Kconfig index e048aded8b..44f30d8242 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -148,4 +148,7 @@ config GENERIC_LIB_LSHRDI3 config GENERIC_LIB_MULDI3 bool +config NLS + bool "Native language support" + endmenu diff --git a/lib/Makefile b/lib/Makefile index e72aa6655c..763516d41a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,6 +67,7 @@ obj-y += parseopt.o obj-y += clz_ctz.o obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o obj-$(CONFIG_CRC8) += crc8.o +obj-$(CONFIG_NLS) += nls_base.o # GCC library routines obj-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o diff --git a/lib/nls_base.c b/lib/nls_base.c new file mode 100644 index 0000000000..cd6a5ff124 --- /dev/null +++ b/lib/nls_base.c @@ -0,0 +1,131 @@ +/* + * linux/fs/nls/nls_base.c + * + * Native language support--charsets and unicode translations. + * By Gordon Chaffee 1996, 1997 + * + * Unicode based case conversion 1999 by Wolfram Pienkoss + * + */ +#include +#include + +/* + * Sample implementation from Unicode home page. + * http://www.stonehand.com/unicode/standard/fss-utf.html + */ +struct utf8_table { + int cmask; + int cval; + int shift; + long lmask; + long lval; +}; + +static const struct utf8_table utf8_table[] = +{ + {0x80, 0x00, 0*6, 0x7F, 0, /* 1 byte sequence */}, + {0xE0, 0xC0, 1*6, 0x7FF, 0x80, /* 2 byte sequence */}, + {0xF0, 0xE0, 2*6, 0xFFFF, 0x800, /* 3 byte sequence */}, + {0xF8, 0xF0, 3*6, 0x1FFFFF, 0x10000, /* 4 byte sequence */}, + {0xFC, 0xF8, 4*6, 0x3FFFFFF, 0x200000, /* 5 byte sequence */}, + {0xFE, 0xFC, 5*6, 0x7FFFFFFF, 0x4000000, /* 6 byte sequence */}, + {0, /* end of table */} +}; + +#define UNICODE_MAX 0x0010ffff +#define PLANE_SIZE 0x00010000 + +#define SURROGATE_MASK 0xfffff800 +#define SURROGATE_PAIR 0x0000d800 +#define SURROGATE_LOW 0x00000400 +#define SURROGATE_BITS 0x000003ff + +int utf8_to_utf32(const u8 *s, int inlen, unicode_t *pu) +{ + unsigned long l; + int c0, c, nc; + const struct utf8_table *t; + + nc = 0; + c0 = *s; + l = c0; + for (t = utf8_table; t->cmask; t++) { + nc++; + if ((c0 & t->cmask) == t->cval) { + l &= t->lmask; + if (l < t->lval || l > UNICODE_MAX || + (l & SURROGATE_MASK) == SURROGATE_PAIR) + return -1; + *pu = (unicode_t) l; + return nc; + } + if (inlen <= nc) + return -1; + s++; + c = (*s ^ 0x80) & 0xFF; + if (c & 0xC0) + return -1; + l = (l << 6) | c; + } + return -1; +} +EXPORT_SYMBOL(utf8_to_utf32); + +static inline void put_utf16(wchar_t *s, unsigned c, enum utf16_endian endian) +{ + switch (endian) { + default: + *s = (wchar_t) c; + break; + case UTF16_LITTLE_ENDIAN: + *s = __cpu_to_le16(c); + break; + case UTF16_BIG_ENDIAN: + *s = __cpu_to_be16(c); + break; + } +} + +int utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian, + wchar_t *pwcs, int maxout) +{ + u16 *op; + int size; + unicode_t u; + + op = pwcs; + while (inlen > 0 && maxout > 0 && *s) { + if (*s & 0x80) { + size = utf8_to_utf32(s, inlen, &u); + if (size < 0) + return -EINVAL; + s += size; + inlen -= size; + + if (u >= PLANE_SIZE) { + if (maxout < 2) + break; + u -= PLANE_SIZE; + put_utf16(op++, SURROGATE_PAIR | + ((u >> 10) & SURROGATE_BITS), + endian); + put_utf16(op++, SURROGATE_PAIR | + SURROGATE_LOW | + (u & SURROGATE_BITS), + endian); + maxout -= 2; + } else { + put_utf16(op++, u, endian); + maxout--; + } + } else { + put_utf16(op++, *s++, endian); + inlen--; + maxout--; + } + } + return op - pwcs; +} +EXPORT_SYMBOL(utf8s_to_utf16s); + -- cgit v1.2.3 From 937fa50d9c2a693a7cdc3780d1398ef628fe5e03 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:29 -0800 Subject: usb: Port Microchip USB251x USB hub driver from Linux Port Microchip USB251x USB hub driver from Linux. Needed by ZII's i.MX8MQ boards. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 1 + drivers/usb/misc/Kconfig | 14 + drivers/usb/misc/Makefile | 6 + drivers/usb/misc/usb251xb.c | 672 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 695 insertions(+) create mode 100644 drivers/usb/misc/Kconfig create mode 100644 drivers/usb/misc/Makefile create mode 100644 drivers/usb/misc/usb251xb.c diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index ee33e0f28c..8ff3d18d4a 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -17,6 +17,8 @@ source drivers/usb/otg/Kconfig source drivers/usb/storage/Kconfig +source "drivers/usb/misc/Kconfig" + endif source drivers/usb/gadget/Kconfig diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 13fca2cbdf..9e98099502 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_STORAGE) += storage/ obj-y += host/ obj-y += otg/ +obj-$(CONFIG_USB) += misc/ diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig new file mode 100644 index 0000000000..7d6c9da594 --- /dev/null +++ b/drivers/usb/misc/Kconfig @@ -0,0 +1,14 @@ +# +# USB Miscellaneous driver configuration +# +comment "USB Miscellaneous drivers" + +config USB_HUB_USB251XB + bool "USB251XB Hub Controller Configuration Driver" + depends on I2C + select NLS + help + This option enables support for configuration via SMBus of the + Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration + parameters may be set in devicetree or platform data. + Say Y or M here if you need to configure such a device via SMBus. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile new file mode 100644 index 0000000000..fb69c454bd --- /dev/null +++ b/drivers/usb/misc/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# +obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c new file mode 100644 index 0000000000..0b721f6d47 --- /dev/null +++ b/drivers/usb/misc/usb251xb.c @@ -0,0 +1,672 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller + * Configuration via SMBus. + * + * Copyright (c) 2017 SKIDATA AG + * + * This work is based on the USB3503 driver by Dongjin Kim and + * a not-accepted patch by Fabien Lahoudere, see: + * https://patchwork.kernel.org/patch/9257715/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Internal Register Set Addresses & Default Values acc. to DS00001692C */ +#define USB251XB_ADDR_VENDOR_ID_LSB 0x00 +#define USB251XB_ADDR_VENDOR_ID_MSB 0x01 +#define USB251XB_DEF_VENDOR_ID 0x0424 + +#define USB251XB_ADDR_PRODUCT_ID_LSB 0x02 +#define USB251XB_ADDR_PRODUCT_ID_MSB 0x03 +#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */ +#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */ +#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */ +#define USB251XB_DEF_PRODUCT_ID_17 0x2517 /* USB2517/17i */ + +#define USB251XB_ADDR_DEVICE_ID_LSB 0x04 +#define USB251XB_ADDR_DEVICE_ID_MSB 0x05 +#define USB251XB_DEF_DEVICE_ID 0x0BB3 + +#define USB251XB_ADDR_CONFIG_DATA_1 0x06 +#define USB251XB_DEF_CONFIG_DATA_1 0x9B +#define USB251XB_ADDR_CONFIG_DATA_2 0x07 +#define USB251XB_DEF_CONFIG_DATA_2 0x20 +#define USB251XB_ADDR_CONFIG_DATA_3 0x08 +#define USB251XB_DEF_CONFIG_DATA_3 0x02 + +#define USB251XB_ADDR_NON_REMOVABLE_DEVICES 0x09 +#define USB251XB_DEF_NON_REMOVABLE_DEVICES 0x00 + +#define USB251XB_ADDR_PORT_DISABLE_SELF 0x0A +#define USB251XB_DEF_PORT_DISABLE_SELF 0x00 +#define USB251XB_ADDR_PORT_DISABLE_BUS 0x0B +#define USB251XB_DEF_PORT_DISABLE_BUS 0x00 + +#define USB251XB_ADDR_MAX_POWER_SELF 0x0C +#define USB251XB_DEF_MAX_POWER_SELF 0x01 +#define USB251XB_ADDR_MAX_POWER_BUS 0x0D +#define USB251XB_DEF_MAX_POWER_BUS 0x32 + +#define USB251XB_ADDR_MAX_CURRENT_SELF 0x0E +#define USB251XB_DEF_MAX_CURRENT_SELF 0x01 +#define USB251XB_ADDR_MAX_CURRENT_BUS 0x0F +#define USB251XB_DEF_MAX_CURRENT_BUS 0x32 + +#define USB251XB_ADDR_POWER_ON_TIME 0x10 +#define USB251XB_DEF_POWER_ON_TIME 0x32 + +#define USB251XB_ADDR_LANGUAGE_ID_HIGH 0x11 +#define USB251XB_ADDR_LANGUAGE_ID_LOW 0x12 +#define USB251XB_DEF_LANGUAGE_ID 0x0000 + +#define USB251XB_STRING_BUFSIZE 62 +#define USB251XB_ADDR_MANUFACTURER_STRING_LEN 0x13 +#define USB251XB_ADDR_MANUFACTURER_STRING 0x16 +#define USB251XB_DEF_MANUFACTURER_STRING "Microchip" + +#define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14 +#define USB251XB_ADDR_PRODUCT_STRING 0x54 +#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi/7i" + +#define USB251XB_ADDR_SERIAL_STRING_LEN 0x15 +#define USB251XB_ADDR_SERIAL_STRING 0x92 +#define USB251XB_DEF_SERIAL_STRING "" + +#define USB251XB_ADDR_BATTERY_CHARGING_ENABLE 0xD0 +#define USB251XB_DEF_BATTERY_CHARGING_ENABLE 0x00 + +#define USB251XB_ADDR_BOOST_UP 0xF6 +#define USB251XB_DEF_BOOST_UP 0x00 +#define USB251XB_ADDR_BOOST_57 0xF7 +#define USB251XB_DEF_BOOST_57 0x00 +#define USB251XB_ADDR_BOOST_14 0xF8 +#define USB251XB_DEF_BOOST_14 0x00 + +#define USB251XB_ADDR_PORT_SWAP 0xFA +#define USB251XB_DEF_PORT_SWAP 0x00 + +#define USB251XB_ADDR_PORT_MAP_12 0xFB +#define USB251XB_DEF_PORT_MAP_12 0x00 +#define USB251XB_ADDR_PORT_MAP_34 0xFC +#define USB251XB_DEF_PORT_MAP_34 0x00 /* USB251{3B/i,4B/i,7/i} only */ +#define USB251XB_ADDR_PORT_MAP_56 0xFD +#define USB251XB_DEF_PORT_MAP_56 0x00 /* USB2517/i only */ +#define USB251XB_ADDR_PORT_MAP_7 0xFE +#define USB251XB_DEF_PORT_MAP_7 0x00 /* USB2517/i only */ + +#define USB251XB_ADDR_STATUS_COMMAND 0xFF +#define USB251XB_STATUS_COMMAND_SMBUS_DOWN 0x04 +#define USB251XB_STATUS_COMMAND_RESET 0x02 +#define USB251XB_STATUS_COMMAND_ATTACH 0x01 + +#define USB251XB_I2C_REG_SZ 0x100 +#define USB251XB_I2C_WRITE_SZ 0x10 + +#define DRIVER_NAME "usb251xb" +#define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller" + +struct usb251xb { + struct device_d *dev; + struct i2c_client *i2c; + u8 skip_config; + int gpio_reset; + u16 vendor_id; + u16 product_id; + u16 device_id; + u8 conf_data1; + u8 conf_data2; + u8 conf_data3; + u8 non_rem_dev; + u8 port_disable_sp; + u8 port_disable_bp; + u8 max_power_sp; + u8 max_power_bp; + u8 max_current_sp; + u8 max_current_bp; + u8 power_on_time; + u16 lang_id; + u8 manufacturer_len; + u8 product_len; + u8 serial_len; + char manufacturer[USB251XB_STRING_BUFSIZE]; + char product[USB251XB_STRING_BUFSIZE]; + char serial[USB251XB_STRING_BUFSIZE]; + u8 bat_charge_en; + u8 boost_up; + u8 boost_57; + u8 boost_14; + u8 port_swap; + u8 port_map12; + u8 port_map34; + u8 port_map56; + u8 port_map7; + u8 status; +}; + +struct usb251xb_data { + u16 product_id; + u8 port_cnt; + bool led_support; + bool bat_support; + char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */ +}; + +static const struct usb251xb_data usb2512b_data = { + .product_id = 0x2512, + .port_cnt = 2, + .led_support = false, + .bat_support = true, + .product_str = "USB2512B", +}; + +static const struct usb251xb_data usb2512bi_data = { + .product_id = 0x2512, + .port_cnt = 2, + .led_support = false, + .bat_support = true, + .product_str = "USB2512Bi", +}; + +static const struct usb251xb_data usb2513b_data = { + .product_id = 0x2513, + .port_cnt = 3, + .led_support = false, + .bat_support = true, + .product_str = "USB2513B", +}; + +static const struct usb251xb_data usb2513bi_data = { + .product_id = 0x2513, + .port_cnt = 3, + .led_support = false, + .bat_support = true, + .product_str = "USB2513Bi", +}; + +static const struct usb251xb_data usb2514b_data = { + .product_id = 0x2514, + .port_cnt = 4, + .led_support = false, + .bat_support = true, + .product_str = "USB2514B", +}; + +static const struct usb251xb_data usb2514bi_data = { + .product_id = 0x2514, + .port_cnt = 4, + .led_support = false, + .bat_support = true, + .product_str = "USB2514Bi", +}; + +static const struct usb251xb_data usb2517_data = { + .product_id = 0x2517, + .port_cnt = 7, + .led_support = true, + .bat_support = false, + .product_str = "USB2517", +}; + +static const struct usb251xb_data usb2517i_data = { + .product_id = 0x2517, + .port_cnt = 7, + .led_support = true, + .bat_support = false, + .product_str = "USB2517i", +}; + +static void usb251xb_reset(struct usb251xb *hub, int state) +{ + if (!gpio_is_valid(hub->gpio_reset)) + return; + + gpio_set_active(hub->gpio_reset, state); + + /* wait for hub recovery/stabilization */ + if (!state) + udelay(750); /* >=500us at power on */ + else + udelay(10); /* >=1us at power down */ +} + +static int usb251xb_connect(struct usb251xb *hub) +{ + struct device_d *dev = hub->dev; + int err, i; + char i2c_wb[USB251XB_I2C_REG_SZ]; + + memset(i2c_wb, 0, USB251XB_I2C_REG_SZ); + + if (hub->skip_config) { + dev_info(dev, "Skip hub configuration, only attach.\n"); + i2c_wb[0] = 0x01; + i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH; + + usb251xb_reset(hub, 0); + + err = i2c_smbus_write_i2c_block_data(hub->i2c, + USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb); + if (err) { + dev_err(dev, "attaching hub failed: %d\n", err); + return err; + } + return 0; + } + + i2c_wb[USB251XB_ADDR_VENDOR_ID_MSB] = (hub->vendor_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_VENDOR_ID_LSB] = hub->vendor_id & 0xFF; + i2c_wb[USB251XB_ADDR_PRODUCT_ID_MSB] = (hub->product_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_PRODUCT_ID_LSB] = hub->product_id & 0xFF; + i2c_wb[USB251XB_ADDR_DEVICE_ID_MSB] = (hub->device_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_DEVICE_ID_LSB] = hub->device_id & 0xFF; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_1] = hub->conf_data1; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_2] = hub->conf_data2; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_3] = hub->conf_data3; + i2c_wb[USB251XB_ADDR_NON_REMOVABLE_DEVICES] = hub->non_rem_dev; + i2c_wb[USB251XB_ADDR_PORT_DISABLE_SELF] = hub->port_disable_sp; + i2c_wb[USB251XB_ADDR_PORT_DISABLE_BUS] = hub->port_disable_bp; + i2c_wb[USB251XB_ADDR_MAX_POWER_SELF] = hub->max_power_sp; + i2c_wb[USB251XB_ADDR_MAX_POWER_BUS] = hub->max_power_bp; + i2c_wb[USB251XB_ADDR_MAX_CURRENT_SELF] = hub->max_current_sp; + i2c_wb[USB251XB_ADDR_MAX_CURRENT_BUS] = hub->max_current_bp; + i2c_wb[USB251XB_ADDR_POWER_ON_TIME] = hub->power_on_time; + i2c_wb[USB251XB_ADDR_LANGUAGE_ID_HIGH] = (hub->lang_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_LANGUAGE_ID_LOW] = hub->lang_id & 0xFF; + i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING_LEN] = hub->manufacturer_len; + i2c_wb[USB251XB_ADDR_PRODUCT_STRING_LEN] = hub->product_len; + i2c_wb[USB251XB_ADDR_SERIAL_STRING_LEN] = hub->serial_len; + memcpy(&i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING], hub->manufacturer, + USB251XB_STRING_BUFSIZE); + memcpy(&i2c_wb[USB251XB_ADDR_SERIAL_STRING], hub->serial, + USB251XB_STRING_BUFSIZE); + memcpy(&i2c_wb[USB251XB_ADDR_PRODUCT_STRING], hub->product, + USB251XB_STRING_BUFSIZE); + i2c_wb[USB251XB_ADDR_BATTERY_CHARGING_ENABLE] = hub->bat_charge_en; + i2c_wb[USB251XB_ADDR_BOOST_UP] = hub->boost_up; + i2c_wb[USB251XB_ADDR_BOOST_57] = hub->boost_57; + i2c_wb[USB251XB_ADDR_BOOST_14] = hub->boost_14; + i2c_wb[USB251XB_ADDR_PORT_SWAP] = hub->port_swap; + i2c_wb[USB251XB_ADDR_PORT_MAP_12] = hub->port_map12; + i2c_wb[USB251XB_ADDR_PORT_MAP_34] = hub->port_map34; + i2c_wb[USB251XB_ADDR_PORT_MAP_56] = hub->port_map56; + i2c_wb[USB251XB_ADDR_PORT_MAP_7] = hub->port_map7; + i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH; + + usb251xb_reset(hub, 0); + + /* write registers */ + for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) { + int offset = i * USB251XB_I2C_WRITE_SZ; + char wbuf[USB251XB_I2C_WRITE_SZ + 1]; + + /* The first data byte transferred tells the hub how many data + * bytes will follow (byte count). + */ + wbuf[0] = USB251XB_I2C_WRITE_SZ; + memcpy(&wbuf[1], &i2c_wb[offset], USB251XB_I2C_WRITE_SZ); + + dev_dbg(dev, "writing %d byte block %d to 0x%02X\n", + USB251XB_I2C_WRITE_SZ, i, offset); + + err = i2c_smbus_write_i2c_block_data(hub->i2c, offset, + USB251XB_I2C_WRITE_SZ + 1, + wbuf); + if (err) + goto out_err; + } + + dev_info(dev, "Hub configuration was successful.\n"); + return 0; + +out_err: + dev_err(dev, "configuring block %d failed: %d\n", i, err); + return err; +} + +#ifdef CONFIG_OFDEVICE +static int usb251xb_get_ofdata(struct usb251xb *hub, + struct usb251xb_data *data) +{ + struct device_d *dev = hub->dev; + struct device_node *np = dev->device_node; + int len, err, i; + u32 property_u32 = 0; + const u32 *cproperty_u32; + const char *cproperty_char; + enum of_gpio_flags of_flags; + unsigned long flags = GPIOF_OUT_INIT_ACTIVE; + char str[USB251XB_STRING_BUFSIZE / 2]; + + if (!np) { + dev_err(dev, "failed to get ofdata\n"); + return -ENODEV; + } + + if (of_get_property(np, "skip-config", NULL)) + hub->skip_config = 1; + else + hub->skip_config = 0; + + hub->gpio_reset = of_get_named_gpio_flags(np, "reset-gpios", 0, + &of_flags); + if (gpio_is_valid(hub->gpio_reset)) { + char *name; + int ret; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + name = basprintf("%s reset", dev_name(dev)); + ret = gpio_request_one(hub->gpio_reset, flags, name); + if (ret < 0) + return ret; + } else if (hub->gpio_reset == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else { + err = hub->gpio_reset; + dev_err(dev, "unable to request GPIO reset pin (%d)\n", err); + return err; + } + + if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1)) + hub->vendor_id = USB251XB_DEF_VENDOR_ID; + + if (of_property_read_u16_array(np, "product-id", + &hub->product_id, 1)) + hub->product_id = data->product_id; + + if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1)) + hub->device_id = USB251XB_DEF_DEVICE_ID; + + hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1; + if (of_get_property(np, "self-powered", NULL)) { + hub->conf_data1 |= BIT(7); + + /* Configure Over-Current sens when self-powered */ + hub->conf_data1 &= ~BIT(2); + if (of_get_property(np, "ganged-sensing", NULL)) + hub->conf_data1 &= ~BIT(1); + else if (of_get_property(np, "individual-sensing", NULL)) + hub->conf_data1 |= BIT(1); + } else if (of_get_property(np, "bus-powered", NULL)) { + hub->conf_data1 &= ~BIT(7); + + /* Disable Over-Current sense when bus-powered */ + hub->conf_data1 |= BIT(2); + } + + if (of_get_property(np, "disable-hi-speed", NULL)) + hub->conf_data1 |= BIT(5); + + if (of_get_property(np, "multi-tt", NULL)) + hub->conf_data1 |= BIT(4); + else if (of_get_property(np, "single-tt", NULL)) + hub->conf_data1 &= ~BIT(4); + + if (of_get_property(np, "disable-eop", NULL)) + hub->conf_data1 |= BIT(3); + + if (of_get_property(np, "individual-port-switching", NULL)) + hub->conf_data1 |= BIT(0); + else if (of_get_property(np, "ganged-port-switching", NULL)) + hub->conf_data1 &= ~BIT(0); + + hub->conf_data2 = USB251XB_DEF_CONFIG_DATA_2; + if (of_get_property(np, "dynamic-power-switching", NULL)) + hub->conf_data2 |= BIT(7); + + if (!of_property_read_u32(np, "oc-delay-us", &property_u32)) { + if (property_u32 == 100) { + /* 100 us*/ + hub->conf_data2 &= ~BIT(5); + hub->conf_data2 &= ~BIT(4); + } else if (property_u32 == 4000) { + /* 4 ms */ + hub->conf_data2 &= ~BIT(5); + hub->conf_data2 |= BIT(4); + } else if (property_u32 == 16000) { + /* 16 ms */ + hub->conf_data2 |= BIT(5); + hub->conf_data2 |= BIT(4); + } else { + /* 8 ms (DEFAULT) */ + hub->conf_data2 |= BIT(5); + hub->conf_data2 &= ~BIT(4); + } + } + + if (of_get_property(np, "compound-device", NULL)) + hub->conf_data2 |= BIT(3); + + hub->conf_data3 = USB251XB_DEF_CONFIG_DATA_3; + if (of_get_property(np, "port-mapping-mode", NULL)) + hub->conf_data3 |= BIT(3); + + if (data->led_support && of_get_property(np, "led-usb-mode", NULL)) + hub->conf_data3 &= ~BIT(1); + + if (of_get_property(np, "string-support", NULL)) + hub->conf_data3 |= BIT(0); + + hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES; + cproperty_u32 = of_get_property(np, "non-removable-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= data->port_cnt)) + hub->non_rem_dev |= BIT(port); + else + dev_warn(dev, "NRD port %u doesn't exist\n", + port); + } + } + + hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF; + cproperty_u32 = of_get_property(np, "sp-disabled-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= data->port_cnt)) + hub->port_disable_sp |= BIT(port); + else + dev_warn(dev, "PDS port %u doesn't exist\n", + port); + } + } + + hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS; + cproperty_u32 = of_get_property(np, "bp-disabled-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= data->port_cnt)) + hub->port_disable_bp |= BIT(port); + else + dev_warn(dev, "PDB port %u doesn't exist\n", + port); + } + } + + hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF; + if (!of_property_read_u32(np, "sp-max-total-current-microamp", + &property_u32)) + hub->max_power_sp = min_t(u8, property_u32 / 2000, 50); + + hub->max_power_bp = USB251XB_DEF_MAX_POWER_BUS; + if (!of_property_read_u32(np, "bp-max-total-current-microamp", + &property_u32)) + hub->max_power_bp = min_t(u8, property_u32 / 2000, 255); + + hub->max_current_sp = USB251XB_DEF_MAX_CURRENT_SELF; + if (!of_property_read_u32(np, "sp-max-removable-current-microamp", + &property_u32)) + hub->max_current_sp = min_t(u8, property_u32 / 2000, 50); + + hub->max_current_bp = USB251XB_DEF_MAX_CURRENT_BUS; + if (!of_property_read_u32(np, "bp-max-removable-current-microamp", + &property_u32)) + hub->max_current_bp = min_t(u8, property_u32 / 2000, 255); + + hub->power_on_time = USB251XB_DEF_POWER_ON_TIME; + if (!of_property_read_u32(np, "power-on-time-ms", &property_u32)) + hub->power_on_time = min_t(u8, property_u32 / 2, 255); + + if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1)) + hub->lang_id = USB251XB_DEF_LANGUAGE_ID; + + cproperty_char = of_get_property(np, "manufacturer", NULL); + strlcpy(str, cproperty_char ? : USB251XB_DEF_MANUFACTURER_STRING, + sizeof(str)); + hub->manufacturer_len = strlen(str) & 0xFF; + memset(hub->manufacturer, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->manufacturer, + USB251XB_STRING_BUFSIZE); + + cproperty_char = of_get_property(np, "product", NULL); + strlcpy(str, cproperty_char ? : data->product_str, sizeof(str)); + hub->product_len = strlen(str) & 0xFF; + memset(hub->product, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->product, + USB251XB_STRING_BUFSIZE); + + cproperty_char = of_get_property(np, "serial", NULL); + strlcpy(str, cproperty_char ? : USB251XB_DEF_SERIAL_STRING, + sizeof(str)); + hub->serial_len = strlen(str) & 0xFF; + memset(hub->serial, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->serial, + USB251XB_STRING_BUFSIZE); + + /* The following parameters are currently not exposed to devicetree, but + * may be as soon as needed. + */ + hub->bat_charge_en = USB251XB_DEF_BATTERY_CHARGING_ENABLE; + hub->boost_up = USB251XB_DEF_BOOST_UP; + hub->boost_57 = USB251XB_DEF_BOOST_57; + hub->boost_14 = USB251XB_DEF_BOOST_14; + hub->port_swap = USB251XB_DEF_PORT_SWAP; + hub->port_map12 = USB251XB_DEF_PORT_MAP_12; + hub->port_map34 = USB251XB_DEF_PORT_MAP_34; + hub->port_map56 = USB251XB_DEF_PORT_MAP_56; + hub->port_map7 = USB251XB_DEF_PORT_MAP_7; + + return 0; +} + +static const struct of_device_id usb251xb_of_match[] = { + { + .compatible = "microchip,usb2512b", + .data = &usb2512b_data, + }, { + .compatible = "microchip,usb2512bi", + .data = &usb2512bi_data, + }, { + .compatible = "microchip,usb2513b", + .data = &usb2513b_data, + }, { + .compatible = "microchip,usb2513bi", + .data = &usb2513bi_data, + }, { + .compatible = "microchip,usb2514b", + .data = &usb2514b_data, + }, { + .compatible = "microchip,usb2514bi", + .data = &usb2514bi_data, + }, { + .compatible = "microchip,usb2517", + .data = &usb2517_data, + }, { + .compatible = "microchip,usb2517i", + .data = &usb2517i_data, + }, { + /* sentinel */ + } +}; +#else /* CONFIG_OF */ +static int usb251xb_get_ofdata(struct usb251xb *hub, + struct usb251xb_data *data) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int usb251xb_probe(struct usb251xb *hub) +{ + struct device_d *dev = hub->dev; + struct device_node *np = dev->device_node; + const struct of_device_id *of_id = of_match_device(usb251xb_of_match, + dev); + int err; + + if (np) { + err = usb251xb_get_ofdata(hub, + (struct usb251xb_data *)of_id->data); + if (err) { + dev_err(dev, "failed to get ofdata: %d\n", err); + return err; + } + } + + err = usb251xb_connect(hub); + if (err) { + dev_err(dev, "Failed to connect hub (%d)\n", err); + return err; + } + + dev_info(dev, "Hub probed successfully\n"); + + return 0; +} + +static int usb251xb_i2c_probe(struct device_d *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct usb251xb *hub; + + hub = xzalloc(sizeof(struct usb251xb)); + + i2c_set_clientdata(i2c, hub); + hub->dev = &i2c->dev; + hub->i2c = i2c; + + return usb251xb_probe(hub); +} + +static const struct platform_device_id usb251xb_id[] = { + { "usb2512b", 0 }, + { "usb2512bi", 0 }, + { "usb2513b", 0 }, + { "usb2513bi", 0 }, + { "usb2514b", 0 }, + { "usb2514bi", 0 }, + { "usb2517", 0 }, + { "usb2517i", 0 }, + { /* sentinel */ } +}; + +static struct driver_d usb251xb_i2c_driver = { + .name = DRIVER_NAME, + .probe = usb251xb_i2c_probe, + .id_table = usb251xb_id, + .of_compatible = DRV_OF_COMPAT(usb251xb_of_match), +}; +device_i2c_driver(usb251xb_i2c_driver); -- cgit v1.2.3 From 1192a7add658e93ba9242a2fc53a23108eb05e8c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 23:29:30 -0800 Subject: usb: usb251xb: add usb data lane port swap feature Port of Linux patch https://patchwork.kernel.org/patch/10766235/ The HW can swap the USB differential-pair (D+/D-) for each port separately. So the USB signals can be re-aligned with a misplaced USB connector on the PCB. Signed-off-by: Marco Felsch Reviewed-by: Richard Leitner Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/usb/misc/usb251xb.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 0b721f6d47..97f55efa82 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -340,12 +340,14 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, struct device_d *dev = hub->dev; struct device_node *np = dev->device_node; int len, err, i; - u32 property_u32 = 0; + u32 port, property_u32 = 0; const u32 *cproperty_u32; const char *cproperty_char; enum of_gpio_flags of_flags; unsigned long flags = GPIOF_OUT_INIT_ACTIVE; char str[USB251XB_STRING_BUFSIZE / 2]; + struct property *prop; + const __be32 *p; if (!np) { dev_err(dev, "failed to get ofdata\n"); @@ -556,6 +558,16 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, (wchar_t *)hub->serial, USB251XB_STRING_BUFSIZE); + /* + * The datasheet documents the register as 'Port Swap' but the register + * controls the USB DP/DM signal swapping + */ + hub->port_swap = USB251XB_DEF_PORT_SWAP; + of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) { + if ((port >= 0) && (port <= data->port_cnt)) + hub->port_swap |= BIT(port); + } + /* The following parameters are currently not exposed to devicetree, but * may be as soon as needed. */ @@ -563,7 +575,6 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, hub->boost_up = USB251XB_DEF_BOOST_UP; hub->boost_57 = USB251XB_DEF_BOOST_57; hub->boost_14 = USB251XB_DEF_BOOST_14; - hub->port_swap = USB251XB_DEF_PORT_SWAP; hub->port_map12 = USB251XB_DEF_PORT_MAP_12; hub->port_map34 = USB251XB_DEF_PORT_MAP_34; hub->port_map56 = USB251XB_DEF_PORT_MAP_56; -- cgit v1.2.3