diff options
Diffstat (limited to 'drivers/usb/cdns3')
-rw-r--r-- | drivers/usb/cdns3/cdns3-imx.c | 2 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.c | 44 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.h | 4 | ||||
-rw-r--r-- | drivers/usb/cdns3/ep0.c | 65 | ||||
-rw-r--r-- | drivers/usb/cdns3/gadget-export.h | 3 | ||||
-rw-r--r-- | drivers/usb/cdns3/gadget.c | 144 | ||||
-rw-r--r-- | drivers/usb/cdns3/gadget.h | 5 | ||||
-rw-r--r-- | drivers/usb/cdns3/host-export.h | 6 | ||||
-rw-r--r-- | drivers/usb/cdns3/host.c | 60 |
9 files changed, 208 insertions, 125 deletions
diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 54a2d70a9c73..22a56c4dce67 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -151,6 +151,7 @@ static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup); static struct cdns3_platform_data cdns_imx_pdata = { .platform_suspend = cdns_imx_platform_suspend, + .quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW, }; static const struct of_dev_auxdata cdns_imx_auxdata[] = { @@ -206,7 +207,6 @@ static int cdns_imx_probe(struct platform_device *pdev) device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_forbid(dev); return ret; err: diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index a0f73d4711ae..1991cb5cf6bf 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -427,7 +427,6 @@ static irqreturn_t cdns3_wakeup_irq(int irq, void *data) */ static int cdns3_probe(struct platform_device *pdev) { - struct usb_role_switch_desc sw_desc = { }; struct device *dev = &pdev->dev; struct resource *res; struct cdns3 *cdns; @@ -466,11 +465,8 @@ static int cdns3_probe(struct platform_device *pdev) cdns->xhci_res[1] = *res; cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral"); - if (cdns->dev_irq == -EPROBE_DEFER) - return cdns->dev_irq; - if (cdns->dev_irq < 0) - dev_err(dev, "couldn't get peripheral irq\n"); + return cdns->dev_irq; regs = devm_platform_ioremap_resource_byname(pdev, "dev"); if (IS_ERR(regs)) @@ -478,14 +474,9 @@ static int cdns3_probe(struct platform_device *pdev) cdns->dev_regs = regs; cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); - if (cdns->otg_irq == -EPROBE_DEFER) + if (cdns->otg_irq < 0) return cdns->otg_irq; - if (cdns->otg_irq < 0) { - dev_err(dev, "couldn't get otg irq\n"); - return cdns->otg_irq; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); if (!res) { dev_err(dev, "couldn't get otg resource\n"); @@ -529,18 +520,21 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) goto err2; - sw_desc.set = cdns3_role_set; - sw_desc.get = cdns3_role_get; - sw_desc.allow_userspace_control = true; - sw_desc.driver_data = cdns; - if (device_property_read_bool(dev, "usb-role-switch")) + if (device_property_read_bool(dev, "usb-role-switch")) { + struct usb_role_switch_desc sw_desc = { }; + + sw_desc.set = cdns3_role_set; + sw_desc.get = cdns3_role_get; + sw_desc.allow_userspace_control = true; + sw_desc.driver_data = cdns; sw_desc.fwnode = dev->fwnode; - cdns->role_sw = usb_role_switch_register(dev, &sw_desc); - if (IS_ERR(cdns->role_sw)) { - ret = PTR_ERR(cdns->role_sw); - dev_warn(dev, "Unable to register Role Switch\n"); - goto err3; + cdns->role_sw = usb_role_switch_register(dev, &sw_desc); + if (IS_ERR(cdns->role_sw)) { + ret = PTR_ERR(cdns->role_sw); + dev_warn(dev, "Unable to register Role Switch\n"); + goto err3; + } } if (cdns->wakeup_irq) { @@ -551,7 +545,7 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) { dev_err(cdns->dev, "couldn't register wakeup irq handler\n"); - goto err3; + goto err4; } } @@ -567,7 +561,8 @@ static int cdns3_probe(struct platform_device *pdev) device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_forbid(dev); + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) + pm_runtime_forbid(dev); /* * The controller needs less time between bus and controller suspend, @@ -582,7 +577,8 @@ static int cdns3_probe(struct platform_device *pdev) return 0; err4: cdns3_drd_exit(cdns); - usb_role_switch_unregister(cdns->role_sw); + if (cdns->role_sw) + usb_role_switch_unregister(cdns->role_sw); err3: set_phy_power_off(cdns); err2: diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 8a40d53d5ede..3176f924293a 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -42,6 +42,8 @@ struct cdns3_role_driver { struct cdns3_platform_data { int (*platform_suspend)(struct device *dev, bool suspend, bool wakeup); + unsigned long quirks; +#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) }; /** @@ -73,6 +75,7 @@ struct cdns3_platform_data { * @wakeup_pending: wakeup interrupt pending * @pdata: platform data from glue layer * @lock: spinlock structure + * @xhci_plat_data: xhci private data structure pointer */ struct cdns3 { struct device *dev; @@ -106,6 +109,7 @@ struct cdns3 { bool wakeup_pending; struct cdns3_platform_data *pdata; spinlock_t lock; + struct xhci_plat_priv *xhci_plat_data; }; int cdns3_hw_role_switch(struct cdns3 *cdns); diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index 4761c852d9c4..d3121a32cc68 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -137,48 +137,36 @@ static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, struct usb_ctrlrequest *ctrl_req) { enum usb_device_state device_state = priv_dev->gadget.state; - struct cdns3_endpoint *priv_ep; u32 config = le16_to_cpu(ctrl_req->wValue); int result = 0; - int i; switch (device_state) { case USB_STATE_ADDRESS: - /* Configure non-control EPs */ - for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { - priv_ep = priv_dev->eps[i]; - if (!priv_ep) - continue; - - if (priv_ep->flags & EP_CLAIMED) - cdns3_ep_config(priv_ep); - } - result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); - if (result) - return result; - - if (!config) { - cdns3_hw_reset_eps_config(priv_dev); - usb_gadget_set_state(&priv_dev->gadget, - USB_STATE_ADDRESS); - } + if (result || !config) + goto reset_config; break; case USB_STATE_CONFIGURED: result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + if (!config && !result) + goto reset_config; - if (!config && !result) { - cdns3_hw_reset_eps_config(priv_dev); - usb_gadget_set_state(&priv_dev->gadget, - USB_STATE_ADDRESS); - } break; default: - result = -EINVAL; + return -EINVAL; } + return 0; + +reset_config: + if (result != USB_GADGET_DELAYED_STATUS) + cdns3_hw_reset_eps_config(priv_dev); + + usb_gadget_set_state(&priv_dev->gadget, + USB_STATE_ADDRESS); + return result; } @@ -705,6 +693,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, unsigned long flags; int ret = 0; u8 zlp = 0; + int i; spin_lock_irqsave(&priv_dev->lock, flags); trace_cdns3_ep0_queue(priv_dev, request); @@ -720,6 +709,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, u32 val; cdns3_select_ep(priv_dev, 0x00); + + /* + * Configure all non-control EPs which are not enabled by class driver + */ + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { + priv_ep = priv_dev->eps[i]; + if (priv_ep && priv_ep->flags & EP_CLAIMED && + !(priv_ep->flags & EP_ENABLED)) + cdns3_ep_config(priv_ep, 0); + } + cdns3_set_hw_configuration(priv_dev); cdns3_ep0_complete_setup(priv_dev, 0, 1); /* wait until configuration set */ @@ -811,6 +811,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) struct cdns3_usb_regs __iomem *regs; struct cdns3_endpoint *priv_ep; u32 max_packet_size = 64; + u32 ep_cfg; regs = priv_dev->regs; @@ -842,8 +843,10 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) BIT(0) | BIT(16)); } - writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), - ®s->ep_cfg); + ep_cfg = EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size); + + if (!(priv_ep->flags & EP_CONFIGURED)) + writel(ep_cfg, ®s->ep_cfg); writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); @@ -851,8 +854,10 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) /* init ep in */ cdns3_select_ep(priv_dev, USB_DIR_IN); - writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), - ®s->ep_cfg); + if (!(priv_ep->flags & EP_CONFIGURED)) + writel(ep_cfg, ®s->ep_cfg); + + priv_ep->flags |= EP_CONFIGURED; writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h index 577469eee961..702c5a267a92 100644 --- a/drivers/usb/cdns3/gadget-export.h +++ b/drivers/usb/cdns3/gadget-export.h @@ -13,7 +13,6 @@ #ifdef CONFIG_USB_CDNS3_GADGET int cdns3_gadget_init(struct cdns3 *cdns); -void cdns3_gadget_exit(struct cdns3 *cdns); #else static inline int cdns3_gadget_init(struct cdns3 *cdns) @@ -21,8 +20,6 @@ static inline int cdns3_gadget_init(struct cdns3 *cdns) return -ENXIO; } -static inline void cdns3_gadget_exit(struct cdns3 *cdns) { } - #endif #endif /* __LINUX_CDNS3_GADGET_EXPORT */ diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 6e7b70a2e352..08a4e693c470 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -296,6 +296,8 @@ static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep) */ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) { + int i; + writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf); cdns3_allow_enable_l1(priv_dev, 0); @@ -304,6 +306,10 @@ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) priv_dev->out_mem_is_allocated = 0; priv_dev->wait_for_setup = 0; priv_dev->using_streams = 0; + + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) + if (priv_dev->eps[i]) + priv_dev->eps[i]->flags &= ~EP_CONFIGURED; } /** @@ -506,7 +512,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, while (!list_empty(&priv_ep->wa2_descmiss_req_list)) { int chunk_end; - int length; descmiss_priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list); @@ -517,7 +522,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, break; chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH; - length = request->actual + descmiss_req->actual; request->status = descmiss_req->status; __cdns3_descmiss_copy_data(request, descmiss_req); list_del_init(&descmiss_priv_req->list); @@ -1110,7 +1114,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, struct cdns3_device *priv_dev = priv_ep->cdns3_dev; struct cdns3_request *priv_req; struct cdns3_trb *trb; - struct cdns3_trb *link_trb; + struct cdns3_trb *link_trb = NULL; dma_addr_t trb_dma; u32 togle_pcs = 1; int sg_iter = 0; @@ -1189,10 +1193,20 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* set incorrect Cycle Bit for first trb*/ control = priv_ep->pcs ? 0 : TRB_CYCLE; + trb->length = 0; + if (priv_dev->dev_ver >= DEV_VER_V2) { + u16 td_size; + + td_size = DIV_ROUND_UP(request->length, + priv_ep->endpoint.maxpacket); + if (priv_dev->gadget.speed == USB_SPEED_SUPER) + trb->length = TRB_TDL_SS_SIZE(td_size); + else + control |= TRB_TDL_HS_SIZE(td_size); + } do { u32 length; - u16 td_size = 0; /* fill TRB */ control |= TRB_TYPE(TRB_NORMAL); @@ -1204,20 +1218,12 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, length = request->length; } - if (likely(priv_dev->dev_ver >= DEV_VER_V2)) - td_size = DIV_ROUND_UP(length, - priv_ep->endpoint.maxpacket); - else if (priv_ep->flags & EP_TDLCHK_EN) + if (priv_ep->flags & EP_TDLCHK_EN) total_tdl += DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length = cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | + trb->length |= cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | TRB_LEN(length)); - if (priv_dev->gadget.speed == USB_SPEED_SUPER) - trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(td_size)); - else - control |= TRB_TDL_HS_SIZE(td_size); - pcs = priv_ep->pcs ? TRB_CYCLE : 0; /* @@ -1254,6 +1260,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, priv_req->end_trb = priv_ep->enqueue; cdns3_ep_inc_enq(priv_ep); trb = priv_ep->trb_pool + priv_ep->enqueue; + trb->length = 0; } while (sg_iter < num_trb); trb = priv_req->trb; @@ -1746,11 +1753,8 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) { - if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) { - spin_unlock(&priv_dev->lock); + if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) priv_dev->gadget_driver->disconnect(&priv_dev->gadget); - spin_lock(&priv_dev->lock); - } } /** @@ -1761,6 +1765,7 @@ static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) */ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, u32 usb_ists) +__must_hold(&priv_dev->lock) { int speed = 0; @@ -1785,7 +1790,9 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, /* Disconnection detected */ if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) { + spin_unlock(&priv_dev->lock); cdns3_disconnect_gadget(priv_dev); + spin_lock(&priv_dev->lock); priv_dev->gadget.speed = USB_SPEED_UNKNOWN; usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); cdns3_hw_reset_eps_config(priv_dev); @@ -1979,27 +1986,6 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev, return 0; } -static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev, - struct cdns3_endpoint *priv_ep) -{ - if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER) - return; - - if (priv_dev->dev_ver >= DEV_VER_V3) { - u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); - - /* - * Stream capable endpoints are handled by using ep_tdl - * register. Other endpoints use TDL from TRB feature. - */ - cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, mask); - } - - /* Enable Stream Bit TDL chk and SID chk */ - cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_STREAM_EN | - EP_CFG_TDL_CHK | EP_CFG_SID_CHK); -} - static void cdns3_configure_dmult(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep) { @@ -2037,8 +2023,9 @@ static void cdns3_configure_dmult(struct cdns3_device *priv_dev, /** * cdns3_ep_config Configure hardware endpoint * @priv_ep: extended endpoint object + * @enable: set EP_CFG_ENABLE bit in ep_cfg register. */ -void cdns3_ep_config(struct cdns3_endpoint *priv_ep) +int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) { bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); struct cdns3_device *priv_dev = priv_ep->cdns3_dev; @@ -2099,7 +2086,7 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) break; default: /* all other speed are not supported */ - return; + return -EINVAL; } if (max_packet_size == 1024) @@ -2109,11 +2096,33 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) else priv_ep->trb_burst_size = 16; - ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, - !!priv_ep->dir); - if (ret) { - dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); - return; + /* onchip buffer is only allocated before configuration */ + if (!priv_dev->hw_configured_flag) { + ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, + !!priv_ep->dir); + if (ret) { + dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); + return ret; + } + } + + if (enable) + ep_cfg |= EP_CFG_ENABLE; + + if (priv_ep->use_streams && priv_dev->gadget.speed >= USB_SPEED_SUPER) { + if (priv_dev->dev_ver >= DEV_VER_V3) { + u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); + + /* + * Stream capable endpoints are handled by using ep_tdl + * register. Other endpoints use TDL from TRB feature. + */ + cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, + mask); + } + + /* Enable Stream Bit TDL chk and SID chk */ + ep_cfg |= EP_CFG_STREAM_EN | EP_CFG_TDL_CHK | EP_CFG_SID_CHK; } ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | @@ -2123,9 +2132,12 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) cdns3_select_ep(priv_dev, bEndpointAddress); writel(ep_cfg, &priv_dev->regs->ep_cfg); + priv_ep->flags |= EP_CONFIGURED; dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n", priv_ep->name, ep_cfg); + + return 0; } /* Find correct direction for HW endpoint according to description */ @@ -2266,7 +2278,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, u32 bEndpointAddress; unsigned long flags; int enable = 1; - int ret; + int ret = 0; int val; priv_ep = ep_to_cdns3_ep(ep); @@ -2305,6 +2317,17 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, bEndpointAddress = priv_ep->num | priv_ep->dir; cdns3_select_ep(priv_dev, bEndpointAddress); + /* + * For some versions of controller at some point during ISO OUT traffic + * DMA reads Transfer Ring for the EP which has never got doorbell. + * This issue was detected only on simulation, but to avoid this issue + * driver add protection against it. To fix it driver enable ISO OUT + * endpoint before setting DRBL. This special treatment of ISO OUT + * endpoints are recommended by controller specification. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) + enable = 0; + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { /* * Enable stream support (SS mode) related interrupts @@ -2315,13 +2338,17 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, EP_STS_EN_SIDERREN | EP_STS_EN_MD_EXITEN | EP_STS_EN_STREAMREN; priv_ep->use_streams = true; - cdns3_stream_ep_reconfig(priv_dev, priv_ep); + ret = cdns3_ep_config(priv_ep, enable); priv_dev->using_streams |= true; } + } else { + ret = cdns3_ep_config(priv_ep, enable); } - ret = cdns3_allocate_trb_pool(priv_ep); + if (ret) + goto exit; + ret = cdns3_allocate_trb_pool(priv_ep); if (ret) goto exit; @@ -2351,20 +2378,6 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, writel(reg, &priv_dev->regs->ep_sts_en); - /* - * For some versions of controller at some point during ISO OUT traffic - * DMA reads Transfer Ring for the EP which has never got doorbell. - * This issue was detected only on simulation, but to avoid this issue - * driver add protection against it. To fix it driver enable ISO OUT - * endpoint before setting DRBL. This special treatment of ISO OUT - * endpoints are recommended by controller specification. - */ - if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) - enable = 0; - - if (enable) - cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE); - ep->desc = desc; priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING | EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN); @@ -3071,7 +3084,7 @@ static void cdns3_gadget_release(struct device *dev) kfree(priv_dev); } -void cdns3_gadget_exit(struct cdns3 *cdns) +static void cdns3_gadget_exit(struct cdns3 *cdns) { struct cdns3_device *priv_dev; @@ -3265,10 +3278,13 @@ err0: } static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup) +__must_hold(&cdns->lock) { struct cdns3_device *priv_dev = cdns->gadget_dev; + spin_unlock(&cdns->lock); cdns3_disconnect_gadget(priv_dev); + spin_lock(&cdns->lock); priv_dev->gadget.speed = USB_SPEED_UNKNOWN; usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 1ccecd237530..21fa461c518e 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1072,7 +1072,7 @@ struct cdns3_trb { #define TRB_TDL_SS_SIZE_GET(p) (((p) & GENMASK(23, 17)) >> 17) /* transfer_len bitmasks - bits 31:24 */ -#define TRB_BURST_LEN(p) (((p) << 24) & GENMASK(31, 24)) +#define TRB_BURST_LEN(p) ((unsigned int)((p) << 24) & GENMASK(31, 24)) #define TRB_BURST_LEN_GET(p) (((p) & GENMASK(31, 24)) >> 24) /* Data buffer pointer bitmasks*/ @@ -1159,6 +1159,7 @@ struct cdns3_endpoint { #define EP_QUIRK_EXTRA_BUF_DET BIT(12) #define EP_QUIRK_EXTRA_BUF_EN BIT(13) #define EP_TDLCHK_EN BIT(15) +#define EP_CONFIGURED BIT(16) u32 flags; struct cdns3_request *descmis_req; @@ -1360,7 +1361,7 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, int cdns3_init_ep0(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep); void cdns3_ep0_config(struct cdns3_device *priv_dev); -void cdns3_ep_config(struct cdns3_endpoint *priv_ep); +int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable); void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev); diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h index ae11810f8826..26041718a086 100644 --- a/drivers/usb/cdns3/host-export.h +++ b/drivers/usb/cdns3/host-export.h @@ -9,9 +9,11 @@ #ifndef __LINUX_CDNS3_HOST_EXPORT #define __LINUX_CDNS3_HOST_EXPORT +struct usb_hcd; #ifdef CONFIG_USB_CDNS3_HOST int cdns3_host_init(struct cdns3 *cdns); +int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd); #else @@ -21,6 +23,10 @@ static inline int cdns3_host_init(struct cdns3 *cdns) } static inline void cdns3_host_exit(struct cdns3 *cdns) { } +static inline int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) +{ + return 0; +} #endif /* CONFIG_USB_CDNS3_HOST */ diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index b3e2cb69762c..ec89f2e5430f 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -14,6 +14,19 @@ #include "drd.h" #include "host-export.h" #include <linux/usb/hcd.h> +#include "../host/xhci.h" +#include "../host/xhci-plat.h" + +#define XECP_PORT_CAP_REG 0x8000 +#define XECP_AUX_CTRL_REG1 0x8120 + +#define CFG_RXDET_P3_EN BIT(15) +#define LPM_2_STB_SWITCH_EN BIT(25) + +static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { + .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI, + .suspend_quirk = xhci_cdns3_suspend_quirk, +}; static int __cdns3_host_init(struct cdns3 *cdns) { @@ -39,10 +52,25 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } + cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci, + sizeof(struct xhci_plat_priv), GFP_KERNEL); + if (!cdns->xhci_plat_data) { + ret = -ENOMEM; + goto err1; + } + + if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) + cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; + + ret = platform_device_add_data(xhci, cdns->xhci_plat_data, + sizeof(struct xhci_plat_priv)); + if (ret) + goto free_memory; + ret = platform_device_add(xhci); if (ret) { dev_err(cdns->dev, "failed to register xHCI device\n"); - goto err1; + goto free_memory; } /* Glue needs to access xHCI region register for Power management */ @@ -51,13 +79,43 @@ static int __cdns3_host_init(struct cdns3 *cdns) cdns->xhci_regs = hcd->regs; return 0; + +free_memory: + kfree(cdns->xhci_plat_data); err1: platform_device_put(xhci); return ret; } +int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 value; + + if (pm_runtime_status_suspended(hcd->self.controller)) + return 0; + + /* set usbcmd.EU3S */ + value = readl(&xhci->op_regs->command); + value |= CMD_PM_INDEX; + writel(value, &xhci->op_regs->command); + + if (hcd->regs) { + value = readl(hcd->regs + XECP_AUX_CTRL_REG1); + value |= CFG_RXDET_P3_EN; + writel(value, hcd->regs + XECP_AUX_CTRL_REG1); + + value = readl(hcd->regs + XECP_PORT_CAP_REG); + value |= LPM_2_STB_SWITCH_EN; + writel(value, hcd->regs + XECP_PORT_CAP_REG); + } + + return 0; +} + static void cdns3_host_exit(struct cdns3 *cdns) { + kfree(cdns->xhci_plat_data); platform_device_unregister(cdns->host_dev); cdns->host_dev = NULL; cdns3_drd_host_off(cdns); |