1aa1f3bb5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * DMA memory management for framework level HCD code (hc_driver)
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * This implementation plugs in through generic "usb_bus" level methods,
6025d4430SRahul Bedarkar * and should work with all USB controllers, regardless of bus type.
7b65fba3dSGreg Kroah-Hartman *
8b65fba3dSGreg Kroah-Hartman * Released under the GPLv2 only.
91da177e4SLinus Torvalds */
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/kernel.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/device.h>
151da177e4SLinus Torvalds #include <linux/mm.h>
160828376dSTobias Ollmann #include <linux/io.h>
171da177e4SLinus Torvalds #include <linux/dma-mapping.h>
181da177e4SLinus Torvalds #include <linux/dmapool.h>
19b0310c2fSLaurentiu Tudor #include <linux/genalloc.h>
201da177e4SLinus Torvalds #include <linux/usb.h>
2127729aadSEric Lescouet #include <linux/usb/hcd.h>
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds
241da177e4SLinus Torvalds /*
251da177e4SLinus Torvalds * DMA-Coherent Buffers
261da177e4SLinus Torvalds */
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds /* FIXME tune these based on pool statistics ... */
295efd2ea8SSebastian Andrzej Siewior static size_t pool_max[HCD_BUFFER_POOLS] = {
305efd2ea8SSebastian Andrzej Siewior 32, 128, 512, 2048,
311da177e4SLinus Torvalds };
321da177e4SLinus Torvalds
usb_init_pool_max(void)335efd2ea8SSebastian Andrzej Siewior void __init usb_init_pool_max(void)
345efd2ea8SSebastian Andrzej Siewior {
355efd2ea8SSebastian Andrzej Siewior /*
365efd2ea8SSebastian Andrzej Siewior * The pool_max values must never be smaller than
37*075efe7cSCatalin Marinas * ARCH_DMA_MINALIGN.
385efd2ea8SSebastian Andrzej Siewior */
39*075efe7cSCatalin Marinas if (ARCH_DMA_MINALIGN <= 32)
405efd2ea8SSebastian Andrzej Siewior ; /* Original value is okay */
41*075efe7cSCatalin Marinas else if (ARCH_DMA_MINALIGN <= 64)
425efd2ea8SSebastian Andrzej Siewior pool_max[0] = 64;
43*075efe7cSCatalin Marinas else if (ARCH_DMA_MINALIGN <= 128)
445efd2ea8SSebastian Andrzej Siewior pool_max[0] = 0; /* Don't use this pool */
455efd2ea8SSebastian Andrzej Siewior else
465efd2ea8SSebastian Andrzej Siewior BUILD_BUG(); /* We don't allow this */
475efd2ea8SSebastian Andrzej Siewior }
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds /* SETUP primitives */
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /**
521da177e4SLinus Torvalds * hcd_buffer_create - initialize buffer pools
531da177e4SLinus Torvalds * @hcd: the bus whose buffer pools are to be initialized
5441631d36SAhmed S. Darwish *
5541631d36SAhmed S. Darwish * Context: task context, might sleep
561da177e4SLinus Torvalds *
571da177e4SLinus Torvalds * Call this as part of initializing a host controller that uses the dma
581da177e4SLinus Torvalds * memory allocators. It initializes some pools of dma-coherent memory that
59626f090cSYacine Belkadi * will be shared by all drivers using that controller.
601da177e4SLinus Torvalds *
611da177e4SLinus Torvalds * Call hcd_buffer_destroy() to clean up after using those pools.
62626f090cSYacine Belkadi *
63626f090cSYacine Belkadi * Return: 0 if successful. A negative errno value otherwise.
641da177e4SLinus Torvalds */
hcd_buffer_create(struct usb_hcd * hcd)651da177e4SLinus Torvalds int hcd_buffer_create(struct usb_hcd *hcd)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds char name[16];
681da177e4SLinus Torvalds int i, size;
691da177e4SLinus Torvalds
70edfbcb32SChristoph Hellwig if (hcd->localmem_pool || !hcd_uses_dma(hcd))
71bd39b7f1SChris Humbert return 0;
72bd39b7f1SChris Humbert
731da177e4SLinus Torvalds for (i = 0; i < HCD_BUFFER_POOLS; i++) {
742c044a48SGreg Kroah-Hartman size = pool_max[i];
752c044a48SGreg Kroah-Hartman if (!size)
761da177e4SLinus Torvalds continue;
77424dd7ddSNizam Haider snprintf(name, sizeof(name), "buffer-%d", size);
78a8c06e40SArnd Bergmann hcd->pool[i] = dma_pool_create(name, hcd->self.sysdev,
791da177e4SLinus Torvalds size, size, 0);
801da177e4SLinus Torvalds if (!hcd->pool[i]) {
811da177e4SLinus Torvalds hcd_buffer_destroy(hcd);
821da177e4SLinus Torvalds return -ENOMEM;
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds return 0;
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds
881da177e4SLinus Torvalds
891da177e4SLinus Torvalds /**
901da177e4SLinus Torvalds * hcd_buffer_destroy - deallocate buffer pools
911da177e4SLinus Torvalds * @hcd: the bus whose buffer pools are to be destroyed
9241631d36SAhmed S. Darwish *
9341631d36SAhmed S. Darwish * Context: task context, might sleep
941da177e4SLinus Torvalds *
951da177e4SLinus Torvalds * This frees the buffer pools created by hcd_buffer_create().
961da177e4SLinus Torvalds */
hcd_buffer_destroy(struct usb_hcd * hcd)971da177e4SLinus Torvalds void hcd_buffer_destroy(struct usb_hcd *hcd)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds int i;
1001da177e4SLinus Torvalds
10158f2266fSGeert Uytterhoeven if (!IS_ENABLED(CONFIG_HAS_DMA))
10258f2266fSGeert Uytterhoeven return;
10358f2266fSGeert Uytterhoeven
1041da177e4SLinus Torvalds for (i = 0; i < HCD_BUFFER_POOLS; i++) {
10571741bd6SSalil Kapur dma_pool_destroy(hcd->pool[i]);
1061da177e4SLinus Torvalds hcd->pool[i] = NULL;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds
111441e143eSChristoph Lameter /* sometimes alloc/free could use kmalloc with GFP_DMA, for
1121da177e4SLinus Torvalds * better sharing and to leverage mm/slab.c intelligence.
1131da177e4SLinus Torvalds */
1141da177e4SLinus Torvalds
hcd_buffer_alloc(struct usb_bus * bus,size_t size,gfp_t mem_flags,dma_addr_t * dma)1151da177e4SLinus Torvalds void *hcd_buffer_alloc(
1161da177e4SLinus Torvalds struct usb_bus *bus,
1171da177e4SLinus Torvalds size_t size,
11855016f10SAl Viro gfp_t mem_flags,
1191da177e4SLinus Torvalds dma_addr_t *dma
1201da177e4SLinus Torvalds )
1211da177e4SLinus Torvalds {
12217200583SAlan Stern struct usb_hcd *hcd = bus_to_hcd(bus);
1231da177e4SLinus Torvalds int i;
1241da177e4SLinus Torvalds
125f5e6253fSChunfeng Yun if (size == 0)
126f5e6253fSChunfeng Yun return NULL;
127f5e6253fSChunfeng Yun
128b0310c2fSLaurentiu Tudor if (hcd->localmem_pool)
129b0310c2fSLaurentiu Tudor return gen_pool_dma_alloc(hcd->localmem_pool, size, dma);
130b0310c2fSLaurentiu Tudor
1311da177e4SLinus Torvalds /* some USB hosts just use PIO */
132edfbcb32SChristoph Hellwig if (!hcd_uses_dma(hcd)) {
1331da177e4SLinus Torvalds *dma = ~(dma_addr_t) 0;
1341da177e4SLinus Torvalds return kmalloc(size, mem_flags);
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds for (i = 0; i < HCD_BUFFER_POOLS; i++) {
1381da177e4SLinus Torvalds if (size <= pool_max[i])
1391da177e4SLinus Torvalds return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
1401da177e4SLinus Torvalds }
141a8c06e40SArnd Bergmann return dma_alloc_coherent(hcd->self.sysdev, size, dma, mem_flags);
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds
hcd_buffer_free(struct usb_bus * bus,size_t size,void * addr,dma_addr_t dma)1441da177e4SLinus Torvalds void hcd_buffer_free(
1451da177e4SLinus Torvalds struct usb_bus *bus,
1461da177e4SLinus Torvalds size_t size,
1471da177e4SLinus Torvalds void *addr,
1481da177e4SLinus Torvalds dma_addr_t dma
1491da177e4SLinus Torvalds )
1501da177e4SLinus Torvalds {
15117200583SAlan Stern struct usb_hcd *hcd = bus_to_hcd(bus);
1521da177e4SLinus Torvalds int i;
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds if (!addr)
1551da177e4SLinus Torvalds return;
1561da177e4SLinus Torvalds
157b0310c2fSLaurentiu Tudor if (hcd->localmem_pool) {
158b0310c2fSLaurentiu Tudor gen_pool_free(hcd->localmem_pool, (unsigned long)addr, size);
159b0310c2fSLaurentiu Tudor return;
160b0310c2fSLaurentiu Tudor }
161b0310c2fSLaurentiu Tudor
162edfbcb32SChristoph Hellwig if (!hcd_uses_dma(hcd)) {
1631da177e4SLinus Torvalds kfree(addr);
1641da177e4SLinus Torvalds return;
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds for (i = 0; i < HCD_BUFFER_POOLS; i++) {
1681da177e4SLinus Torvalds if (size <= pool_max[i]) {
1691da177e4SLinus Torvalds dma_pool_free(hcd->pool[i], addr, dma);
1701da177e4SLinus Torvalds return;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds }
173a8c06e40SArnd Bergmann dma_free_coherent(hcd->self.sysdev, size, addr, dma);
1741da177e4SLinus Torvalds }
175
hcd_buffer_alloc_pages(struct usb_hcd * hcd,size_t size,gfp_t mem_flags,dma_addr_t * dma)176 void *hcd_buffer_alloc_pages(struct usb_hcd *hcd,
177 size_t size, gfp_t mem_flags, dma_addr_t *dma)
178 {
179 if (size == 0)
180 return NULL;
181
182 if (hcd->localmem_pool)
183 return gen_pool_dma_alloc_align(hcd->localmem_pool,
184 size, dma, PAGE_SIZE);
185
186 /* some USB hosts just use PIO */
187 if (!hcd_uses_dma(hcd)) {
188 *dma = DMA_MAPPING_ERROR;
189 return (void *)__get_free_pages(mem_flags,
190 get_order(size));
191 }
192
193 return dma_alloc_coherent(hcd->self.sysdev,
194 size, dma, mem_flags);
195 }
196
hcd_buffer_free_pages(struct usb_hcd * hcd,size_t size,void * addr,dma_addr_t dma)197 void hcd_buffer_free_pages(struct usb_hcd *hcd,
198 size_t size, void *addr, dma_addr_t dma)
199 {
200 if (!addr)
201 return;
202
203 if (hcd->localmem_pool) {
204 gen_pool_free(hcd->localmem_pool,
205 (unsigned long)addr, size);
206 return;
207 }
208
209 if (!hcd_uses_dma(hcd)) {
210 free_pages((unsigned long)addr, get_order(size));
211 return;
212 }
213
214 dma_free_coherent(hcd->self.sysdev, size, addr, dma);
215 }
216