xhci: Refactor interrupter code for initial multi interrupter support.
[platform/kernel/linux-rpi.git] / drivers / usb / host / xhci-mem.c
index bf9bb29..6b83c5c 100644 (file)
@@ -1819,17 +1819,43 @@ int xhci_alloc_erst(struct xhci_hcd *xhci,
        return 0;
 }
 
-void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
+static void
+xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
 {
-       size_t size;
        struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+       size_t erst_size;
+       u64 tmp64;
+       u32 tmp;
 
-       size = sizeof(struct xhci_erst_entry) * (erst->num_entries);
-       if (erst->entries)
-               dma_free_coherent(dev, size,
-                               erst->entries,
-                               erst->erst_dma_addr);
-       erst->entries = NULL;
+       if (!ir)
+               return;
+
+       erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries);
+       if (ir->erst.entries)
+               dma_free_coherent(dev, erst_size,
+                                 ir->erst.entries,
+                                 ir->erst.erst_dma_addr);
+       ir->erst.entries = NULL;
+
+       /*
+        * Clean out interrupter registers except ERSTBA. Clearing either the
+        * low or high 32 bits of ERSTBA immediately causes the controller to
+        * dereference the partially cleared 64 bit address, causing IOMMU error.
+        */
+       tmp = readl(&ir->ir_set->erst_size);
+       tmp &= ERST_SIZE_MASK;
+       writel(tmp, &ir->ir_set->erst_size);
+
+       tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
+       tmp64 &= (u64) ERST_PTR_MASK;
+       xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
+
+       /* free interrrupter event ring */
+       if (ir->event_ring)
+               xhci_ring_free(xhci, ir->event_ring);
+       ir->event_ring = NULL;
+
+       kfree(ir);
 }
 
 void xhci_mem_cleanup(struct xhci_hcd *xhci)
@@ -1839,12 +1865,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 
        cancel_delayed_work_sync(&xhci->cmd_timer);
 
-       xhci_free_erst(xhci, &xhci->erst);
-
-       if (xhci->event_ring)
-               xhci_ring_free(xhci, xhci->event_ring);
-       xhci->event_ring = NULL;
-       xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring");
+       xhci_free_interrupter(xhci, xhci->interrupter);
+       xhci->interrupter = NULL;
+       xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");
 
        if (xhci->cmd_ring)
                xhci_ring_free(xhci, xhci->cmd_ring);
@@ -1929,18 +1952,18 @@ no_bw:
        xhci->usb3_rhub.bus_state.bus_suspended = 0;
 }
 
-static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
+static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
 {
        u64 temp;
        dma_addr_t deq;
 
-       deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
-                       xhci->event_ring->dequeue);
+       deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg,
+                       ir->event_ring->dequeue);
        if (!deq)
                xhci_warn(xhci, "WARN something wrong with SW event ring "
                                "dequeue ptr.\n");
        /* Update HC event ring dequeue pointer */
-       temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+       temp = xhci_read_64(xhci, &ir->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.
@@ -1950,7 +1973,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
                        "// Write event ring dequeue pointer, "
                        "preserving EHB bit");
        xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
-                       &xhci->ir_set->erst_dequeue);
+                       &ir->ir_set->erst_dequeue);
 }
 
 static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
@@ -2217,6 +2240,68 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
        return 0;
 }
 
+static struct xhci_interrupter *
+xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags)
+{
+       struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+       struct xhci_interrupter *ir;
+       u64 erst_base;
+       u32 erst_size;
+       int ret;
+
+       if (intr_num > xhci->max_interrupters) {
+               xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n",
+                         intr_num, xhci->max_interrupters);
+               return NULL;
+       }
+
+       if (xhci->interrupter) {
+               xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num);
+               return NULL;
+       }
+
+       ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
+       if (!ir)
+               return NULL;
+
+       ir->ir_set = &xhci->run_regs->ir_set[intr_num];
+       ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
+                                       0, flags);
+       if (!ir->event_ring) {
+               xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num);
+               goto fail_ir;
+       }
+
+       ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags);
+       if (ret) {
+               xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num);
+               goto fail_ev;
+
+       }
+       /* set ERST count with the number of entries in the segment table */
+       erst_size = readl(&ir->ir_set->erst_size);
+       erst_size &= ERST_SIZE_MASK;
+       erst_size |= ERST_NUM_SEGS;
+       writel(erst_size, &ir->ir_set->erst_size);
+
+       erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
+       erst_base &= ERST_PTR_MASK;
+       erst_base |= (ir->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK);
+       xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
+
+       /* Set the event ring dequeue address of this interrupter */
+       xhci_set_hc_event_deq(xhci, ir);
+
+       return ir;
+
+fail_ev:
+       xhci_ring_free(xhci, ir->event_ring);
+fail_ir:
+       kfree(ir);
+
+       return NULL;
+}
+
 int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 {
        dma_addr_t      dma;
@@ -2224,7 +2309,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        unsigned int    val, val2;
        u64             val_64;
        u32             page_size, temp;
-       int             i, ret;
+       int             i;
 
        INIT_LIST_HEAD(&xhci->cmd_list);
 
@@ -2337,46 +2422,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
                        " from cap regs base addr", val);
        xhci->dba = (void __iomem *) xhci->cap_regs + val;
        /* Set ir_set to interrupt register set 0 */
-       xhci->ir_set = &xhci->run_regs->ir_set[0];
-
-       /*
-        * Event ring setup: Allocate a normal ring, but also setup
-        * the event ring segment table (ERST).  Section 4.9.3.
-        */
-       xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring");
-       xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
-                                       0, flags);
-       if (!xhci->event_ring)
-               goto fail;
-
-       ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags);
-       if (ret)
-               goto fail;
-
-       /* set ERST count with the number of entries in the segment table */
-       val = readl(&xhci->ir_set->erst_size);
-       val &= ERST_SIZE_MASK;
-       val |= ERST_NUM_SEGS;
-       xhci_dbg_trace(xhci, trace_xhci_dbg_init,
-                       "// Write ERST size = %i to ir_set 0 (some bits preserved)",
-                       val);
-       writel(val, &xhci->ir_set->erst_size);
 
+       /* allocate and set up primary interrupter with an event ring. */
        xhci_dbg_trace(xhci, trace_xhci_dbg_init,
-                       "// Set ERST entries to point to event ring.");
-       /* set the segment table base address */
-       xhci_dbg_trace(xhci, trace_xhci_dbg_init,
-                       "// Set ERST base address for ir_set 0 = 0x%llx",
-                       (unsigned long long)xhci->erst.erst_dma_addr);
-       val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base);
-       val_64 &= ERST_BASE_RSVDP;
-       val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP);
-       xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base);
-
-       /* Set the event ring dequeue address */
-       xhci_set_hc_event_deq(xhci);
-       xhci_dbg_trace(xhci, trace_xhci_dbg_init,
-                       "Wrote ERST address to ir_set 0.");
+                      "Allocating primary event ring");
+       xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags);
+       if (!xhci->interrupter)
+               goto fail;
 
        xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;