diff options
Diffstat (limited to 'drivers/usb/core/buffer.c')
| -rw-r--r-- | drivers/usb/core/buffer.c | 86 |
1 files changed, 64 insertions, 22 deletions
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index b64568cf572c..87230869e1fa 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * DMA memory management for framework level HCD code (hc_driver) * @@ -5,7 +6,6 @@ * and should work with all USB controllers, regardless of bus type. * * Released under the GPLv2 only. - * SPDX-License-Identifier: GPL-2.0 */ #include <linux/module.h> @@ -16,6 +16,7 @@ #include <linux/io.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> +#include <linux/genalloc.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -33,13 +34,13 @@ void __init usb_init_pool_max(void) { /* * The pool_max values must never be smaller than - * ARCH_KMALLOC_MINALIGN. + * ARCH_DMA_MINALIGN. */ - if (ARCH_KMALLOC_MINALIGN <= 32) + if (ARCH_DMA_MINALIGN <= 32) ; /* Original value is okay */ - else if (ARCH_KMALLOC_MINALIGN <= 64) + else if (ARCH_DMA_MINALIGN <= 64) pool_max[0] = 64; - else if (ARCH_KMALLOC_MINALIGN <= 128) + else if (ARCH_DMA_MINALIGN <= 128) pool_max[0] = 0; /* Don't use this pool */ else BUILD_BUG(); /* We don't allow this */ @@ -50,7 +51,8 @@ void __init usb_init_pool_max(void) /** * hcd_buffer_create - initialize buffer pools * @hcd: the bus whose buffer pools are to be initialized - * Context: !in_interrupt() + * + * Context: task context, might sleep * * Call this as part of initializing a host controller that uses the dma * memory allocators. It initializes some pools of dma-coherent memory that @@ -65,9 +67,7 @@ int hcd_buffer_create(struct usb_hcd *hcd) char name[16]; int i, size; - if (!IS_ENABLED(CONFIG_HAS_DMA) || - (!is_device_dma_capable(hcd->self.sysdev) && - !(hcd->driver->flags & HCD_LOCAL_MEM))) + if (hcd->localmem_pool || !hcd_uses_dma(hcd)) return 0; for (i = 0; i < HCD_BUFFER_POOLS; i++) { @@ -89,7 +89,8 @@ int hcd_buffer_create(struct usb_hcd *hcd) /** * hcd_buffer_destroy - deallocate buffer pools * @hcd: the bus whose buffer pools are to be destroyed - * Context: !in_interrupt() + * + * Context: task context, might sleep * * This frees the buffer pools created by hcd_buffer_create(). */ @@ -101,12 +102,8 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) return; for (i = 0; i < HCD_BUFFER_POOLS; i++) { - struct dma_pool *pool = hcd->pool[i]; - - if (pool) { - dma_pool_destroy(pool); - hcd->pool[i] = NULL; - } + dma_pool_destroy(hcd->pool[i]); + hcd->pool[i] = NULL; } } @@ -128,10 +125,11 @@ void *hcd_buffer_alloc( if (size == 0) return NULL; + if (hcd->localmem_pool) + return gen_pool_dma_alloc(hcd->localmem_pool, size, dma); + /* some USB hosts just use PIO */ - if (!IS_ENABLED(CONFIG_HAS_DMA) || - (!is_device_dma_capable(bus->sysdev) && - !(hcd->driver->flags & HCD_LOCAL_MEM))) { + if (!hcd_uses_dma(hcd)) { *dma = ~(dma_addr_t) 0; return kmalloc(size, mem_flags); } @@ -156,9 +154,12 @@ void hcd_buffer_free( if (!addr) return; - if (!IS_ENABLED(CONFIG_HAS_DMA) || - (!is_device_dma_capable(bus->sysdev) && - !(hcd->driver->flags & HCD_LOCAL_MEM))) { + if (hcd->localmem_pool) { + gen_pool_free(hcd->localmem_pool, (unsigned long)addr, size); + return; + } + + if (!hcd_uses_dma(hcd)) { kfree(addr); return; } @@ -171,3 +172,44 @@ void hcd_buffer_free( } dma_free_coherent(hcd->self.sysdev, size, addr, dma); } + +void *hcd_buffer_alloc_pages(struct usb_hcd *hcd, + size_t size, gfp_t mem_flags, dma_addr_t *dma) +{ + if (size == 0) + return NULL; + + if (hcd->localmem_pool) + return gen_pool_dma_alloc_align(hcd->localmem_pool, + size, dma, PAGE_SIZE); + + /* some USB hosts just use PIO */ + if (!hcd_uses_dma(hcd)) { + *dma = DMA_MAPPING_ERROR; + return (void *)__get_free_pages(mem_flags, + get_order(size)); + } + + return dma_alloc_coherent(hcd->self.sysdev, + size, dma, mem_flags); +} + +void hcd_buffer_free_pages(struct usb_hcd *hcd, + size_t size, void *addr, dma_addr_t dma) +{ + if (!addr) + return; + + if (hcd->localmem_pool) { + gen_pool_free(hcd->localmem_pool, + (unsigned long)addr, size); + return; + } + + if (!hcd_uses_dma(hcd)) { + free_pages((unsigned long)addr, get_order(size)); + return; + } + + dma_free_coherent(hcd->self.sysdev, size, addr, dma); +} |
