Branch data Line data Source code
1 : : /*
2 : : * DMA memory management for framework level HCD code (hc_driver)
3 : : *
4 : : * This implementation plugs in through generic "usb_bus" level methods,
5 : : * and should work with all USB controllers, regardles of bus type.
6 : : */
7 : :
8 : : #include <linux/module.h>
9 : : #include <linux/kernel.h>
10 : : #include <linux/slab.h>
11 : : #include <linux/device.h>
12 : : #include <linux/mm.h>
13 : : #include <linux/io.h>
14 : : #include <linux/dma-mapping.h>
15 : : #include <linux/dmapool.h>
16 : : #include <linux/usb.h>
17 : : #include <linux/usb/hcd.h>
18 : :
19 : :
20 : : /*
21 : : * DMA-Coherent Buffers
22 : : */
23 : :
24 : : /* FIXME tune these based on pool statistics ... */
25 : : static const size_t pool_max[HCD_BUFFER_POOLS] = {
26 : : /* platforms without dma-friendly caches might need to
27 : : * prevent cacheline sharing...
28 : : */
29 : : 32,
30 : : 128,
31 : : 512,
32 : : PAGE_SIZE / 2
33 : : /* bigger --> allocate pages */
34 : : };
35 : :
36 : :
37 : : /* SETUP primitives */
38 : :
39 : : /**
40 : : * hcd_buffer_create - initialize buffer pools
41 : : * @hcd: the bus whose buffer pools are to be initialized
42 : : * Context: !in_interrupt()
43 : : *
44 : : * Call this as part of initializing a host controller that uses the dma
45 : : * memory allocators. It initializes some pools of dma-coherent memory that
46 : : * will be shared by all drivers using that controller.
47 : : *
48 : : * Call hcd_buffer_destroy() to clean up after using those pools.
49 : : *
50 : : * Return: 0 if successful. A negative errno value otherwise.
51 : : */
52 : 0 : int hcd_buffer_create(struct usb_hcd *hcd)
53 : : {
54 : : char name[16];
55 : : int i, size;
56 : :
57 [ # # ][ # # ]: 0 : if (!hcd->self.controller->dma_mask &&
58 : 0 : !(hcd->driver->flags & HCD_LOCAL_MEM))
59 : : return 0;
60 : :
61 [ # # ]: 0 : for (i = 0; i < HCD_BUFFER_POOLS; i++) {
62 : 0 : size = pool_max[i];
63 [ # # ]: 0 : if (!size)
64 : 0 : continue;
65 : 0 : snprintf(name, sizeof name, "buffer-%d", size);
66 : 0 : hcd->pool[i] = dma_pool_create(name, hcd->self.controller,
67 : : size, size, 0);
68 [ # # ]: 0 : if (!hcd->pool[i]) {
69 : 0 : hcd_buffer_destroy(hcd);
70 : 0 : return -ENOMEM;
71 : : }
72 : : }
73 : : return 0;
74 : : }
75 : :
76 : :
77 : : /**
78 : : * hcd_buffer_destroy - deallocate buffer pools
79 : : * @hcd: the bus whose buffer pools are to be destroyed
80 : : * Context: !in_interrupt()
81 : : *
82 : : * This frees the buffer pools created by hcd_buffer_create().
83 : : */
84 : 0 : void hcd_buffer_destroy(struct usb_hcd *hcd)
85 : : {
86 : : int i;
87 : :
88 [ # # ]: 0 : for (i = 0; i < HCD_BUFFER_POOLS; i++) {
89 : 0 : struct dma_pool *pool = hcd->pool[i];
90 [ # # ]: 0 : if (pool) {
91 : 0 : dma_pool_destroy(pool);
92 : 0 : hcd->pool[i] = NULL;
93 : : }
94 : : }
95 : 0 : }
96 : :
97 : :
98 : : /* sometimes alloc/free could use kmalloc with GFP_DMA, for
99 : : * better sharing and to leverage mm/slab.c intelligence.
100 : : */
101 : :
102 : 0 : void *hcd_buffer_alloc(
103 : : struct usb_bus *bus,
104 : : size_t size,
105 : : gfp_t mem_flags,
106 : : dma_addr_t *dma
107 : : )
108 : : {
109 : : struct usb_hcd *hcd = bus_to_hcd(bus);
110 : : int i;
111 : :
112 : : /* some USB hosts just use PIO */
113 [ # # ][ # # ]: 0 : if (!bus->controller->dma_mask &&
114 : 0 : !(hcd->driver->flags & HCD_LOCAL_MEM)) {
115 : 0 : *dma = ~(dma_addr_t) 0;
116 : 0 : return kmalloc(size, mem_flags);
117 : : }
118 : :
119 [ # # ]: 0 : for (i = 0; i < HCD_BUFFER_POOLS; i++) {
120 [ # # ]: 0 : if (size <= pool_max[i])
121 : 0 : return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
122 : : }
123 : 0 : return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags);
124 : : }
125 : :
126 : 0 : void hcd_buffer_free(
127 : : struct usb_bus *bus,
128 : : size_t size,
129 : : void *addr,
130 : : dma_addr_t dma
131 : : )
132 : : {
133 : : struct usb_hcd *hcd = bus_to_hcd(bus);
134 : : int i;
135 : :
136 [ # # ]: 0 : if (!addr)
137 : : return;
138 : :
139 [ # # ][ # # ]: 0 : if (!bus->controller->dma_mask &&
140 : 0 : !(hcd->driver->flags & HCD_LOCAL_MEM)) {
141 : 0 : kfree(addr);
142 : 0 : return;
143 : : }
144 : :
145 [ # # ]: 0 : for (i = 0; i < HCD_BUFFER_POOLS; i++) {
146 [ # # ]: 0 : if (size <= pool_max[i]) {
147 : 0 : dma_pool_free(hcd->pool[i], addr, dma);
148 : 0 : return;
149 : : }
150 : : }
151 : : dma_free_coherent(hcd->self.controller, size, addr, dma);
152 : : }
|