Commit 9bc46a12 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'usb-v5.13-rc1' of...

Merge tag 'usb-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb into usb-next

Peter writes:

Several Cadence3 improvements are introduced in v5.13-rc1:
- Add recovery during resume if the controller was lost power at system suspend
- Reduce DMA memory footprint
- Other small improvements

* tag 'usb-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb:
  usb: cdnsp: Fixes issue with Configure Endpoint command
  usb: cdnsp: remove redundant initialization of variable ret
  usb: cdns3: delete repeated clear operations
  usb: cdns3: Optimize DMA request buffer allocation
  usb: cdns3: Use dma_pool_* api to alloc trb pool
  usb: cdns3: fix static checker warning.
  usb: cdns3: imx: mark cdns_imx_system_resume as __maybe_unused
  usb: cdns3: trace: delete the trace parameter for request->trb
  usb: cdns3: imx: add power lost support for system resume
  usb: cdns3: add power lost support for system resume
parents 5bdb080f 10076de3
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/dmapool.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include "core.h" #include "core.h"
...@@ -190,28 +191,12 @@ dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep, ...@@ -190,28 +191,12 @@ dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
return priv_ep->trb_pool_dma + offset; 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) static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
{ {
struct cdns3_device *priv_dev = priv_ep->cdns3_dev; struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
if (priv_ep->trb_pool) { if (priv_ep->trb_pool) {
dma_free_coherent(priv_dev->sysdev, dma_pool_free(priv_dev->eps_dma_pool,
cdns3_ring_size(priv_ep),
priv_ep->trb_pool, priv_ep->trb_pool_dma); priv_ep->trb_pool, priv_ep->trb_pool_dma);
priv_ep->trb_pool = NULL; priv_ep->trb_pool = NULL;
} }
...@@ -226,7 +211,7 @@ static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep) ...@@ -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) int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
{ {
struct cdns3_device *priv_dev = priv_ep->cdns3_dev; 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; int num_trbs = ring_size / TRB_SIZE;
struct cdns3_trb *link_trb; struct cdns3_trb *link_trb;
...@@ -234,10 +219,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) ...@@ -234,10 +219,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
cdns3_free_trb_pool(priv_ep); cdns3_free_trb_pool(priv_ep);
if (!priv_ep->trb_pool) { if (!priv_ep->trb_pool) {
priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev, priv_ep->trb_pool = dma_pool_alloc(priv_dev->eps_dma_pool,
ring_size, GFP_DMA32 | GFP_ATOMIC,
&priv_ep->trb_pool_dma, &priv_ep->trb_pool_dma);
GFP_DMA32 | GFP_ATOMIC);
if (!priv_ep->trb_pool) if (!priv_ep->trb_pool)
return -ENOMEM; return -ENOMEM;
...@@ -834,9 +819,15 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, ...@@ -834,9 +819,15 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
priv_ep->dir); priv_ep->dir);
if ((priv_req->flags & REQUEST_UNALIGNED) && 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, memcpy(request->buf, priv_req->aligned_buf->buf,
request->length); request->length);
}
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
/* All TRBs have finished, clear the counter */ /* All TRBs have finished, clear the counter */
...@@ -898,8 +889,8 @@ static void cdns3_free_aligned_request_buf(struct work_struct *work) ...@@ -898,8 +889,8 @@ static void cdns3_free_aligned_request_buf(struct work_struct *work)
* interrupts. * interrupts.
*/ */
spin_unlock_irqrestore(&priv_dev->lock, flags); spin_unlock_irqrestore(&priv_dev->lock, flags);
dma_free_coherent(priv_dev->sysdev, buf->size, dma_free_noncoherent(priv_dev->sysdev, buf->size,
buf->buf, buf->dma); buf->buf, buf->dma, buf->dir);
kfree(buf); kfree(buf);
spin_lock_irqsave(&priv_dev->lock, flags); spin_lock_irqsave(&priv_dev->lock, flags);
} }
...@@ -926,10 +917,13 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) ...@@ -926,10 +917,13 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
return -ENOMEM; return -ENOMEM;
buf->size = priv_req->request.length; 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->size,
&buf->dma, &buf->dma,
buf->dir,
GFP_ATOMIC); GFP_ATOMIC);
if (!buf->buf) { if (!buf->buf) {
kfree(buf); kfree(buf);
...@@ -951,10 +945,17 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) ...@@ -951,10 +945,17 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
} }
if (priv_ep->dir == USB_DIR_IN) { 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, memcpy(buf->buf, priv_req->request.buf,
priv_req->request.length); 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; priv_req->flags |= REQUEST_UNALIGNED;
trace_cdns3_prepare_aligned_request(priv_req); trace_cdns3_prepare_aligned_request(priv_req);
...@@ -3103,9 +3104,10 @@ static void cdns3_gadget_exit(struct cdns *cdns) ...@@ -3103,9 +3104,10 @@ static void cdns3_gadget_exit(struct cdns *cdns)
struct cdns3_aligned_buf *buf; struct cdns3_aligned_buf *buf;
buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list); 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->buf,
buf->dma); buf->dma,
buf->dir);
list_del(&buf->list); list_del(&buf->list);
kfree(buf); kfree(buf);
...@@ -3113,6 +3115,7 @@ static void cdns3_gadget_exit(struct cdns *cdns) ...@@ -3113,6 +3115,7 @@ static void cdns3_gadget_exit(struct cdns *cdns)
dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf, dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
priv_dev->setup_dma); priv_dev->setup_dma);
dma_pool_destroy(priv_dev->eps_dma_pool);
kfree(priv_dev->zlp_buf); kfree(priv_dev->zlp_buf);
usb_put_gadget(&priv_dev->gadget); usb_put_gadget(&priv_dev->gadget);
...@@ -3185,6 +3188,14 @@ static int cdns3_gadget_start(struct cdns *cdns) ...@@ -3185,6 +3188,14 @@ static int cdns3_gadget_start(struct cdns *cdns)
/* initialize endpoint container */ /* initialize endpoint container */
INIT_LIST_HEAD(&priv_dev->gadget.ep_list); INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
INIT_LIST_HEAD(&priv_dev->aligned_buf_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); ret = cdns3_init_eps(priv_dev);
if (ret) { if (ret) {
...@@ -3235,6 +3246,8 @@ static int cdns3_gadget_start(struct cdns *cdns) ...@@ -3235,6 +3246,8 @@ static int cdns3_gadget_start(struct cdns *cdns)
err2: err2:
cdns3_free_all_eps(priv_dev); cdns3_free_all_eps(priv_dev);
err1: err1:
dma_pool_destroy(priv_dev->eps_dma_pool);
usb_put_gadget(&priv_dev->gadget); usb_put_gadget(&priv_dev->gadget);
cdns->gadget_dev = NULL; cdns->gadget_dev = NULL;
return ret; return ret;
...@@ -3304,6 +3317,8 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) ...@@ -3304,6 +3317,8 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
return 0; return 0;
cdns3_gadget_config(priv_dev); cdns3_gadget_config(priv_dev);
if (hibernated)
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
return 0; return 0;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#ifndef __LINUX_CDNS3_GADGET #ifndef __LINUX_CDNS3_GADGET
#define __LINUX_CDNS3_GADGET #define __LINUX_CDNS3_GADGET
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/dma-direction.h>
/* /*
* USBSS-DEV register interface. * USBSS-DEV register interface.
...@@ -1205,6 +1206,7 @@ struct cdns3_aligned_buf { ...@@ -1205,6 +1206,7 @@ struct cdns3_aligned_buf {
void *buf; void *buf;
dma_addr_t dma; dma_addr_t dma;
u32 size; u32 size;
enum dma_data_direction dir;
unsigned in_use:1; unsigned in_use:1;
struct list_head list; struct list_head list;
}; };
...@@ -1298,6 +1300,7 @@ struct cdns3_device { ...@@ -1298,6 +1300,7 @@ struct cdns3_device {
struct cdns3_usb_regs __iomem *regs; struct cdns3_usb_regs __iomem *regs;
struct dma_pool *eps_dma_pool;
struct usb_ctrlrequest *setup_buf; struct usb_ctrlrequest *setup_buf;
dma_addr_t setup_dma; dma_addr_t setup_dma;
void *zlp_buf; void *zlp_buf;
......
...@@ -361,6 +361,39 @@ static int cdns_imx_suspend(struct device *dev) ...@@ -361,6 +361,39 @@ static int cdns_imx_suspend(struct device *dev)
return 0; 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 #else
static int cdns_imx_platform_suspend(struct device *dev, static int cdns_imx_platform_suspend(struct device *dev,
bool suspend, bool wakeup) bool suspend, bool wakeup)
...@@ -372,6 +405,7 @@ static int cdns_imx_platform_suspend(struct device *dev, ...@@ -372,6 +405,7 @@ static int cdns_imx_platform_suspend(struct device *dev,
static const struct dev_pm_ops cdns_imx_pm_ops = { static const struct dev_pm_ops cdns_imx_pm_ops = {
SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) 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[] = { static const struct of_device_id cdns_imx_of_match[] = {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "core.h" #include "core.h"
#include "gadget-export.h" #include "gadget-export.h"
#include "drd.h"
static int set_phy_power_on(struct cdns *cdns) 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) ...@@ -236,6 +237,18 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
if (!cdns->in_lpm) if (!cdns->in_lpm)
return 0; 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); ret = set_phy_power_on(cdns);
if (ret) if (ret)
return ret; return ret;
...@@ -270,10 +283,18 @@ static int cdns3_plat_runtime_resume(struct device *dev) ...@@ -270,10 +283,18 @@ static int cdns3_plat_runtime_resume(struct device *dev)
static int cdns3_plat_suspend(struct device *dev) static int cdns3_plat_suspend(struct device *dev)
{ {
struct cdns *cdns = dev_get_drvdata(dev); struct cdns *cdns = dev_get_drvdata(dev);
int ret;
cdns_suspend(cdns); 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) static int cdns3_plat_resume(struct device *dev)
......
...@@ -214,7 +214,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request, ...@@ -214,7 +214,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
__field(int, no_interrupt) __field(int, no_interrupt)
__field(int, start_trb) __field(int, start_trb)
__field(int, end_trb) __field(int, end_trb)
__field(struct cdns3_trb *, start_trb_addr)
__field(int, flags) __field(int, flags)
__field(unsigned int, stream_id) __field(unsigned int, stream_id)
), ),
...@@ -230,12 +229,11 @@ DECLARE_EVENT_CLASS(cdns3_log_request, ...@@ -230,12 +229,11 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
__entry->no_interrupt = req->request.no_interrupt; __entry->no_interrupt = req->request.no_interrupt;
__entry->start_trb = req->start_trb; __entry->start_trb = req->start_trb;
__entry->end_trb = req->end_trb; __entry->end_trb = req->end_trb;
__entry->start_trb_addr = req->trb;
__entry->flags = req->flags; __entry->flags = req->flags;
__entry->stream_id = req->request.stream_id; __entry->stream_id = req->request.stream_id;
), ),
TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d," 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, __get_str(name), __entry->req, __entry->buf, __entry->actual,
__entry->length, __entry->length,
__entry->zero ? "Z" : "z", __entry->zero ? "Z" : "z",
...@@ -244,7 +242,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request, ...@@ -244,7 +242,6 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
__entry->status, __entry->status,
__entry->start_trb, __entry->start_trb,
__entry->end_trb, __entry->end_trb,
__entry->start_trb_addr,
__entry->flags, __entry->flags,
__entry->stream_id __entry->stream_id
) )
......
...@@ -727,7 +727,7 @@ int cdnsp_reset_device(struct cdnsp_device *pdev) ...@@ -727,7 +727,7 @@ int cdnsp_reset_device(struct cdnsp_device *pdev)
* are in Disabled state. * are in Disabled state.
*/ */
for (i = 1; i < CDNSP_ENDPOINTS_NUM; ++i) 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); trace_cdnsp_handle_cmd_reset_dev(slot_ctx);
...@@ -942,6 +942,7 @@ static int cdnsp_gadget_ep_enable(struct usb_ep *ep, ...@@ -942,6 +942,7 @@ static int cdnsp_gadget_ep_enable(struct usb_ep *ep,
pep = to_cdnsp_ep(ep); pep = to_cdnsp_ep(ep);
pdev = pep->pdev; pdev = pep->pdev;
pep->ep_state &= ~EP_UNCONFIGURED;
if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED, if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED,
"%s is already enabled\n", pep->name)) "%s is already enabled\n", pep->name))
...@@ -1023,9 +1024,13 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) ...@@ -1023,9 +1024,13 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep)
goto finish; goto finish;
} }
cdnsp_cmd_stop_ep(pdev, pep);
pep->ep_state |= EP_DIS_IN_RROGRESS; pep->ep_state |= EP_DIS_IN_RROGRESS;
/* 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); cdnsp_cmd_flush_ep(pdev, pep);
}
/* Remove all queued USB requests. */ /* Remove all queued USB requests. */
while (!list_empty(&pep->pending_list)) { while (!list_empty(&pep->pending_list)) {
...@@ -1043,10 +1048,12 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) ...@@ -1043,10 +1048,12 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep)
cdnsp_endpoint_zero(pdev, pep); cdnsp_endpoint_zero(pdev, pep);
if (!(pep->ep_state & EP_UNCONFIGURED))
ret = cdnsp_update_eps_configuration(pdev, pep); ret = cdnsp_update_eps_configuration(pdev, pep);
cdnsp_free_endpoint_rings(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; pep->ep_state |= EP_STOPPED;
finish: finish:
......
...@@ -835,6 +835,7 @@ struct cdnsp_ep { ...@@ -835,6 +835,7 @@ struct cdnsp_ep {
#define EP_WEDGE BIT(4) #define EP_WEDGE BIT(4)
#define EP0_HALTED_STATUS BIT(5) #define EP0_HALTED_STATUS BIT(5)
#define EP_HAS_STREAMS BIT(6) #define EP_HAS_STREAMS BIT(6)
#define EP_UNCONFIGURED BIT(7)
bool skip; bool skip;
}; };
......
...@@ -686,7 +686,7 @@ static void cdnsp_free_priv_device(struct cdnsp_device *pdev) ...@@ -686,7 +686,7 @@ static void cdnsp_free_priv_device(struct cdnsp_device *pdev)
static int cdnsp_alloc_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); ret = cdnsp_init_device_ctx(pdev);
if (ret) if (ret)
...@@ -1231,7 +1231,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) ...@@ -1231,7 +1231,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev)
if (!pdev->dcbaa) if (!pdev->dcbaa)
return -ENOMEM; return -ENOMEM;
memset(pdev->dcbaa, 0, sizeof(*pdev->dcbaa));
pdev->dcbaa->dma = dma; pdev->dcbaa->dma = dma;
cdnsp_write_64(dma, &pdev->op_regs->dcbaa_ptr); cdnsp_write_64(dma, &pdev->op_regs->dcbaa_ptr);
......
...@@ -525,9 +525,36 @@ EXPORT_SYMBOL_GPL(cdns_suspend); ...@@ -525,9 +525,36 @@ EXPORT_SYMBOL_GPL(cdns_suspend);
int cdns_resume(struct cdns *cdns, u8 set_active) int cdns_resume(struct cdns *cdns, u8 set_active)
{ {
struct device *dev = cdns->dev; 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) 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) { if (set_active) {
pm_runtime_disable(dev); pm_runtime_disable(dev);
......
...@@ -478,3 +478,18 @@ int cdns_drd_exit(struct cdns *cdns) ...@@ -478,3 +478,18 @@ int cdns_drd_exit(struct cdns *cdns)
return 0; 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);
...@@ -215,5 +215,5 @@ int cdns_drd_gadget_on(struct cdns *cdns); ...@@ -215,5 +215,5 @@ int cdns_drd_gadget_on(struct cdns *cdns);
void cdns_drd_gadget_off(struct cdns *cdns); void cdns_drd_gadget_off(struct cdns *cdns);
int cdns_drd_host_on(struct cdns *cdns); int cdns_drd_host_on(struct cdns *cdns);
void cdns_drd_host_off(struct cdns *cdns); void cdns_drd_host_off(struct cdns *cdns);
bool cdns_power_is_lost(struct cdns *cdns);
#endif /* __LINUX_CDNS3_DRD */ #endif /* __LINUX_CDNS3_DRD */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment