--- /dev/null
+From f8c63edfd78905320e86b6b2be2b7a5ac768fa4e Mon Sep 17 00:00:00 2001
+From: Fredrik Noring <noring@nocrew.org>
+Date: Tue, 10 Dec 2019 18:29:05 +0100
+Subject: USB: Fix incorrect DMA allocations for local memory pool drivers
+
+From: Fredrik Noring <noring@nocrew.org>
+
+commit f8c63edfd78905320e86b6b2be2b7a5ac768fa4e upstream.
+
+Fix commit 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of
+guestimating DMA capabilities") where local memory USB drivers
+erroneously allocate DMA memory instead of pool memory, causing
+
+ OHCI Unrecoverable Error, disabled
+ HC died; cleaning up
+
+The order between hcd_uses_dma() and hcd->localmem_pool is now
+arranged as in hcd_buffer_alloc() and hcd_buffer_free(), with the
+test for hcd->localmem_pool placed first.
+
+As an alternative, one might consider adjusting hcd_uses_dma() with
+
+ static inline bool hcd_uses_dma(struct usb_hcd *hcd)
+ {
+- return IS_ENABLED(CONFIG_HAS_DMA) && (hcd->driver->flags & HCD_DMA);
++ return IS_ENABLED(CONFIG_HAS_DMA) &&
++ (hcd->driver->flags & HCD_DMA) &&
++ (hcd->localmem_pool == NULL);
+ }
+
+One can also consider unsetting HCD_DMA for local memory pool drivers.
+
+Fixes: 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of guestimating DMA capabilities")
+Cc: stable <stable@vger.kernel.org>
+Signed-off-by: Fredrik Noring <noring@nocrew.org>
+Link: https://lore.kernel.org/r/20191210172905.GA52526@sx9
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hcd.c | 42 ++++++++++++++++++++---------------------
+ drivers/usb/storage/scsiglue.c | 3 +-
+ 2 files changed, 23 insertions(+), 22 deletions(-)
+
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -1409,7 +1409,17 @@ int usb_hcd_map_urb_for_dma(struct usb_h
+ if (usb_endpoint_xfer_control(&urb->ep->desc)) {
+ if (hcd->self.uses_pio_for_control)
+ return ret;
+- if (hcd_uses_dma(hcd)) {
++ if (hcd->localmem_pool) {
++ ret = hcd_alloc_coherent(
++ urb->dev->bus, mem_flags,
++ &urb->setup_dma,
++ (void **)&urb->setup_packet,
++ sizeof(struct usb_ctrlrequest),
++ DMA_TO_DEVICE);
++ if (ret)
++ return ret;
++ urb->transfer_flags |= URB_SETUP_MAP_LOCAL;
++ } else if (hcd_uses_dma(hcd)) {
+ if (is_vmalloc_addr(urb->setup_packet)) {
+ WARN_ONCE(1, "setup packet is not dma capable\n");
+ return -EAGAIN;
+@@ -1427,23 +1437,22 @@ int usb_hcd_map_urb_for_dma(struct usb_h
+ urb->setup_dma))
+ return -EAGAIN;
+ urb->transfer_flags |= URB_SETUP_MAP_SINGLE;
+- } else if (hcd->localmem_pool) {
+- ret = hcd_alloc_coherent(
+- urb->dev->bus, mem_flags,
+- &urb->setup_dma,
+- (void **)&urb->setup_packet,
+- sizeof(struct usb_ctrlrequest),
+- DMA_TO_DEVICE);
+- if (ret)
+- return ret;
+- urb->transfer_flags |= URB_SETUP_MAP_LOCAL;
+ }
+ }
+
+ dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ if (urb->transfer_buffer_length != 0
+ && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+- if (hcd_uses_dma(hcd)) {
++ if (hcd->localmem_pool) {
++ ret = hcd_alloc_coherent(
++ urb->dev->bus, mem_flags,
++ &urb->transfer_dma,
++ &urb->transfer_buffer,
++ urb->transfer_buffer_length,
++ dir);
++ if (ret == 0)
++ urb->transfer_flags |= URB_MAP_LOCAL;
++ } else if (hcd_uses_dma(hcd)) {
+ if (urb->num_sgs) {
+ int n;
+
+@@ -1497,15 +1506,6 @@ int usb_hcd_map_urb_for_dma(struct usb_h
+ else
+ urb->transfer_flags |= URB_DMA_MAP_SINGLE;
+ }
+- } else if (hcd->localmem_pool) {
+- ret = hcd_alloc_coherent(
+- urb->dev->bus, mem_flags,
+- &urb->transfer_dma,
+- &urb->transfer_buffer,
+- urb->transfer_buffer_length,
+- dir);
+- if (ret == 0)
+- urb->transfer_flags |= URB_MAP_LOCAL;
+ }
+ if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
+ URB_SETUP_MAP_LOCAL)))
+--- a/drivers/usb/storage/scsiglue.c
++++ b/drivers/usb/storage/scsiglue.c
+@@ -135,7 +135,8 @@ static int slave_configure(struct scsi_d
+ * For such controllers we need to make sure the block layer sets
+ * up bounce buffers in addressable memory.
+ */
+- if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)))
++ if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) ||
++ (bus_to_hcd(us->pusb_dev->bus)->localmem_pool != NULL))
+ blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH);
+
+ /*