diff options
-rw-r--r-- | drivers/usb/cdns3/cdns3-gadget.c | 73 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-gadget.h | 3 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-imx.c | 34 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-plat.c | 23 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-trace.h | 5 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-gadget.c | 17 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-gadget.h | 1 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdnsp-mem.c | 3 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.c | 29 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.c | 15 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.h | 2 |
11 files changed, 162 insertions, 43 deletions
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 582bfeceedb4..9b1bd417cec0 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -59,6 +59,7 @@ #include <linux/dma-mapping.h> #include <linux/usb/gadget.h> #include <linux/module.h> +#include <linux/dmapool.h> #include <linux/iopoll.h> #include "core.h" @@ -190,29 +191,13 @@ dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep, return priv_ep->trb_pool_dma + offset; } -static int cdns3_ring_size(struct cdns3_endpoint *priv_ep) -{ - switch (priv_ep->type) { - case USB_ENDPOINT_XFER_ISOC: - return TRB_ISO_RING_SIZE; - case USB_ENDPOINT_XFER_CONTROL: - return TRB_CTRL_RING_SIZE; - default: - if (priv_ep->use_streams) - return TRB_STREAM_RING_SIZE; - else - return TRB_RING_SIZE; - } -} - static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; if (priv_ep->trb_pool) { - dma_free_coherent(priv_dev->sysdev, - cdns3_ring_size(priv_ep), - priv_ep->trb_pool, priv_ep->trb_pool_dma); + dma_pool_free(priv_dev->eps_dma_pool, + priv_ep->trb_pool, priv_ep->trb_pool_dma); priv_ep->trb_pool = NULL; } } @@ -226,7 +211,7 @@ static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; - int ring_size = cdns3_ring_size(priv_ep); + int ring_size = TRB_RING_SIZE; int num_trbs = ring_size / TRB_SIZE; struct cdns3_trb *link_trb; @@ -234,10 +219,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) cdns3_free_trb_pool(priv_ep); if (!priv_ep->trb_pool) { - priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev, - ring_size, - &priv_ep->trb_pool_dma, - GFP_DMA32 | GFP_ATOMIC); + priv_ep->trb_pool = dma_pool_alloc(priv_dev->eps_dma_pool, + GFP_DMA32 | GFP_ATOMIC, + &priv_ep->trb_pool_dma); + if (!priv_ep->trb_pool) return -ENOMEM; @@ -834,9 +819,15 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, priv_ep->dir); if ((priv_req->flags & REQUEST_UNALIGNED) && - priv_ep->dir == USB_DIR_OUT && !request->status) + priv_ep->dir == USB_DIR_OUT && !request->status) { + /* Make DMA buffer CPU accessible */ + dma_sync_single_for_cpu(priv_dev->sysdev, + priv_req->aligned_buf->dma, + priv_req->aligned_buf->size, + priv_req->aligned_buf->dir); memcpy(request->buf, priv_req->aligned_buf->buf, request->length); + } priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); /* All TRBs have finished, clear the counter */ @@ -898,8 +889,8 @@ static void cdns3_free_aligned_request_buf(struct work_struct *work) * interrupts. */ spin_unlock_irqrestore(&priv_dev->lock, flags); - dma_free_coherent(priv_dev->sysdev, buf->size, - buf->buf, buf->dma); + dma_free_noncoherent(priv_dev->sysdev, buf->size, + buf->buf, buf->dma, buf->dir); kfree(buf); spin_lock_irqsave(&priv_dev->lock, flags); } @@ -926,10 +917,13 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) return -ENOMEM; buf->size = priv_req->request.length; + buf->dir = usb_endpoint_dir_in(priv_ep->endpoint.desc) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; - buf->buf = dma_alloc_coherent(priv_dev->sysdev, + buf->buf = dma_alloc_noncoherent(priv_dev->sysdev, buf->size, &buf->dma, + buf->dir, GFP_ATOMIC); if (!buf->buf) { kfree(buf); @@ -951,10 +945,17 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) } if (priv_ep->dir == USB_DIR_IN) { + /* Make DMA buffer CPU accessible */ + dma_sync_single_for_cpu(priv_dev->sysdev, + buf->dma, buf->size, buf->dir); memcpy(buf->buf, priv_req->request.buf, priv_req->request.length); } + /* Transfer DMA buffer ownership back to device */ + dma_sync_single_for_device(priv_dev->sysdev, + buf->dma, buf->size, buf->dir); + priv_req->flags |= REQUEST_UNALIGNED; trace_cdns3_prepare_aligned_request(priv_req); @@ -3103,9 +3104,10 @@ static void cdns3_gadget_exit(struct cdns *cdns) struct cdns3_aligned_buf *buf; buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list); - dma_free_coherent(priv_dev->sysdev, buf->size, + dma_free_noncoherent(priv_dev->sysdev, buf->size, buf->buf, - buf->dma); + buf->dma, + buf->dir); list_del(&buf->list); kfree(buf); @@ -3113,6 +3115,7 @@ static void cdns3_gadget_exit(struct cdns *cdns) dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf, priv_dev->setup_dma); + dma_pool_destroy(priv_dev->eps_dma_pool); kfree(priv_dev->zlp_buf); usb_put_gadget(&priv_dev->gadget); @@ -3185,6 +3188,14 @@ static int cdns3_gadget_start(struct cdns *cdns) /* initialize endpoint container */ INIT_LIST_HEAD(&priv_dev->gadget.ep_list); INIT_LIST_HEAD(&priv_dev->aligned_buf_list); + priv_dev->eps_dma_pool = dma_pool_create("cdns3_eps_dma_pool", + priv_dev->sysdev, + TRB_RING_SIZE, 8, 0); + if (!priv_dev->eps_dma_pool) { + dev_err(priv_dev->dev, "Failed to create TRB dma pool\n"); + ret = -ENOMEM; + goto err1; + } ret = cdns3_init_eps(priv_dev); if (ret) { @@ -3235,6 +3246,8 @@ err3: err2: cdns3_free_all_eps(priv_dev); err1: + dma_pool_destroy(priv_dev->eps_dma_pool); + usb_put_gadget(&priv_dev->gadget); cdns->gadget_dev = NULL; return ret; @@ -3304,6 +3317,8 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) return 0; cdns3_gadget_config(priv_dev); + if (hibernated) + writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); return 0; } diff --git a/drivers/usb/cdns3/cdns3-gadget.h b/drivers/usb/cdns3/cdns3-gadget.h index 21fa461c518e..c5660f2c4293 100644 --- a/drivers/usb/cdns3/cdns3-gadget.h +++ b/drivers/usb/cdns3/cdns3-gadget.h @@ -12,6 +12,7 @@ #ifndef __LINUX_CDNS3_GADGET #define __LINUX_CDNS3_GADGET #include <linux/usb/gadget.h> +#include <linux/dma-direction.h> /* * USBSS-DEV register interface. @@ -1205,6 +1206,7 @@ struct cdns3_aligned_buf { void *buf; dma_addr_t dma; u32 size; + enum dma_data_direction dir; unsigned in_use:1; struct list_head list; }; @@ -1298,6 +1300,7 @@ struct cdns3_device { struct cdns3_usb_regs __iomem *regs; + struct dma_pool *eps_dma_pool; struct usb_ctrlrequest *setup_buf; dma_addr_t setup_dma; void *zlp_buf; diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 8f88eec0b0ea..74e758dc0895 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -361,6 +361,39 @@ static int cdns_imx_suspend(struct device *dev) return 0; } + + +/* Indicate if the controller was power lost before */ +static inline bool cdns_imx_is_power_lost(struct cdns_imx *data) +{ + u32 value; + + value = cdns_imx_readl(data, USB3_CORE_CTRL1); + if ((value & SW_RESET_MASK) == ALL_SW_RESET) + return true; + else + return false; +} + +static int __maybe_unused cdns_imx_system_resume(struct device *dev) +{ + struct cdns_imx *data = dev_get_drvdata(dev); + int ret; + + ret = cdns_imx_resume(dev); + if (ret) + return ret; + + if (cdns_imx_is_power_lost(data)) { + dev_dbg(dev, "resume from power lost\n"); + ret = cdns_imx_noncore_init(data); + if (ret) + cdns_imx_suspend(dev); + } + + return ret; +} + #else static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup) @@ -372,6 +405,7 @@ static int cdns_imx_platform_suspend(struct device *dev, static const struct dev_pm_ops cdns_imx_pm_ops = { SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_suspend, cdns_imx_system_resume) }; static const struct of_device_id cdns_imx_of_match[] = { diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 4b18e1c6a4bb..e1deeada425c 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -19,6 +19,7 @@ #include "core.h" #include "gadget-export.h" +#include "drd.h" static int set_phy_power_on(struct cdns *cdns) { @@ -236,6 +237,18 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg) if (!cdns->in_lpm) return 0; + if (cdns_power_is_lost(cdns)) { + phy_exit(cdns->usb2_phy); + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + + phy_exit(cdns->usb3_phy); + ret = phy_init(cdns->usb3_phy); + if (ret) + return ret; + } + ret = set_phy_power_on(cdns); if (ret) return ret; @@ -270,10 +283,18 @@ static int cdns3_plat_runtime_resume(struct device *dev) static int cdns3_plat_suspend(struct device *dev) { struct cdns *cdns = dev_get_drvdata(dev); + int ret; cdns_suspend(cdns); - return cdns3_controller_suspend(dev, PMSG_SUSPEND); + ret = cdns3_controller_suspend(dev, PMSG_SUSPEND); + if (ret) + return ret; + + if (device_may_wakeup(dev) && cdns->wakeup_irq) + enable_irq_wake(cdns->wakeup_irq); + + return ret; } static int cdns3_plat_resume(struct device *dev) diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h index 8648c7a7a9dd..7574b4a62813 100644 --- a/drivers/usb/cdns3/cdns3-trace.h +++ b/drivers/usb/cdns3/cdns3-trace.h @@ -214,7 +214,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __field(int, no_interrupt) __field(int, start_trb) __field(int, end_trb) - __field(struct cdns3_trb *, start_trb_addr) __field(int, flags) __field(unsigned int, stream_id) ), @@ -230,12 +229,11 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __entry->no_interrupt = req->request.no_interrupt; __entry->start_trb = req->start_trb; __entry->end_trb = req->end_trb; - __entry->start_trb_addr = req->trb; __entry->flags = req->flags; __entry->stream_id = req->request.stream_id; ), TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d," - " trb: [start:%d, end:%d: virt addr %pa], flags:%x SID: %u", + " trb: [start:%d, end:%d], flags:%x SID: %u", __get_str(name), __entry->req, __entry->buf, __entry->actual, __entry->length, __entry->zero ? "Z" : "z", @@ -244,7 +242,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request, __entry->status, __entry->start_trb, __entry->end_trb, - __entry->start_trb_addr, __entry->flags, __entry->stream_id ) diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index d7d4bdd57f46..56707b6b0f57 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -727,7 +727,7 @@ int cdnsp_reset_device(struct cdnsp_device *pdev) * are in Disabled state. */ for (i = 1; i < CDNSP_ENDPOINTS_NUM; ++i) - pdev->eps[i].ep_state |= EP_STOPPED; + pdev->eps[i].ep_state |= EP_STOPPED | EP_UNCONFIGURED; trace_cdnsp_handle_cmd_reset_dev(slot_ctx); @@ -942,6 +942,7 @@ static int cdnsp_gadget_ep_enable(struct usb_ep *ep, pep = to_cdnsp_ep(ep); pdev = pep->pdev; + pep->ep_state &= ~EP_UNCONFIGURED; if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED, "%s is already enabled\n", pep->name)) @@ -1023,9 +1024,13 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) goto finish; } - cdnsp_cmd_stop_ep(pdev, pep); pep->ep_state |= EP_DIS_IN_RROGRESS; - cdnsp_cmd_flush_ep(pdev, pep); + + /* Endpoint was unconfigured by Reset Device command. */ + if (!(pep->ep_state & EP_UNCONFIGURED)) { + cdnsp_cmd_stop_ep(pdev, pep); + cdnsp_cmd_flush_ep(pdev, pep); + } /* Remove all queued USB requests. */ while (!list_empty(&pep->pending_list)) { @@ -1043,10 +1048,12 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) cdnsp_endpoint_zero(pdev, pep); - ret = cdnsp_update_eps_configuration(pdev, pep); + if (!(pep->ep_state & EP_UNCONFIGURED)) + ret = cdnsp_update_eps_configuration(pdev, pep); + cdnsp_free_endpoint_rings(pdev, pep); - pep->ep_state &= ~EP_ENABLED; + pep->ep_state &= ~(EP_ENABLED | EP_UNCONFIGURED); pep->ep_state |= EP_STOPPED; finish: diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index 6bbb26548c04..783ca8ffde00 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -835,6 +835,7 @@ struct cdnsp_ep { #define EP_WEDGE BIT(4) #define EP0_HALTED_STATUS BIT(5) #define EP_HAS_STREAMS BIT(6) +#define EP_UNCONFIGURED BIT(7) bool skip; }; diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 7a84e928710e..5d4c4bfe15b7 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -686,7 +686,7 @@ static void cdnsp_free_priv_device(struct cdnsp_device *pdev) static int cdnsp_alloc_priv_device(struct cdnsp_device *pdev) { - int ret = -ENOMEM; + int ret; ret = cdnsp_init_device_ctx(pdev); if (ret) @@ -1231,7 +1231,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) if (!pdev->dcbaa) return -ENOMEM; - memset(pdev->dcbaa, 0, sizeof(*pdev->dcbaa)); pdev->dcbaa->dma = dma; cdnsp_write_64(dma, &pdev->op_regs->dcbaa_ptr); diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 199713769289..bb739d88179f 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -525,9 +525,36 @@ EXPORT_SYMBOL_GPL(cdns_suspend); int cdns_resume(struct cdns *cdns, u8 set_active) { struct device *dev = cdns->dev; + enum usb_role real_role; + bool role_changed = false; + int ret = 0; + + if (cdns_power_is_lost(cdns)) { + if (cdns->role_sw) { + cdns->role = cdns_role_get(cdns->role_sw); + } else { + real_role = cdns_hw_role_state_machine(cdns); + if (real_role != cdns->role) { + ret = cdns_hw_role_switch(cdns); + if (ret) + return ret; + role_changed = true; + } + } + + if (!role_changed) { + if (cdns->role == USB_ROLE_HOST) + ret = cdns_drd_host_on(cdns); + else if (cdns->role == USB_ROLE_DEVICE) + ret = cdns_drd_gadget_on(cdns); + + if (ret) + return ret; + } + } if (cdns->roles[cdns->role]->resume) - cdns->roles[cdns->role]->resume(cdns, false); + cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns)); if (set_active) { pm_runtime_disable(dev); diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index fa5318ade3e1..55c73b1d8704 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -478,3 +478,18 @@ int cdns_drd_exit(struct cdns *cdns) return 0; } + + +/* Indicate the cdns3 core was power lost before */ +bool cdns_power_is_lost(struct cdns *cdns) +{ + if (cdns->version == CDNS3_CONTROLLER_V1) { + if (!(readl(&cdns->otg_v1_regs->simulate) & BIT(0))) + return true; + } else { + if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0))) + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(cdns_power_is_lost); diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 9724acdecbbb..cbdf94f73ed9 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -215,5 +215,5 @@ int cdns_drd_gadget_on(struct cdns *cdns); void cdns_drd_gadget_off(struct cdns *cdns); int cdns_drd_host_on(struct cdns *cdns); void cdns_drd_host_off(struct cdns *cdns); - +bool cdns_power_is_lost(struct cdns *cdns); #endif /* __LINUX_CDNS3_DRD */ |