1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * DMA memory management for framework level HCD code (hc_driver) |
4 | * |
5 | * This implementation plugs in through generic "usb_bus" level methods, |
6 | * and should work with all USB controllers, regardless of bus type. |
7 | * |
8 | * Released under the GPLv2 only. |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/device.h> |
15 | #include <linux/mm.h> |
16 | #include <linux/io.h> |
17 | #include <linux/dma-mapping.h> |
18 | #include <linux/dmapool.h> |
19 | #include <linux/genalloc.h> |
20 | #include <linux/usb.h> |
21 | #include <linux/usb/hcd.h> |
22 | |
23 | |
24 | /* |
25 | * DMA-Coherent Buffers |
26 | */ |
27 | |
28 | /* FIXME tune these based on pool statistics ... */ |
29 | static size_t pool_max[HCD_BUFFER_POOLS] = { |
30 | 32, 128, 512, 2048, |
31 | }; |
32 | |
33 | void __init usb_init_pool_max(void) |
34 | { |
35 | /* |
36 | * The pool_max values must never be smaller than |
37 | * ARCH_DMA_MINALIGN. |
38 | */ |
39 | if (ARCH_DMA_MINALIGN <= 32) |
40 | ; /* Original value is okay */ |
41 | else if (ARCH_DMA_MINALIGN <= 64) |
42 | pool_max[0] = 64; |
43 | else if (ARCH_DMA_MINALIGN <= 128) |
44 | pool_max[0] = 0; /* Don't use this pool */ |
45 | else |
46 | BUILD_BUG(); /* We don't allow this */ |
47 | } |
48 | |
49 | /* SETUP primitives */ |
50 | |
51 | /** |
52 | * hcd_buffer_create - initialize buffer pools |
53 | * @hcd: the bus whose buffer pools are to be initialized |
54 | * |
55 | * Context: task context, might sleep |
56 | * |
57 | * Call this as part of initializing a host controller that uses the dma |
58 | * memory allocators. It initializes some pools of dma-coherent memory that |
59 | * will be shared by all drivers using that controller. |
60 | * |
61 | * Call hcd_buffer_destroy() to clean up after using those pools. |
62 | * |
63 | * Return: 0 if successful. A negative errno value otherwise. |
64 | */ |
65 | int hcd_buffer_create(struct usb_hcd *hcd) |
66 | { |
67 | char name[16]; |
68 | int i, size; |
69 | |
70 | if (hcd->localmem_pool || !hcd_uses_dma(hcd)) |
71 | return 0; |
72 | |
73 | for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
74 | size = pool_max[i]; |
75 | if (!size) |
76 | continue; |
77 | snprintf(buf: name, size: sizeof(name), fmt: "buffer-%d" , size); |
78 | hcd->pool[i] = dma_pool_create(name, dev: hcd->self.sysdev, |
79 | size, align: size, allocation: 0); |
80 | if (!hcd->pool[i]) { |
81 | hcd_buffer_destroy(hcd); |
82 | return -ENOMEM; |
83 | } |
84 | } |
85 | return 0; |
86 | } |
87 | |
88 | |
89 | /** |
90 | * hcd_buffer_destroy - deallocate buffer pools |
91 | * @hcd: the bus whose buffer pools are to be destroyed |
92 | * |
93 | * Context: task context, might sleep |
94 | * |
95 | * This frees the buffer pools created by hcd_buffer_create(). |
96 | */ |
97 | void hcd_buffer_destroy(struct usb_hcd *hcd) |
98 | { |
99 | int i; |
100 | |
101 | if (!IS_ENABLED(CONFIG_HAS_DMA)) |
102 | return; |
103 | |
104 | for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
105 | dma_pool_destroy(pool: hcd->pool[i]); |
106 | hcd->pool[i] = NULL; |
107 | } |
108 | } |
109 | |
110 | |
111 | /* sometimes alloc/free could use kmalloc with GFP_DMA, for |
112 | * better sharing and to leverage mm/slab.c intelligence. |
113 | */ |
114 | |
115 | void *hcd_buffer_alloc( |
116 | struct usb_bus *bus, |
117 | size_t size, |
118 | gfp_t mem_flags, |
119 | dma_addr_t *dma |
120 | ) |
121 | { |
122 | struct usb_hcd *hcd = bus_to_hcd(bus); |
123 | int i; |
124 | |
125 | if (size == 0) |
126 | return NULL; |
127 | |
128 | if (hcd->localmem_pool) |
129 | return gen_pool_dma_alloc(pool: hcd->localmem_pool, size, dma); |
130 | |
131 | /* some USB hosts just use PIO */ |
132 | if (!hcd_uses_dma(hcd)) { |
133 | *dma = ~(dma_addr_t) 0; |
134 | return kmalloc(size, flags: mem_flags); |
135 | } |
136 | |
137 | for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
138 | if (size <= pool_max[i]) |
139 | return dma_pool_alloc(pool: hcd->pool[i], mem_flags, handle: dma); |
140 | } |
141 | return dma_alloc_coherent(dev: hcd->self.sysdev, size, dma_handle: dma, gfp: mem_flags); |
142 | } |
143 | |
144 | void hcd_buffer_free( |
145 | struct usb_bus *bus, |
146 | size_t size, |
147 | void *addr, |
148 | dma_addr_t dma |
149 | ) |
150 | { |
151 | struct usb_hcd *hcd = bus_to_hcd(bus); |
152 | int i; |
153 | |
154 | if (!addr) |
155 | return; |
156 | |
157 | if (hcd->localmem_pool) { |
158 | gen_pool_free(pool: hcd->localmem_pool, addr: (unsigned long)addr, size); |
159 | return; |
160 | } |
161 | |
162 | if (!hcd_uses_dma(hcd)) { |
163 | kfree(objp: addr); |
164 | return; |
165 | } |
166 | |
167 | for (i = 0; i < HCD_BUFFER_POOLS; i++) { |
168 | if (size <= pool_max[i]) { |
169 | dma_pool_free(pool: hcd->pool[i], vaddr: addr, addr: dma); |
170 | return; |
171 | } |
172 | } |
173 | dma_free_coherent(dev: hcd->self.sysdev, size, cpu_addr: addr, dma_handle: dma); |
174 | } |
175 | |
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(pool: 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(gfp_mask: mem_flags, |
190 | order: get_order(size)); |
191 | } |
192 | |
193 | return dma_alloc_coherent(dev: hcd->self.sysdev, |
194 | size, dma_handle: dma, gfp: mem_flags); |
195 | } |
196 | |
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(pool: hcd->localmem_pool, |
205 | addr: (unsigned long)addr, size); |
206 | return; |
207 | } |
208 | |
209 | if (!hcd_uses_dma(hcd)) { |
210 | free_pages(addr: (unsigned long)addr, order: get_order(size)); |
211 | return; |
212 | } |
213 | |
214 | dma_free_coherent(dev: hcd->self.sysdev, size, cpu_addr: addr, dma_handle: dma); |
215 | } |
216 | |