Merge tag 'dma-mapping-5.20-2022-08-06' of git://git.infradead.org/users/hch/dma...
[sfrench/cifs-2.6.git] / drivers / usb / core / hcd.c
index 06eea8848ccc29bb8a98f596d94588e2ce15b51e..94b305bbd621b4b41d90b778c7ebded548ff62df 100644 (file)
@@ -1251,7 +1251,8 @@ void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb)
 EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
 
 /*
- * Some usb host controllers can only perform dma using a small SRAM area.
+ * Some usb host controllers can only perform dma using a small SRAM area,
+ * or have restrictions on addressable DRAM.
  * The usb core itself is however optimized for host controllers that can dma
  * using regular system memory - like pci devices doing bus mastering.
  *
@@ -1691,7 +1692,6 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t)
 
        spin_lock_irq(&bh->lock);
        bh->running = true;
- restart:
        list_replace_init(&bh->head, &local_list);
        spin_unlock_irq(&bh->lock);
 
@@ -1705,10 +1705,17 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t)
                bh->completing_ep = NULL;
        }
 
-       /* check if there are new URBs to giveback */
+       /*
+        * giveback new URBs next time to prevent this function
+        * from not exiting for a long time.
+        */
        spin_lock_irq(&bh->lock);
-       if (!list_empty(&bh->head))
-               goto restart;
+       if (!list_empty(&bh->head)) {
+               if (bh->high_prio)
+                       tasklet_hi_schedule(&bh->bh);
+               else
+                       tasklet_schedule(&bh->bh);
+       }
        bh->running = false;
        spin_unlock_irq(&bh->lock);
 }
@@ -1737,7 +1744,7 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t)
 void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
 {
        struct giveback_urb_bh *bh;
-       bool running, high_prio_bh;
+       bool running;
 
        /* pass status to tasklet via unlinked */
        if (likely(!urb->unlinked))
@@ -1748,13 +1755,10 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
                return;
        }
 
-       if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
+       if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe))
                bh = &hcd->high_prio_bh;
-               high_prio_bh = true;
-       } else {
+       else
                bh = &hcd->low_prio_bh;
-               high_prio_bh = false;
-       }
 
        spin_lock(&bh->lock);
        list_add_tail(&urb->urb_list, &bh->head);
@@ -1763,7 +1767,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
 
        if (running)
                ;
-       else if (high_prio_bh)
+       else if (bh->high_prio)
                tasklet_hi_schedule(&bh->bh);
        else
                tasklet_schedule(&bh->bh);
@@ -2959,6 +2963,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
 
        /* initialize tasklets */
        init_giveback_urb_bh(&hcd->high_prio_bh);
+       hcd->high_prio_bh.high_prio = true;
        init_giveback_urb_bh(&hcd->low_prio_bh);
 
        /* enable irqs just before we start the controller,
@@ -3033,9 +3038,15 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
  */
 void usb_remove_hcd(struct usb_hcd *hcd)
 {
-       struct usb_device *rhdev = hcd->self.root_hub;
+       struct usb_device *rhdev;
        bool rh_registered;
 
+       if (!hcd) {
+               pr_debug("%s: hcd is NULL\n", __func__);
+               return;
+       }
+       rhdev = hcd->self.root_hub;
+
        dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
 
        usb_get_dev(rhdev);
@@ -3117,8 +3128,18 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr,
        if (IS_ERR(hcd->localmem_pool))
                return PTR_ERR(hcd->localmem_pool);
 
-       local_mem = devm_memremap(hcd->self.sysdev, phys_addr,
-                                 size, MEMREMAP_WC);
+       /*
+        * if a physical SRAM address was passed, map it, otherwise
+        * allocate system memory as a buffer.
+        */
+       if (phys_addr)
+               local_mem = devm_memremap(hcd->self.sysdev, phys_addr,
+                                         size, MEMREMAP_WC);
+       else
+               local_mem = dmam_alloc_attrs(hcd->self.sysdev, size, &dma,
+                                            GFP_KERNEL,
+                                            DMA_ATTR_WRITE_COMBINE);
+
        if (IS_ERR(local_mem))
                return PTR_ERR(local_mem);