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 2702ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h> 2802ac6454SAndrew Thompson #include <dev/usb/usb_error.h> 2902ac6454SAndrew Thompson #include <dev/usb/usb.h> 3002ac6454SAndrew Thompson 3102ac6454SAndrew Thompson #define USB_DEBUG_VAR usb2_debug 3202ac6454SAndrew Thompson 3302ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 3402ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 3502ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 3602ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 3702ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 3802ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 3902ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 4002ac6454SAndrew Thompson 4102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 4202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 4302ac6454SAndrew Thompson 44bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 45578d0effSAndrew Thompson static void usb2_dma_tag_create(struct usb2_dma_tag *, usb2_size_t, usb2_size_t); 4602ac6454SAndrew Thompson static void usb2_dma_tag_destroy(struct usb2_dma_tag *); 47bdc081c6SAndrew Thompson #endif 4802ac6454SAndrew Thompson 49bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA && defined(__FreeBSD__) 5002ac6454SAndrew Thompson static void usb2_dma_lock_cb(void *, bus_dma_lock_op_t); 5102ac6454SAndrew Thompson static void usb2_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); 5202ac6454SAndrew Thompson static void usb2_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); 5302ac6454SAndrew Thompson static void usb2_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, 5402ac6454SAndrew Thompson uint8_t); 5502ac6454SAndrew Thompson #endif 5602ac6454SAndrew Thompson 57bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA && defined(__NetBSD__) 5802ac6454SAndrew Thompson static void usb2_pc_common_mem_cb(struct usb2_page_cache *, 5902ac6454SAndrew Thompson bus_dma_segment_t *, int, int, uint8_t); 6002ac6454SAndrew Thompson #endif 6102ac6454SAndrew Thompson 6202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 6302ac6454SAndrew Thompson * usb2_get_page - lookup DMA-able memory for the given offset 6402ac6454SAndrew Thompson * 6502ac6454SAndrew Thompson * NOTE: Only call this function when the "page_cache" structure has 6602ac6454SAndrew Thompson * been properly initialized ! 6702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 6802ac6454SAndrew Thompson void 69578d0effSAndrew Thompson usb2_get_page(struct usb2_page_cache *pc, usb2_frlength_t offset, 7002ac6454SAndrew Thompson struct usb2_page_search *res) 7102ac6454SAndrew Thompson { 7202ac6454SAndrew Thompson struct usb2_page *page; 7302ac6454SAndrew Thompson 74bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 7502ac6454SAndrew Thompson if (pc->page_start) { 7602ac6454SAndrew Thompson 7702ac6454SAndrew Thompson /* Case 1 - something has been loaded into DMA */ 7802ac6454SAndrew Thompson 7902ac6454SAndrew Thompson if (pc->buffer) { 8002ac6454SAndrew Thompson 8102ac6454SAndrew Thompson /* Case 1a - Kernel Virtual Address */ 8202ac6454SAndrew Thompson 8302ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 8402ac6454SAndrew Thompson } 8502ac6454SAndrew Thompson offset += pc->page_offset_buf; 8602ac6454SAndrew Thompson 8702ac6454SAndrew Thompson /* compute destination page */ 8802ac6454SAndrew Thompson 8902ac6454SAndrew Thompson page = pc->page_start; 9002ac6454SAndrew Thompson 9102ac6454SAndrew Thompson if (pc->ismultiseg) { 9202ac6454SAndrew Thompson 9302ac6454SAndrew Thompson page += (offset / USB_PAGE_SIZE); 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson offset %= USB_PAGE_SIZE; 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson res->length = USB_PAGE_SIZE - offset; 9802ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 9902ac6454SAndrew Thompson } else { 10002ac6454SAndrew Thompson res->length = 0 - 1; 10102ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 10202ac6454SAndrew Thompson } 10302ac6454SAndrew Thompson if (!pc->buffer) { 10402ac6454SAndrew Thompson 10502ac6454SAndrew Thompson /* Case 1b - Non Kernel Virtual Address */ 10602ac6454SAndrew Thompson 10702ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(page->buffer, offset); 10802ac6454SAndrew Thompson } 109bdc081c6SAndrew Thompson return; 110bdc081c6SAndrew Thompson } 111bdc081c6SAndrew Thompson #endif 11202ac6454SAndrew Thompson /* Case 2 - Plain PIO */ 11302ac6454SAndrew Thompson 11402ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 11502ac6454SAndrew Thompson res->length = 0 - 1; 116bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 11702ac6454SAndrew Thompson res->physaddr = 0; 118bdc081c6SAndrew Thompson #endif 11902ac6454SAndrew Thompson } 12002ac6454SAndrew Thompson 12102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 12202ac6454SAndrew Thompson * usb2_copy_in - copy directly to DMA-able memory 12302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 12402ac6454SAndrew Thompson void 125578d0effSAndrew Thompson usb2_copy_in(struct usb2_page_cache *cache, usb2_frlength_t offset, 126578d0effSAndrew Thompson const void *ptr, usb2_frlength_t len) 12702ac6454SAndrew Thompson { 12802ac6454SAndrew Thompson struct usb2_page_search buf_res; 12902ac6454SAndrew Thompson 13002ac6454SAndrew Thompson while (len != 0) { 13102ac6454SAndrew Thompson 13202ac6454SAndrew Thompson usb2_get_page(cache, offset, &buf_res); 13302ac6454SAndrew Thompson 13402ac6454SAndrew Thompson if (buf_res.length > len) { 13502ac6454SAndrew Thompson buf_res.length = len; 13602ac6454SAndrew Thompson } 13702ac6454SAndrew Thompson bcopy(ptr, buf_res.buffer, buf_res.length); 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson offset += buf_res.length; 14002ac6454SAndrew Thompson len -= buf_res.length; 14102ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 14202ac6454SAndrew Thompson } 14302ac6454SAndrew Thompson } 14402ac6454SAndrew Thompson 14502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 14602ac6454SAndrew Thompson * usb2_copy_in_user - copy directly to DMA-able memory from userland 14702ac6454SAndrew Thompson * 14802ac6454SAndrew Thompson * Return values: 14902ac6454SAndrew Thompson * 0: Success 15002ac6454SAndrew Thompson * Else: Failure 15102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 152bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 15302ac6454SAndrew Thompson int 154578d0effSAndrew Thompson usb2_copy_in_user(struct usb2_page_cache *cache, usb2_frlength_t offset, 155578d0effSAndrew Thompson const void *ptr, usb2_frlength_t len) 15602ac6454SAndrew Thompson { 15702ac6454SAndrew Thompson struct usb2_page_search buf_res; 15802ac6454SAndrew Thompson int error; 15902ac6454SAndrew Thompson 16002ac6454SAndrew Thompson while (len != 0) { 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson usb2_get_page(cache, offset, &buf_res); 16302ac6454SAndrew Thompson 16402ac6454SAndrew Thompson if (buf_res.length > len) { 16502ac6454SAndrew Thompson buf_res.length = len; 16602ac6454SAndrew Thompson } 16702ac6454SAndrew Thompson error = copyin(ptr, buf_res.buffer, buf_res.length); 16802ac6454SAndrew Thompson if (error) 16902ac6454SAndrew Thompson return (error); 17002ac6454SAndrew Thompson 17102ac6454SAndrew Thompson offset += buf_res.length; 17202ac6454SAndrew Thompson len -= buf_res.length; 17302ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 17402ac6454SAndrew Thompson } 17502ac6454SAndrew Thompson return (0); /* success */ 17602ac6454SAndrew Thompson } 177bdc081c6SAndrew Thompson #endif 17802ac6454SAndrew Thompson 17902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 18002ac6454SAndrew Thompson * usb2_m_copy_in - copy a mbuf chain directly into DMA-able memory 18102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 182bdc081c6SAndrew Thompson #if USB_HAVE_MBUF 18302ac6454SAndrew Thompson struct usb2_m_copy_in_arg { 18402ac6454SAndrew Thompson struct usb2_page_cache *cache; 185578d0effSAndrew Thompson usb2_frlength_t dst_offset; 18602ac6454SAndrew Thompson }; 18702ac6454SAndrew Thompson 188578d0effSAndrew Thompson static int 18902ac6454SAndrew Thompson #ifdef __FreeBSD__ 19002ac6454SAndrew Thompson usb2_m_copy_in_cb(void *arg, void *src, uint32_t count) 19102ac6454SAndrew Thompson #else 19202ac6454SAndrew Thompson usb2_m_copy_in_cb(void *arg, caddr_t src, uint32_t count) 19302ac6454SAndrew Thompson #endif 19402ac6454SAndrew Thompson { 19502ac6454SAndrew Thompson register struct usb2_m_copy_in_arg *ua = arg; 19602ac6454SAndrew Thompson 19702ac6454SAndrew Thompson usb2_copy_in(ua->cache, ua->dst_offset, src, count); 19802ac6454SAndrew Thompson ua->dst_offset += count; 19902ac6454SAndrew Thompson return (0); 20002ac6454SAndrew Thompson } 20102ac6454SAndrew Thompson 20202ac6454SAndrew Thompson void 203578d0effSAndrew Thompson usb2_m_copy_in(struct usb2_page_cache *cache, usb2_frlength_t dst_offset, 204578d0effSAndrew Thompson struct mbuf *m, usb2_size_t src_offset, usb2_frlength_t src_len) 20502ac6454SAndrew Thompson { 20602ac6454SAndrew Thompson struct usb2_m_copy_in_arg arg = {cache, dst_offset}; 207578d0effSAndrew Thompson int error; 20802ac6454SAndrew Thompson 20902ac6454SAndrew Thompson error = m_apply(m, src_offset, src_len, &usb2_m_copy_in_cb, &arg); 21002ac6454SAndrew Thompson } 211bdc081c6SAndrew Thompson #endif 21202ac6454SAndrew Thompson 21302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 21402ac6454SAndrew Thompson * usb2_uiomove - factored out code 21502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 216bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 21702ac6454SAndrew Thompson int 21802ac6454SAndrew Thompson usb2_uiomove(struct usb2_page_cache *pc, struct uio *uio, 219578d0effSAndrew Thompson usb2_frlength_t pc_offset, usb2_frlength_t len) 22002ac6454SAndrew Thompson { 22102ac6454SAndrew Thompson struct usb2_page_search res; 22202ac6454SAndrew Thompson int error = 0; 22302ac6454SAndrew Thompson 22402ac6454SAndrew Thompson while (len != 0) { 22502ac6454SAndrew Thompson 22602ac6454SAndrew Thompson usb2_get_page(pc, pc_offset, &res); 22702ac6454SAndrew Thompson 22802ac6454SAndrew Thompson if (res.length > len) { 22902ac6454SAndrew Thompson res.length = len; 23002ac6454SAndrew Thompson } 23102ac6454SAndrew Thompson /* 23202ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 23302ac6454SAndrew Thompson * exiting the mutex and checking things 23402ac6454SAndrew Thompson */ 23502ac6454SAndrew Thompson error = uiomove(res.buffer, res.length, uio); 23602ac6454SAndrew Thompson 23702ac6454SAndrew Thompson if (error) { 23802ac6454SAndrew Thompson break; 23902ac6454SAndrew Thompson } 24002ac6454SAndrew Thompson pc_offset += res.length; 24102ac6454SAndrew Thompson len -= res.length; 24202ac6454SAndrew Thompson } 24302ac6454SAndrew Thompson return (error); 24402ac6454SAndrew Thompson } 245bdc081c6SAndrew Thompson #endif 24602ac6454SAndrew Thompson 24702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 24802ac6454SAndrew Thompson * usb2_copy_out - copy directly from DMA-able memory 24902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 25002ac6454SAndrew Thompson void 251578d0effSAndrew Thompson usb2_copy_out(struct usb2_page_cache *cache, usb2_frlength_t offset, 252578d0effSAndrew Thompson void *ptr, usb2_frlength_t len) 25302ac6454SAndrew Thompson { 25402ac6454SAndrew Thompson struct usb2_page_search res; 25502ac6454SAndrew Thompson 25602ac6454SAndrew Thompson while (len != 0) { 25702ac6454SAndrew Thompson 25802ac6454SAndrew Thompson usb2_get_page(cache, offset, &res); 25902ac6454SAndrew Thompson 26002ac6454SAndrew Thompson if (res.length > len) { 26102ac6454SAndrew Thompson res.length = len; 26202ac6454SAndrew Thompson } 26302ac6454SAndrew Thompson bcopy(res.buffer, ptr, res.length); 26402ac6454SAndrew Thompson 26502ac6454SAndrew Thompson offset += res.length; 26602ac6454SAndrew Thompson len -= res.length; 26702ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 26802ac6454SAndrew Thompson } 26902ac6454SAndrew Thompson } 27002ac6454SAndrew Thompson 27102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 27202ac6454SAndrew Thompson * usb2_copy_out_user - copy directly from DMA-able memory to userland 27302ac6454SAndrew Thompson * 27402ac6454SAndrew Thompson * Return values: 27502ac6454SAndrew Thompson * 0: Success 27602ac6454SAndrew Thompson * Else: Failure 27702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 278bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 27902ac6454SAndrew Thompson int 280578d0effSAndrew Thompson usb2_copy_out_user(struct usb2_page_cache *cache, usb2_frlength_t offset, 281578d0effSAndrew Thompson void *ptr, usb2_frlength_t len) 28202ac6454SAndrew Thompson { 28302ac6454SAndrew Thompson struct usb2_page_search res; 28402ac6454SAndrew Thompson int error; 28502ac6454SAndrew Thompson 28602ac6454SAndrew Thompson while (len != 0) { 28702ac6454SAndrew Thompson 28802ac6454SAndrew Thompson usb2_get_page(cache, offset, &res); 28902ac6454SAndrew Thompson 29002ac6454SAndrew Thompson if (res.length > len) { 29102ac6454SAndrew Thompson res.length = len; 29202ac6454SAndrew Thompson } 29302ac6454SAndrew Thompson error = copyout(res.buffer, ptr, res.length); 29402ac6454SAndrew Thompson if (error) 29502ac6454SAndrew Thompson return (error); 29602ac6454SAndrew Thompson 29702ac6454SAndrew Thompson offset += res.length; 29802ac6454SAndrew Thompson len -= res.length; 29902ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 30002ac6454SAndrew Thompson } 30102ac6454SAndrew Thompson return (0); /* success */ 30202ac6454SAndrew Thompson } 303bdc081c6SAndrew Thompson #endif 30402ac6454SAndrew Thompson 30502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 30602ac6454SAndrew Thompson * usb2_bzero - zero DMA-able memory 30702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 30802ac6454SAndrew Thompson void 309578d0effSAndrew Thompson usb2_bzero(struct usb2_page_cache *cache, usb2_frlength_t offset, 310578d0effSAndrew Thompson usb2_frlength_t len) 31102ac6454SAndrew Thompson { 31202ac6454SAndrew Thompson struct usb2_page_search res; 31302ac6454SAndrew Thompson 31402ac6454SAndrew Thompson while (len != 0) { 31502ac6454SAndrew Thompson 31602ac6454SAndrew Thompson usb2_get_page(cache, offset, &res); 31702ac6454SAndrew Thompson 31802ac6454SAndrew Thompson if (res.length > len) { 31902ac6454SAndrew Thompson res.length = len; 32002ac6454SAndrew Thompson } 32102ac6454SAndrew Thompson bzero(res.buffer, res.length); 32202ac6454SAndrew Thompson 32302ac6454SAndrew Thompson offset += res.length; 32402ac6454SAndrew Thompson len -= res.length; 32502ac6454SAndrew Thompson } 32602ac6454SAndrew Thompson } 32702ac6454SAndrew Thompson 328bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA && defined(__FreeBSD__) 32902ac6454SAndrew Thompson 33002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 33102ac6454SAndrew Thompson * usb2_dma_lock_cb - dummy callback 33202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 33302ac6454SAndrew Thompson static void 33402ac6454SAndrew Thompson usb2_dma_lock_cb(void *arg, bus_dma_lock_op_t op) 33502ac6454SAndrew Thompson { 33602ac6454SAndrew Thompson /* we use "mtx_owned()" instead of this function */ 33702ac6454SAndrew Thompson } 33802ac6454SAndrew Thompson 33902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 34002ac6454SAndrew Thompson * usb2_dma_tag_create - allocate a DMA tag 34102ac6454SAndrew Thompson * 34202ac6454SAndrew Thompson * NOTE: If the "align" parameter has a value of 1 the DMA-tag will 34302ac6454SAndrew Thompson * allow multi-segment mappings. Else all mappings are single-segment. 34402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 34502ac6454SAndrew Thompson static void 34602ac6454SAndrew Thompson usb2_dma_tag_create(struct usb2_dma_tag *udt, 347578d0effSAndrew Thompson usb2_size_t size, usb2_size_t align) 34802ac6454SAndrew Thompson { 34902ac6454SAndrew Thompson bus_dma_tag_t tag; 35002ac6454SAndrew Thompson 35102ac6454SAndrew Thompson if (bus_dma_tag_create 35202ac6454SAndrew Thompson ( /* parent */ udt->tag_parent->tag, 35302ac6454SAndrew Thompson /* alignment */ align, 35402ac6454SAndrew Thompson /* boundary */ USB_PAGE_SIZE, 35502ac6454SAndrew Thompson /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1, 35602ac6454SAndrew Thompson /* highaddr */ BUS_SPACE_MAXADDR, 35702ac6454SAndrew Thompson /* filter */ NULL, 35802ac6454SAndrew Thompson /* filterarg */ NULL, 35902ac6454SAndrew Thompson /* maxsize */ size, 36002ac6454SAndrew Thompson /* nsegments */ (align == 1) ? 36102ac6454SAndrew Thompson (2 + (size / USB_PAGE_SIZE)) : 1, 36202ac6454SAndrew Thompson /* maxsegsz */ (align == 1) ? 36302ac6454SAndrew Thompson USB_PAGE_SIZE : size, 36402ac6454SAndrew Thompson /* flags */ BUS_DMA_KEEP_PG_OFFSET, 36502ac6454SAndrew Thompson /* lockfn */ &usb2_dma_lock_cb, 36602ac6454SAndrew Thompson /* lockarg */ NULL, 36702ac6454SAndrew Thompson &tag)) { 36802ac6454SAndrew Thompson tag = NULL; 36902ac6454SAndrew Thompson } 37002ac6454SAndrew Thompson udt->tag = tag; 37102ac6454SAndrew Thompson } 37202ac6454SAndrew Thompson 37302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 37402ac6454SAndrew Thompson * usb2_dma_tag_free - free a DMA tag 37502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37602ac6454SAndrew Thompson static void 37702ac6454SAndrew Thompson usb2_dma_tag_destroy(struct usb2_dma_tag *udt) 37802ac6454SAndrew Thompson { 37902ac6454SAndrew Thompson bus_dma_tag_destroy(udt->tag); 38002ac6454SAndrew Thompson } 38102ac6454SAndrew Thompson 38202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 38302ac6454SAndrew Thompson * usb2_pc_alloc_mem_cb - BUS-DMA callback function 38402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38502ac6454SAndrew Thompson static void 38602ac6454SAndrew Thompson usb2_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, 38702ac6454SAndrew Thompson int nseg, int error) 38802ac6454SAndrew Thompson { 38902ac6454SAndrew Thompson usb2_pc_common_mem_cb(arg, segs, nseg, error, 0); 39002ac6454SAndrew Thompson } 39102ac6454SAndrew Thompson 39202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 39302ac6454SAndrew Thompson * usb2_pc_load_mem_cb - BUS-DMA callback function 39402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39502ac6454SAndrew Thompson static void 39602ac6454SAndrew Thompson usb2_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, 39702ac6454SAndrew Thompson int nseg, int error) 39802ac6454SAndrew Thompson { 39902ac6454SAndrew Thompson usb2_pc_common_mem_cb(arg, segs, nseg, error, 1); 40002ac6454SAndrew Thompson } 40102ac6454SAndrew Thompson 40202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 40302ac6454SAndrew Thompson * usb2_pc_common_mem_cb - BUS-DMA callback function 40402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 40502ac6454SAndrew Thompson static void 40602ac6454SAndrew Thompson usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, 40702ac6454SAndrew Thompson int nseg, int error, uint8_t isload) 40802ac6454SAndrew Thompson { 40902ac6454SAndrew Thompson struct usb2_dma_parent_tag *uptag; 41002ac6454SAndrew Thompson struct usb2_page_cache *pc; 41102ac6454SAndrew Thompson struct usb2_page *pg; 412578d0effSAndrew Thompson usb2_size_t rem; 41302ac6454SAndrew Thompson uint8_t owned; 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson pc = arg; 41602ac6454SAndrew Thompson uptag = pc->tag_parent; 41702ac6454SAndrew Thompson 41802ac6454SAndrew Thompson /* 41902ac6454SAndrew Thompson * XXX There is sometimes recursive locking here. 42002ac6454SAndrew Thompson * XXX We should try to find a better solution. 42102ac6454SAndrew Thompson * XXX Until further the "owned" variable does 42202ac6454SAndrew Thompson * XXX the trick. 42302ac6454SAndrew Thompson */ 42402ac6454SAndrew Thompson 42502ac6454SAndrew Thompson if (error) { 42602ac6454SAndrew Thompson goto done; 42702ac6454SAndrew Thompson } 42802ac6454SAndrew Thompson pg = pc->page_start; 42902ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 43002ac6454SAndrew Thompson rem = segs->ds_addr & (USB_PAGE_SIZE - 1); 43102ac6454SAndrew Thompson pc->page_offset_buf = rem; 43202ac6454SAndrew Thompson pc->page_offset_end += rem; 43302ac6454SAndrew Thompson nseg--; 43402ac6454SAndrew Thompson #if (USB_DEBUG != 0) 43502ac6454SAndrew Thompson if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { 43602ac6454SAndrew Thompson /* 43702ac6454SAndrew Thompson * This check verifies that the physical address is correct: 43802ac6454SAndrew Thompson */ 43902ac6454SAndrew Thompson DPRINTFN(0, "Page offset was not preserved!\n"); 44002ac6454SAndrew Thompson error = 1; 44102ac6454SAndrew Thompson goto done; 44202ac6454SAndrew Thompson } 44302ac6454SAndrew Thompson #endif 44402ac6454SAndrew Thompson while (nseg > 0) { 44502ac6454SAndrew Thompson nseg--; 44602ac6454SAndrew Thompson segs++; 44702ac6454SAndrew Thompson pg++; 44802ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 44902ac6454SAndrew Thompson } 45002ac6454SAndrew Thompson 45102ac6454SAndrew Thompson done: 45202ac6454SAndrew Thompson owned = mtx_owned(uptag->mtx); 45302ac6454SAndrew Thompson if (!owned) 45402ac6454SAndrew Thompson mtx_lock(uptag->mtx); 45502ac6454SAndrew Thompson 45602ac6454SAndrew Thompson uptag->dma_error = (error ? 1 : 0); 45702ac6454SAndrew Thompson if (isload) { 45802ac6454SAndrew Thompson (uptag->func) (uptag); 45902ac6454SAndrew Thompson } else { 46002ac6454SAndrew Thompson usb2_cv_broadcast(uptag->cv); 46102ac6454SAndrew Thompson } 46202ac6454SAndrew Thompson if (!owned) 46302ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 46402ac6454SAndrew Thompson } 46502ac6454SAndrew Thompson 46602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 46702ac6454SAndrew Thompson * usb2_pc_alloc_mem - allocate DMA'able memory 46802ac6454SAndrew Thompson * 46902ac6454SAndrew Thompson * Returns: 47002ac6454SAndrew Thompson * 0: Success 47102ac6454SAndrew Thompson * Else: Failure 47202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 47302ac6454SAndrew Thompson uint8_t 47402ac6454SAndrew Thompson usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg, 475578d0effSAndrew Thompson usb2_size_t size, usb2_size_t align) 47602ac6454SAndrew Thompson { 47702ac6454SAndrew Thompson struct usb2_dma_parent_tag *uptag; 47802ac6454SAndrew Thompson struct usb2_dma_tag *utag; 47902ac6454SAndrew Thompson bus_dmamap_t map; 48002ac6454SAndrew Thompson void *ptr; 48102ac6454SAndrew Thompson int err; 48202ac6454SAndrew Thompson 48302ac6454SAndrew Thompson uptag = pc->tag_parent; 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson if (align != 1) { 48602ac6454SAndrew Thompson /* 48702ac6454SAndrew Thompson * The alignment must be greater or equal to the 48802ac6454SAndrew Thompson * "size" else the object can be split between two 48902ac6454SAndrew Thompson * memory pages and we get a problem! 49002ac6454SAndrew Thompson */ 49102ac6454SAndrew Thompson while (align < size) { 49202ac6454SAndrew Thompson align *= 2; 49302ac6454SAndrew Thompson if (align == 0) { 49402ac6454SAndrew Thompson goto error; 49502ac6454SAndrew Thompson } 49602ac6454SAndrew Thompson } 49702ac6454SAndrew Thompson #if 1 49802ac6454SAndrew Thompson /* 49902ac6454SAndrew Thompson * XXX BUS-DMA workaround - FIXME later: 50002ac6454SAndrew Thompson * 50102ac6454SAndrew Thompson * We assume that that the aligment at this point of 50202ac6454SAndrew Thompson * the code is greater than or equal to the size and 50302ac6454SAndrew Thompson * less than two times the size, so that if we double 50402ac6454SAndrew Thompson * the size, the size will be greater than the 50502ac6454SAndrew Thompson * alignment. 50602ac6454SAndrew Thompson * 50702ac6454SAndrew Thompson * The bus-dma system has a check for "alignment" 50802ac6454SAndrew Thompson * being less than "size". If that check fails we end 50902ac6454SAndrew Thompson * up using contigmalloc which is page based even for 51002ac6454SAndrew Thompson * small allocations. Try to avoid that to save 51102ac6454SAndrew Thompson * memory, hence we sometimes to a large number of 51202ac6454SAndrew Thompson * small allocations! 51302ac6454SAndrew Thompson */ 51402ac6454SAndrew Thompson if (size <= (USB_PAGE_SIZE / 2)) { 51502ac6454SAndrew Thompson size *= 2; 51602ac6454SAndrew Thompson } 51702ac6454SAndrew Thompson #endif 51802ac6454SAndrew Thompson } 51902ac6454SAndrew Thompson /* get the correct DMA tag */ 52002ac6454SAndrew Thompson utag = usb2_dma_tag_find(uptag, size, align); 52102ac6454SAndrew Thompson if (utag == NULL) { 52202ac6454SAndrew Thompson goto error; 52302ac6454SAndrew Thompson } 52402ac6454SAndrew Thompson /* allocate memory */ 52502ac6454SAndrew Thompson if (bus_dmamem_alloc( 52602ac6454SAndrew Thompson utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { 52702ac6454SAndrew Thompson goto error; 52802ac6454SAndrew Thompson } 52902ac6454SAndrew Thompson /* setup page cache */ 53002ac6454SAndrew Thompson pc->buffer = ptr; 53102ac6454SAndrew Thompson pc->page_start = pg; 53202ac6454SAndrew Thompson pc->page_offset_buf = 0; 53302ac6454SAndrew Thompson pc->page_offset_end = size; 53402ac6454SAndrew Thompson pc->map = map; 53502ac6454SAndrew Thompson pc->tag = utag->tag; 53602ac6454SAndrew Thompson pc->ismultiseg = (align == 1); 53702ac6454SAndrew Thompson 53802ac6454SAndrew Thompson mtx_lock(uptag->mtx); 53902ac6454SAndrew Thompson 54002ac6454SAndrew Thompson /* load memory into DMA */ 54102ac6454SAndrew Thompson err = bus_dmamap_load( 54202ac6454SAndrew Thompson utag->tag, map, ptr, size, &usb2_pc_alloc_mem_cb, 54302ac6454SAndrew Thompson pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); 54402ac6454SAndrew Thompson 54502ac6454SAndrew Thompson if (err == EINPROGRESS) { 54602ac6454SAndrew Thompson usb2_cv_wait(uptag->cv, uptag->mtx); 54702ac6454SAndrew Thompson err = 0; 54802ac6454SAndrew Thompson } 54902ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 55002ac6454SAndrew Thompson 55102ac6454SAndrew Thompson if (err || uptag->dma_error) { 55202ac6454SAndrew Thompson bus_dmamem_free(utag->tag, ptr, map); 55302ac6454SAndrew Thompson goto error; 55402ac6454SAndrew Thompson } 55502ac6454SAndrew Thompson bzero(ptr, size); 55602ac6454SAndrew Thompson 55702ac6454SAndrew Thompson usb2_pc_cpu_flush(pc); 55802ac6454SAndrew Thompson 55902ac6454SAndrew Thompson return (0); 56002ac6454SAndrew Thompson 56102ac6454SAndrew Thompson error: 56202ac6454SAndrew Thompson /* reset most of the page cache */ 56302ac6454SAndrew Thompson pc->buffer = NULL; 56402ac6454SAndrew Thompson pc->page_start = NULL; 56502ac6454SAndrew Thompson pc->page_offset_buf = 0; 56602ac6454SAndrew Thompson pc->page_offset_end = 0; 56702ac6454SAndrew Thompson pc->map = NULL; 56802ac6454SAndrew Thompson pc->tag = NULL; 56902ac6454SAndrew Thompson return (1); 57002ac6454SAndrew Thompson } 57102ac6454SAndrew Thompson 57202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 57302ac6454SAndrew Thompson * usb2_pc_free_mem - free DMA memory 57402ac6454SAndrew Thompson * 57502ac6454SAndrew Thompson * This function is NULL safe. 57602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 57702ac6454SAndrew Thompson void 57802ac6454SAndrew Thompson usb2_pc_free_mem(struct usb2_page_cache *pc) 57902ac6454SAndrew Thompson { 58002ac6454SAndrew Thompson if (pc && pc->buffer) { 58102ac6454SAndrew Thompson 58202ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 58302ac6454SAndrew Thompson 58402ac6454SAndrew Thompson bus_dmamem_free(pc->tag, pc->buffer, pc->map); 58502ac6454SAndrew Thompson 58602ac6454SAndrew Thompson pc->buffer = NULL; 58702ac6454SAndrew Thompson } 58802ac6454SAndrew Thompson } 58902ac6454SAndrew Thompson 59002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 59102ac6454SAndrew Thompson * usb2_pc_load_mem - load virtual memory into DMA 59202ac6454SAndrew Thompson * 59302ac6454SAndrew Thompson * Return values: 59402ac6454SAndrew Thompson * 0: Success 59502ac6454SAndrew Thompson * Else: Error 59602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 59702ac6454SAndrew Thompson uint8_t 598578d0effSAndrew Thompson usb2_pc_load_mem(struct usb2_page_cache *pc, usb2_size_t size, uint8_t sync) 59902ac6454SAndrew Thompson { 60002ac6454SAndrew Thompson /* setup page cache */ 60102ac6454SAndrew Thompson pc->page_offset_buf = 0; 60202ac6454SAndrew Thompson pc->page_offset_end = size; 60302ac6454SAndrew Thompson pc->ismultiseg = 1; 60402ac6454SAndrew Thompson 60502ac6454SAndrew Thompson mtx_assert(pc->tag_parent->mtx, MA_OWNED); 60602ac6454SAndrew Thompson 60702ac6454SAndrew Thompson if (size > 0) { 60802ac6454SAndrew Thompson if (sync) { 60902ac6454SAndrew Thompson struct usb2_dma_parent_tag *uptag; 61002ac6454SAndrew Thompson int err; 61102ac6454SAndrew Thompson 61202ac6454SAndrew Thompson uptag = pc->tag_parent; 61302ac6454SAndrew Thompson 61402ac6454SAndrew Thompson /* 61502ac6454SAndrew Thompson * We have to unload the previous loaded DMA 61602ac6454SAndrew Thompson * pages before trying to load a new one! 61702ac6454SAndrew Thompson */ 61802ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 61902ac6454SAndrew Thompson 62002ac6454SAndrew Thompson /* 62102ac6454SAndrew Thompson * Try to load memory into DMA. 62202ac6454SAndrew Thompson */ 62302ac6454SAndrew Thompson err = bus_dmamap_load( 62402ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 62502ac6454SAndrew Thompson &usb2_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK); 62602ac6454SAndrew Thompson if (err == EINPROGRESS) { 62702ac6454SAndrew Thompson usb2_cv_wait(uptag->cv, uptag->mtx); 62802ac6454SAndrew Thompson err = 0; 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson if (err || uptag->dma_error) { 63102ac6454SAndrew Thompson return (1); 63202ac6454SAndrew Thompson } 63302ac6454SAndrew Thompson } else { 63402ac6454SAndrew Thompson 63502ac6454SAndrew Thompson /* 63602ac6454SAndrew Thompson * We have to unload the previous loaded DMA 63702ac6454SAndrew Thompson * pages before trying to load a new one! 63802ac6454SAndrew Thompson */ 63902ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 64002ac6454SAndrew Thompson 64102ac6454SAndrew Thompson /* 64202ac6454SAndrew Thompson * Try to load memory into DMA. The callback 64302ac6454SAndrew Thompson * will be called in all cases: 64402ac6454SAndrew Thompson */ 64502ac6454SAndrew Thompson if (bus_dmamap_load( 64602ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 64702ac6454SAndrew Thompson &usb2_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) { 64802ac6454SAndrew Thompson } 64902ac6454SAndrew Thompson } 65002ac6454SAndrew Thompson } else { 65102ac6454SAndrew Thompson if (!sync) { 65202ac6454SAndrew Thompson /* 65302ac6454SAndrew Thompson * Call callback so that refcount is decremented 65402ac6454SAndrew Thompson * properly: 65502ac6454SAndrew Thompson */ 65602ac6454SAndrew Thompson pc->tag_parent->dma_error = 0; 65702ac6454SAndrew Thompson (pc->tag_parent->func) (pc->tag_parent); 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson } 66002ac6454SAndrew Thompson return (0); 66102ac6454SAndrew Thompson } 66202ac6454SAndrew Thompson 66302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 66402ac6454SAndrew Thompson * usb2_pc_cpu_invalidate - invalidate CPU cache 66502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 66602ac6454SAndrew Thompson void 66702ac6454SAndrew Thompson usb2_pc_cpu_invalidate(struct usb2_page_cache *pc) 66802ac6454SAndrew Thompson { 66902ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 67002ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 67102ac6454SAndrew Thompson return; 67202ac6454SAndrew Thompson } 67302ac6454SAndrew Thompson bus_dmamap_sync(pc->tag, pc->map, 67402ac6454SAndrew Thompson BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 67502ac6454SAndrew Thompson } 67602ac6454SAndrew Thompson 67702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 67802ac6454SAndrew Thompson * usb2_pc_cpu_flush - flush CPU cache 67902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 68002ac6454SAndrew Thompson void 68102ac6454SAndrew Thompson usb2_pc_cpu_flush(struct usb2_page_cache *pc) 68202ac6454SAndrew Thompson { 68302ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 68402ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 68502ac6454SAndrew Thompson return; 68602ac6454SAndrew Thompson } 68702ac6454SAndrew Thompson bus_dmamap_sync(pc->tag, pc->map, 68802ac6454SAndrew Thompson BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 68902ac6454SAndrew Thompson } 69002ac6454SAndrew Thompson 69102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 69202ac6454SAndrew Thompson * usb2_pc_dmamap_create - create a DMA map 69302ac6454SAndrew Thompson * 69402ac6454SAndrew Thompson * Returns: 69502ac6454SAndrew Thompson * 0: Success 69602ac6454SAndrew Thompson * Else: Failure 69702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 69802ac6454SAndrew Thompson uint8_t 699578d0effSAndrew Thompson usb2_pc_dmamap_create(struct usb2_page_cache *pc, usb2_size_t size) 70002ac6454SAndrew Thompson { 70102ac6454SAndrew Thompson struct usb2_xfer_root *info; 70202ac6454SAndrew Thompson struct usb2_dma_tag *utag; 70302ac6454SAndrew Thompson 70402ac6454SAndrew Thompson /* get info */ 705bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(pc->tag_parent); 70602ac6454SAndrew Thompson 70702ac6454SAndrew Thompson /* sanity check */ 70802ac6454SAndrew Thompson if (info == NULL) { 70902ac6454SAndrew Thompson goto error; 71002ac6454SAndrew Thompson } 71102ac6454SAndrew Thompson utag = usb2_dma_tag_find(pc->tag_parent, size, 1); 71202ac6454SAndrew Thompson if (utag == NULL) { 71302ac6454SAndrew Thompson goto error; 71402ac6454SAndrew Thompson } 71502ac6454SAndrew Thompson /* create DMA map */ 71602ac6454SAndrew Thompson if (bus_dmamap_create(utag->tag, 0, &pc->map)) { 71702ac6454SAndrew Thompson goto error; 71802ac6454SAndrew Thompson } 71902ac6454SAndrew Thompson pc->tag = utag->tag; 72002ac6454SAndrew Thompson return 0; /* success */ 72102ac6454SAndrew Thompson 72202ac6454SAndrew Thompson error: 72302ac6454SAndrew Thompson pc->map = NULL; 72402ac6454SAndrew Thompson pc->tag = NULL; 72502ac6454SAndrew Thompson return 1; /* failure */ 72602ac6454SAndrew Thompson } 72702ac6454SAndrew Thompson 72802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 72902ac6454SAndrew Thompson * usb2_pc_dmamap_destroy 73002ac6454SAndrew Thompson * 73102ac6454SAndrew Thompson * This function is NULL safe. 73202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 73302ac6454SAndrew Thompson void 73402ac6454SAndrew Thompson usb2_pc_dmamap_destroy(struct usb2_page_cache *pc) 73502ac6454SAndrew Thompson { 73602ac6454SAndrew Thompson if (pc && pc->tag) { 73702ac6454SAndrew Thompson bus_dmamap_destroy(pc->tag, pc->map); 73802ac6454SAndrew Thompson pc->tag = NULL; 73902ac6454SAndrew Thompson pc->map = NULL; 74002ac6454SAndrew Thompson } 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson 74302ac6454SAndrew Thompson #endif 74402ac6454SAndrew Thompson 745bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA && defined(__NetBSD__) 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 74802ac6454SAndrew Thompson * usb2_dma_tag_create - allocate a DMA tag 74902ac6454SAndrew Thompson * 75002ac6454SAndrew Thompson * NOTE: If the "align" parameter has a value of 1 the DMA-tag will 75102ac6454SAndrew Thompson * allow multi-segment mappings. Else all mappings are single-segment. 75202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 75302ac6454SAndrew Thompson static void 75402ac6454SAndrew Thompson usb2_dma_tag_create(struct usb2_dma_tag *udt, 755578d0effSAndrew Thompson usb2_size_t size, usb2_size_t align) 75602ac6454SAndrew Thompson { 757578d0effSAndrew Thompson usb2_size_t nseg; 75802ac6454SAndrew Thompson 75902ac6454SAndrew Thompson if (align == 1) { 76002ac6454SAndrew Thompson nseg = (2 + (size / USB_PAGE_SIZE)); 76102ac6454SAndrew Thompson } else { 76202ac6454SAndrew Thompson nseg = 1; 76302ac6454SAndrew Thompson } 76402ac6454SAndrew Thompson 76502ac6454SAndrew Thompson udt->p_seg = malloc(nseg * sizeof(*(udt->p_seg)), 76602ac6454SAndrew Thompson M_USB, M_WAITOK | M_ZERO); 76702ac6454SAndrew Thompson 76802ac6454SAndrew Thompson if (udt->p_seg == NULL) { 76902ac6454SAndrew Thompson return; 77002ac6454SAndrew Thompson } 77102ac6454SAndrew Thompson udt->tag = udt->tag_parent->tag; 77202ac6454SAndrew Thompson udt->n_seg = nseg; 77302ac6454SAndrew Thompson } 77402ac6454SAndrew Thompson 77502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 77602ac6454SAndrew Thompson * usb2_dma_tag_free - free a DMA tag 77702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 77802ac6454SAndrew Thompson static void 77902ac6454SAndrew Thompson usb2_dma_tag_destroy(struct usb2_dma_tag *udt) 78002ac6454SAndrew Thompson { 78102ac6454SAndrew Thompson free(udt->p_seg, M_USB); 78202ac6454SAndrew Thompson } 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 78502ac6454SAndrew Thompson * usb2_pc_common_mem_cb - BUS-DMA callback function 78602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 78702ac6454SAndrew Thompson static void 78802ac6454SAndrew Thompson usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs, 78902ac6454SAndrew Thompson int nseg, int error, uint8_t isload, uint8_t dolock) 79002ac6454SAndrew Thompson { 79102ac6454SAndrew Thompson struct usb2_dma_parent_tag *uptag; 79202ac6454SAndrew Thompson struct usb2_page *pg; 793578d0effSAndrew Thompson usb2_size_t rem; 79402ac6454SAndrew Thompson uint8_t ext_seg; /* extend last segment */ 79502ac6454SAndrew Thompson 79602ac6454SAndrew Thompson uptag = pc->tag_parent; 79702ac6454SAndrew Thompson 79802ac6454SAndrew Thompson if (error) { 79902ac6454SAndrew Thompson goto done; 80002ac6454SAndrew Thompson } 80102ac6454SAndrew Thompson pg = pc->page_start; 80202ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 80302ac6454SAndrew Thompson rem = segs->ds_addr & (USB_PAGE_SIZE - 1); 80402ac6454SAndrew Thompson pc->page_offset_buf = rem; 80502ac6454SAndrew Thompson pc->page_offset_end += rem; 80602ac6454SAndrew Thompson if (nseg < ((pc->page_offset_end + 80702ac6454SAndrew Thompson (USB_PAGE_SIZE - 1)) / USB_PAGE_SIZE)) { 80802ac6454SAndrew Thompson ext_seg = 1; 80902ac6454SAndrew Thompson } else { 81002ac6454SAndrew Thompson ext_seg = 0; 81102ac6454SAndrew Thompson } 81202ac6454SAndrew Thompson nseg--; 81302ac6454SAndrew Thompson #if (USB_DEBUG != 0) 81402ac6454SAndrew Thompson if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { 81502ac6454SAndrew Thompson /* 81602ac6454SAndrew Thompson * This check verifies that the physical address is correct: 81702ac6454SAndrew Thompson */ 81802ac6454SAndrew Thompson DPRINTFN(0, "Page offset was not preserved!\n"); 81902ac6454SAndrew Thompson error = 1; 82002ac6454SAndrew Thompson goto done; 82102ac6454SAndrew Thompson } 82202ac6454SAndrew Thompson #endif 82302ac6454SAndrew Thompson while (nseg > 0) { 82402ac6454SAndrew Thompson nseg--; 82502ac6454SAndrew Thompson segs++; 82602ac6454SAndrew Thompson pg++; 82702ac6454SAndrew Thompson pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); 82802ac6454SAndrew Thompson } 82902ac6454SAndrew Thompson 83002ac6454SAndrew Thompson /* 83102ac6454SAndrew Thompson * XXX The segments we get from BUS-DMA are not aligned, 83202ac6454SAndrew Thompson * XXX so we need to extend the last segment if we are 83302ac6454SAndrew Thompson * XXX unaligned and cross the segment boundary! 83402ac6454SAndrew Thompson */ 83502ac6454SAndrew Thompson if (ext_seg && pc->ismultiseg) { 83602ac6454SAndrew Thompson (pg + 1)->physaddr = pg->physaddr + USB_PAGE_SIZE; 83702ac6454SAndrew Thompson } 83802ac6454SAndrew Thompson done: 83902ac6454SAndrew Thompson if (dolock) 84002ac6454SAndrew Thompson mtx_lock(uptag->mtx); 84102ac6454SAndrew Thompson 84202ac6454SAndrew Thompson uptag->dma_error = (error ? 1 : 0); 84302ac6454SAndrew Thompson if (isload) { 84402ac6454SAndrew Thompson (uptag->func) (uptag); 84502ac6454SAndrew Thompson } 84602ac6454SAndrew Thompson if (dolock) 84702ac6454SAndrew Thompson mtx_unlock(uptag->mtx); 84802ac6454SAndrew Thompson } 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 85102ac6454SAndrew Thompson * usb2_pc_alloc_mem - allocate DMA'able memory 85202ac6454SAndrew Thompson * 85302ac6454SAndrew Thompson * Returns: 85402ac6454SAndrew Thompson * 0: Success 85502ac6454SAndrew Thompson * Else: Failure 85602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 85702ac6454SAndrew Thompson uint8_t 85802ac6454SAndrew Thompson usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg, 859578d0effSAndrew Thompson usb2_size_t size, usb2_size_t align) 86002ac6454SAndrew Thompson { 86102ac6454SAndrew Thompson struct usb2_dma_parent_tag *uptag; 86202ac6454SAndrew Thompson struct usb2_dma_tag *utag; 86302ac6454SAndrew Thompson caddr_t ptr = NULL; 86402ac6454SAndrew Thompson bus_dmamap_t map; 86502ac6454SAndrew Thompson int seg_count; 86602ac6454SAndrew Thompson 86702ac6454SAndrew Thompson uptag = pc->tag_parent; 86802ac6454SAndrew Thompson 86902ac6454SAndrew Thompson if (align != 1) { 87002ac6454SAndrew Thompson /* 87102ac6454SAndrew Thompson * The alignment must be greater or equal to the 87202ac6454SAndrew Thompson * "size" else the object can be split between two 87302ac6454SAndrew Thompson * memory pages and we get a problem! 87402ac6454SAndrew Thompson */ 87502ac6454SAndrew Thompson while (align < size) { 87602ac6454SAndrew Thompson align *= 2; 87702ac6454SAndrew Thompson if (align == 0) { 87802ac6454SAndrew Thompson goto done_5; 87902ac6454SAndrew Thompson } 88002ac6454SAndrew Thompson } 88102ac6454SAndrew Thompson } 88202ac6454SAndrew Thompson /* get the correct DMA tag */ 88302ac6454SAndrew Thompson utag = usb2_dma_tag_find(pc->tag_parent, size, align); 88402ac6454SAndrew Thompson if (utag == NULL) { 88502ac6454SAndrew Thompson goto done_5; 88602ac6454SAndrew Thompson } 88702ac6454SAndrew Thompson if (bus_dmamem_alloc(utag->tag, size, align, 0, utag->p_seg, 88802ac6454SAndrew Thompson utag->n_seg, &seg_count, BUS_DMA_WAITOK)) { 88902ac6454SAndrew Thompson goto done_4; 89002ac6454SAndrew Thompson } 89102ac6454SAndrew Thompson if (bus_dmamem_map(utag->tag, utag->p_seg, seg_count, size, 89202ac6454SAndrew Thompson &ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT)) { 89302ac6454SAndrew Thompson goto done_3; 89402ac6454SAndrew Thompson } 89502ac6454SAndrew Thompson if (bus_dmamap_create(utag->tag, size, utag->n_seg, (align == 1) ? 89602ac6454SAndrew Thompson USB_PAGE_SIZE : size, 0, BUS_DMA_WAITOK, &map)) { 89702ac6454SAndrew Thompson goto done_2; 89802ac6454SAndrew Thompson } 89902ac6454SAndrew Thompson if (bus_dmamap_load(utag->tag, map, ptr, size, NULL, 90002ac6454SAndrew Thompson BUS_DMA_WAITOK)) { 90102ac6454SAndrew Thompson goto done_1; 90202ac6454SAndrew Thompson } 90302ac6454SAndrew Thompson pc->p_seg = malloc(seg_count * sizeof(*(pc->p_seg)), 90402ac6454SAndrew Thompson M_USB, M_WAITOK | M_ZERO); 90502ac6454SAndrew Thompson if (pc->p_seg == NULL) { 90602ac6454SAndrew Thompson goto done_0; 90702ac6454SAndrew Thompson } 90802ac6454SAndrew Thompson /* store number if actual segments used */ 90902ac6454SAndrew Thompson pc->n_seg = seg_count; 91002ac6454SAndrew Thompson 91102ac6454SAndrew Thompson /* make a copy of the segments */ 91202ac6454SAndrew Thompson bcopy(utag->p_seg, pc->p_seg, 91302ac6454SAndrew Thompson seg_count * sizeof(*(pc->p_seg))); 91402ac6454SAndrew Thompson 91502ac6454SAndrew Thompson /* setup page cache */ 91602ac6454SAndrew Thompson pc->buffer = ptr; 91702ac6454SAndrew Thompson pc->page_start = pg; 91802ac6454SAndrew Thompson pc->page_offset_buf = 0; 91902ac6454SAndrew Thompson pc->page_offset_end = size; 92002ac6454SAndrew Thompson pc->map = map; 92102ac6454SAndrew Thompson pc->tag = utag->tag; 92202ac6454SAndrew Thompson pc->ismultiseg = (align == 1); 92302ac6454SAndrew Thompson 92402ac6454SAndrew Thompson usb2_pc_common_mem_cb(pc, utag->p_seg, seg_count, 0, 0, 1); 92502ac6454SAndrew Thompson 92602ac6454SAndrew Thompson bzero(ptr, size); 92702ac6454SAndrew Thompson 92802ac6454SAndrew Thompson usb2_pc_cpu_flush(pc); 92902ac6454SAndrew Thompson 93002ac6454SAndrew Thompson return (0); 93102ac6454SAndrew Thompson 93202ac6454SAndrew Thompson done_0: 93302ac6454SAndrew Thompson bus_dmamap_unload(utag->tag, map); 93402ac6454SAndrew Thompson done_1: 93502ac6454SAndrew Thompson bus_dmamap_destroy(utag->tag, map); 93602ac6454SAndrew Thompson done_2: 93702ac6454SAndrew Thompson bus_dmamem_unmap(utag->tag, ptr, size); 93802ac6454SAndrew Thompson done_3: 93902ac6454SAndrew Thompson bus_dmamem_free(utag->tag, utag->p_seg, seg_count); 94002ac6454SAndrew Thompson done_4: 94102ac6454SAndrew Thompson /* utag is destroyed later */ 94202ac6454SAndrew Thompson done_5: 94302ac6454SAndrew Thompson /* reset most of the page cache */ 94402ac6454SAndrew Thompson pc->buffer = NULL; 94502ac6454SAndrew Thompson pc->page_start = NULL; 94602ac6454SAndrew Thompson pc->page_offset_buf = 0; 94702ac6454SAndrew Thompson pc->page_offset_end = 0; 94802ac6454SAndrew Thompson pc->map = NULL; 94902ac6454SAndrew Thompson pc->tag = NULL; 95002ac6454SAndrew Thompson pc->n_seg = 0; 95102ac6454SAndrew Thompson pc->p_seg = NULL; 95202ac6454SAndrew Thompson return (1); 95302ac6454SAndrew Thompson } 95402ac6454SAndrew Thompson 95502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 95602ac6454SAndrew Thompson * usb2_pc_free_mem - free DMA memory 95702ac6454SAndrew Thompson * 95802ac6454SAndrew Thompson * This function is NULL safe. 95902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 96002ac6454SAndrew Thompson void 96102ac6454SAndrew Thompson usb2_pc_free_mem(struct usb2_page_cache *pc) 96202ac6454SAndrew Thompson { 96302ac6454SAndrew Thompson if (pc && pc->buffer) { 96402ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 96502ac6454SAndrew Thompson bus_dmamap_destroy(pc->tag, pc->map); 96602ac6454SAndrew Thompson bus_dmamem_unmap(pc->tag, pc->buffer, 96702ac6454SAndrew Thompson pc->page_offset_end - pc->page_offset_buf); 96802ac6454SAndrew Thompson bus_dmamem_free(pc->tag, pc->p_seg, pc->n_seg); 96902ac6454SAndrew Thompson free(pc->p_seg, M_USB); 97002ac6454SAndrew Thompson pc->buffer = NULL; 97102ac6454SAndrew Thompson } 97202ac6454SAndrew Thompson } 97302ac6454SAndrew Thompson 97402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 97502ac6454SAndrew Thompson * usb2_pc_load_mem - load virtual memory into DMA 97602ac6454SAndrew Thompson * 97702ac6454SAndrew Thompson * Return values: 97802ac6454SAndrew Thompson * 0: Success 97902ac6454SAndrew Thompson * Else: Error 98002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 98102ac6454SAndrew Thompson uint8_t 982578d0effSAndrew Thompson usb2_pc_load_mem(struct usb2_page_cache *pc, usb2_size_t size, uint8_t sync) 98302ac6454SAndrew Thompson { 98402ac6454SAndrew Thompson int error; 98502ac6454SAndrew Thompson 98602ac6454SAndrew Thompson /* setup page cache */ 98702ac6454SAndrew Thompson pc->page_offset_buf = 0; 98802ac6454SAndrew Thompson pc->page_offset_end = size; 98902ac6454SAndrew Thompson pc->ismultiseg = 1; 99002ac6454SAndrew Thompson 99102ac6454SAndrew Thompson if (size > 0) { 99202ac6454SAndrew Thompson 99302ac6454SAndrew Thompson /* 99402ac6454SAndrew Thompson * We have to unload the previous loaded DMA 99502ac6454SAndrew Thompson * pages before trying to load a new one! 99602ac6454SAndrew Thompson */ 99702ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 99802ac6454SAndrew Thompson 99902ac6454SAndrew Thompson /* try to load memory into DMA using using no wait option */ 100002ac6454SAndrew Thompson if (bus_dmamap_load(pc->tag, pc->map, pc->buffer, 100102ac6454SAndrew Thompson size, NULL, BUS_DMA_NOWAIT)) { 100202ac6454SAndrew Thompson error = ENOMEM; 100302ac6454SAndrew Thompson } else { 100402ac6454SAndrew Thompson error = 0; 100502ac6454SAndrew Thompson } 100602ac6454SAndrew Thompson 100702ac6454SAndrew Thompson usb2_pc_common_mem_cb(pc, pc->map->dm_segs, 100802ac6454SAndrew Thompson pc->map->dm_nsegs, error, !sync); 100902ac6454SAndrew Thompson 101002ac6454SAndrew Thompson if (error) { 101102ac6454SAndrew Thompson return (1); 101202ac6454SAndrew Thompson } 101302ac6454SAndrew Thompson } else { 101402ac6454SAndrew Thompson if (!sync) { 101502ac6454SAndrew Thompson /* 101602ac6454SAndrew Thompson * Call callback so that refcount is decremented 101702ac6454SAndrew Thompson * properly: 101802ac6454SAndrew Thompson */ 101902ac6454SAndrew Thompson pc->tag_parent->dma_error = 0; 102002ac6454SAndrew Thompson (pc->tag_parent->func) (pc->tag_parent); 102102ac6454SAndrew Thompson } 102202ac6454SAndrew Thompson } 102302ac6454SAndrew Thompson return (0); 102402ac6454SAndrew Thompson } 102502ac6454SAndrew Thompson 102602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 102702ac6454SAndrew Thompson * usb2_pc_cpu_invalidate - invalidate CPU cache 102802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 102902ac6454SAndrew Thompson void 103002ac6454SAndrew Thompson usb2_pc_cpu_invalidate(struct usb2_page_cache *pc) 103102ac6454SAndrew Thompson { 1032578d0effSAndrew Thompson usb2_size_t len; 103302ac6454SAndrew Thompson 103402ac6454SAndrew Thompson len = pc->page_offset_end - pc->page_offset_buf; 103502ac6454SAndrew Thompson 103602ac6454SAndrew Thompson if (len == 0) { 103702ac6454SAndrew Thompson /* nothing has been loaded into this page cache */ 103802ac6454SAndrew Thompson return; 103902ac6454SAndrew Thompson } 104002ac6454SAndrew Thompson bus_dmamap_sync(pc->tag, pc->map, 0, len, 104102ac6454SAndrew Thompson BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 104202ac6454SAndrew Thompson } 104302ac6454SAndrew Thompson 104402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 104502ac6454SAndrew Thompson * usb2_pc_cpu_flush - flush CPU cache 104602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 104702ac6454SAndrew Thompson void 104802ac6454SAndrew Thompson usb2_pc_cpu_flush(struct usb2_page_cache *pc) 104902ac6454SAndrew Thompson { 1050578d0effSAndrew Thompson usb2_size_t len; 105102ac6454SAndrew Thompson 105202ac6454SAndrew Thompson len = pc->page_offset_end - pc->page_offset_buf; 105302ac6454SAndrew Thompson 105402ac6454SAndrew Thompson if (len == 0) { 105502ac6454SAndrew Thompson /* nothing has been loaded into this page cache */ 105602ac6454SAndrew Thompson return; 105702ac6454SAndrew Thompson } 105802ac6454SAndrew Thompson bus_dmamap_sync(pc->tag, pc->map, 0, len, 105902ac6454SAndrew Thompson BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 106002ac6454SAndrew Thompson } 106102ac6454SAndrew Thompson 106202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 106302ac6454SAndrew Thompson * usb2_pc_dmamap_create - create a DMA map 106402ac6454SAndrew Thompson * 106502ac6454SAndrew Thompson * Returns: 106602ac6454SAndrew Thompson * 0: Success 106702ac6454SAndrew Thompson * Else: Failure 106802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 106902ac6454SAndrew Thompson uint8_t 1070578d0effSAndrew Thompson usb2_pc_dmamap_create(struct usb2_page_cache *pc, usb2_size_t size) 107102ac6454SAndrew Thompson { 107202ac6454SAndrew Thompson struct usb2_xfer_root *info; 107302ac6454SAndrew Thompson struct usb2_dma_tag *utag; 107402ac6454SAndrew Thompson 107502ac6454SAndrew Thompson /* get info */ 1076bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(pc->tag_parent); 107702ac6454SAndrew Thompson 107802ac6454SAndrew Thompson /* sanity check */ 107902ac6454SAndrew Thompson if (info == NULL) { 108002ac6454SAndrew Thompson goto error; 108102ac6454SAndrew Thompson } 108202ac6454SAndrew Thompson utag = usb2_dma_tag_find(pc->tag_parent, size, 1); 108302ac6454SAndrew Thompson if (utag == NULL) { 108402ac6454SAndrew Thompson goto error; 108502ac6454SAndrew Thompson } 108602ac6454SAndrew Thompson if (bus_dmamap_create(utag->tag, size, utag->n_seg, 108702ac6454SAndrew Thompson USB_PAGE_SIZE, 0, BUS_DMA_WAITOK, &pc->map)) { 108802ac6454SAndrew Thompson goto error; 108902ac6454SAndrew Thompson } 109002ac6454SAndrew Thompson pc->tag = utag->tag; 109102ac6454SAndrew Thompson pc->p_seg = utag->p_seg; 109202ac6454SAndrew Thompson pc->n_seg = utag->n_seg; 109302ac6454SAndrew Thompson return 0; /* success */ 109402ac6454SAndrew Thompson 109502ac6454SAndrew Thompson error: 109602ac6454SAndrew Thompson pc->map = NULL; 109702ac6454SAndrew Thompson pc->tag = NULL; 109802ac6454SAndrew Thompson pc->p_seg = NULL; 109902ac6454SAndrew Thompson pc->n_seg = 0; 110002ac6454SAndrew Thompson return 1; /* failure */ 110102ac6454SAndrew Thompson } 110202ac6454SAndrew Thompson 110302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 110402ac6454SAndrew Thompson * usb2_pc_dmamap_destroy 110502ac6454SAndrew Thompson * 110602ac6454SAndrew Thompson * This function is NULL safe. 110702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 110802ac6454SAndrew Thompson void 110902ac6454SAndrew Thompson usb2_pc_dmamap_destroy(struct usb2_page_cache *pc) 111002ac6454SAndrew Thompson { 111102ac6454SAndrew Thompson if (pc && pc->tag) { 111202ac6454SAndrew Thompson bus_dmamap_destroy(pc->tag, pc->map); 111302ac6454SAndrew Thompson pc->tag = NULL; 111402ac6454SAndrew Thompson pc->map = NULL; 111502ac6454SAndrew Thompson } 111602ac6454SAndrew Thompson } 111702ac6454SAndrew Thompson 111802ac6454SAndrew Thompson #endif 111902ac6454SAndrew Thompson 1120bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 1121bdc081c6SAndrew Thompson 112202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 112302ac6454SAndrew Thompson * usb2_dma_tag_find - factored out code 112402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 112502ac6454SAndrew Thompson struct usb2_dma_tag * 112602ac6454SAndrew Thompson usb2_dma_tag_find(struct usb2_dma_parent_tag *udpt, 1127578d0effSAndrew Thompson usb2_size_t size, usb2_size_t align) 112802ac6454SAndrew Thompson { 112902ac6454SAndrew Thompson struct usb2_dma_tag *udt; 113002ac6454SAndrew Thompson uint8_t nudt; 113102ac6454SAndrew Thompson 113202ac6454SAndrew Thompson USB_ASSERT(align > 0, ("Invalid parameter align = 0!\n")); 113302ac6454SAndrew Thompson USB_ASSERT(size > 0, ("Invalid parameter size = 0!\n")); 113402ac6454SAndrew Thompson 113502ac6454SAndrew Thompson udt = udpt->utag_first; 113602ac6454SAndrew Thompson nudt = udpt->utag_max; 113702ac6454SAndrew Thompson 113802ac6454SAndrew Thompson while (nudt--) { 113902ac6454SAndrew Thompson 114002ac6454SAndrew Thompson if (udt->align == 0) { 114102ac6454SAndrew Thompson usb2_dma_tag_create(udt, size, align); 114202ac6454SAndrew Thompson if (udt->tag == NULL) { 114302ac6454SAndrew Thompson return (NULL); 114402ac6454SAndrew Thompson } 114502ac6454SAndrew Thompson udt->align = align; 114602ac6454SAndrew Thompson udt->size = size; 114702ac6454SAndrew Thompson return (udt); 114802ac6454SAndrew Thompson } 114902ac6454SAndrew Thompson if ((udt->align == align) && (udt->size == size)) { 115002ac6454SAndrew Thompson return (udt); 115102ac6454SAndrew Thompson } 115202ac6454SAndrew Thompson udt++; 115302ac6454SAndrew Thompson } 115402ac6454SAndrew Thompson return (NULL); 115502ac6454SAndrew Thompson } 115602ac6454SAndrew Thompson 115702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 115802ac6454SAndrew Thompson * usb2_dma_tag_setup - initialise USB DMA tags 115902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 116002ac6454SAndrew Thompson void 116102ac6454SAndrew Thompson usb2_dma_tag_setup(struct usb2_dma_parent_tag *udpt, 116202ac6454SAndrew Thompson struct usb2_dma_tag *udt, bus_dma_tag_t dmat, 116302ac6454SAndrew Thompson struct mtx *mtx, usb2_dma_callback_t *func, 1164bdc081c6SAndrew Thompson uint8_t ndmabits, uint8_t nudt) 116502ac6454SAndrew Thompson { 116602ac6454SAndrew Thompson bzero(udpt, sizeof(*udpt)); 116702ac6454SAndrew Thompson 116802ac6454SAndrew Thompson /* sanity checking */ 116902ac6454SAndrew Thompson if ((nudt == 0) || 117002ac6454SAndrew Thompson (ndmabits == 0) || 117102ac6454SAndrew Thompson (mtx == NULL)) { 117202ac6454SAndrew Thompson /* something is corrupt */ 117302ac6454SAndrew Thompson return; 117402ac6454SAndrew Thompson } 117502ac6454SAndrew Thompson #ifdef __FreeBSD__ 117602ac6454SAndrew Thompson /* initialise condition variable */ 117702ac6454SAndrew Thompson usb2_cv_init(udpt->cv, "USB DMA CV"); 117802ac6454SAndrew Thompson #endif 117902ac6454SAndrew Thompson 118002ac6454SAndrew Thompson /* store some information */ 118102ac6454SAndrew Thompson udpt->mtx = mtx; 118202ac6454SAndrew Thompson udpt->func = func; 118302ac6454SAndrew Thompson udpt->tag = dmat; 118402ac6454SAndrew Thompson udpt->utag_first = udt; 118502ac6454SAndrew Thompson udpt->utag_max = nudt; 118602ac6454SAndrew Thompson udpt->dma_bits = ndmabits; 118702ac6454SAndrew Thompson 118802ac6454SAndrew Thompson while (nudt--) { 118902ac6454SAndrew Thompson bzero(udt, sizeof(*udt)); 119002ac6454SAndrew Thompson udt->tag_parent = udpt; 119102ac6454SAndrew Thompson udt++; 119202ac6454SAndrew Thompson } 119302ac6454SAndrew Thompson } 119402ac6454SAndrew Thompson 119502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 119602ac6454SAndrew Thompson * usb2_bus_tag_unsetup - factored out code 119702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 119802ac6454SAndrew Thompson void 119902ac6454SAndrew Thompson usb2_dma_tag_unsetup(struct usb2_dma_parent_tag *udpt) 120002ac6454SAndrew Thompson { 120102ac6454SAndrew Thompson struct usb2_dma_tag *udt; 120202ac6454SAndrew Thompson uint8_t nudt; 120302ac6454SAndrew Thompson 120402ac6454SAndrew Thompson udt = udpt->utag_first; 120502ac6454SAndrew Thompson nudt = udpt->utag_max; 120602ac6454SAndrew Thompson 120702ac6454SAndrew Thompson while (nudt--) { 120802ac6454SAndrew Thompson 120902ac6454SAndrew Thompson if (udt->align) { 121002ac6454SAndrew Thompson /* destroy the USB DMA tag */ 121102ac6454SAndrew Thompson usb2_dma_tag_destroy(udt); 121202ac6454SAndrew Thompson udt->align = 0; 121302ac6454SAndrew Thompson } 121402ac6454SAndrew Thompson udt++; 121502ac6454SAndrew Thompson } 121602ac6454SAndrew Thompson 121702ac6454SAndrew Thompson if (udpt->utag_max) { 121802ac6454SAndrew Thompson #ifdef __FreeBSD__ 121902ac6454SAndrew Thompson /* destroy the condition variable */ 122002ac6454SAndrew Thompson usb2_cv_destroy(udpt->cv); 122102ac6454SAndrew Thompson #endif 122202ac6454SAndrew Thompson } 122302ac6454SAndrew Thompson } 122402ac6454SAndrew Thompson 122502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 122602ac6454SAndrew Thompson * usb2_bdma_work_loop 122702ac6454SAndrew Thompson * 122802ac6454SAndrew Thompson * This function handles loading of virtual buffers into DMA and is 122902ac6454SAndrew Thompson * only called when "dma_refcount" is zero. 123002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 123102ac6454SAndrew Thompson void 123202ac6454SAndrew Thompson usb2_bdma_work_loop(struct usb2_xfer_queue *pq) 123302ac6454SAndrew Thompson { 123402ac6454SAndrew Thompson struct usb2_xfer_root *info; 123502ac6454SAndrew Thompson struct usb2_xfer *xfer; 1236578d0effSAndrew Thompson usb2_frcount_t nframes; 123702ac6454SAndrew Thompson 123802ac6454SAndrew Thompson xfer = pq->curr; 123902ac6454SAndrew Thompson info = xfer->xroot; 124002ac6454SAndrew Thompson 124102ac6454SAndrew Thompson mtx_assert(info->xfer_mtx, MA_OWNED); 124202ac6454SAndrew Thompson 124302ac6454SAndrew Thompson if (xfer->error) { 124402ac6454SAndrew Thompson /* some error happened */ 124502ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 124602ac6454SAndrew Thompson usb2_transfer_done(xfer, 0); 124702ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 124802ac6454SAndrew Thompson return; 124902ac6454SAndrew Thompson } 125002ac6454SAndrew Thompson if (!xfer->flags_int.bdma_setup) { 125102ac6454SAndrew Thompson struct usb2_page *pg; 1252578d0effSAndrew Thompson usb2_frlength_t frlength_0; 125302ac6454SAndrew Thompson uint8_t isread; 125402ac6454SAndrew Thompson 125502ac6454SAndrew Thompson xfer->flags_int.bdma_setup = 1; 125602ac6454SAndrew Thompson 125702ac6454SAndrew Thompson /* reset BUS-DMA load state */ 125802ac6454SAndrew Thompson 125902ac6454SAndrew Thompson info->dma_error = 0; 126002ac6454SAndrew Thompson 126102ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 126202ac6454SAndrew Thompson /* only one frame buffer */ 126302ac6454SAndrew Thompson nframes = 1; 126402ac6454SAndrew Thompson frlength_0 = xfer->sumlen; 126502ac6454SAndrew Thompson } else { 126602ac6454SAndrew Thompson /* can be multiple frame buffers */ 126702ac6454SAndrew Thompson nframes = xfer->nframes; 126802ac6454SAndrew Thompson frlength_0 = xfer->frlengths[0]; 126902ac6454SAndrew Thompson } 127002ac6454SAndrew Thompson 127102ac6454SAndrew Thompson /* 127202ac6454SAndrew Thompson * Set DMA direction first. This is needed to 127302ac6454SAndrew Thompson * select the correct cache invalidate and cache 127402ac6454SAndrew Thompson * flush operations. 127502ac6454SAndrew Thompson */ 127602ac6454SAndrew Thompson isread = USB_GET_DATA_ISREAD(xfer); 127702ac6454SAndrew Thompson pg = xfer->dma_page_ptr; 127802ac6454SAndrew Thompson 127902ac6454SAndrew Thompson if (xfer->flags_int.control_xfr && 128002ac6454SAndrew Thompson xfer->flags_int.control_hdr) { 128102ac6454SAndrew Thompson /* special case */ 128202ac6454SAndrew Thompson if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { 128302ac6454SAndrew Thompson /* The device controller writes to memory */ 128402ac6454SAndrew Thompson xfer->frbuffers[0].isread = 1; 128502ac6454SAndrew Thompson } else { 128602ac6454SAndrew Thompson /* The host controller reads from memory */ 128702ac6454SAndrew Thompson xfer->frbuffers[0].isread = 0; 128802ac6454SAndrew Thompson } 128902ac6454SAndrew Thompson } else { 129002ac6454SAndrew Thompson /* default case */ 129102ac6454SAndrew Thompson xfer->frbuffers[0].isread = isread; 129202ac6454SAndrew Thompson } 129302ac6454SAndrew Thompson 129402ac6454SAndrew Thompson /* 129502ac6454SAndrew Thompson * Setup the "page_start" pointer which points to an array of 129602ac6454SAndrew Thompson * USB pages where information about the physical address of a 129702ac6454SAndrew Thompson * page will be stored. Also initialise the "isread" field of 129802ac6454SAndrew Thompson * the USB page caches. 129902ac6454SAndrew Thompson */ 130002ac6454SAndrew Thompson xfer->frbuffers[0].page_start = pg; 130102ac6454SAndrew Thompson 130202ac6454SAndrew Thompson info->dma_nframes = nframes; 130302ac6454SAndrew Thompson info->dma_currframe = 0; 130402ac6454SAndrew Thompson info->dma_frlength_0 = frlength_0; 130502ac6454SAndrew Thompson 130602ac6454SAndrew Thompson pg += (frlength_0 / USB_PAGE_SIZE); 130702ac6454SAndrew Thompson pg += 2; 130802ac6454SAndrew Thompson 130902ac6454SAndrew Thompson while (--nframes > 0) { 131002ac6454SAndrew Thompson xfer->frbuffers[nframes].isread = isread; 131102ac6454SAndrew Thompson xfer->frbuffers[nframes].page_start = pg; 131202ac6454SAndrew Thompson 131302ac6454SAndrew Thompson pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); 131402ac6454SAndrew Thompson pg += 2; 131502ac6454SAndrew Thompson } 131602ac6454SAndrew Thompson 131702ac6454SAndrew Thompson } 131802ac6454SAndrew Thompson if (info->dma_error) { 131902ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 132002ac6454SAndrew Thompson usb2_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); 132102ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 132202ac6454SAndrew Thompson return; 132302ac6454SAndrew Thompson } 132402ac6454SAndrew Thompson if (info->dma_currframe != info->dma_nframes) { 132502ac6454SAndrew Thompson 132602ac6454SAndrew Thompson if (info->dma_currframe == 0) { 132702ac6454SAndrew Thompson /* special case */ 132802ac6454SAndrew Thompson usb2_pc_load_mem(xfer->frbuffers, 132902ac6454SAndrew Thompson info->dma_frlength_0, 0); 133002ac6454SAndrew Thompson } else { 133102ac6454SAndrew Thompson /* default case */ 133202ac6454SAndrew Thompson nframes = info->dma_currframe; 133302ac6454SAndrew Thompson usb2_pc_load_mem(xfer->frbuffers + nframes, 133402ac6454SAndrew Thompson xfer->frlengths[nframes], 0); 133502ac6454SAndrew Thompson } 133602ac6454SAndrew Thompson 133702ac6454SAndrew Thompson /* advance frame index */ 133802ac6454SAndrew Thompson info->dma_currframe++; 133902ac6454SAndrew Thompson 134002ac6454SAndrew Thompson return; 134102ac6454SAndrew Thompson } 134202ac6454SAndrew Thompson /* go ahead */ 134302ac6454SAndrew Thompson usb2_bdma_pre_sync(xfer); 134402ac6454SAndrew Thompson 134502ac6454SAndrew Thompson /* start loading next USB transfer, if any */ 134602ac6454SAndrew Thompson usb2_command_wrapper(pq, NULL); 134702ac6454SAndrew Thompson 134802ac6454SAndrew Thompson /* finally start the hardware */ 134902ac6454SAndrew Thompson usb2_pipe_enter(xfer); 135002ac6454SAndrew Thompson } 135102ac6454SAndrew Thompson 135202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 135302ac6454SAndrew Thompson * usb2_bdma_done_event 135402ac6454SAndrew Thompson * 135502ac6454SAndrew Thompson * This function is called when the BUS-DMA has loaded virtual memory 135602ac6454SAndrew Thompson * into DMA, if any. 135702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 135802ac6454SAndrew Thompson void 135902ac6454SAndrew Thompson usb2_bdma_done_event(struct usb2_dma_parent_tag *udpt) 136002ac6454SAndrew Thompson { 136102ac6454SAndrew Thompson struct usb2_xfer_root *info; 136202ac6454SAndrew Thompson 1363bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(udpt); 136402ac6454SAndrew Thompson 136502ac6454SAndrew Thompson mtx_assert(info->xfer_mtx, MA_OWNED); 136602ac6454SAndrew Thompson 136702ac6454SAndrew Thompson /* copy error */ 136802ac6454SAndrew Thompson info->dma_error = udpt->dma_error; 136902ac6454SAndrew Thompson 137002ac6454SAndrew Thompson /* enter workloop again */ 137102ac6454SAndrew Thompson usb2_command_wrapper(&info->dma_q, 137202ac6454SAndrew Thompson info->dma_q.curr); 137302ac6454SAndrew Thompson } 137402ac6454SAndrew Thompson 137502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 137602ac6454SAndrew Thompson * usb2_bdma_pre_sync 137702ac6454SAndrew Thompson * 137802ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done before 137902ac6454SAndrew Thompson * an USB transfer is started. 138002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 138102ac6454SAndrew Thompson void 138202ac6454SAndrew Thompson usb2_bdma_pre_sync(struct usb2_xfer *xfer) 138302ac6454SAndrew Thompson { 138402ac6454SAndrew Thompson struct usb2_page_cache *pc; 1385578d0effSAndrew Thompson usb2_frcount_t nframes; 138602ac6454SAndrew Thompson 138702ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 138802ac6454SAndrew Thompson /* only one frame buffer */ 138902ac6454SAndrew Thompson nframes = 1; 139002ac6454SAndrew Thompson } else { 139102ac6454SAndrew Thompson /* can be multiple frame buffers */ 139202ac6454SAndrew Thompson nframes = xfer->nframes; 139302ac6454SAndrew Thompson } 139402ac6454SAndrew Thompson 139502ac6454SAndrew Thompson pc = xfer->frbuffers; 139602ac6454SAndrew Thompson 139702ac6454SAndrew Thompson while (nframes--) { 139802ac6454SAndrew Thompson 139902ac6454SAndrew Thompson if (pc->isread) { 140002ac6454SAndrew Thompson usb2_pc_cpu_invalidate(pc); 140102ac6454SAndrew Thompson } else { 140202ac6454SAndrew Thompson usb2_pc_cpu_flush(pc); 140302ac6454SAndrew Thompson } 140402ac6454SAndrew Thompson pc++; 140502ac6454SAndrew Thompson } 140602ac6454SAndrew Thompson } 140702ac6454SAndrew Thompson 140802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 140902ac6454SAndrew Thompson * usb2_bdma_post_sync 141002ac6454SAndrew Thompson * 141102ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done after 141202ac6454SAndrew Thompson * an USB transfer is complete. 141302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 141402ac6454SAndrew Thompson void 141502ac6454SAndrew Thompson usb2_bdma_post_sync(struct usb2_xfer *xfer) 141602ac6454SAndrew Thompson { 141702ac6454SAndrew Thompson struct usb2_page_cache *pc; 1418578d0effSAndrew Thompson usb2_frcount_t nframes; 141902ac6454SAndrew Thompson 142002ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 142102ac6454SAndrew Thompson /* only one frame buffer */ 142202ac6454SAndrew Thompson nframes = 1; 142302ac6454SAndrew Thompson } else { 142402ac6454SAndrew Thompson /* can be multiple frame buffers */ 142502ac6454SAndrew Thompson nframes = xfer->nframes; 142602ac6454SAndrew Thompson } 142702ac6454SAndrew Thompson 142802ac6454SAndrew Thompson pc = xfer->frbuffers; 142902ac6454SAndrew Thompson 143002ac6454SAndrew Thompson while (nframes--) { 143102ac6454SAndrew Thompson if (pc->isread) { 143202ac6454SAndrew Thompson usb2_pc_cpu_invalidate(pc); 143302ac6454SAndrew Thompson } 143402ac6454SAndrew Thompson pc++; 143502ac6454SAndrew Thompson } 143602ac6454SAndrew Thompson } 1437bdc081c6SAndrew Thompson 1438bdc081c6SAndrew Thompson #endif 1439