102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 27ed6d949aSAndrew Thompson #include <sys/stdint.h> 28ed6d949aSAndrew Thompson #include <sys/stddef.h> 29ed6d949aSAndrew Thompson #include <sys/param.h> 30ed6d949aSAndrew Thompson #include <sys/queue.h> 31ed6d949aSAndrew Thompson #include <sys/types.h> 32ed6d949aSAndrew Thompson #include <sys/systm.h> 33ed6d949aSAndrew Thompson #include <sys/kernel.h> 34ed6d949aSAndrew Thompson #include <sys/bus.h> 35ed6d949aSAndrew Thompson #include <sys/linker_set.h> 36ed6d949aSAndrew Thompson #include <sys/module.h> 37ed6d949aSAndrew Thompson #include <sys/lock.h> 38ed6d949aSAndrew Thompson #include <sys/mutex.h> 39ed6d949aSAndrew Thompson #include <sys/condvar.h> 40ed6d949aSAndrew Thompson #include <sys/sysctl.h> 41ed6d949aSAndrew Thompson #include <sys/sx.h> 42ed6d949aSAndrew Thompson #include <sys/unistd.h> 43ed6d949aSAndrew Thompson #include <sys/callout.h> 44ed6d949aSAndrew Thompson #include <sys/malloc.h> 45ed6d949aSAndrew Thompson #include <sys/priv.h> 46ed6d949aSAndrew Thompson 4702ac6454SAndrew Thompson #include <dev/usb/usb.h> 48ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 49ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5002ac6454SAndrew Thompson 51a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5202ac6454SAndrew Thompson 5302ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5402ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5502ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5602ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6002ac6454SAndrew Thompson 6102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6302ac6454SAndrew Thompson 64bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 65a593f6b8SAndrew Thompson static void usb_dma_tag_create(struct usb_dma_tag *, usb_size_t, usb_size_t); 66a593f6b8SAndrew Thompson static void usb_dma_tag_destroy(struct usb_dma_tag *); 67a593f6b8SAndrew Thompson static void usb_dma_lock_cb(void *, bus_dma_lock_op_t); 68a593f6b8SAndrew Thompson static void usb_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); 69a593f6b8SAndrew Thompson static void usb_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); 70a593f6b8SAndrew Thompson static void usb_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, 7102ac6454SAndrew Thompson uint8_t); 7202ac6454SAndrew Thompson #endif 7302ac6454SAndrew Thompson 7402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 75a593f6b8SAndrew Thompson * usbd_get_page - lookup DMA-able memory for the given offset 7602ac6454SAndrew Thompson * 7702ac6454SAndrew Thompson * NOTE: Only call this function when the "page_cache" structure has 7802ac6454SAndrew Thompson * been properly initialized ! 7902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 8002ac6454SAndrew Thompson void 81a593f6b8SAndrew Thompson usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, 82760bc48eSAndrew Thompson struct usb_page_search *res) 8302ac6454SAndrew Thompson { 84760bc48eSAndrew Thompson struct usb_page *page; 8502ac6454SAndrew Thompson 86bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 8702ac6454SAndrew Thompson if (pc->page_start) { 8802ac6454SAndrew Thompson 8902ac6454SAndrew Thompson /* Case 1 - something has been loaded into DMA */ 9002ac6454SAndrew Thompson 9102ac6454SAndrew Thompson if (pc->buffer) { 9202ac6454SAndrew Thompson 9302ac6454SAndrew Thompson /* Case 1a - Kernel Virtual Address */ 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 9602ac6454SAndrew Thompson } 9702ac6454SAndrew Thompson offset += pc->page_offset_buf; 9802ac6454SAndrew Thompson 9902ac6454SAndrew Thompson /* compute destination page */ 10002ac6454SAndrew Thompson 10102ac6454SAndrew Thompson page = pc->page_start; 10202ac6454SAndrew Thompson 10302ac6454SAndrew Thompson if (pc->ismultiseg) { 10402ac6454SAndrew Thompson 10502ac6454SAndrew Thompson page += (offset / USB_PAGE_SIZE); 10602ac6454SAndrew Thompson 10702ac6454SAndrew Thompson offset %= USB_PAGE_SIZE; 10802ac6454SAndrew Thompson 10902ac6454SAndrew Thompson res->length = USB_PAGE_SIZE - offset; 11002ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 11102ac6454SAndrew Thompson } else { 11202ac6454SAndrew Thompson res->length = 0 - 1; 11302ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 11402ac6454SAndrew Thompson } 11502ac6454SAndrew Thompson if (!pc->buffer) { 11602ac6454SAndrew Thompson 11702ac6454SAndrew Thompson /* Case 1b - Non Kernel Virtual Address */ 11802ac6454SAndrew Thompson 11902ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(page->buffer, offset); 12002ac6454SAndrew Thompson } 121bdc081c6SAndrew Thompson return; 122bdc081c6SAndrew Thompson } 123bdc081c6SAndrew Thompson #endif 12402ac6454SAndrew Thompson /* Case 2 - Plain PIO */ 12502ac6454SAndrew Thompson 12602ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 12702ac6454SAndrew Thompson res->length = 0 - 1; 128bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 12902ac6454SAndrew Thompson res->physaddr = 0; 130bdc081c6SAndrew Thompson #endif 13102ac6454SAndrew Thompson } 13202ac6454SAndrew Thompson 13302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 134a593f6b8SAndrew Thompson * usbd_copy_in - copy directly to DMA-able memory 13502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 13602ac6454SAndrew Thompson void 137a593f6b8SAndrew Thompson usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, 138e0a69b51SAndrew Thompson const void *ptr, usb_frlength_t len) 13902ac6454SAndrew Thompson { 140760bc48eSAndrew Thompson struct usb_page_search buf_res; 14102ac6454SAndrew Thompson 14202ac6454SAndrew Thompson while (len != 0) { 14302ac6454SAndrew Thompson 144a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &buf_res); 14502ac6454SAndrew Thompson 14602ac6454SAndrew Thompson if (buf_res.length > len) { 14702ac6454SAndrew Thompson buf_res.length = len; 14802ac6454SAndrew Thompson } 14902ac6454SAndrew Thompson bcopy(ptr, buf_res.buffer, buf_res.length); 15002ac6454SAndrew Thompson 15102ac6454SAndrew Thompson offset += buf_res.length; 15202ac6454SAndrew Thompson len -= buf_res.length; 15302ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 15402ac6454SAndrew Thompson } 15502ac6454SAndrew Thompson } 15602ac6454SAndrew Thompson 15702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 158a593f6b8SAndrew Thompson * usbd_copy_in_user - copy directly to DMA-able memory from userland 15902ac6454SAndrew Thompson * 16002ac6454SAndrew Thompson * Return values: 16102ac6454SAndrew Thompson * 0: Success 16202ac6454SAndrew Thompson * Else: Failure 16302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 164bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 16502ac6454SAndrew Thompson int 166a593f6b8SAndrew Thompson usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, 167e0a69b51SAndrew Thompson const void *ptr, usb_frlength_t len) 16802ac6454SAndrew Thompson { 169760bc48eSAndrew Thompson struct usb_page_search buf_res; 17002ac6454SAndrew Thompson int error; 17102ac6454SAndrew Thompson 17202ac6454SAndrew Thompson while (len != 0) { 17302ac6454SAndrew Thompson 174a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &buf_res); 17502ac6454SAndrew Thompson 17602ac6454SAndrew Thompson if (buf_res.length > len) { 17702ac6454SAndrew Thompson buf_res.length = len; 17802ac6454SAndrew Thompson } 17902ac6454SAndrew Thompson error = copyin(ptr, buf_res.buffer, buf_res.length); 18002ac6454SAndrew Thompson if (error) 18102ac6454SAndrew Thompson return (error); 18202ac6454SAndrew Thompson 18302ac6454SAndrew Thompson offset += buf_res.length; 18402ac6454SAndrew Thompson len -= buf_res.length; 18502ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 18602ac6454SAndrew Thompson } 18702ac6454SAndrew Thompson return (0); /* success */ 18802ac6454SAndrew Thompson } 189bdc081c6SAndrew Thompson #endif 19002ac6454SAndrew Thompson 19102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 192a593f6b8SAndrew Thompson * usbd_m_copy_in - copy a mbuf chain directly into DMA-able memory 19302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 194bdc081c6SAndrew Thompson #if USB_HAVE_MBUF 195a593f6b8SAndrew Thompson struct usb_m_copy_in_arg { 196760bc48eSAndrew Thompson struct usb_page_cache *cache; 197e0a69b51SAndrew Thompson usb_frlength_t dst_offset; 19802ac6454SAndrew Thompson }; 19902ac6454SAndrew Thompson 200578d0effSAndrew Thompson static int 201a593f6b8SAndrew Thompson usbd_m_copy_in_cb(void *arg, void *src, uint32_t count) 20202ac6454SAndrew Thompson { 203a593f6b8SAndrew Thompson register struct usb_m_copy_in_arg *ua = arg; 20402ac6454SAndrew Thompson 205a593f6b8SAndrew Thompson usbd_copy_in(ua->cache, ua->dst_offset, src, count); 20602ac6454SAndrew Thompson ua->dst_offset += count; 20702ac6454SAndrew Thompson return (0); 20802ac6454SAndrew Thompson } 20902ac6454SAndrew Thompson 21002ac6454SAndrew Thompson void 211a593f6b8SAndrew Thompson usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, 212f9cb546cSAndrew Thompson struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len) 21302ac6454SAndrew Thompson { 214a593f6b8SAndrew Thompson struct usb_m_copy_in_arg arg = {cache, dst_offset}; 215578d0effSAndrew Thompson int error; 21602ac6454SAndrew Thompson 217a593f6b8SAndrew Thompson error = m_apply(m, src_offset, src_len, &usbd_m_copy_in_cb, &arg); 21802ac6454SAndrew Thompson } 219bdc081c6SAndrew Thompson #endif 22002ac6454SAndrew Thompson 22102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 222a593f6b8SAndrew Thompson * usb_uiomove - factored out code 22302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 224bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 22502ac6454SAndrew Thompson int 226a593f6b8SAndrew Thompson usb_uiomove(struct usb_page_cache *pc, struct uio *uio, 227e0a69b51SAndrew Thompson usb_frlength_t pc_offset, usb_frlength_t len) 22802ac6454SAndrew Thompson { 229760bc48eSAndrew Thompson struct usb_page_search res; 23002ac6454SAndrew Thompson int error = 0; 23102ac6454SAndrew Thompson 23202ac6454SAndrew Thompson while (len != 0) { 23302ac6454SAndrew Thompson 234a593f6b8SAndrew Thompson usbd_get_page(pc, pc_offset, &res); 23502ac6454SAndrew Thompson 23602ac6454SAndrew Thompson if (res.length > len) { 23702ac6454SAndrew Thompson res.length = len; 23802ac6454SAndrew Thompson } 23902ac6454SAndrew Thompson /* 24002ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 24102ac6454SAndrew Thompson * exiting the mutex and checking things 24202ac6454SAndrew Thompson */ 24302ac6454SAndrew Thompson error = uiomove(res.buffer, res.length, uio); 24402ac6454SAndrew Thompson 24502ac6454SAndrew Thompson if (error) { 24602ac6454SAndrew Thompson break; 24702ac6454SAndrew Thompson } 24802ac6454SAndrew Thompson pc_offset += res.length; 24902ac6454SAndrew Thompson len -= res.length; 25002ac6454SAndrew Thompson } 25102ac6454SAndrew Thompson return (error); 25202ac6454SAndrew Thompson } 253bdc081c6SAndrew Thompson #endif 25402ac6454SAndrew Thompson 25502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 256a593f6b8SAndrew Thompson * usbd_copy_out - copy directly from DMA-able memory 25702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 25802ac6454SAndrew Thompson void 259a593f6b8SAndrew Thompson usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, 260e0a69b51SAndrew Thompson void *ptr, usb_frlength_t len) 26102ac6454SAndrew Thompson { 262760bc48eSAndrew Thompson struct usb_page_search res; 26302ac6454SAndrew Thompson 26402ac6454SAndrew Thompson while (len != 0) { 26502ac6454SAndrew Thompson 266a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 26702ac6454SAndrew Thompson 26802ac6454SAndrew Thompson if (res.length > len) { 26902ac6454SAndrew Thompson res.length = len; 27002ac6454SAndrew Thompson } 27102ac6454SAndrew Thompson bcopy(res.buffer, ptr, res.length); 27202ac6454SAndrew Thompson 27302ac6454SAndrew Thompson offset += res.length; 27402ac6454SAndrew Thompson len -= res.length; 27502ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 27602ac6454SAndrew Thompson } 27702ac6454SAndrew Thompson } 27802ac6454SAndrew Thompson 27902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 280a593f6b8SAndrew Thompson * usbd_copy_out_user - copy directly from DMA-able memory to userland 28102ac6454SAndrew Thompson * 28202ac6454SAndrew Thompson * Return values: 28302ac6454SAndrew Thompson * 0: Success 28402ac6454SAndrew Thompson * Else: Failure 28502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 286bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 28702ac6454SAndrew Thompson int 288a593f6b8SAndrew Thompson usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, 289e0a69b51SAndrew Thompson void *ptr, usb_frlength_t len) 29002ac6454SAndrew Thompson { 291760bc48eSAndrew Thompson struct usb_page_search res; 29202ac6454SAndrew Thompson int error; 29302ac6454SAndrew Thompson 29402ac6454SAndrew Thompson while (len != 0) { 29502ac6454SAndrew Thompson 296a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 29702ac6454SAndrew Thompson 29802ac6454SAndrew Thompson if (res.length > len) { 29902ac6454SAndrew Thompson res.length = len; 30002ac6454SAndrew Thompson } 30102ac6454SAndrew Thompson error = copyout(res.buffer, ptr, res.length); 30202ac6454SAndrew Thompson if (error) 30302ac6454SAndrew Thompson return (error); 30402ac6454SAndrew Thompson 30502ac6454SAndrew Thompson offset += res.length; 30602ac6454SAndrew Thompson len -= res.length; 30702ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 30802ac6454SAndrew Thompson } 30902ac6454SAndrew Thompson return (0); /* success */ 31002ac6454SAndrew Thompson } 311bdc081c6SAndrew Thompson #endif 31202ac6454SAndrew Thompson 31302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 314a593f6b8SAndrew Thompson * usbd_frame_zero - zero DMA-able memory 31502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 31602ac6454SAndrew Thompson void 317a593f6b8SAndrew Thompson usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, 318e0a69b51SAndrew Thompson usb_frlength_t len) 31902ac6454SAndrew Thompson { 320760bc48eSAndrew Thompson struct usb_page_search res; 32102ac6454SAndrew Thompson 32202ac6454SAndrew Thompson while (len != 0) { 32302ac6454SAndrew Thompson 324a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 32502ac6454SAndrew Thompson 32602ac6454SAndrew Thompson if (res.length > len) { 32702ac6454SAndrew Thompson res.length = len; 32802ac6454SAndrew Thompson } 32902ac6454SAndrew Thompson bzero(res.buffer, res.length); 33002ac6454SAndrew Thompson 33102ac6454SAndrew Thompson offset += res.length; 33202ac6454SAndrew Thompson len -= res.length; 33302ac6454SAndrew Thompson } 33402ac6454SAndrew Thompson } 33502ac6454SAndrew Thompson 3363e873976SAndrew Thompson #if USB_HAVE_BUSDMA 33702ac6454SAndrew Thompson 33802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 339a593f6b8SAndrew Thompson * usb_dma_lock_cb - dummy callback 34002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 34102ac6454SAndrew Thompson static void 342a593f6b8SAndrew Thompson usb_dma_lock_cb(void *arg, bus_dma_lock_op_t op) 34302ac6454SAndrew Thompson { 34402ac6454SAndrew Thompson /* we use "mtx_owned()" instead of this function */ 34502ac6454SAndrew Thompson } 34602ac6454SAndrew Thompson 34702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 348a593f6b8SAndrew Thompson * usb_dma_tag_create - allocate a DMA tag 34902ac6454SAndrew Thompson * 35002ac6454SAndrew Thompson * NOTE: If the "align" parameter has a value of 1 the DMA-tag will 35102ac6454SAndrew Thompson * allow multi-segment mappings. Else all mappings are single-segment. 35202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 35302ac6454SAndrew Thompson static void 354a593f6b8SAndrew Thompson usb_dma_tag_create(struct usb_dma_tag *udt, 355f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 35602ac6454SAndrew Thompson { 35702ac6454SAndrew Thompson bus_dma_tag_t tag; 35802ac6454SAndrew Thompson 35902ac6454SAndrew Thompson if (bus_dma_tag_create 36002ac6454SAndrew Thompson ( /* parent */ udt->tag_parent->tag, 36102ac6454SAndrew Thompson /* alignment */ align, 36258f6d273SMarcel Moolenaar /* boundary */ (align == 1) ? 36358f6d273SMarcel Moolenaar USB_PAGE_SIZE : 0, 36402ac6454SAndrew Thompson /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1, 36502ac6454SAndrew Thompson /* highaddr */ BUS_SPACE_MAXADDR, 36602ac6454SAndrew Thompson /* filter */ NULL, 36702ac6454SAndrew Thompson /* filterarg */ NULL, 36802ac6454SAndrew Thompson /* maxsize */ size, 36902ac6454SAndrew Thompson /* nsegments */ (align == 1) ? 37002ac6454SAndrew Thompson (2 + (size / USB_PAGE_SIZE)) : 1, 37102ac6454SAndrew Thompson /* maxsegsz */ (align == 1) ? 37202ac6454SAndrew Thompson USB_PAGE_SIZE : size, 37302ac6454SAndrew Thompson /* flags */ BUS_DMA_KEEP_PG_OFFSET, 374a593f6b8SAndrew Thompson /* lockfn */ &usb_dma_lock_cb, 37502ac6454SAndrew Thompson /* lockarg */ NULL, 37602ac6454SAndrew Thompson &tag)) { 37702ac6454SAndrew Thompson tag = NULL; 37802ac6454SAndrew Thompson } 37902ac6454SAndrew Thompson udt->tag = tag; 38002ac6454SAndrew Thompson } 38102ac6454SAndrew Thompson 38202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 383a593f6b8SAndrew Thompson * usb_dma_tag_free - free a DMA tag 38402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38502ac6454SAndrew Thompson static void 386a593f6b8SAndrew Thompson usb_dma_tag_destroy(struct usb_dma_tag *udt) 38702ac6454SAndrew Thompson { 38802ac6454SAndrew Thompson bus_dma_tag_destroy(udt->tag); 38902ac6454SAndrew Thompson } 39002ac6454SAndrew Thompson 39102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 392a593f6b8SAndrew Thompson * usb_pc_alloc_mem_cb - BUS-DMA callback function 39302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39402ac6454SAndrew Thompson static void 395a593f6b8SAndrew Thompson usb_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, 39602ac6454SAndrew Thompson int nseg, int error) 39702ac6454SAndrew Thompson { 398a593f6b8SAndrew Thompson usb_pc_common_mem_cb(arg, segs, nseg, error, 0); 39902ac6454SAndrew Thompson } 40002ac6454SAndrew Thompson 40102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 402a593f6b8SAndrew Thompson * usb_pc_load_mem_cb - BUS-DMA callback function 40302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 40402ac6454SAndrew Thompson static void 405a593f6b8SAndrew Thompson usb_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, 40602ac6454SAndrew Thompson int nseg, int error) 40702ac6454SAndrew Thompson { 408a593f6b8SAndrew Thompson usb_pc_common_mem_cb(arg, segs, nseg, error, 1); 40902ac6454SAndrew Thompson } 41002ac6454SAndrew Thompson 41102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 412a593f6b8SAndrew Thompson * usb_pc_common_mem_cb - BUS-DMA callback function 41302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 41402ac6454SAndrew Thompson static void 415a593f6b8SAndrew Thompson usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, 41602ac6454SAndrew Thompson int nseg, int error, uint8_t isload) 41702ac6454SAndrew Thompson { 418760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 419760bc48eSAndrew Thompson struct usb_page_cache *pc; 420760bc48eSAndrew Thompson struct usb_page *pg; 421f9cb546cSAndrew Thompson usb_size_t rem; 42202ac6454SAndrew Thompson uint8_t owned; 42302ac6454SAndrew Thompson 42402ac6454SAndrew Thompson pc = arg; 42502ac6454SAndrew Thompson uptag = pc->tag_parent; 42602ac6454SAndrew Thompson 42702ac6454SAndrew Thompson /* 42802ac6454SAndrew Thompson * XXX There is sometimes recursive locking here. 42902ac6454SAndrew Thompson * XXX We should try to find a better solution. 43002ac6454SAndrew Thompson * XXX Until further the "owned" variable does 43102ac6454SAndrew Thompson * XXX the trick. 43202ac6454SAndrew Thompson */ 43302ac6454SAndrew Thompson 43402ac6454SAndrew Thompson if (error) { 43502ac6454SAndrew Thompson goto done; 43602ac6454SAndrew Thompson } 43702ac6454SAndrew Thompson pg = pc->page_start; 43802ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 43902ac6454SAndrew Thompson rem = segs->ds_addr & (USB_PAGE_SIZE - 1); 44002ac6454SAndrew Thompson pc->page_offset_buf = rem; 44102ac6454SAndrew Thompson pc->page_offset_end += rem; 44202ac6454SAndrew Thompson nseg--; 443ed6d949aSAndrew Thompson #ifdef USB_DEBUG 44402ac6454SAndrew Thompson if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { 44502ac6454SAndrew Thompson /* 44602ac6454SAndrew Thompson * This check verifies that the physical address is correct: 44702ac6454SAndrew Thompson */ 448767cb2e2SAndrew Thompson DPRINTFN(0, "Page offset was not preserved\n"); 44902ac6454SAndrew Thompson error = 1; 45002ac6454SAndrew Thompson goto done; 45102ac6454SAndrew Thompson } 45202ac6454SAndrew Thompson #endif 45302ac6454SAndrew Thompson while (nseg > 0) { 45402ac6454SAndrew Thompson nseg--; 45502ac6454SAndrew Thompson segs++; 45602ac6454SAndrew Thompson pg++; 45702ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 45802ac6454SAndrew Thompson } 45902ac6454SAndrew Thompson 46002ac6454SAndrew Thompson done: 46102ac6454SAndrew Thompson owned = mtx_owned(uptag->mtx); 46202ac6454SAndrew Thompson if (!owned) 46302ac6454SAndrew Thompson mtx_lock(uptag->mtx); 46402ac6454SAndrew Thompson 46502ac6454SAndrew Thompson uptag->dma_error = (error ? 1 : 0); 46602ac6454SAndrew Thompson if (isload) { 46702ac6454SAndrew Thompson (uptag->func) (uptag); 46802ac6454SAndrew Thompson } else { 4698437751dSAndrew Thompson cv_broadcast(uptag->cv); 47002ac6454SAndrew Thompson } 47102ac6454SAndrew Thompson if (!owned) 47202ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 47302ac6454SAndrew Thompson } 47402ac6454SAndrew Thompson 47502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 476a593f6b8SAndrew Thompson * usb_pc_alloc_mem - allocate DMA'able memory 47702ac6454SAndrew Thompson * 47802ac6454SAndrew Thompson * Returns: 47902ac6454SAndrew Thompson * 0: Success 48002ac6454SAndrew Thompson * Else: Failure 48102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 48202ac6454SAndrew Thompson uint8_t 483a593f6b8SAndrew Thompson usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, 484f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 48502ac6454SAndrew Thompson { 486760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 487760bc48eSAndrew Thompson struct usb_dma_tag *utag; 48802ac6454SAndrew Thompson bus_dmamap_t map; 48902ac6454SAndrew Thompson void *ptr; 49002ac6454SAndrew Thompson int err; 49102ac6454SAndrew Thompson 49202ac6454SAndrew Thompson uptag = pc->tag_parent; 49302ac6454SAndrew Thompson 49402ac6454SAndrew Thompson if (align != 1) { 49502ac6454SAndrew Thompson /* 49602ac6454SAndrew Thompson * The alignment must be greater or equal to the 49702ac6454SAndrew Thompson * "size" else the object can be split between two 49802ac6454SAndrew Thompson * memory pages and we get a problem! 49902ac6454SAndrew Thompson */ 50002ac6454SAndrew Thompson while (align < size) { 50102ac6454SAndrew Thompson align *= 2; 50202ac6454SAndrew Thompson if (align == 0) { 50302ac6454SAndrew Thompson goto error; 50402ac6454SAndrew Thompson } 50502ac6454SAndrew Thompson } 50602ac6454SAndrew Thompson #if 1 50702ac6454SAndrew Thompson /* 50802ac6454SAndrew Thompson * XXX BUS-DMA workaround - FIXME later: 50902ac6454SAndrew Thompson * 51002ac6454SAndrew Thompson * We assume that that the aligment at this point of 51102ac6454SAndrew Thompson * the code is greater than or equal to the size and 51202ac6454SAndrew Thompson * less than two times the size, so that if we double 51302ac6454SAndrew Thompson * the size, the size will be greater than the 51402ac6454SAndrew Thompson * alignment. 51502ac6454SAndrew Thompson * 51602ac6454SAndrew Thompson * The bus-dma system has a check for "alignment" 51702ac6454SAndrew Thompson * being less than "size". If that check fails we end 51802ac6454SAndrew Thompson * up using contigmalloc which is page based even for 51902ac6454SAndrew Thompson * small allocations. Try to avoid that to save 52002ac6454SAndrew Thompson * memory, hence we sometimes to a large number of 52102ac6454SAndrew Thompson * small allocations! 52202ac6454SAndrew Thompson */ 52302ac6454SAndrew Thompson if (size <= (USB_PAGE_SIZE / 2)) { 52402ac6454SAndrew Thompson size *= 2; 52502ac6454SAndrew Thompson } 52602ac6454SAndrew Thompson #endif 52702ac6454SAndrew Thompson } 52802ac6454SAndrew Thompson /* get the correct DMA tag */ 529a593f6b8SAndrew Thompson utag = usb_dma_tag_find(uptag, size, align); 53002ac6454SAndrew Thompson if (utag == NULL) { 53102ac6454SAndrew Thompson goto error; 53202ac6454SAndrew Thompson } 53302ac6454SAndrew Thompson /* allocate memory */ 53402ac6454SAndrew Thompson if (bus_dmamem_alloc( 53502ac6454SAndrew Thompson utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { 53602ac6454SAndrew Thompson goto error; 53702ac6454SAndrew Thompson } 53802ac6454SAndrew Thompson /* setup page cache */ 53902ac6454SAndrew Thompson pc->buffer = ptr; 54002ac6454SAndrew Thompson pc->page_start = pg; 54102ac6454SAndrew Thompson pc->page_offset_buf = 0; 54202ac6454SAndrew Thompson pc->page_offset_end = size; 54302ac6454SAndrew Thompson pc->map = map; 54402ac6454SAndrew Thompson pc->tag = utag->tag; 54502ac6454SAndrew Thompson pc->ismultiseg = (align == 1); 54602ac6454SAndrew Thompson 54702ac6454SAndrew Thompson mtx_lock(uptag->mtx); 54802ac6454SAndrew Thompson 54902ac6454SAndrew Thompson /* load memory into DMA */ 55002ac6454SAndrew Thompson err = bus_dmamap_load( 551a593f6b8SAndrew Thompson utag->tag, map, ptr, size, &usb_pc_alloc_mem_cb, 55202ac6454SAndrew Thompson pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); 55302ac6454SAndrew Thompson 55402ac6454SAndrew Thompson if (err == EINPROGRESS) { 5558437751dSAndrew Thompson cv_wait(uptag->cv, uptag->mtx); 55602ac6454SAndrew Thompson err = 0; 55702ac6454SAndrew Thompson } 55802ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 55902ac6454SAndrew Thompson 56002ac6454SAndrew Thompson if (err || uptag->dma_error) { 56102ac6454SAndrew Thompson bus_dmamem_free(utag->tag, ptr, map); 56202ac6454SAndrew Thompson goto error; 56302ac6454SAndrew Thompson } 56402ac6454SAndrew Thompson bzero(ptr, size); 56502ac6454SAndrew Thompson 566a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 56702ac6454SAndrew Thompson 56802ac6454SAndrew Thompson return (0); 56902ac6454SAndrew Thompson 57002ac6454SAndrew Thompson error: 57102ac6454SAndrew Thompson /* reset most of the page cache */ 57202ac6454SAndrew Thompson pc->buffer = NULL; 57302ac6454SAndrew Thompson pc->page_start = NULL; 57402ac6454SAndrew Thompson pc->page_offset_buf = 0; 57502ac6454SAndrew Thompson pc->page_offset_end = 0; 57602ac6454SAndrew Thompson pc->map = NULL; 57702ac6454SAndrew Thompson pc->tag = NULL; 57802ac6454SAndrew Thompson return (1); 57902ac6454SAndrew Thompson } 58002ac6454SAndrew Thompson 58102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 582a593f6b8SAndrew Thompson * usb_pc_free_mem - free DMA memory 58302ac6454SAndrew Thompson * 58402ac6454SAndrew Thompson * This function is NULL safe. 58502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 58602ac6454SAndrew Thompson void 587a593f6b8SAndrew Thompson usb_pc_free_mem(struct usb_page_cache *pc) 58802ac6454SAndrew Thompson { 58902ac6454SAndrew Thompson if (pc && pc->buffer) { 59002ac6454SAndrew Thompson 59102ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 59202ac6454SAndrew Thompson 59302ac6454SAndrew Thompson bus_dmamem_free(pc->tag, pc->buffer, pc->map); 59402ac6454SAndrew Thompson 59502ac6454SAndrew Thompson pc->buffer = NULL; 59602ac6454SAndrew Thompson } 59702ac6454SAndrew Thompson } 59802ac6454SAndrew Thompson 59902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 600a593f6b8SAndrew Thompson * usb_pc_load_mem - load virtual memory into DMA 60102ac6454SAndrew Thompson * 60202ac6454SAndrew Thompson * Return values: 60302ac6454SAndrew Thompson * 0: Success 60402ac6454SAndrew Thompson * Else: Error 60502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 60602ac6454SAndrew Thompson uint8_t 607a593f6b8SAndrew Thompson usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync) 60802ac6454SAndrew Thompson { 60902ac6454SAndrew Thompson /* setup page cache */ 61002ac6454SAndrew Thompson pc->page_offset_buf = 0; 61102ac6454SAndrew Thompson pc->page_offset_end = size; 61202ac6454SAndrew Thompson pc->ismultiseg = 1; 61302ac6454SAndrew Thompson 61402ac6454SAndrew Thompson mtx_assert(pc->tag_parent->mtx, MA_OWNED); 61502ac6454SAndrew Thompson 61602ac6454SAndrew Thompson if (size > 0) { 61702ac6454SAndrew Thompson if (sync) { 618760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 61902ac6454SAndrew Thompson int err; 62002ac6454SAndrew Thompson 62102ac6454SAndrew Thompson uptag = pc->tag_parent; 62202ac6454SAndrew Thompson 62302ac6454SAndrew Thompson /* 62402ac6454SAndrew Thompson * We have to unload the previous loaded DMA 62502ac6454SAndrew Thompson * pages before trying to load a new one! 62602ac6454SAndrew Thompson */ 62702ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 62802ac6454SAndrew Thompson 62902ac6454SAndrew Thompson /* 63002ac6454SAndrew Thompson * Try to load memory into DMA. 63102ac6454SAndrew Thompson */ 63202ac6454SAndrew Thompson err = bus_dmamap_load( 63302ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 634a593f6b8SAndrew Thompson &usb_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK); 63502ac6454SAndrew Thompson if (err == EINPROGRESS) { 6368437751dSAndrew Thompson cv_wait(uptag->cv, uptag->mtx); 63702ac6454SAndrew Thompson err = 0; 63802ac6454SAndrew Thompson } 63902ac6454SAndrew Thompson if (err || uptag->dma_error) { 64002ac6454SAndrew Thompson return (1); 64102ac6454SAndrew Thompson } 64202ac6454SAndrew Thompson } else { 64302ac6454SAndrew Thompson 64402ac6454SAndrew Thompson /* 64502ac6454SAndrew Thompson * We have to unload the previous loaded DMA 64602ac6454SAndrew Thompson * pages before trying to load a new one! 64702ac6454SAndrew Thompson */ 64802ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 64902ac6454SAndrew Thompson 65002ac6454SAndrew Thompson /* 65102ac6454SAndrew Thompson * Try to load memory into DMA. The callback 65202ac6454SAndrew Thompson * will be called in all cases: 65302ac6454SAndrew Thompson */ 65402ac6454SAndrew Thompson if (bus_dmamap_load( 65502ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 656a593f6b8SAndrew Thompson &usb_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) { 65702ac6454SAndrew Thompson } 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson } else { 66002ac6454SAndrew Thompson if (!sync) { 66102ac6454SAndrew Thompson /* 66202ac6454SAndrew Thompson * Call callback so that refcount is decremented 66302ac6454SAndrew Thompson * properly: 66402ac6454SAndrew Thompson */ 66502ac6454SAndrew Thompson pc->tag_parent->dma_error = 0; 66602ac6454SAndrew Thompson (pc->tag_parent->func) (pc->tag_parent); 66702ac6454SAndrew Thompson } 66802ac6454SAndrew Thompson } 66902ac6454SAndrew Thompson return (0); 67002ac6454SAndrew Thompson } 67102ac6454SAndrew Thompson 67202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 673a593f6b8SAndrew Thompson * usb_pc_cpu_invalidate - invalidate CPU cache 67402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 67502ac6454SAndrew Thompson void 676a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(struct usb_page_cache *pc) 67702ac6454SAndrew Thompson { 67802ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 67902ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 68002ac6454SAndrew Thompson return; 68102ac6454SAndrew Thompson } 682f2db024fSAlfred Perlstein 683f2db024fSAlfred Perlstein /* 684f2db024fSAlfred Perlstein * TODO: We currently do XXX_POSTREAD and XXX_PREREAD at the 685f2db024fSAlfred Perlstein * same time, but in the future we should try to isolate the 686f2db024fSAlfred Perlstein * different cases to optimise the code. --HPS 687f2db024fSAlfred Perlstein */ 68835d01728SRafal Jaworowski bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_POSTREAD); 68935d01728SRafal Jaworowski bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREREAD); 69002ac6454SAndrew Thompson } 69102ac6454SAndrew Thompson 69202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 693a593f6b8SAndrew Thompson * usb_pc_cpu_flush - flush CPU cache 69402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 69502ac6454SAndrew Thompson void 696a593f6b8SAndrew Thompson usb_pc_cpu_flush(struct usb_page_cache *pc) 69702ac6454SAndrew Thompson { 69802ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 69902ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 70002ac6454SAndrew Thompson return; 70102ac6454SAndrew Thompson } 70235d01728SRafal Jaworowski bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREWRITE); 70302ac6454SAndrew Thompson } 70402ac6454SAndrew Thompson 70502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 706a593f6b8SAndrew Thompson * usb_pc_dmamap_create - create a DMA map 70702ac6454SAndrew Thompson * 70802ac6454SAndrew Thompson * Returns: 70902ac6454SAndrew Thompson * 0: Success 71002ac6454SAndrew Thompson * Else: Failure 71102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 71202ac6454SAndrew Thompson uint8_t 713a593f6b8SAndrew Thompson usb_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size) 71402ac6454SAndrew Thompson { 715760bc48eSAndrew Thompson struct usb_xfer_root *info; 716760bc48eSAndrew Thompson struct usb_dma_tag *utag; 71702ac6454SAndrew Thompson 71802ac6454SAndrew Thompson /* get info */ 719bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(pc->tag_parent); 72002ac6454SAndrew Thompson 72102ac6454SAndrew Thompson /* sanity check */ 72202ac6454SAndrew Thompson if (info == NULL) { 72302ac6454SAndrew Thompson goto error; 72402ac6454SAndrew Thompson } 725a593f6b8SAndrew Thompson utag = usb_dma_tag_find(pc->tag_parent, size, 1); 72602ac6454SAndrew Thompson if (utag == NULL) { 72702ac6454SAndrew Thompson goto error; 72802ac6454SAndrew Thompson } 72902ac6454SAndrew Thompson /* create DMA map */ 73002ac6454SAndrew Thompson if (bus_dmamap_create(utag->tag, 0, &pc->map)) { 73102ac6454SAndrew Thompson goto error; 73202ac6454SAndrew Thompson } 73302ac6454SAndrew Thompson pc->tag = utag->tag; 73402ac6454SAndrew Thompson return 0; /* success */ 73502ac6454SAndrew Thompson 73602ac6454SAndrew Thompson error: 73702ac6454SAndrew Thompson pc->map = NULL; 73802ac6454SAndrew Thompson pc->tag = NULL; 73902ac6454SAndrew Thompson return 1; /* failure */ 74002ac6454SAndrew Thompson } 74102ac6454SAndrew Thompson 74202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 743a593f6b8SAndrew Thompson * usb_pc_dmamap_destroy 74402ac6454SAndrew Thompson * 74502ac6454SAndrew Thompson * This function is NULL safe. 74602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 74702ac6454SAndrew Thompson void 748a593f6b8SAndrew Thompson usb_pc_dmamap_destroy(struct usb_page_cache *pc) 74902ac6454SAndrew Thompson { 75002ac6454SAndrew Thompson if (pc && pc->tag) { 75102ac6454SAndrew Thompson bus_dmamap_destroy(pc->tag, pc->map); 75202ac6454SAndrew Thompson pc->tag = NULL; 75302ac6454SAndrew Thompson pc->map = NULL; 75402ac6454SAndrew Thompson } 75502ac6454SAndrew Thompson } 75602ac6454SAndrew Thompson 75702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 758a593f6b8SAndrew Thompson * usb_dma_tag_find - factored out code 75902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 760760bc48eSAndrew Thompson struct usb_dma_tag * 761a593f6b8SAndrew Thompson usb_dma_tag_find(struct usb_dma_parent_tag *udpt, 762f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 76302ac6454SAndrew Thompson { 764760bc48eSAndrew Thompson struct usb_dma_tag *udt; 76502ac6454SAndrew Thompson uint8_t nudt; 76602ac6454SAndrew Thompson 767767cb2e2SAndrew Thompson USB_ASSERT(align > 0, ("Invalid parameter align = 0\n")); 768767cb2e2SAndrew Thompson USB_ASSERT(size > 0, ("Invalid parameter size = 0\n")); 76902ac6454SAndrew Thompson 77002ac6454SAndrew Thompson udt = udpt->utag_first; 77102ac6454SAndrew Thompson nudt = udpt->utag_max; 77202ac6454SAndrew Thompson 77302ac6454SAndrew Thompson while (nudt--) { 77402ac6454SAndrew Thompson 77502ac6454SAndrew Thompson if (udt->align == 0) { 776a593f6b8SAndrew Thompson usb_dma_tag_create(udt, size, align); 77702ac6454SAndrew Thompson if (udt->tag == NULL) { 77802ac6454SAndrew Thompson return (NULL); 77902ac6454SAndrew Thompson } 78002ac6454SAndrew Thompson udt->align = align; 78102ac6454SAndrew Thompson udt->size = size; 78202ac6454SAndrew Thompson return (udt); 78302ac6454SAndrew Thompson } 78402ac6454SAndrew Thompson if ((udt->align == align) && (udt->size == size)) { 78502ac6454SAndrew Thompson return (udt); 78602ac6454SAndrew Thompson } 78702ac6454SAndrew Thompson udt++; 78802ac6454SAndrew Thompson } 78902ac6454SAndrew Thompson return (NULL); 79002ac6454SAndrew Thompson } 79102ac6454SAndrew Thompson 79202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 793a593f6b8SAndrew Thompson * usb_dma_tag_setup - initialise USB DMA tags 79402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 79502ac6454SAndrew Thompson void 796a593f6b8SAndrew Thompson usb_dma_tag_setup(struct usb_dma_parent_tag *udpt, 797760bc48eSAndrew Thompson struct usb_dma_tag *udt, bus_dma_tag_t dmat, 798e0a69b51SAndrew Thompson struct mtx *mtx, usb_dma_callback_t *func, 799bdc081c6SAndrew Thompson uint8_t ndmabits, uint8_t nudt) 80002ac6454SAndrew Thompson { 80102ac6454SAndrew Thompson bzero(udpt, sizeof(*udpt)); 80202ac6454SAndrew Thompson 80302ac6454SAndrew Thompson /* sanity checking */ 80402ac6454SAndrew Thompson if ((nudt == 0) || 80502ac6454SAndrew Thompson (ndmabits == 0) || 80602ac6454SAndrew Thompson (mtx == NULL)) { 80702ac6454SAndrew Thompson /* something is corrupt */ 80802ac6454SAndrew Thompson return; 80902ac6454SAndrew Thompson } 81002ac6454SAndrew Thompson /* initialise condition variable */ 8118437751dSAndrew Thompson cv_init(udpt->cv, "USB DMA CV"); 81202ac6454SAndrew Thompson 81302ac6454SAndrew Thompson /* store some information */ 81402ac6454SAndrew Thompson udpt->mtx = mtx; 81502ac6454SAndrew Thompson udpt->func = func; 81602ac6454SAndrew Thompson udpt->tag = dmat; 81702ac6454SAndrew Thompson udpt->utag_first = udt; 81802ac6454SAndrew Thompson udpt->utag_max = nudt; 81902ac6454SAndrew Thompson udpt->dma_bits = ndmabits; 82002ac6454SAndrew Thompson 82102ac6454SAndrew Thompson while (nudt--) { 82202ac6454SAndrew Thompson bzero(udt, sizeof(*udt)); 82302ac6454SAndrew Thompson udt->tag_parent = udpt; 82402ac6454SAndrew Thompson udt++; 82502ac6454SAndrew Thompson } 82602ac6454SAndrew Thompson } 82702ac6454SAndrew Thompson 82802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 829a593f6b8SAndrew Thompson * usb_bus_tag_unsetup - factored out code 83002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 83102ac6454SAndrew Thompson void 832a593f6b8SAndrew Thompson usb_dma_tag_unsetup(struct usb_dma_parent_tag *udpt) 83302ac6454SAndrew Thompson { 834760bc48eSAndrew Thompson struct usb_dma_tag *udt; 83502ac6454SAndrew Thompson uint8_t nudt; 83602ac6454SAndrew Thompson 83702ac6454SAndrew Thompson udt = udpt->utag_first; 83802ac6454SAndrew Thompson nudt = udpt->utag_max; 83902ac6454SAndrew Thompson 84002ac6454SAndrew Thompson while (nudt--) { 84102ac6454SAndrew Thompson 84202ac6454SAndrew Thompson if (udt->align) { 84302ac6454SAndrew Thompson /* destroy the USB DMA tag */ 844a593f6b8SAndrew Thompson usb_dma_tag_destroy(udt); 84502ac6454SAndrew Thompson udt->align = 0; 84602ac6454SAndrew Thompson } 84702ac6454SAndrew Thompson udt++; 84802ac6454SAndrew Thompson } 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson if (udpt->utag_max) { 85102ac6454SAndrew Thompson /* destroy the condition variable */ 8528437751dSAndrew Thompson cv_destroy(udpt->cv); 85302ac6454SAndrew Thompson } 85402ac6454SAndrew Thompson } 85502ac6454SAndrew Thompson 85602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 857a593f6b8SAndrew Thompson * usb_bdma_work_loop 85802ac6454SAndrew Thompson * 85902ac6454SAndrew Thompson * This function handles loading of virtual buffers into DMA and is 86002ac6454SAndrew Thompson * only called when "dma_refcount" is zero. 86102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 86202ac6454SAndrew Thompson void 863a593f6b8SAndrew Thompson usb_bdma_work_loop(struct usb_xfer_queue *pq) 86402ac6454SAndrew Thompson { 865760bc48eSAndrew Thompson struct usb_xfer_root *info; 866760bc48eSAndrew Thompson struct usb_xfer *xfer; 867e0a69b51SAndrew Thompson usb_frcount_t nframes; 86802ac6454SAndrew Thompson 86902ac6454SAndrew Thompson xfer = pq->curr; 87002ac6454SAndrew Thompson info = xfer->xroot; 87102ac6454SAndrew Thompson 87202ac6454SAndrew Thompson mtx_assert(info->xfer_mtx, MA_OWNED); 87302ac6454SAndrew Thompson 87402ac6454SAndrew Thompson if (xfer->error) { 87502ac6454SAndrew Thompson /* some error happened */ 87602ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 877a593f6b8SAndrew Thompson usbd_transfer_done(xfer, 0); 87802ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 87902ac6454SAndrew Thompson return; 88002ac6454SAndrew Thompson } 88102ac6454SAndrew Thompson if (!xfer->flags_int.bdma_setup) { 882760bc48eSAndrew Thompson struct usb_page *pg; 883e0a69b51SAndrew Thompson usb_frlength_t frlength_0; 88402ac6454SAndrew Thompson uint8_t isread; 88502ac6454SAndrew Thompson 88602ac6454SAndrew Thompson xfer->flags_int.bdma_setup = 1; 88702ac6454SAndrew Thompson 88802ac6454SAndrew Thompson /* reset BUS-DMA load state */ 88902ac6454SAndrew Thompson 89002ac6454SAndrew Thompson info->dma_error = 0; 89102ac6454SAndrew Thompson 89202ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 89302ac6454SAndrew Thompson /* only one frame buffer */ 89402ac6454SAndrew Thompson nframes = 1; 89502ac6454SAndrew Thompson frlength_0 = xfer->sumlen; 89602ac6454SAndrew Thompson } else { 89702ac6454SAndrew Thompson /* can be multiple frame buffers */ 89802ac6454SAndrew Thompson nframes = xfer->nframes; 89902ac6454SAndrew Thompson frlength_0 = xfer->frlengths[0]; 90002ac6454SAndrew Thompson } 90102ac6454SAndrew Thompson 90202ac6454SAndrew Thompson /* 90302ac6454SAndrew Thompson * Set DMA direction first. This is needed to 90402ac6454SAndrew Thompson * select the correct cache invalidate and cache 90502ac6454SAndrew Thompson * flush operations. 90602ac6454SAndrew Thompson */ 90702ac6454SAndrew Thompson isread = USB_GET_DATA_ISREAD(xfer); 90802ac6454SAndrew Thompson pg = xfer->dma_page_ptr; 90902ac6454SAndrew Thompson 91002ac6454SAndrew Thompson if (xfer->flags_int.control_xfr && 91102ac6454SAndrew Thompson xfer->flags_int.control_hdr) { 91202ac6454SAndrew Thompson /* special case */ 913f29a0724SAndrew Thompson if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { 91402ac6454SAndrew Thompson /* The device controller writes to memory */ 91502ac6454SAndrew Thompson xfer->frbuffers[0].isread = 1; 91602ac6454SAndrew Thompson } else { 91702ac6454SAndrew Thompson /* The host controller reads from memory */ 91802ac6454SAndrew Thompson xfer->frbuffers[0].isread = 0; 91902ac6454SAndrew Thompson } 92002ac6454SAndrew Thompson } else { 92102ac6454SAndrew Thompson /* default case */ 92202ac6454SAndrew Thompson xfer->frbuffers[0].isread = isread; 92302ac6454SAndrew Thompson } 92402ac6454SAndrew Thompson 92502ac6454SAndrew Thompson /* 92602ac6454SAndrew Thompson * Setup the "page_start" pointer which points to an array of 92702ac6454SAndrew Thompson * USB pages where information about the physical address of a 92802ac6454SAndrew Thompson * page will be stored. Also initialise the "isread" field of 92902ac6454SAndrew Thompson * the USB page caches. 93002ac6454SAndrew Thompson */ 93102ac6454SAndrew Thompson xfer->frbuffers[0].page_start = pg; 93202ac6454SAndrew Thompson 93302ac6454SAndrew Thompson info->dma_nframes = nframes; 93402ac6454SAndrew Thompson info->dma_currframe = 0; 93502ac6454SAndrew Thompson info->dma_frlength_0 = frlength_0; 93602ac6454SAndrew Thompson 93702ac6454SAndrew Thompson pg += (frlength_0 / USB_PAGE_SIZE); 93802ac6454SAndrew Thompson pg += 2; 93902ac6454SAndrew Thompson 94002ac6454SAndrew Thompson while (--nframes > 0) { 94102ac6454SAndrew Thompson xfer->frbuffers[nframes].isread = isread; 94202ac6454SAndrew Thompson xfer->frbuffers[nframes].page_start = pg; 94302ac6454SAndrew Thompson 94402ac6454SAndrew Thompson pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); 94502ac6454SAndrew Thompson pg += 2; 94602ac6454SAndrew Thompson } 94702ac6454SAndrew Thompson 94802ac6454SAndrew Thompson } 94902ac6454SAndrew Thompson if (info->dma_error) { 95002ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 951a593f6b8SAndrew Thompson usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); 95202ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 95302ac6454SAndrew Thompson return; 95402ac6454SAndrew Thompson } 95502ac6454SAndrew Thompson if (info->dma_currframe != info->dma_nframes) { 95602ac6454SAndrew Thompson 95702ac6454SAndrew Thompson if (info->dma_currframe == 0) { 95802ac6454SAndrew Thompson /* special case */ 959a593f6b8SAndrew Thompson usb_pc_load_mem(xfer->frbuffers, 96002ac6454SAndrew Thompson info->dma_frlength_0, 0); 96102ac6454SAndrew Thompson } else { 96202ac6454SAndrew Thompson /* default case */ 96302ac6454SAndrew Thompson nframes = info->dma_currframe; 964a593f6b8SAndrew Thompson usb_pc_load_mem(xfer->frbuffers + nframes, 96502ac6454SAndrew Thompson xfer->frlengths[nframes], 0); 96602ac6454SAndrew Thompson } 96702ac6454SAndrew Thompson 96802ac6454SAndrew Thompson /* advance frame index */ 96902ac6454SAndrew Thompson info->dma_currframe++; 97002ac6454SAndrew Thompson 97102ac6454SAndrew Thompson return; 97202ac6454SAndrew Thompson } 97302ac6454SAndrew Thompson /* go ahead */ 974a593f6b8SAndrew Thompson usb_bdma_pre_sync(xfer); 97502ac6454SAndrew Thompson 97602ac6454SAndrew Thompson /* start loading next USB transfer, if any */ 977a593f6b8SAndrew Thompson usb_command_wrapper(pq, NULL); 97802ac6454SAndrew Thompson 97902ac6454SAndrew Thompson /* finally start the hardware */ 980a593f6b8SAndrew Thompson usbd_pipe_enter(xfer); 98102ac6454SAndrew Thompson } 98202ac6454SAndrew Thompson 98302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 984a593f6b8SAndrew Thompson * usb_bdma_done_event 98502ac6454SAndrew Thompson * 98602ac6454SAndrew Thompson * This function is called when the BUS-DMA has loaded virtual memory 98702ac6454SAndrew Thompson * into DMA, if any. 98802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 98902ac6454SAndrew Thompson void 990a593f6b8SAndrew Thompson usb_bdma_done_event(struct usb_dma_parent_tag *udpt) 99102ac6454SAndrew Thompson { 992760bc48eSAndrew Thompson struct usb_xfer_root *info; 99302ac6454SAndrew Thompson 994bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(udpt); 99502ac6454SAndrew Thompson 99602ac6454SAndrew Thompson mtx_assert(info->xfer_mtx, MA_OWNED); 99702ac6454SAndrew Thompson 99802ac6454SAndrew Thompson /* copy error */ 99902ac6454SAndrew Thompson info->dma_error = udpt->dma_error; 100002ac6454SAndrew Thompson 100102ac6454SAndrew Thompson /* enter workloop again */ 1002a593f6b8SAndrew Thompson usb_command_wrapper(&info->dma_q, 100302ac6454SAndrew Thompson info->dma_q.curr); 100402ac6454SAndrew Thompson } 100502ac6454SAndrew Thompson 100602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1007a593f6b8SAndrew Thompson * usb_bdma_pre_sync 100802ac6454SAndrew Thompson * 100902ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done before 101002ac6454SAndrew Thompson * an USB transfer is started. 101102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 101202ac6454SAndrew Thompson void 1013a593f6b8SAndrew Thompson usb_bdma_pre_sync(struct usb_xfer *xfer) 101402ac6454SAndrew Thompson { 1015760bc48eSAndrew Thompson struct usb_page_cache *pc; 1016e0a69b51SAndrew Thompson usb_frcount_t nframes; 101702ac6454SAndrew Thompson 101802ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 101902ac6454SAndrew Thompson /* only one frame buffer */ 102002ac6454SAndrew Thompson nframes = 1; 102102ac6454SAndrew Thompson } else { 102202ac6454SAndrew Thompson /* can be multiple frame buffers */ 102302ac6454SAndrew Thompson nframes = xfer->nframes; 102402ac6454SAndrew Thompson } 102502ac6454SAndrew Thompson 102602ac6454SAndrew Thompson pc = xfer->frbuffers; 102702ac6454SAndrew Thompson 102802ac6454SAndrew Thompson while (nframes--) { 102902ac6454SAndrew Thompson 103002ac6454SAndrew Thompson if (pc->isread) { 1031a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(pc); 103202ac6454SAndrew Thompson } else { 1033a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 103402ac6454SAndrew Thompson } 103502ac6454SAndrew Thompson pc++; 103602ac6454SAndrew Thompson } 103702ac6454SAndrew Thompson } 103802ac6454SAndrew Thompson 103902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1040a593f6b8SAndrew Thompson * usb_bdma_post_sync 104102ac6454SAndrew Thompson * 104202ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done after 104302ac6454SAndrew Thompson * an USB transfer is complete. 104402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 104502ac6454SAndrew Thompson void 1046a593f6b8SAndrew Thompson usb_bdma_post_sync(struct usb_xfer *xfer) 104702ac6454SAndrew Thompson { 1048760bc48eSAndrew Thompson struct usb_page_cache *pc; 1049e0a69b51SAndrew Thompson usb_frcount_t nframes; 105002ac6454SAndrew Thompson 105102ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 105202ac6454SAndrew Thompson /* only one frame buffer */ 105302ac6454SAndrew Thompson nframes = 1; 105402ac6454SAndrew Thompson } else { 105502ac6454SAndrew Thompson /* can be multiple frame buffers */ 105602ac6454SAndrew Thompson nframes = xfer->nframes; 105702ac6454SAndrew Thompson } 105802ac6454SAndrew Thompson 105902ac6454SAndrew Thompson pc = xfer->frbuffers; 106002ac6454SAndrew Thompson 106102ac6454SAndrew Thompson while (nframes--) { 106202ac6454SAndrew Thompson if (pc->isread) { 1063a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(pc); 106402ac6454SAndrew Thompson } 106502ac6454SAndrew Thompson pc++; 106602ac6454SAndrew Thompson } 106702ac6454SAndrew Thompson } 1068bdc081c6SAndrew Thompson 1069bdc081c6SAndrew Thompson #endif 1070