summaryrefslogtreecommitdiff
path: root/drivers/usb/cdns3/cdnsp-gadget.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/cdns3/cdnsp-gadget.c')
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c111
1 files changed, 106 insertions, 5 deletions
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index 7a516e509198..e84405352caa 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -1338,7 +1338,6 @@ static int cdnsp_run(struct cdnsp_device *pdev,
cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
-
ret = cdnsp_start(pdev);
if (ret) {
ret = -ENODEV;
@@ -1837,6 +1836,82 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev)
readl(&pdev->rev_cap->tx_buff_size));
}
+static void cdnsp_set_event_deq(struct cdnsp_device *pdev)
+{
+ dma_addr_t deq;
+ u64 temp;
+
+ deq = cdnsp_trb_virt_to_dma(pdev->event_ring->deq_seg,
+ pdev->event_ring->dequeue);
+
+ /* Update controller event ring dequeue pointer */
+ temp = cdnsp_read_64(&pdev->ir_set->erst_dequeue);
+ temp &= ERST_PTR_MASK;
+
+ /*
+ * Don't clear the EHB bit (which is RW1C) because
+ * there might be more events to service.
+ */
+ temp &= ~ERST_EHB;
+
+ cdnsp_write_64(((u64)deq & (u64)~ERST_PTR_MASK) | temp,
+ &pdev->ir_set->erst_dequeue);
+}
+
+static void cdnsp_add_interrupter(struct cdnsp_device *pdev)
+{
+ u64 erst_base;
+ u32 erst_size;
+
+ /* Set ERST count with the number of entries in the segment table. */
+ erst_size = readl(&pdev->ir_set->erst_size);
+ erst_size &= ERST_SIZE_MASK;
+ erst_size |= ERST_NUM_SEGS;
+ writel(erst_size, &pdev->ir_set->erst_size);
+
+ /* Set the segment table base address. */
+ erst_base = cdnsp_read_64(&pdev->ir_set->erst_base);
+ erst_base &= ERST_PTR_MASK;
+ erst_base |= (pdev->erst.erst_dma_addr & (u64)~ERST_PTR_MASK);
+ cdnsp_write_64(erst_base, &pdev->ir_set->erst_base);
+
+ /* Set the event ring dequeue address. */
+ cdnsp_set_event_deq(pdev);
+}
+
+/* Set up basic CDNSP registers */
+static void cdnsp_init(struct cdnsp_device *pdev)
+{
+ unsigned int val;
+ u64 val_64;
+
+ val = readl(&pdev->op_regs->config_reg);
+ val |= ((val & ~MAX_DEVS) | CDNSP_DEV_MAX_SLOTS) | CONFIG_U3E;
+ writel(val, &pdev->op_regs->config_reg);
+
+ /* Initialize the Command ring */
+ cdnsp_ring_init(pdev, pdev->cmd_ring);
+
+ /* Set the address in the Command Ring Control register */
+ val_64 = cdnsp_read_64(&pdev->op_regs->cmd_ring);
+ val_64 = (val_64 & (u64)CMD_RING_RSVD_BITS) |
+ (pdev->cmd_ring->first_seg->dma & (u64)~CMD_RING_RSVD_BITS) |
+ pdev->cmd_ring->cycle_state;
+ cdnsp_write_64(val_64, &pdev->op_regs->cmd_ring);
+
+ /* Set Device Context Base Address Array pointer */
+ cdnsp_write_64(pdev->dcbaa->dma, &pdev->op_regs->dcbaa_ptr);
+
+ /* Set Doorbell array pointer */
+ val = readl(&pdev->cap_regs->db_off);
+ val &= DBOFF_MASK;
+ pdev->dba = (void __iomem *)pdev->cap_regs + val;
+
+ /* Initialize the Primary interrupter */
+ cdnsp_ring_init(pdev, pdev->event_ring);
+ cdnsp_add_interrupter(pdev);
+}
+
static int cdnsp_gen_setup(struct cdnsp_device *pdev)
{
int ret;
@@ -1902,6 +1977,8 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev)
if (ret)
return ret;
+ cdnsp_init(pdev);
+
/*
* Software workaround for U1: after transition
* to U1 the controller starts gating clock, and in some cases,
@@ -2031,9 +2108,6 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup)
struct cdnsp_device *pdev = cdns->gadget_dev;
unsigned long flags;
- if (pdev->link_state == XDEV_U3)
- return 0;
-
spin_lock_irqsave(&pdev->lock, flags);
cdnsp_disconnect_gadget(pdev);
cdnsp_stop(pdev);
@@ -2047,12 +2121,38 @@ static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power)
struct cdnsp_device *pdev = cdns->gadget_dev;
enum usb_device_speed max_speed;
unsigned long flags;
+ bool context_lost;
+ u32 val;
int ret;
if (!pdev->gadget_driver)
return 0;
spin_lock_irqsave(&pdev->lock, flags);
+ val = readl(&pdev->port3x_regs->mode_2);
+ context_lost = !!(val & CFG_3XPORT_U1_PIPE_CLK_GATE_EN) || lost_power;
+
+ if (context_lost) {
+ cdnsp_halt(pdev);
+ cdnsp_set_apb_timeout_value(pdev);
+
+ /* Reset the internal controller memory state and registers. */
+ ret = cdnsp_reset(pdev);
+ if (ret)
+ goto unlock;
+
+ val = readl(&pdev->port3x_regs->mode_2);
+ val &= ~CFG_3XPORT_U1_PIPE_CLK_GATE_EN;
+ writel(val, &pdev->port3x_regs->mode_2);
+
+ cdnsp_clear_cmd_ring(pdev);
+
+ memset(pdev->event_ring->first_seg->trbs, 0,
+ sizeof(union cdnsp_trb) * (TRBS_PER_SEGMENT));
+
+ cdnsp_init(pdev);
+ }
+
max_speed = pdev->gadget_driver->max_speed;
/* Limit speed if necessary. */
@@ -2060,9 +2160,10 @@ static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power)
ret = cdnsp_run(pdev, max_speed);
- if (pdev->link_state == XDEV_U3)
+ if (!context_lost && pdev->link_state == XDEV_U3)
__cdnsp_gadget_wakeup(pdev);
+unlock:
spin_unlock_irqrestore(&pdev->lock, flags);
return ret;