xref: /linux-6.15/drivers/usb/core/buffer.c (revision 075efe7c)
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