| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * DMA memory management for framework level HCD code (hc_driver) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This implementation plugs in through generic "usb_bus" level methods, | 
					
						
							| 
									
										
										
										
											2014-01-04 11:24:41 +05:30
										 |  |  |  * and should work with all USB controllers, regardless of bus type. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/dma-mapping.h>
 | 
					
						
							|  |  |  | #include <linux/dmapool.h>
 | 
					
						
							|  |  |  | #include <linux/usb.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-24 23:21:52 +02:00
										 |  |  | #include <linux/usb/hcd.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * DMA-Coherent Buffers | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* FIXME tune these based on pool statistics ... */ | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | static const size_t	pool_max[HCD_BUFFER_POOLS] = { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/* platforms without dma-friendly caches might need to
 | 
					
						
							|  |  |  | 	 * prevent cacheline sharing... | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	32, | 
					
						
							|  |  |  | 	128, | 
					
						
							|  |  |  | 	512, | 
					
						
							|  |  |  | 	PAGE_SIZE / 2 | 
					
						
							|  |  |  | 	/* bigger --> allocate pages */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* SETUP primitives */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * hcd_buffer_create - initialize buffer pools | 
					
						
							|  |  |  |  * @hcd: the bus whose buffer pools are to be initialized | 
					
						
							|  |  |  |  * Context: !in_interrupt() | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Call this as part of initializing a host controller that uses the dma | 
					
						
							|  |  |  |  * memory allocators.  It initializes some pools of dma-coherent memory that | 
					
						
							| 
									
										
										
										
											2013-08-02 20:10:04 +02:00
										 |  |  |  * will be shared by all drivers using that controller. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Call hcd_buffer_destroy() to clean up after using those pools. | 
					
						
							| 
									
										
										
										
											2013-08-02 20:10:04 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Return: 0 if successful. A negative errno value otherwise. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | int hcd_buffer_create(struct usb_hcd *hcd) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 	char		name[16]; | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 	int		i, size; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-23 15:58:35 +09:00
										 |  |  | 	if (!hcd->self.controller->dma_mask && | 
					
						
							|  |  |  | 	    !(hcd->driver->flags & HCD_LOCAL_MEM)) | 
					
						
							| 
									
										
										
										
											2005-11-28 09:29:23 -08:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 15:21:33 -08:00
										 |  |  | 	for (i = 0; i < HCD_BUFFER_POOLS; i++) { | 
					
						
							|  |  |  | 		size = pool_max[i]; | 
					
						
							|  |  |  | 		if (!size) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 		snprintf(name, sizeof name, "buffer-%d", size); | 
					
						
							|  |  |  | 		hcd->pool[i] = dma_pool_create(name, hcd->self.controller, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				size, size, 0); | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 		if (!hcd->pool[i]) { | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 			hcd_buffer_destroy(hcd); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * hcd_buffer_destroy - deallocate buffer pools | 
					
						
							|  |  |  |  * @hcd: the bus whose buffer pools are to be destroyed | 
					
						
							|  |  |  |  * Context: !in_interrupt() | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This frees the buffer pools created by hcd_buffer_create(). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | void hcd_buffer_destroy(struct usb_hcd *hcd) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-01-30 15:21:33 -08:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-30 15:21:33 -08:00
										 |  |  | 	for (i = 0; i < HCD_BUFFER_POOLS; i++) { | 
					
						
							|  |  |  | 		struct dma_pool *pool = hcd->pool[i]; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (pool) { | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 			dma_pool_destroy(pool); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			hcd->pool[i] = NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-06 20:33:19 -08:00
										 |  |  | /* sometimes alloc/free could use kmalloc with GFP_DMA, for
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * better sharing and to leverage mm/slab.c intelligence. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | void *hcd_buffer_alloc( | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 	struct usb_bus		*bus, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	size_t			size, | 
					
						
							| 
									
										
										
										
											2005-10-21 03:21:58 -04:00
										 |  |  | 	gfp_t			mem_flags, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	dma_addr_t		*dma | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-08-30 11:32:52 -04:00
										 |  |  | 	struct usb_hcd		*hcd = bus_to_hcd(bus); | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 	int			i; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* some USB hosts just use PIO */ | 
					
						
							| 
									
										
										
										
											2008-01-23 15:58:35 +09:00
										 |  |  | 	if (!bus->controller->dma_mask && | 
					
						
							|  |  |  | 	    !(hcd->driver->flags & HCD_LOCAL_MEM)) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		*dma = ~(dma_addr_t) 0; | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 		return kmalloc(size, mem_flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < HCD_BUFFER_POOLS; i++) { | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 		if (size <= pool_max[i]) | 
					
						
							|  |  |  | 			return dma_pool_alloc(hcd->pool[i], mem_flags, dma); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-18 11:00:39 +02:00
										 |  |  | 	return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | void hcd_buffer_free( | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 	struct usb_bus		*bus, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	size_t			size, | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 	void			*addr, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	dma_addr_t		dma | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-08-30 11:32:52 -04:00
										 |  |  | 	struct usb_hcd		*hcd = bus_to_hcd(bus); | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 	int			i; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!addr) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-23 15:58:35 +09:00
										 |  |  | 	if (!bus->controller->dma_mask && | 
					
						
							|  |  |  | 	    !(hcd->driver->flags & HCD_LOCAL_MEM)) { | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 		kfree(addr); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < HCD_BUFFER_POOLS; i++) { | 
					
						
							| 
									
										
										
										
											2010-12-25 11:17:01 +01:00
										 |  |  | 		if (size <= pool_max[i]) { | 
					
						
							|  |  |  | 			dma_pool_free(hcd->pool[i], addr, dma); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-01-25 11:17:41 +01:00
										 |  |  | 	dma_free_coherent(hcd->self.controller, size, addr, dma); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |