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