102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 3718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4718cf2ccSPedro F. Giffuni * 502ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 902ac6454SAndrew Thompson * are met: 1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1502ac6454SAndrew Thompson * 1602ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1702ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1802ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1902ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2002ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2102ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2202ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2302ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2402ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2502ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2602ac6454SAndrew Thompson * SUCH DAMAGE. 2702ac6454SAndrew Thompson */ 2802ac6454SAndrew Thompson 29d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 30d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 31d2b99310SHans Petter Selasky #else 32ed6d949aSAndrew Thompson #include <sys/stdint.h> 33ed6d949aSAndrew Thompson #include <sys/stddef.h> 34ed6d949aSAndrew Thompson #include <sys/param.h> 35ed6d949aSAndrew Thompson #include <sys/queue.h> 36ed6d949aSAndrew Thompson #include <sys/types.h> 37ed6d949aSAndrew Thompson #include <sys/systm.h> 38ed6d949aSAndrew Thompson #include <sys/kernel.h> 39ed6d949aSAndrew Thompson #include <sys/bus.h> 40ed6d949aSAndrew Thompson #include <sys/module.h> 41ed6d949aSAndrew Thompson #include <sys/lock.h> 42ed6d949aSAndrew Thompson #include <sys/mutex.h> 43ed6d949aSAndrew Thompson #include <sys/condvar.h> 44ed6d949aSAndrew Thompson #include <sys/sysctl.h> 45ed6d949aSAndrew Thompson #include <sys/sx.h> 46ed6d949aSAndrew Thompson #include <sys/unistd.h> 47ed6d949aSAndrew Thompson #include <sys/callout.h> 48ed6d949aSAndrew Thompson #include <sys/malloc.h> 49ed6d949aSAndrew Thompson #include <sys/priv.h> 50ed6d949aSAndrew Thompson 5102ac6454SAndrew Thompson #include <dev/usb/usb.h> 52ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 53ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5402ac6454SAndrew Thompson 55a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5602ac6454SAndrew Thompson 5702ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6402ac6454SAndrew Thompson 6502ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 67d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6802ac6454SAndrew Thompson 69bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 70a593f6b8SAndrew Thompson static void usb_dma_tag_create(struct usb_dma_tag *, usb_size_t, usb_size_t); 71a593f6b8SAndrew Thompson static void usb_dma_tag_destroy(struct usb_dma_tag *); 72a593f6b8SAndrew Thompson static void usb_dma_lock_cb(void *, bus_dma_lock_op_t); 73a593f6b8SAndrew Thompson static void usb_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); 74a593f6b8SAndrew Thompson static void usb_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); 75a593f6b8SAndrew Thompson static void usb_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, 7602ac6454SAndrew Thompson uint8_t); 7702ac6454SAndrew Thompson #endif 7802ac6454SAndrew Thompson 7902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 80a593f6b8SAndrew Thompson * usbd_get_page - lookup DMA-able memory for the given offset 8102ac6454SAndrew Thompson * 8202ac6454SAndrew Thompson * NOTE: Only call this function when the "page_cache" structure has 8302ac6454SAndrew Thompson * been properly initialized ! 8402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 8502ac6454SAndrew Thompson void 86a593f6b8SAndrew Thompson usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, 87760bc48eSAndrew Thompson struct usb_page_search *res) 8802ac6454SAndrew Thompson { 89271ae033SHans Petter Selasky #if USB_HAVE_BUSDMA 90760bc48eSAndrew Thompson struct usb_page *page; 9102ac6454SAndrew Thompson 9202ac6454SAndrew Thompson if (pc->page_start) { 9302ac6454SAndrew Thompson /* Case 1 - something has been loaded into DMA */ 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson if (pc->buffer) { 9602ac6454SAndrew Thompson /* Case 1a - Kernel Virtual Address */ 9702ac6454SAndrew Thompson 9802ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 9902ac6454SAndrew Thompson } 10002ac6454SAndrew Thompson offset += pc->page_offset_buf; 10102ac6454SAndrew Thompson 10202ac6454SAndrew Thompson /* compute destination page */ 10302ac6454SAndrew Thompson 10402ac6454SAndrew Thompson page = pc->page_start; 10502ac6454SAndrew Thompson 10602ac6454SAndrew Thompson if (pc->ismultiseg) { 10702ac6454SAndrew Thompson page += (offset / USB_PAGE_SIZE); 10802ac6454SAndrew Thompson 10902ac6454SAndrew Thompson offset %= USB_PAGE_SIZE; 11002ac6454SAndrew Thompson 11102ac6454SAndrew Thompson res->length = USB_PAGE_SIZE - offset; 11202ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 11302ac6454SAndrew Thompson } else { 1146d917491SHans Petter Selasky res->length = (usb_size_t)-1; 11502ac6454SAndrew Thompson res->physaddr = page->physaddr + offset; 11602ac6454SAndrew Thompson } 11702ac6454SAndrew Thompson if (!pc->buffer) { 11802ac6454SAndrew Thompson /* Case 1b - Non Kernel Virtual Address */ 11902ac6454SAndrew Thompson 12002ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(page->buffer, offset); 12102ac6454SAndrew Thompson } 122bdc081c6SAndrew Thompson return; 123bdc081c6SAndrew Thompson } 124bdc081c6SAndrew Thompson #endif 12502ac6454SAndrew Thompson /* Case 2 - Plain PIO */ 12602ac6454SAndrew Thompson 12702ac6454SAndrew Thompson res->buffer = USB_ADD_BYTES(pc->buffer, offset); 1286d917491SHans Petter Selasky res->length = (usb_size_t)-1; 129bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 13002ac6454SAndrew Thompson res->physaddr = 0; 131bdc081c6SAndrew Thompson #endif 13202ac6454SAndrew Thompson } 13302ac6454SAndrew Thompson 13402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 135c6b6f546SHans Petter Selasky * usb_pc_buffer_is_aligned - verify alignment 136c6b6f546SHans Petter Selasky * 137c6b6f546SHans Petter Selasky * This function is used to check if a page cache buffer is properly 138c6b6f546SHans Petter Selasky * aligned to reduce the use of bounce buffers in PIO mode. 139c6b6f546SHans Petter Selasky *------------------------------------------------------------------------*/ 140c6b6f546SHans Petter Selasky uint8_t 141c6b6f546SHans Petter Selasky usb_pc_buffer_is_aligned(struct usb_page_cache *pc, usb_frlength_t offset, 142c6b6f546SHans Petter Selasky usb_frlength_t len, usb_frlength_t mask) 143c6b6f546SHans Petter Selasky { 144c6b6f546SHans Petter Selasky struct usb_page_search buf_res; 145c6b6f546SHans Petter Selasky 146c6b6f546SHans Petter Selasky while (len != 0) { 147c6b6f546SHans Petter Selasky usbd_get_page(pc, offset, &buf_res); 148c6b6f546SHans Petter Selasky 149c6b6f546SHans Petter Selasky if (buf_res.length > len) 150c6b6f546SHans Petter Selasky buf_res.length = len; 151c6b6f546SHans Petter Selasky if (USB_P2U(buf_res.buffer) & mask) 152c6b6f546SHans Petter Selasky return (0); 153c6b6f546SHans Petter Selasky if (buf_res.length & mask) 154c6b6f546SHans Petter Selasky return (0); 155c6b6f546SHans Petter Selasky 156c6b6f546SHans Petter Selasky offset += buf_res.length; 157c6b6f546SHans Petter Selasky len -= buf_res.length; 158c6b6f546SHans Petter Selasky } 159c6b6f546SHans Petter Selasky return (1); 160c6b6f546SHans Petter Selasky } 161c6b6f546SHans Petter Selasky 162c6b6f546SHans Petter Selasky /*------------------------------------------------------------------------* 163a593f6b8SAndrew Thompson * usbd_copy_in - copy directly to DMA-able memory 16402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 16502ac6454SAndrew Thompson void 166a593f6b8SAndrew Thompson usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, 167e0a69b51SAndrew Thompson const void *ptr, usb_frlength_t len) 16802ac6454SAndrew Thompson { 169760bc48eSAndrew Thompson struct usb_page_search buf_res; 17002ac6454SAndrew Thompson 17102ac6454SAndrew Thompson while (len != 0) { 172a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &buf_res); 17302ac6454SAndrew Thompson 17402ac6454SAndrew Thompson if (buf_res.length > len) { 17502ac6454SAndrew Thompson buf_res.length = len; 17602ac6454SAndrew Thompson } 177271ae033SHans Petter Selasky memcpy(buf_res.buffer, ptr, buf_res.length); 17802ac6454SAndrew Thompson 17902ac6454SAndrew Thompson offset += buf_res.length; 18002ac6454SAndrew Thompson len -= buf_res.length; 18102ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 18202ac6454SAndrew Thompson } 18302ac6454SAndrew Thompson } 18402ac6454SAndrew Thompson 18502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 186a593f6b8SAndrew Thompson * usbd_copy_in_user - copy directly to DMA-able memory from userland 18702ac6454SAndrew Thompson * 18802ac6454SAndrew Thompson * Return values: 18902ac6454SAndrew Thompson * 0: Success 19002ac6454SAndrew Thompson * Else: Failure 19102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 192bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 19302ac6454SAndrew Thompson int 194a593f6b8SAndrew Thompson usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, 195e0a69b51SAndrew Thompson const void *ptr, usb_frlength_t len) 19602ac6454SAndrew Thompson { 197760bc48eSAndrew Thompson struct usb_page_search buf_res; 19802ac6454SAndrew Thompson int error; 19902ac6454SAndrew Thompson 20002ac6454SAndrew Thompson while (len != 0) { 201a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &buf_res); 20202ac6454SAndrew Thompson 20302ac6454SAndrew Thompson if (buf_res.length > len) { 20402ac6454SAndrew Thompson buf_res.length = len; 20502ac6454SAndrew Thompson } 20602ac6454SAndrew Thompson error = copyin(ptr, buf_res.buffer, buf_res.length); 20702ac6454SAndrew Thompson if (error) 20802ac6454SAndrew Thompson return (error); 20902ac6454SAndrew Thompson 21002ac6454SAndrew Thompson offset += buf_res.length; 21102ac6454SAndrew Thompson len -= buf_res.length; 21202ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, buf_res.length); 21302ac6454SAndrew Thompson } 21402ac6454SAndrew Thompson return (0); /* success */ 21502ac6454SAndrew Thompson } 216bdc081c6SAndrew Thompson #endif 21702ac6454SAndrew Thompson 21802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 219a593f6b8SAndrew Thompson * usbd_m_copy_in - copy a mbuf chain directly into DMA-able memory 22002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 221bdc081c6SAndrew Thompson #if USB_HAVE_MBUF 222a593f6b8SAndrew Thompson struct usb_m_copy_in_arg { 223760bc48eSAndrew Thompson struct usb_page_cache *cache; 224e0a69b51SAndrew Thompson usb_frlength_t dst_offset; 22502ac6454SAndrew Thompson }; 22602ac6454SAndrew Thompson 227578d0effSAndrew Thompson static int 228a593f6b8SAndrew Thompson usbd_m_copy_in_cb(void *arg, void *src, uint32_t count) 22902ac6454SAndrew Thompson { 2303e85b721SEd Maste struct usb_m_copy_in_arg *ua = arg; 23102ac6454SAndrew Thompson 232a593f6b8SAndrew Thompson usbd_copy_in(ua->cache, ua->dst_offset, src, count); 23302ac6454SAndrew Thompson ua->dst_offset += count; 23402ac6454SAndrew Thompson return (0); 23502ac6454SAndrew Thompson } 23602ac6454SAndrew Thompson 23702ac6454SAndrew Thompson void 238a593f6b8SAndrew Thompson usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, 239f9cb546cSAndrew Thompson struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len) 24002ac6454SAndrew Thompson { 241a593f6b8SAndrew Thompson struct usb_m_copy_in_arg arg = {cache, dst_offset}; 242bce421e9SHans Petter Selasky (void) m_apply(m, src_offset, src_len, &usbd_m_copy_in_cb, &arg); 24302ac6454SAndrew Thompson } 244bdc081c6SAndrew Thompson #endif 24502ac6454SAndrew Thompson 24602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 247a593f6b8SAndrew Thompson * usb_uiomove - factored out code 24802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 249bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 25002ac6454SAndrew Thompson int 251a593f6b8SAndrew Thompson usb_uiomove(struct usb_page_cache *pc, struct uio *uio, 252e0a69b51SAndrew Thompson usb_frlength_t pc_offset, usb_frlength_t len) 25302ac6454SAndrew Thompson { 254760bc48eSAndrew Thompson struct usb_page_search res; 25502ac6454SAndrew Thompson int error = 0; 25602ac6454SAndrew Thompson 25702ac6454SAndrew Thompson while (len != 0) { 258a593f6b8SAndrew Thompson usbd_get_page(pc, pc_offset, &res); 25902ac6454SAndrew Thompson 26002ac6454SAndrew Thompson if (res.length > len) { 26102ac6454SAndrew Thompson res.length = len; 26202ac6454SAndrew Thompson } 26302ac6454SAndrew Thompson /* 26402ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 26502ac6454SAndrew Thompson * exiting the mutex and checking things 26602ac6454SAndrew Thompson */ 26702ac6454SAndrew Thompson error = uiomove(res.buffer, res.length, uio); 26802ac6454SAndrew Thompson 26902ac6454SAndrew Thompson if (error) { 27002ac6454SAndrew Thompson break; 27102ac6454SAndrew Thompson } 27202ac6454SAndrew Thompson pc_offset += res.length; 27302ac6454SAndrew Thompson len -= res.length; 27402ac6454SAndrew Thompson } 27502ac6454SAndrew Thompson return (error); 27602ac6454SAndrew Thompson } 277bdc081c6SAndrew Thompson #endif 27802ac6454SAndrew Thompson 27902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 280a593f6b8SAndrew Thompson * usbd_copy_out - copy directly from DMA-able memory 28102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 28202ac6454SAndrew Thompson void 283a593f6b8SAndrew Thompson usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, 284e0a69b51SAndrew Thompson void *ptr, usb_frlength_t len) 28502ac6454SAndrew Thompson { 286760bc48eSAndrew Thompson struct usb_page_search res; 28702ac6454SAndrew Thompson 28802ac6454SAndrew Thompson while (len != 0) { 289a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 29002ac6454SAndrew Thompson 29102ac6454SAndrew Thompson if (res.length > len) { 29202ac6454SAndrew Thompson res.length = len; 29302ac6454SAndrew Thompson } 294271ae033SHans Petter Selasky memcpy(ptr, res.buffer, res.length); 29502ac6454SAndrew Thompson 29602ac6454SAndrew Thompson offset += res.length; 29702ac6454SAndrew Thompson len -= res.length; 29802ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 29902ac6454SAndrew Thompson } 30002ac6454SAndrew Thompson } 30102ac6454SAndrew Thompson 30202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 303a593f6b8SAndrew Thompson * usbd_copy_out_user - copy directly from DMA-able memory to userland 30402ac6454SAndrew Thompson * 30502ac6454SAndrew Thompson * Return values: 30602ac6454SAndrew Thompson * 0: Success 30702ac6454SAndrew Thompson * Else: Failure 30802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 309bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO 31002ac6454SAndrew Thompson int 311a593f6b8SAndrew Thompson usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, 312e0a69b51SAndrew Thompson void *ptr, usb_frlength_t len) 31302ac6454SAndrew Thompson { 314760bc48eSAndrew Thompson struct usb_page_search res; 31502ac6454SAndrew Thompson int error; 31602ac6454SAndrew Thompson 31702ac6454SAndrew Thompson while (len != 0) { 318a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 31902ac6454SAndrew Thompson 32002ac6454SAndrew Thompson if (res.length > len) { 32102ac6454SAndrew Thompson res.length = len; 32202ac6454SAndrew Thompson } 32302ac6454SAndrew Thompson error = copyout(res.buffer, ptr, res.length); 32402ac6454SAndrew Thompson if (error) 32502ac6454SAndrew Thompson return (error); 32602ac6454SAndrew Thompson 32702ac6454SAndrew Thompson offset += res.length; 32802ac6454SAndrew Thompson len -= res.length; 32902ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, res.length); 33002ac6454SAndrew Thompson } 33102ac6454SAndrew Thompson return (0); /* success */ 33202ac6454SAndrew Thompson } 333bdc081c6SAndrew Thompson #endif 33402ac6454SAndrew Thompson 33502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 336a593f6b8SAndrew Thompson * usbd_frame_zero - zero DMA-able memory 33702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 33802ac6454SAndrew Thompson void 339a593f6b8SAndrew Thompson usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, 340e0a69b51SAndrew Thompson usb_frlength_t len) 34102ac6454SAndrew Thompson { 342760bc48eSAndrew Thompson struct usb_page_search res; 34302ac6454SAndrew Thompson 34402ac6454SAndrew Thompson while (len != 0) { 345a593f6b8SAndrew Thompson usbd_get_page(cache, offset, &res); 34602ac6454SAndrew Thompson 34702ac6454SAndrew Thompson if (res.length > len) { 34802ac6454SAndrew Thompson res.length = len; 34902ac6454SAndrew Thompson } 350271ae033SHans Petter Selasky memset(res.buffer, 0, res.length); 35102ac6454SAndrew Thompson 35202ac6454SAndrew Thompson offset += res.length; 35302ac6454SAndrew Thompson len -= res.length; 35402ac6454SAndrew Thompson } 35502ac6454SAndrew Thompson } 35602ac6454SAndrew Thompson 3573e873976SAndrew Thompson #if USB_HAVE_BUSDMA 35802ac6454SAndrew Thompson 35902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 360a593f6b8SAndrew Thompson * usb_dma_lock_cb - dummy callback 36102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 36202ac6454SAndrew Thompson static void 363a593f6b8SAndrew Thompson usb_dma_lock_cb(void *arg, bus_dma_lock_op_t op) 36402ac6454SAndrew Thompson { 36502ac6454SAndrew Thompson /* we use "mtx_owned()" instead of this function */ 36602ac6454SAndrew Thompson } 36702ac6454SAndrew Thompson 36802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 369a593f6b8SAndrew Thompson * usb_dma_tag_create - allocate a DMA tag 37002ac6454SAndrew Thompson * 37102ac6454SAndrew Thompson * NOTE: If the "align" parameter has a value of 1 the DMA-tag will 37202ac6454SAndrew Thompson * allow multi-segment mappings. Else all mappings are single-segment. 37302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37402ac6454SAndrew Thompson static void 375a593f6b8SAndrew Thompson usb_dma_tag_create(struct usb_dma_tag *udt, 376f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 37702ac6454SAndrew Thompson { 37802ac6454SAndrew Thompson bus_dma_tag_t tag; 37902ac6454SAndrew Thompson 38002ac6454SAndrew Thompson if (bus_dma_tag_create 38102ac6454SAndrew Thompson ( /* parent */ udt->tag_parent->tag, 38202ac6454SAndrew Thompson /* alignment */ align, 3831a7c3e90SHans Petter Selasky /* boundary */ 0, 38402ac6454SAndrew Thompson /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1, 38502ac6454SAndrew Thompson /* highaddr */ BUS_SPACE_MAXADDR, 38602ac6454SAndrew Thompson /* filter */ NULL, 38702ac6454SAndrew Thompson /* filterarg */ NULL, 38802ac6454SAndrew Thompson /* maxsize */ size, 3896c3c4d71SMarius Strobl /* nsegments */ (align == 1 && size > 1) ? 39002ac6454SAndrew Thompson (2 + (size / USB_PAGE_SIZE)) : 1, 3916c3c4d71SMarius Strobl /* maxsegsz */ (align == 1 && size > USB_PAGE_SIZE) ? 39202ac6454SAndrew Thompson USB_PAGE_SIZE : size, 39302ac6454SAndrew Thompson /* flags */ BUS_DMA_KEEP_PG_OFFSET, 394a593f6b8SAndrew Thompson /* lockfn */ &usb_dma_lock_cb, 39502ac6454SAndrew Thompson /* lockarg */ NULL, 39602ac6454SAndrew Thompson &tag)) { 39702ac6454SAndrew Thompson tag = NULL; 39802ac6454SAndrew Thompson } 39902ac6454SAndrew Thompson udt->tag = tag; 40002ac6454SAndrew Thompson } 40102ac6454SAndrew Thompson 40202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 403a593f6b8SAndrew Thompson * usb_dma_tag_free - free a DMA tag 40402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 40502ac6454SAndrew Thompson static void 406a593f6b8SAndrew Thompson usb_dma_tag_destroy(struct usb_dma_tag *udt) 40702ac6454SAndrew Thompson { 40802ac6454SAndrew Thompson bus_dma_tag_destroy(udt->tag); 40902ac6454SAndrew Thompson } 41002ac6454SAndrew Thompson 41102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 412a593f6b8SAndrew Thompson * usb_pc_alloc_mem_cb - BUS-DMA callback function 41302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 41402ac6454SAndrew Thompson static void 415a593f6b8SAndrew Thompson usb_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, 41602ac6454SAndrew Thompson int nseg, int error) 41702ac6454SAndrew Thompson { 418a593f6b8SAndrew Thompson usb_pc_common_mem_cb(arg, segs, nseg, error, 0); 41902ac6454SAndrew Thompson } 42002ac6454SAndrew Thompson 42102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 422a593f6b8SAndrew Thompson * usb_pc_load_mem_cb - BUS-DMA callback function 42302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 42402ac6454SAndrew Thompson static void 425a593f6b8SAndrew Thompson usb_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, 42602ac6454SAndrew Thompson int nseg, int error) 42702ac6454SAndrew Thompson { 428a593f6b8SAndrew Thompson usb_pc_common_mem_cb(arg, segs, nseg, error, 1); 42902ac6454SAndrew Thompson } 43002ac6454SAndrew Thompson 43102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 432a593f6b8SAndrew Thompson * usb_pc_common_mem_cb - BUS-DMA callback function 43302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 43402ac6454SAndrew Thompson static void 435a593f6b8SAndrew Thompson usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, 43602ac6454SAndrew Thompson int nseg, int error, uint8_t isload) 43702ac6454SAndrew Thompson { 438760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 439760bc48eSAndrew Thompson struct usb_page_cache *pc; 440760bc48eSAndrew Thompson struct usb_page *pg; 441f9cb546cSAndrew Thompson usb_size_t rem; 4421a7c3e90SHans Petter Selasky bus_size_t off; 44302ac6454SAndrew Thompson uint8_t owned; 44402ac6454SAndrew Thompson 44502ac6454SAndrew Thompson pc = arg; 44602ac6454SAndrew Thompson uptag = pc->tag_parent; 44702ac6454SAndrew Thompson 44802ac6454SAndrew Thompson /* 44902ac6454SAndrew Thompson * XXX There is sometimes recursive locking here. 45002ac6454SAndrew Thompson * XXX We should try to find a better solution. 45102ac6454SAndrew Thompson * XXX Until further the "owned" variable does 45202ac6454SAndrew Thompson * XXX the trick. 45302ac6454SAndrew Thompson */ 45402ac6454SAndrew Thompson 45502ac6454SAndrew Thompson if (error) { 45602ac6454SAndrew Thompson goto done; 45702ac6454SAndrew Thompson } 4581a7c3e90SHans Petter Selasky 4591a7c3e90SHans Petter Selasky off = 0; 46002ac6454SAndrew Thompson pg = pc->page_start; 461d9c9c81cSPedro F. Giffuni pg->physaddr = rounddown2(segs->ds_addr, USB_PAGE_SIZE); 46202ac6454SAndrew Thompson rem = segs->ds_addr & (USB_PAGE_SIZE - 1); 46302ac6454SAndrew Thompson pc->page_offset_buf = rem; 46402ac6454SAndrew Thompson pc->page_offset_end += rem; 465ed6d949aSAndrew Thompson #ifdef USB_DEBUG 466288edd19SHans Petter Selasky if (nseg > 1) { 467288edd19SHans Petter Selasky int x; 468288edd19SHans Petter Selasky 469288edd19SHans Petter Selasky for (x = 0; x != nseg - 1; x++) { 470288edd19SHans Petter Selasky if (((segs[x].ds_addr + segs[x].ds_len) & (USB_PAGE_SIZE - 1)) == 471288edd19SHans Petter Selasky ((segs[x + 1].ds_addr & (USB_PAGE_SIZE - 1)))) 472288edd19SHans Petter Selasky continue; 47302ac6454SAndrew Thompson /* 474288edd19SHans Petter Selasky * This check verifies there is no page offset 475288edd19SHans Petter Selasky * hole between any of the segments. See the 476a2aa9bebSHans Petter Selasky * BUS_DMA_KEEP_PG_OFFSET flag. 47702ac6454SAndrew Thompson */ 478767cb2e2SAndrew Thompson DPRINTFN(0, "Page offset was not preserved\n"); 47902ac6454SAndrew Thompson error = 1; 48002ac6454SAndrew Thompson goto done; 48102ac6454SAndrew Thompson } 482288edd19SHans Petter Selasky } 48302ac6454SAndrew Thompson #endif 4845fb878a9SHans Petter Selasky while (pc->ismultiseg) { 4851a7c3e90SHans Petter Selasky off += USB_PAGE_SIZE; 4861a7c3e90SHans Petter Selasky if (off >= (segs->ds_len + rem)) { 4871a7c3e90SHans Petter Selasky /* page crossing */ 48802ac6454SAndrew Thompson nseg--; 48902ac6454SAndrew Thompson segs++; 4901a7c3e90SHans Petter Selasky off = 0; 4911a7c3e90SHans Petter Selasky rem = 0; 492b7b11d00SHans Petter Selasky if (nseg == 0) 493b7b11d00SHans Petter Selasky break; 4941a7c3e90SHans Petter Selasky } 49502ac6454SAndrew Thompson pg++; 496d9c9c81cSPedro F. Giffuni pg->physaddr = rounddown2(segs->ds_addr + off, USB_PAGE_SIZE); 49702ac6454SAndrew Thompson } 49802ac6454SAndrew Thompson 49902ac6454SAndrew Thompson done: 50002ac6454SAndrew Thompson owned = mtx_owned(uptag->mtx); 50102ac6454SAndrew Thompson if (!owned) 5020eb8d462SHans Petter Selasky USB_MTX_LOCK(uptag->mtx); 50302ac6454SAndrew Thompson 50402ac6454SAndrew Thompson uptag->dma_error = (error ? 1 : 0); 50502ac6454SAndrew Thompson if (isload) { 50602ac6454SAndrew Thompson (uptag->func) (uptag); 50702ac6454SAndrew Thompson } else { 5088437751dSAndrew Thompson cv_broadcast(uptag->cv); 50902ac6454SAndrew Thompson } 51002ac6454SAndrew Thompson if (!owned) 5110eb8d462SHans Petter Selasky USB_MTX_UNLOCK(uptag->mtx); 51202ac6454SAndrew Thompson } 51302ac6454SAndrew Thompson 51402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 515a593f6b8SAndrew Thompson * usb_pc_alloc_mem - allocate DMA'able memory 51602ac6454SAndrew Thompson * 51702ac6454SAndrew Thompson * Returns: 51802ac6454SAndrew Thompson * 0: Success 51902ac6454SAndrew Thompson * Else: Failure 52002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 52102ac6454SAndrew Thompson uint8_t 522a593f6b8SAndrew Thompson usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, 523f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 52402ac6454SAndrew Thompson { 525760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 526760bc48eSAndrew Thompson struct usb_dma_tag *utag; 52702ac6454SAndrew Thompson bus_dmamap_t map; 52802ac6454SAndrew Thompson void *ptr; 52902ac6454SAndrew Thompson int err; 53002ac6454SAndrew Thompson 53102ac6454SAndrew Thompson uptag = pc->tag_parent; 53202ac6454SAndrew Thompson 53302ac6454SAndrew Thompson if (align != 1) { 53402ac6454SAndrew Thompson /* 53502ac6454SAndrew Thompson * The alignment must be greater or equal to the 53602ac6454SAndrew Thompson * "size" else the object can be split between two 53702ac6454SAndrew Thompson * memory pages and we get a problem! 53802ac6454SAndrew Thompson */ 53902ac6454SAndrew Thompson while (align < size) { 54002ac6454SAndrew Thompson align *= 2; 54102ac6454SAndrew Thompson if (align == 0) { 54202ac6454SAndrew Thompson goto error; 54302ac6454SAndrew Thompson } 54402ac6454SAndrew Thompson } 54502ac6454SAndrew Thompson #if 1 54602ac6454SAndrew Thompson /* 54702ac6454SAndrew Thompson * XXX BUS-DMA workaround - FIXME later: 54802ac6454SAndrew Thompson * 549a1581cd7SGordon Bergling * We assume that that the alignment at this point of 55002ac6454SAndrew Thompson * the code is greater than or equal to the size and 55102ac6454SAndrew Thompson * less than two times the size, so that if we double 55202ac6454SAndrew Thompson * the size, the size will be greater than the 55302ac6454SAndrew Thompson * alignment. 55402ac6454SAndrew Thompson * 55502ac6454SAndrew Thompson * The bus-dma system has a check for "alignment" 55602ac6454SAndrew Thompson * being less than "size". If that check fails we end 55702ac6454SAndrew Thompson * up using contigmalloc which is page based even for 55802ac6454SAndrew Thompson * small allocations. Try to avoid that to save 55902ac6454SAndrew Thompson * memory, hence we sometimes to a large number of 56002ac6454SAndrew Thompson * small allocations! 56102ac6454SAndrew Thompson */ 56202ac6454SAndrew Thompson if (size <= (USB_PAGE_SIZE / 2)) { 56302ac6454SAndrew Thompson size *= 2; 56402ac6454SAndrew Thompson } 56502ac6454SAndrew Thompson #endif 56602ac6454SAndrew Thompson } 56702ac6454SAndrew Thompson /* get the correct DMA tag */ 568a593f6b8SAndrew Thompson utag = usb_dma_tag_find(uptag, size, align); 56902ac6454SAndrew Thompson if (utag == NULL) { 57002ac6454SAndrew Thompson goto error; 57102ac6454SAndrew Thompson } 57202ac6454SAndrew Thompson /* allocate memory */ 57302ac6454SAndrew Thompson if (bus_dmamem_alloc( 57402ac6454SAndrew Thompson utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { 57502ac6454SAndrew Thompson goto error; 57602ac6454SAndrew Thompson } 57702ac6454SAndrew Thompson /* setup page cache */ 57802ac6454SAndrew Thompson pc->buffer = ptr; 57902ac6454SAndrew Thompson pc->page_start = pg; 58002ac6454SAndrew Thompson pc->page_offset_buf = 0; 58102ac6454SAndrew Thompson pc->page_offset_end = size; 58202ac6454SAndrew Thompson pc->map = map; 58302ac6454SAndrew Thompson pc->tag = utag->tag; 58402ac6454SAndrew Thompson pc->ismultiseg = (align == 1); 58502ac6454SAndrew Thompson 5860eb8d462SHans Petter Selasky USB_MTX_LOCK(uptag->mtx); 58702ac6454SAndrew Thompson 58802ac6454SAndrew Thompson /* load memory into DMA */ 58902ac6454SAndrew Thompson err = bus_dmamap_load( 590a593f6b8SAndrew Thompson utag->tag, map, ptr, size, &usb_pc_alloc_mem_cb, 59102ac6454SAndrew Thompson pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); 59202ac6454SAndrew Thompson 59302ac6454SAndrew Thompson if (err == EINPROGRESS) { 5948437751dSAndrew Thompson cv_wait(uptag->cv, uptag->mtx); 59502ac6454SAndrew Thompson err = 0; 59602ac6454SAndrew Thompson } 5970eb8d462SHans Petter Selasky USB_MTX_UNLOCK(uptag->mtx); 59802ac6454SAndrew Thompson 59902ac6454SAndrew Thompson if (err || uptag->dma_error) { 60002ac6454SAndrew Thompson bus_dmamem_free(utag->tag, ptr, map); 60102ac6454SAndrew Thompson goto error; 60202ac6454SAndrew Thompson } 603*dc91a971SIan Lepore pc->isloaded = 1; 604271ae033SHans Petter Selasky memset(ptr, 0, size); 60502ac6454SAndrew Thompson 606a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 60702ac6454SAndrew Thompson 60802ac6454SAndrew Thompson return (0); 60902ac6454SAndrew Thompson 61002ac6454SAndrew Thompson error: 61102ac6454SAndrew Thompson /* reset most of the page cache */ 61202ac6454SAndrew Thompson pc->buffer = NULL; 61302ac6454SAndrew Thompson pc->page_start = NULL; 61402ac6454SAndrew Thompson pc->page_offset_buf = 0; 61502ac6454SAndrew Thompson pc->page_offset_end = 0; 616*dc91a971SIan Lepore pc->isloaded = 0; 61702ac6454SAndrew Thompson pc->map = NULL; 61802ac6454SAndrew Thompson pc->tag = NULL; 61902ac6454SAndrew Thompson return (1); 62002ac6454SAndrew Thompson } 62102ac6454SAndrew Thompson 62202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 623a593f6b8SAndrew Thompson * usb_pc_free_mem - free DMA memory 62402ac6454SAndrew Thompson * 62502ac6454SAndrew Thompson * This function is NULL safe. 62602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 62702ac6454SAndrew Thompson void 628a593f6b8SAndrew Thompson usb_pc_free_mem(struct usb_page_cache *pc) 62902ac6454SAndrew Thompson { 63002ac6454SAndrew Thompson if (pc && pc->buffer) { 631*dc91a971SIan Lepore if (pc->isloaded) 63202ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 63302ac6454SAndrew Thompson 63402ac6454SAndrew Thompson bus_dmamem_free(pc->tag, pc->buffer, pc->map); 63502ac6454SAndrew Thompson 63602ac6454SAndrew Thompson pc->buffer = NULL; 637*dc91a971SIan Lepore pc->isloaded = 0; 63802ac6454SAndrew Thompson } 63902ac6454SAndrew Thompson } 64002ac6454SAndrew Thompson 64102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 642a593f6b8SAndrew Thompson * usb_pc_load_mem - load virtual memory into DMA 64302ac6454SAndrew Thompson * 64402ac6454SAndrew Thompson * Return values: 64502ac6454SAndrew Thompson * 0: Success 64602ac6454SAndrew Thompson * Else: Error 64702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 64802ac6454SAndrew Thompson uint8_t 649a593f6b8SAndrew Thompson usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync) 65002ac6454SAndrew Thompson { 65102ac6454SAndrew Thompson /* setup page cache */ 65202ac6454SAndrew Thompson pc->page_offset_buf = 0; 65302ac6454SAndrew Thompson pc->page_offset_end = size; 65402ac6454SAndrew Thompson pc->ismultiseg = 1; 65502ac6454SAndrew Thompson 6560eb8d462SHans Petter Selasky USB_MTX_ASSERT(pc->tag_parent->mtx, MA_OWNED); 65702ac6454SAndrew Thompson 65802ac6454SAndrew Thompson if (size > 0) { 65902ac6454SAndrew Thompson if (sync) { 660760bc48eSAndrew Thompson struct usb_dma_parent_tag *uptag; 66102ac6454SAndrew Thompson int err; 66202ac6454SAndrew Thompson 66302ac6454SAndrew Thompson uptag = pc->tag_parent; 66402ac6454SAndrew Thompson 66502ac6454SAndrew Thompson /* 66602ac6454SAndrew Thompson * We have to unload the previous loaded DMA 66702ac6454SAndrew Thompson * pages before trying to load a new one! 66802ac6454SAndrew Thompson */ 669*dc91a971SIan Lepore if (pc->isloaded) 67002ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 67102ac6454SAndrew Thompson 67202ac6454SAndrew Thompson /* 67302ac6454SAndrew Thompson * Try to load memory into DMA. 67402ac6454SAndrew Thompson */ 67502ac6454SAndrew Thompson err = bus_dmamap_load( 67602ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 677a593f6b8SAndrew Thompson &usb_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK); 67802ac6454SAndrew Thompson if (err == EINPROGRESS) { 6798437751dSAndrew Thompson cv_wait(uptag->cv, uptag->mtx); 68002ac6454SAndrew Thompson err = 0; 68102ac6454SAndrew Thompson } 68202ac6454SAndrew Thompson if (err || uptag->dma_error) { 683*dc91a971SIan Lepore pc->isloaded = 0; 68402ac6454SAndrew Thompson return (1); 68502ac6454SAndrew Thompson } 68602ac6454SAndrew Thompson } else { 68702ac6454SAndrew Thompson /* 68802ac6454SAndrew Thompson * We have to unload the previous loaded DMA 68902ac6454SAndrew Thompson * pages before trying to load a new one! 69002ac6454SAndrew Thompson */ 691*dc91a971SIan Lepore if (pc->isloaded) 69202ac6454SAndrew Thompson bus_dmamap_unload(pc->tag, pc->map); 69302ac6454SAndrew Thompson 69402ac6454SAndrew Thompson /* 69502ac6454SAndrew Thompson * Try to load memory into DMA. The callback 69602ac6454SAndrew Thompson * will be called in all cases: 69702ac6454SAndrew Thompson */ 69802ac6454SAndrew Thompson if (bus_dmamap_load( 69902ac6454SAndrew Thompson pc->tag, pc->map, pc->buffer, size, 700a593f6b8SAndrew Thompson &usb_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) { 70102ac6454SAndrew Thompson } 70202ac6454SAndrew Thompson } 703*dc91a971SIan Lepore pc->isloaded = 1; 70402ac6454SAndrew Thompson } else { 70502ac6454SAndrew Thompson if (!sync) { 70602ac6454SAndrew Thompson /* 70702ac6454SAndrew Thompson * Call callback so that refcount is decremented 70802ac6454SAndrew Thompson * properly: 70902ac6454SAndrew Thompson */ 71002ac6454SAndrew Thompson pc->tag_parent->dma_error = 0; 71102ac6454SAndrew Thompson (pc->tag_parent->func) (pc->tag_parent); 71202ac6454SAndrew Thompson } 71302ac6454SAndrew Thompson } 71402ac6454SAndrew Thompson return (0); 71502ac6454SAndrew Thompson } 71602ac6454SAndrew Thompson 71702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 718a593f6b8SAndrew Thompson * usb_pc_cpu_invalidate - invalidate CPU cache 71902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 72002ac6454SAndrew Thompson void 721a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(struct usb_page_cache *pc) 72202ac6454SAndrew Thompson { 72302ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 72402ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 72502ac6454SAndrew Thompson return; 72602ac6454SAndrew Thompson } 727f2db024fSAlfred Perlstein 728f2db024fSAlfred Perlstein /* 729f2db024fSAlfred Perlstein * TODO: We currently do XXX_POSTREAD and XXX_PREREAD at the 730f2db024fSAlfred Perlstein * same time, but in the future we should try to isolate the 731f2db024fSAlfred Perlstein * different cases to optimise the code. --HPS 732f2db024fSAlfred Perlstein */ 73335d01728SRafal Jaworowski bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_POSTREAD); 73435d01728SRafal Jaworowski bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREREAD); 73502ac6454SAndrew Thompson } 73602ac6454SAndrew Thompson 73702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 738a593f6b8SAndrew Thompson * usb_pc_cpu_flush - flush CPU cache 73902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 74002ac6454SAndrew Thompson void 741a593f6b8SAndrew Thompson usb_pc_cpu_flush(struct usb_page_cache *pc) 74202ac6454SAndrew Thompson { 74302ac6454SAndrew Thompson if (pc->page_offset_end == pc->page_offset_buf) { 74402ac6454SAndrew Thompson /* nothing has been loaded into this page cache! */ 74502ac6454SAndrew Thompson return; 74602ac6454SAndrew Thompson } 74735d01728SRafal Jaworowski bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREWRITE); 74802ac6454SAndrew Thompson } 74902ac6454SAndrew Thompson 75002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 751a593f6b8SAndrew Thompson * usb_pc_dmamap_create - create a DMA map 75202ac6454SAndrew Thompson * 75302ac6454SAndrew Thompson * Returns: 75402ac6454SAndrew Thompson * 0: Success 75502ac6454SAndrew Thompson * Else: Failure 75602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 75702ac6454SAndrew Thompson uint8_t 758a593f6b8SAndrew Thompson usb_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size) 75902ac6454SAndrew Thompson { 760760bc48eSAndrew Thompson struct usb_xfer_root *info; 761760bc48eSAndrew Thompson struct usb_dma_tag *utag; 76202ac6454SAndrew Thompson 76302ac6454SAndrew Thompson /* get info */ 764bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(pc->tag_parent); 76502ac6454SAndrew Thompson 76602ac6454SAndrew Thompson /* sanity check */ 76702ac6454SAndrew Thompson if (info == NULL) { 76802ac6454SAndrew Thompson goto error; 76902ac6454SAndrew Thompson } 770a593f6b8SAndrew Thompson utag = usb_dma_tag_find(pc->tag_parent, size, 1); 77102ac6454SAndrew Thompson if (utag == NULL) { 77202ac6454SAndrew Thompson goto error; 77302ac6454SAndrew Thompson } 77402ac6454SAndrew Thompson /* create DMA map */ 77502ac6454SAndrew Thompson if (bus_dmamap_create(utag->tag, 0, &pc->map)) { 77602ac6454SAndrew Thompson goto error; 77702ac6454SAndrew Thompson } 77802ac6454SAndrew Thompson pc->tag = utag->tag; 77902ac6454SAndrew Thompson return 0; /* success */ 78002ac6454SAndrew Thompson 78102ac6454SAndrew Thompson error: 78202ac6454SAndrew Thompson pc->map = NULL; 78302ac6454SAndrew Thompson pc->tag = NULL; 78402ac6454SAndrew Thompson return 1; /* failure */ 78502ac6454SAndrew Thompson } 78602ac6454SAndrew Thompson 78702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 788a593f6b8SAndrew Thompson * usb_pc_dmamap_destroy 78902ac6454SAndrew Thompson * 79002ac6454SAndrew Thompson * This function is NULL safe. 79102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 79202ac6454SAndrew Thompson void 793a593f6b8SAndrew Thompson usb_pc_dmamap_destroy(struct usb_page_cache *pc) 79402ac6454SAndrew Thompson { 79502ac6454SAndrew Thompson if (pc && pc->tag) { 796*dc91a971SIan Lepore if (pc->isloaded) 797*dc91a971SIan Lepore bus_dmamap_unload(pc->tag, pc->map); 79802ac6454SAndrew Thompson bus_dmamap_destroy(pc->tag, pc->map); 79902ac6454SAndrew Thompson pc->tag = NULL; 80002ac6454SAndrew Thompson pc->map = NULL; 80102ac6454SAndrew Thompson } 80202ac6454SAndrew Thompson } 80302ac6454SAndrew Thompson 80402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 805a593f6b8SAndrew Thompson * usb_dma_tag_find - factored out code 80602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 807760bc48eSAndrew Thompson struct usb_dma_tag * 808a593f6b8SAndrew Thompson usb_dma_tag_find(struct usb_dma_parent_tag *udpt, 809f9cb546cSAndrew Thompson usb_size_t size, usb_size_t align) 81002ac6454SAndrew Thompson { 811760bc48eSAndrew Thompson struct usb_dma_tag *udt; 81202ac6454SAndrew Thompson uint8_t nudt; 81302ac6454SAndrew Thompson 814767cb2e2SAndrew Thompson USB_ASSERT(align > 0, ("Invalid parameter align = 0\n")); 815767cb2e2SAndrew Thompson USB_ASSERT(size > 0, ("Invalid parameter size = 0\n")); 81602ac6454SAndrew Thompson 81702ac6454SAndrew Thompson udt = udpt->utag_first; 81802ac6454SAndrew Thompson nudt = udpt->utag_max; 81902ac6454SAndrew Thompson 82002ac6454SAndrew Thompson while (nudt--) { 82102ac6454SAndrew Thompson if (udt->align == 0) { 822a593f6b8SAndrew Thompson usb_dma_tag_create(udt, size, align); 82302ac6454SAndrew Thompson if (udt->tag == NULL) { 82402ac6454SAndrew Thompson return (NULL); 82502ac6454SAndrew Thompson } 82602ac6454SAndrew Thompson udt->align = align; 82702ac6454SAndrew Thompson udt->size = size; 82802ac6454SAndrew Thompson return (udt); 82902ac6454SAndrew Thompson } 83002ac6454SAndrew Thompson if ((udt->align == align) && (udt->size == size)) { 83102ac6454SAndrew Thompson return (udt); 83202ac6454SAndrew Thompson } 83302ac6454SAndrew Thompson udt++; 83402ac6454SAndrew Thompson } 83502ac6454SAndrew Thompson return (NULL); 83602ac6454SAndrew Thompson } 83702ac6454SAndrew Thompson 83802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 839a593f6b8SAndrew Thompson * usb_dma_tag_setup - initialise USB DMA tags 84002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 84102ac6454SAndrew Thompson void 842a593f6b8SAndrew Thompson usb_dma_tag_setup(struct usb_dma_parent_tag *udpt, 843760bc48eSAndrew Thompson struct usb_dma_tag *udt, bus_dma_tag_t dmat, 844e0a69b51SAndrew Thompson struct mtx *mtx, usb_dma_callback_t *func, 845bdc081c6SAndrew Thompson uint8_t ndmabits, uint8_t nudt) 84602ac6454SAndrew Thompson { 847271ae033SHans Petter Selasky memset(udpt, 0, sizeof(*udpt)); 84802ac6454SAndrew Thompson 84902ac6454SAndrew Thompson /* sanity checking */ 85002ac6454SAndrew Thompson if ((nudt == 0) || 85102ac6454SAndrew Thompson (ndmabits == 0) || 85202ac6454SAndrew Thompson (mtx == NULL)) { 85302ac6454SAndrew Thompson /* something is corrupt */ 85402ac6454SAndrew Thompson return; 85502ac6454SAndrew Thompson } 85602ac6454SAndrew Thompson /* initialise condition variable */ 8578437751dSAndrew Thompson cv_init(udpt->cv, "USB DMA CV"); 85802ac6454SAndrew Thompson 85902ac6454SAndrew Thompson /* store some information */ 86002ac6454SAndrew Thompson udpt->mtx = mtx; 86102ac6454SAndrew Thompson udpt->func = func; 86202ac6454SAndrew Thompson udpt->tag = dmat; 86302ac6454SAndrew Thompson udpt->utag_first = udt; 86402ac6454SAndrew Thompson udpt->utag_max = nudt; 86502ac6454SAndrew Thompson udpt->dma_bits = ndmabits; 86602ac6454SAndrew Thompson 86702ac6454SAndrew Thompson while (nudt--) { 868271ae033SHans Petter Selasky memset(udt, 0, sizeof(*udt)); 86902ac6454SAndrew Thompson udt->tag_parent = udpt; 87002ac6454SAndrew Thompson udt++; 87102ac6454SAndrew Thompson } 87202ac6454SAndrew Thompson } 87302ac6454SAndrew Thompson 87402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 875a593f6b8SAndrew Thompson * usb_bus_tag_unsetup - factored out code 87602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 87702ac6454SAndrew Thompson void 878a593f6b8SAndrew Thompson usb_dma_tag_unsetup(struct usb_dma_parent_tag *udpt) 87902ac6454SAndrew Thompson { 880760bc48eSAndrew Thompson struct usb_dma_tag *udt; 88102ac6454SAndrew Thompson uint8_t nudt; 88202ac6454SAndrew Thompson 88302ac6454SAndrew Thompson udt = udpt->utag_first; 88402ac6454SAndrew Thompson nudt = udpt->utag_max; 88502ac6454SAndrew Thompson 88602ac6454SAndrew Thompson while (nudt--) { 88702ac6454SAndrew Thompson if (udt->align) { 88802ac6454SAndrew Thompson /* destroy the USB DMA tag */ 889a593f6b8SAndrew Thompson usb_dma_tag_destroy(udt); 89002ac6454SAndrew Thompson udt->align = 0; 89102ac6454SAndrew Thompson } 89202ac6454SAndrew Thompson udt++; 89302ac6454SAndrew Thompson } 89402ac6454SAndrew Thompson 89502ac6454SAndrew Thompson if (udpt->utag_max) { 89602ac6454SAndrew Thompson /* destroy the condition variable */ 8978437751dSAndrew Thompson cv_destroy(udpt->cv); 89802ac6454SAndrew Thompson } 89902ac6454SAndrew Thompson } 90002ac6454SAndrew Thompson 90102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 902a593f6b8SAndrew Thompson * usb_bdma_work_loop 90302ac6454SAndrew Thompson * 90402ac6454SAndrew Thompson * This function handles loading of virtual buffers into DMA and is 90502ac6454SAndrew Thompson * only called when "dma_refcount" is zero. 90602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 90702ac6454SAndrew Thompson void 908a593f6b8SAndrew Thompson usb_bdma_work_loop(struct usb_xfer_queue *pq) 90902ac6454SAndrew Thompson { 910760bc48eSAndrew Thompson struct usb_xfer_root *info; 911760bc48eSAndrew Thompson struct usb_xfer *xfer; 912e0a69b51SAndrew Thompson usb_frcount_t nframes; 91302ac6454SAndrew Thompson 91402ac6454SAndrew Thompson xfer = pq->curr; 91502ac6454SAndrew Thompson info = xfer->xroot; 91602ac6454SAndrew Thompson 9170eb8d462SHans Petter Selasky USB_MTX_ASSERT(info->xfer_mtx, MA_OWNED); 91802ac6454SAndrew Thompson 91902ac6454SAndrew Thompson if (xfer->error) { 92002ac6454SAndrew Thompson /* some error happened */ 92102ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 922a593f6b8SAndrew Thompson usbd_transfer_done(xfer, 0); 92302ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 92402ac6454SAndrew Thompson return; 92502ac6454SAndrew Thompson } 92602ac6454SAndrew Thompson if (!xfer->flags_int.bdma_setup) { 927760bc48eSAndrew Thompson struct usb_page *pg; 928e0a69b51SAndrew Thompson usb_frlength_t frlength_0; 92902ac6454SAndrew Thompson uint8_t isread; 93002ac6454SAndrew Thompson 93102ac6454SAndrew Thompson xfer->flags_int.bdma_setup = 1; 93202ac6454SAndrew Thompson 93302ac6454SAndrew Thompson /* reset BUS-DMA load state */ 93402ac6454SAndrew Thompson 93502ac6454SAndrew Thompson info->dma_error = 0; 93602ac6454SAndrew Thompson 93702ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 93802ac6454SAndrew Thompson /* only one frame buffer */ 93902ac6454SAndrew Thompson nframes = 1; 94002ac6454SAndrew Thompson frlength_0 = xfer->sumlen; 94102ac6454SAndrew Thompson } else { 94202ac6454SAndrew Thompson /* can be multiple frame buffers */ 94302ac6454SAndrew Thompson nframes = xfer->nframes; 94402ac6454SAndrew Thompson frlength_0 = xfer->frlengths[0]; 94502ac6454SAndrew Thompson } 94602ac6454SAndrew Thompson 94702ac6454SAndrew Thompson /* 94802ac6454SAndrew Thompson * Set DMA direction first. This is needed to 94902ac6454SAndrew Thompson * select the correct cache invalidate and cache 95002ac6454SAndrew Thompson * flush operations. 95102ac6454SAndrew Thompson */ 95202ac6454SAndrew Thompson isread = USB_GET_DATA_ISREAD(xfer); 95302ac6454SAndrew Thompson pg = xfer->dma_page_ptr; 95402ac6454SAndrew Thompson 95502ac6454SAndrew Thompson if (xfer->flags_int.control_xfr && 95602ac6454SAndrew Thompson xfer->flags_int.control_hdr) { 95702ac6454SAndrew Thompson /* special case */ 958f29a0724SAndrew Thompson if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { 95902ac6454SAndrew Thompson /* The device controller writes to memory */ 96002ac6454SAndrew Thompson xfer->frbuffers[0].isread = 1; 96102ac6454SAndrew Thompson } else { 96202ac6454SAndrew Thompson /* The host controller reads from memory */ 96302ac6454SAndrew Thompson xfer->frbuffers[0].isread = 0; 96402ac6454SAndrew Thompson } 96502ac6454SAndrew Thompson } else { 96602ac6454SAndrew Thompson /* default case */ 96702ac6454SAndrew Thompson xfer->frbuffers[0].isread = isread; 96802ac6454SAndrew Thompson } 96902ac6454SAndrew Thompson 97002ac6454SAndrew Thompson /* 97102ac6454SAndrew Thompson * Setup the "page_start" pointer which points to an array of 97202ac6454SAndrew Thompson * USB pages where information about the physical address of a 97302ac6454SAndrew Thompson * page will be stored. Also initialise the "isread" field of 97402ac6454SAndrew Thompson * the USB page caches. 97502ac6454SAndrew Thompson */ 97602ac6454SAndrew Thompson xfer->frbuffers[0].page_start = pg; 97702ac6454SAndrew Thompson 97802ac6454SAndrew Thompson info->dma_nframes = nframes; 97902ac6454SAndrew Thompson info->dma_currframe = 0; 98002ac6454SAndrew Thompson info->dma_frlength_0 = frlength_0; 98102ac6454SAndrew Thompson 98202ac6454SAndrew Thompson pg += (frlength_0 / USB_PAGE_SIZE); 98302ac6454SAndrew Thompson pg += 2; 98402ac6454SAndrew Thompson 98502ac6454SAndrew Thompson while (--nframes > 0) { 98602ac6454SAndrew Thompson xfer->frbuffers[nframes].isread = isread; 98702ac6454SAndrew Thompson xfer->frbuffers[nframes].page_start = pg; 98802ac6454SAndrew Thompson 98902ac6454SAndrew Thompson pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); 99002ac6454SAndrew Thompson pg += 2; 99102ac6454SAndrew Thompson } 99202ac6454SAndrew Thompson } 99302ac6454SAndrew Thompson if (info->dma_error) { 99402ac6454SAndrew Thompson USB_BUS_LOCK(info->bus); 995a593f6b8SAndrew Thompson usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); 99602ac6454SAndrew Thompson USB_BUS_UNLOCK(info->bus); 99702ac6454SAndrew Thompson return; 99802ac6454SAndrew Thompson } 99902ac6454SAndrew Thompson if (info->dma_currframe != info->dma_nframes) { 100002ac6454SAndrew Thompson if (info->dma_currframe == 0) { 100102ac6454SAndrew Thompson /* special case */ 1002a593f6b8SAndrew Thompson usb_pc_load_mem(xfer->frbuffers, 100302ac6454SAndrew Thompson info->dma_frlength_0, 0); 100402ac6454SAndrew Thompson } else { 100502ac6454SAndrew Thompson /* default case */ 100602ac6454SAndrew Thompson nframes = info->dma_currframe; 1007a593f6b8SAndrew Thompson usb_pc_load_mem(xfer->frbuffers + nframes, 100802ac6454SAndrew Thompson xfer->frlengths[nframes], 0); 100902ac6454SAndrew Thompson } 101002ac6454SAndrew Thompson 101102ac6454SAndrew Thompson /* advance frame index */ 101202ac6454SAndrew Thompson info->dma_currframe++; 101302ac6454SAndrew Thompson 101402ac6454SAndrew Thompson return; 101502ac6454SAndrew Thompson } 101602ac6454SAndrew Thompson /* go ahead */ 1017a593f6b8SAndrew Thompson usb_bdma_pre_sync(xfer); 101802ac6454SAndrew Thompson 101902ac6454SAndrew Thompson /* start loading next USB transfer, if any */ 1020a593f6b8SAndrew Thompson usb_command_wrapper(pq, NULL); 102102ac6454SAndrew Thompson 102202ac6454SAndrew Thompson /* finally start the hardware */ 1023a593f6b8SAndrew Thompson usbd_pipe_enter(xfer); 102402ac6454SAndrew Thompson } 102502ac6454SAndrew Thompson 102602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1027a593f6b8SAndrew Thompson * usb_bdma_done_event 102802ac6454SAndrew Thompson * 102902ac6454SAndrew Thompson * This function is called when the BUS-DMA has loaded virtual memory 103002ac6454SAndrew Thompson * into DMA, if any. 103102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 103202ac6454SAndrew Thompson void 1033a593f6b8SAndrew Thompson usb_bdma_done_event(struct usb_dma_parent_tag *udpt) 103402ac6454SAndrew Thompson { 1035760bc48eSAndrew Thompson struct usb_xfer_root *info; 103602ac6454SAndrew Thompson 1037bdc081c6SAndrew Thompson info = USB_DMATAG_TO_XROOT(udpt); 103802ac6454SAndrew Thompson 10390eb8d462SHans Petter Selasky USB_MTX_ASSERT(info->xfer_mtx, MA_OWNED); 104002ac6454SAndrew Thompson 104102ac6454SAndrew Thompson /* copy error */ 104202ac6454SAndrew Thompson info->dma_error = udpt->dma_error; 104302ac6454SAndrew Thompson 104402ac6454SAndrew Thompson /* enter workloop again */ 1045a593f6b8SAndrew Thompson usb_command_wrapper(&info->dma_q, 104602ac6454SAndrew Thompson info->dma_q.curr); 104702ac6454SAndrew Thompson } 104802ac6454SAndrew Thompson 104902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1050a593f6b8SAndrew Thompson * usb_bdma_pre_sync 105102ac6454SAndrew Thompson * 105202ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done before 105302ac6454SAndrew Thompson * an USB transfer is started. 105402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 105502ac6454SAndrew Thompson void 1056a593f6b8SAndrew Thompson usb_bdma_pre_sync(struct usb_xfer *xfer) 105702ac6454SAndrew Thompson { 1058760bc48eSAndrew Thompson struct usb_page_cache *pc; 1059e0a69b51SAndrew Thompson usb_frcount_t nframes; 106002ac6454SAndrew Thompson 106102ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 106202ac6454SAndrew Thompson /* only one frame buffer */ 106302ac6454SAndrew Thompson nframes = 1; 106402ac6454SAndrew Thompson } else { 106502ac6454SAndrew Thompson /* can be multiple frame buffers */ 106602ac6454SAndrew Thompson nframes = xfer->nframes; 106702ac6454SAndrew Thompson } 106802ac6454SAndrew Thompson 106902ac6454SAndrew Thompson pc = xfer->frbuffers; 107002ac6454SAndrew Thompson 107102ac6454SAndrew Thompson while (nframes--) { 107202ac6454SAndrew Thompson if (pc->isread) { 1073a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(pc); 107402ac6454SAndrew Thompson } else { 1075a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 107602ac6454SAndrew Thompson } 107702ac6454SAndrew Thompson pc++; 107802ac6454SAndrew Thompson } 107902ac6454SAndrew Thompson } 108002ac6454SAndrew Thompson 108102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1082a593f6b8SAndrew Thompson * usb_bdma_post_sync 108302ac6454SAndrew Thompson * 108402ac6454SAndrew Thompson * This function handles DMA synchronisation that must be done after 108502ac6454SAndrew Thompson * an USB transfer is complete. 108602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 108702ac6454SAndrew Thompson void 1088a593f6b8SAndrew Thompson usb_bdma_post_sync(struct usb_xfer *xfer) 108902ac6454SAndrew Thompson { 1090760bc48eSAndrew Thompson struct usb_page_cache *pc; 1091e0a69b51SAndrew Thompson usb_frcount_t nframes; 109202ac6454SAndrew Thompson 109302ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 109402ac6454SAndrew Thompson /* only one frame buffer */ 109502ac6454SAndrew Thompson nframes = 1; 109602ac6454SAndrew Thompson } else { 109702ac6454SAndrew Thompson /* can be multiple frame buffers */ 109802ac6454SAndrew Thompson nframes = xfer->nframes; 109902ac6454SAndrew Thompson } 110002ac6454SAndrew Thompson 110102ac6454SAndrew Thompson pc = xfer->frbuffers; 110202ac6454SAndrew Thompson 110302ac6454SAndrew Thompson while (nframes--) { 110402ac6454SAndrew Thompson if (pc->isread) { 1105a593f6b8SAndrew Thompson usb_pc_cpu_invalidate(pc); 110602ac6454SAndrew Thompson } 110702ac6454SAndrew Thompson pc++; 110802ac6454SAndrew Thompson } 110902ac6454SAndrew Thompson } 1110bdc081c6SAndrew Thompson 1111bdc081c6SAndrew Thompson #endif 1112