1 /* $NetBSD: usb_mem.c,v 1.37 2008/06/28 17:42:53 bouyer Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 * USB DMA memory allocation.
35 * We need to allocate a lot of small (many 8 byte, some larger)
36 * memory blocks that can be used for DMA. Using the bus_dma
37 * routines directly would incur large overheads in space and time.
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: usb_mem.c,v 1.37 2008/06/28 17:42:53 bouyer Exp $");
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/malloc.h>
47 #include <sys/queue.h>
48 #include <sys/device.h> /* for usbdivar.h */
52 #include <sys/extent.h>
53 #include <uvm/uvm_extern.h>
60 #include <dev/usb/usb.h>
61 #include <dev/usb/usbdi.h>
62 #include <dev/usb/usbdivar.h> /* just for usb_dma_t */
63 #include <dev/usb/usb_mem.h>
66 #define DPRINTF(x) if (usbdebug) logprintf x
67 #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
74 MALLOC_DEFINE(M_USB
, "USB", "USB misc. memory");
75 MALLOC_DEFINE(M_USBDEV
, "USB device", "USB device driver");
76 MALLOC_DEFINE(M_USBHC
, "USB HC", "USB host controller");
78 #define USB_MEM_SMALL 64
79 #define USB_MEM_CHUNKS 64
80 #define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS)
82 /* This struct is overlayed on free fragments. */
84 usb_dma_block_t
*block
;
86 LIST_ENTRY(usb_frag_dma
) next
;
89 Static usbd_status
usb_block_allocmem(bus_dma_tag_t
, size_t, size_t,
91 Static
void usb_block_freemem(usb_dma_block_t
*);
93 Static
LIST_HEAD(, usb_dma_block
) usb_blk_freelist
=
94 LIST_HEAD_INITIALIZER(usb_blk_freelist
);
95 Static
int usb_blk_nfree
= 0;
96 /* XXX should have different free list for different tags (for speed) */
97 Static
LIST_HEAD(, usb_frag_dma
) usb_frag_freelist
=
98 LIST_HEAD_INITIALIZER(usb_frag_freelist
);
101 usb_block_allocmem(bus_dma_tag_t tag
, size_t size
, size_t align
,
102 usb_dma_block_t
**dmap
)
108 DPRINTFN(5, ("usb_block_allocmem: size=%lu align=%lu\n",
109 (u_long
)size
, (u_long
)align
));
113 printf("usb_block_allocmem: in interrupt context, size=%lu\n",
114 (unsigned long) size
);
119 /* First check the free list. */
120 for (p
= LIST_FIRST(&usb_blk_freelist
); p
; p
= LIST_NEXT(p
, next
)) {
121 if (p
->tag
== tag
&& p
->size
>= size
&& p
->align
>= align
) {
122 LIST_REMOVE(p
, next
);
126 DPRINTFN(6,("usb_block_allocmem: free list size=%lu\n",
128 return (USBD_NORMAL_COMPLETION
);
135 printf("usb_block_allocmem: in interrupt context, failed\n");
140 DPRINTFN(6, ("usb_block_allocmem: no free\n"));
141 p
= malloc(sizeof *p
, M_USB
, M_NOWAIT
);
148 error
= bus_dmamem_alloc(tag
, p
->size
, align
, 0,
149 p
->segs
, sizeof(p
->segs
)/sizeof(p
->segs
[0]),
150 &p
->nsegs
, BUS_DMA_NOWAIT
);
154 error
= bus_dmamem_map(tag
, p
->segs
, p
->nsegs
, p
->size
,
155 &p
->kaddr
, BUS_DMA_NOWAIT
|BUS_DMA_COHERENT
);
159 error
= bus_dmamap_create(tag
, p
->size
, 1, p
->size
,
160 0, BUS_DMA_NOWAIT
, &p
->map
);
164 error
= bus_dmamap_load(tag
, p
->map
, p
->kaddr
, p
->size
, NULL
,
170 return (USBD_NORMAL_COMPLETION
);
173 bus_dmamap_destroy(tag
, p
->map
);
175 bus_dmamem_unmap(tag
, p
->kaddr
, p
->size
);
177 bus_dmamem_free(tag
, p
->segs
, p
->nsegs
);
185 usb_block_real_freemem(usb_dma_block_t
*p
)
189 printf("usb_block_real_freemem: in interrupt context\n");
193 bus_dmamap_unload(p
->tag
, p
->map
);
194 bus_dmamap_destroy(p
->tag
, p
->map
);
195 bus_dmamem_unmap(p
->tag
, p
->kaddr
, p
->size
);
196 bus_dmamem_free(p
->tag
, p
->segs
, p
->nsegs
);
202 * Do not free the memory unconditionally since we might be called
203 * from an interrupt context and that is BAD.
204 * XXX when should we really free?
207 usb_block_freemem(usb_dma_block_t
*p
)
211 DPRINTFN(6, ("usb_block_freemem: size=%lu\n", (u_long
)p
->size
));
213 LIST_INSERT_HEAD(&usb_blk_freelist
, p
, next
);
219 usb_allocmem(usbd_bus_handle bus
, size_t size
, size_t align
, usb_dma_t
*p
)
221 bus_dma_tag_t tag
= bus
->dmatag
;
223 struct usb_frag_dma
*f
;
228 /* If the request is large then just use a full block. */
229 if (size
> USB_MEM_SMALL
|| align
> USB_MEM_SMALL
) {
230 DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size
));
231 size
= (size
+ USB_MEM_BLOCK
- 1) & ~(USB_MEM_BLOCK
- 1);
232 err
= usb_block_allocmem(tag
, size
, align
, &p
->block
);
234 p
->block
->flags
= USB_DMA_FULLBLOCK
;
241 /* Check for free fragments. */
242 for (f
= LIST_FIRST(&usb_frag_freelist
); f
; f
= LIST_NEXT(f
, next
))
243 if (f
->block
->tag
== tag
)
246 DPRINTFN(1, ("usb_allocmem: adding fragments\n"));
247 err
= usb_block_allocmem(tag
, USB_MEM_BLOCK
, USB_MEM_SMALL
,&b
);
253 for (i
= 0; i
< USB_MEM_BLOCK
; i
+= USB_MEM_SMALL
) {
254 f
= (struct usb_frag_dma
*)((char *)b
->kaddr
+ i
);
257 LIST_INSERT_HEAD(&usb_frag_freelist
, f
, next
);
259 f
= LIST_FIRST(&usb_frag_freelist
);
263 p
->block
->flags
&= ~USB_DMA_RESERVE
;
264 LIST_REMOVE(f
, next
);
266 DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f
, (int)size
));
267 return (USBD_NORMAL_COMPLETION
);
271 usb_freemem(usbd_bus_handle bus
, usb_dma_t
*p
)
273 struct usb_frag_dma
*f
;
276 if (p
->block
->flags
& USB_DMA_FULLBLOCK
) {
277 DPRINTFN(1, ("usb_freemem: large free\n"));
278 usb_block_freemem(p
->block
);
285 LIST_INSERT_HEAD(&usb_frag_freelist
, f
, next
);
287 DPRINTFN(5, ("usb_freemem: frag=%p\n", f
));
291 usb_syncmem(usb_dma_t
*p
, bus_addr_t offset
, bus_size_t len
, int ops
)
293 bus_dmamap_sync(p
->block
->tag
, p
->block
->map
, p
->offs
+ offset
,
300 usb_reserve_allocm(struct usb_dma_reserve
*rs
, usb_dma_t
*dma
, u_int32_t size
)
306 if (rs
->vaddr
== 0 || size
> USB_MEM_RESERVE
)
309 dma
->block
= malloc(sizeof *dma
->block
, M_USB
, M_ZERO
| M_NOWAIT
);
310 if (dma
->block
== NULL
)
313 error
= extent_alloc(rs
->extent
, size
, PAGE_SIZE
, 0,
317 aprint_error_dev(rs
->dv
,
318 "usb_reserve_allocm of size %u failed (error %d)\n",
324 dma
->offs
= baddr
- rs
->paddr
;
325 dma
->block
->flags
= USB_DMA_RESERVE
;
326 dma
->block
->align
= PAGE_SIZE
;
327 dma
->block
->size
= size
;
328 dma
->block
->nsegs
= 1;
329 /* XXX segs appears to be unused */
330 dma
->block
->segs
[0] = rs
->map
->dm_segs
[0];
331 dma
->block
->map
= rs
->map
;
332 dma
->block
->kaddr
= rs
->vaddr
;
333 dma
->block
->tag
= rs
->dtag
;
335 return USBD_NORMAL_COMPLETION
;
339 usb_reserve_freem(struct usb_dma_reserve
*rs
, usb_dma_t
*dma
)
343 error
= extent_free(rs
->extent
,
344 (u_long
)(rs
->paddr
+ dma
->offs
), dma
->block
->size
, 0);
345 free(dma
->block
, M_USB
);
349 usb_setup_reserve(device_t dv
, struct usb_dma_reserve
*rs
, bus_dma_tag_t dtag
,
353 bus_dma_segment_t seg
;
359 error
= bus_dmamem_alloc(dtag
, USB_MEM_RESERVE
, PAGE_SIZE
, 0,
360 &seg
, 1, &nseg
, BUS_DMA_NOWAIT
);
364 error
= bus_dmamem_map(dtag
, &seg
, nseg
, USB_MEM_RESERVE
,
365 &rs
->vaddr
, BUS_DMA_NOWAIT
|BUS_DMA_COHERENT
);
369 error
= bus_dmamap_create(dtag
, USB_MEM_RESERVE
, 1,
370 USB_MEM_RESERVE
, 0, BUS_DMA_NOWAIT
, &rs
->map
);
374 error
= bus_dmamap_load(dtag
, rs
->map
, rs
->vaddr
, USB_MEM_RESERVE
,
375 NULL
, BUS_DMA_NOWAIT
);
379 rs
->paddr
= rs
->map
->dm_segs
[0].ds_addr
;
380 rs
->extent
= extent_create(device_xname(dv
), (u_long
)rs
->paddr
,
381 (u_long
)(rs
->paddr
+ USB_MEM_RESERVE
- 1),
383 if (rs
->extent
== NULL
) {
391 bus_dmamap_destroy(dtag
, rs
->map
);
393 bus_dmamem_unmap(dtag
, rs
->vaddr
, size
);
395 bus_dmamem_free(dtag
, &seg
, nseg
);