1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * Copyright (c) 2013 Hans Petter Selasky. All rights reserved.
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
5f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
6f9f848faSopenharmony_ci * are met:
7f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
8f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
9f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
10f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
11f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
12f9f848faSopenharmony_ci *
13f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23f9f848faSopenharmony_ci * SUCH DAMAGE.
24f9f848faSopenharmony_ci */
25f9f848faSopenharmony_ci
26f9f848faSopenharmony_ci#include <los_memory.h>
27f9f848faSopenharmony_ci#include "los_vm_iomap.h"
28f9f848faSopenharmony_ci#include "los_vm_map.h"
29f9f848faSopenharmony_ci#include <user_copy.h>
30f9f848faSopenharmony_ci
31f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
32f9f848faSopenharmony_ci
33f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
34f9f848faSopenharmony_cistatic void	usb_pc_common_mem_cb(struct usb_page_cache *pc,
35f9f848faSopenharmony_ci							    void *vaddr, uint32_t length);
36f9f848faSopenharmony_ci#endif
37f9f848faSopenharmony_ci
38f9f848faSopenharmony_civoid
39f9f848faSopenharmony_ciusb_dma_cache_invalid(void *addr, unsigned int size)
40f9f848faSopenharmony_ci{
41f9f848faSopenharmony_ci	UINTPTR start = (UINTPTR)addr & ~(USB_CACHE_ALIGN_SIZE - 1);
42f9f848faSopenharmony_ci	UINTPTR end = (UINTPTR)addr + size;
43f9f848faSopenharmony_ci
44f9f848faSopenharmony_ci	end = ALIGN(end, USB_CACHE_ALIGN_SIZE);
45f9f848faSopenharmony_ci	DCacheInvRange(start, end);
46f9f848faSopenharmony_ci}
47f9f848faSopenharmony_ci
48f9f848faSopenharmony_civoid
49f9f848faSopenharmony_ciusb_dma_cache_flush(void *addr, unsigned int size)
50f9f848faSopenharmony_ci{
51f9f848faSopenharmony_ci	UINTPTR start = (UINTPTR)addr & ~(USB_CACHE_ALIGN_SIZE - 1);
52f9f848faSopenharmony_ci	UINTPTR end = (UINTPTR)addr + size;
53f9f848faSopenharmony_ci
54f9f848faSopenharmony_ci	end = ALIGN(end, USB_CACHE_ALIGN_SIZE);
55f9f848faSopenharmony_ci	DCacheFlushRange(start, end);
56f9f848faSopenharmony_ci}
57f9f848faSopenharmony_ci
58f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
59f9f848faSopenharmony_ci *  usbd_get_page - lookup DMA-able memory for the given offset
60f9f848faSopenharmony_ci *
61f9f848faSopenharmony_ci * NOTE: Only call this function when the "page_cache" structure has
62f9f848faSopenharmony_ci * been properly initialized !
63f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
64f9f848faSopenharmony_civoid
65f9f848faSopenharmony_ciusbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset,
66f9f848faSopenharmony_ci	struct usb_page_search *res)
67f9f848faSopenharmony_ci{
68f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
69f9f848faSopenharmony_ci	struct usb_page *page;
70f9f848faSopenharmony_ci	if (pc->page_start) {
71f9f848faSopenharmony_ci		/* Case 1 - something has been loaded into DMA */
72f9f848faSopenharmony_ci
73f9f848faSopenharmony_ci		if (pc->buffer) {
74f9f848faSopenharmony_ci
75f9f848faSopenharmony_ci			/* Case 1a - Kernel Virtual Address */
76f9f848faSopenharmony_ci
77f9f848faSopenharmony_ci			res->buffer = USB_ADD_BYTES(pc->buffer, offset);
78f9f848faSopenharmony_ci		}
79f9f848faSopenharmony_ci		offset += pc->page_offset_buf;
80f9f848faSopenharmony_ci
81f9f848faSopenharmony_ci		/* compute destination page */
82f9f848faSopenharmony_ci
83f9f848faSopenharmony_ci		page = pc->page_start;
84f9f848faSopenharmony_ci
85f9f848faSopenharmony_ci		if (pc->ismultiseg) {
86f9f848faSopenharmony_ci
87f9f848faSopenharmony_ci			page += (offset / USB_PAGE_SIZE);
88f9f848faSopenharmony_ci
89f9f848faSopenharmony_ci			offset %= USB_PAGE_SIZE;
90f9f848faSopenharmony_ci
91f9f848faSopenharmony_ci			res->length = USB_PAGE_SIZE - offset;
92f9f848faSopenharmony_ci			res->physaddr = page->physaddr + offset;
93f9f848faSopenharmony_ci		} else {
94f9f848faSopenharmony_ci			res->length = (usb_size_t)-1;
95f9f848faSopenharmony_ci			res->physaddr = page->physaddr + offset;
96f9f848faSopenharmony_ci		}
97f9f848faSopenharmony_ci		if (!pc->buffer) {
98f9f848faSopenharmony_ci
99f9f848faSopenharmony_ci			/* Case 1b - Non Kernel Virtual Address */
100f9f848faSopenharmony_ci
101f9f848faSopenharmony_ci			res->buffer = USB_ADD_BYTES(page->buffer, offset);
102f9f848faSopenharmony_ci		}
103f9f848faSopenharmony_ci		return;
104f9f848faSopenharmony_ci	}
105f9f848faSopenharmony_ci#endif
106f9f848faSopenharmony_ci	/* Case 2 - Plain PIO */
107f9f848faSopenharmony_ci
108f9f848faSopenharmony_ci	res->buffer = USB_ADD_BYTES(pc->buffer, offset);
109f9f848faSopenharmony_ci	res->length = (usb_size_t)-1;
110f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
111f9f848faSopenharmony_ci	res->physaddr = 0;
112f9f848faSopenharmony_ci#endif
113f9f848faSopenharmony_ci}
114f9f848faSopenharmony_ci
115f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
116f9f848faSopenharmony_ci *  usbd_copy_in - copy directly to DMA-able memory
117f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
118f9f848faSopenharmony_civoid
119f9f848faSopenharmony_ciusbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset,
120f9f848faSopenharmony_ci	const void *ptr, usb_frlength_t len)
121f9f848faSopenharmony_ci{
122f9f848faSopenharmony_ci	struct usb_page_search buf_res;
123f9f848faSopenharmony_ci	int ret;
124f9f848faSopenharmony_ci
125f9f848faSopenharmony_ci	while (len != 0) {
126f9f848faSopenharmony_ci
127f9f848faSopenharmony_ci		usbd_get_page(cache, offset, &buf_res);
128f9f848faSopenharmony_ci
129f9f848faSopenharmony_ci		if (buf_res.length > len) {
130f9f848faSopenharmony_ci			buf_res.length = len;
131f9f848faSopenharmony_ci		}
132f9f848faSopenharmony_ci		ret = memcpy_s(buf_res.buffer, buf_res.length, ptr, buf_res.length);
133f9f848faSopenharmony_ci		if (ret != EOK) {
134f9f848faSopenharmony_ci			return;
135f9f848faSopenharmony_ci		}
136f9f848faSopenharmony_ci
137f9f848faSopenharmony_ci		offset += buf_res.length;
138f9f848faSopenharmony_ci		len -= buf_res.length;
139f9f848faSopenharmony_ci		ptr = USB_ADD_BYTES(ptr, buf_res.length);
140f9f848faSopenharmony_ci	}
141f9f848faSopenharmony_ci}
142f9f848faSopenharmony_ci
143f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
144f9f848faSopenharmony_ci *  usbd_copy_out - copy directly from DMA-able memory
145f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
146f9f848faSopenharmony_civoid
147f9f848faSopenharmony_ciusbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset,
148f9f848faSopenharmony_ci	void *ptr, usb_frlength_t len)
149f9f848faSopenharmony_ci{
150f9f848faSopenharmony_ci	struct usb_page_search res;
151f9f848faSopenharmony_ci
152f9f848faSopenharmony_ci	while (len != 0) {
153f9f848faSopenharmony_ci
154f9f848faSopenharmony_ci		usbd_get_page(cache, offset, &res);
155f9f848faSopenharmony_ci
156f9f848faSopenharmony_ci		if (res.length > len) {
157f9f848faSopenharmony_ci			res.length = len;
158f9f848faSopenharmony_ci		}
159f9f848faSopenharmony_ci		(void)memcpy_s(ptr, len, res.buffer, res.length);
160f9f848faSopenharmony_ci
161f9f848faSopenharmony_ci		offset += res.length;
162f9f848faSopenharmony_ci		len -= res.length;
163f9f848faSopenharmony_ci		ptr = USB_ADD_BYTES(ptr, res.length);
164f9f848faSopenharmony_ci	}
165f9f848faSopenharmony_ci}
166f9f848faSopenharmony_ci
167f9f848faSopenharmony_ciint
168f9f848faSopenharmony_cicopyin(const void *uaddr, void *kaddr, size_t len)
169f9f848faSopenharmony_ci{
170f9f848faSopenharmony_ci	size_t ret = LOS_ArchCopyFromUser(kaddr, uaddr, len);
171f9f848faSopenharmony_ci	return ret ? EFAULT : 0;
172f9f848faSopenharmony_ci}
173f9f848faSopenharmony_ci
174f9f848faSopenharmony_ciint
175f9f848faSopenharmony_cicopyout(const void *kaddr, void *uaddr, size_t len)
176f9f848faSopenharmony_ci{
177f9f848faSopenharmony_ci	size_t ret = LOS_ArchCopyToUser(uaddr, kaddr, len);
178f9f848faSopenharmony_ci	return ret ? EFAULT : 0;
179f9f848faSopenharmony_ci}
180f9f848faSopenharmony_ci
181f9f848faSopenharmony_ci/* In user mode, the src buffer is from user */
182f9f848faSopenharmony_ciint
183f9f848faSopenharmony_ciusbd_copy_from_user(void *dest, uint32_t dest_len, const void *src, uint32_t src_len)
184f9f848faSopenharmony_ci{
185f9f848faSopenharmony_ci    int ret;
186f9f848faSopenharmony_ci
187f9f848faSopenharmony_ci    if (!LOS_IsUserAddressRange((vaddr_t)(UINTPTR)src, src_len)) {
188f9f848faSopenharmony_ci        ret = memcpy_s(dest, dest_len, src, src_len);
189f9f848faSopenharmony_ci    } else {
190f9f848faSopenharmony_ci        ret = ((dest_len >= src_len) ? LOS_ArchCopyFromUser(dest, src, src_len) : ERANGE_AND_RESET);
191f9f848faSopenharmony_ci    }
192f9f848faSopenharmony_ci
193f9f848faSopenharmony_ci    return ret ? EFAULT : 0;
194f9f848faSopenharmony_ci}
195f9f848faSopenharmony_ci
196f9f848faSopenharmony_ci/* In user mode, the dest buffer is from user */
197f9f848faSopenharmony_ciint
198f9f848faSopenharmony_ciusbd_copy_to_user(void *dest, uint32_t dest_len, const void *src, uint32_t src_len)
199f9f848faSopenharmony_ci{
200f9f848faSopenharmony_ci    int ret;
201f9f848faSopenharmony_ci
202f9f848faSopenharmony_ci    if (!LOS_IsUserAddressRange((vaddr_t)(UINTPTR)dest, dest_len)) {
203f9f848faSopenharmony_ci        ret = memcpy_s(dest, dest_len, src, src_len);
204f9f848faSopenharmony_ci    } else {
205f9f848faSopenharmony_ci        ret = ((dest_len >= src_len) ? LOS_ArchCopyToUser(dest, src, src_len) : ERANGE_AND_RESET);
206f9f848faSopenharmony_ci    }
207f9f848faSopenharmony_ci
208f9f848faSopenharmony_ci    return ret ? EFAULT : 0;
209f9f848faSopenharmony_ci}
210f9f848faSopenharmony_ci
211f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
212f9f848faSopenharmony_ci *  usbd_frame_zero - zero DMA-able memory
213f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
214f9f848faSopenharmony_civoid
215f9f848faSopenharmony_ciusbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset,
216f9f848faSopenharmony_ci	usb_frlength_t len)
217f9f848faSopenharmony_ci{
218f9f848faSopenharmony_ci	struct usb_page_search res;
219f9f848faSopenharmony_ci
220f9f848faSopenharmony_ci	while (len != 0) {
221f9f848faSopenharmony_ci
222f9f848faSopenharmony_ci		usbd_get_page(cache, offset, &res);
223f9f848faSopenharmony_ci
224f9f848faSopenharmony_ci		if (res.length > len) {
225f9f848faSopenharmony_ci			res.length = len;
226f9f848faSopenharmony_ci		}
227f9f848faSopenharmony_ci		(void)memset_s(res.buffer, res.length, 0, res.length);
228f9f848faSopenharmony_ci
229f9f848faSopenharmony_ci		offset += res.length;
230f9f848faSopenharmony_ci		len -= res.length;
231f9f848faSopenharmony_ci	}
232f9f848faSopenharmony_ci}
233f9f848faSopenharmony_ci
234f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
235f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
236f9f848faSopenharmony_ci *	usb_pc_common_mem_cb - BUS-DMA callback function
237f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
238f9f848faSopenharmony_cistatic void
239f9f848faSopenharmony_ciusb_pc_common_mem_cb(struct usb_page_cache *pc, void *dma_handle, uint32_t length)
240f9f848faSopenharmony_ci{
241f9f848faSopenharmony_ci	struct usb_page *pg;
242f9f848faSopenharmony_ci	usb_size_t rem;
243f9f848faSopenharmony_ci	bus_size_t off;
244f9f848faSopenharmony_ci	bus_addr_t phys = (bus_addr_t)(UINTPTR)dma_handle;
245f9f848faSopenharmony_ci
246f9f848faSopenharmony_ci	pg = pc->page_start;
247f9f848faSopenharmony_ci	pg->physaddr = phys & ~(USB_PAGE_SIZE - 1);
248f9f848faSopenharmony_ci	rem = phys & (USB_PAGE_SIZE - 1);
249f9f848faSopenharmony_ci	pc->page_offset_buf = rem;
250f9f848faSopenharmony_ci	pc->page_offset_end += rem;
251f9f848faSopenharmony_ci	length += rem;
252f9f848faSopenharmony_ci
253f9f848faSopenharmony_ci	for (off = USB_PAGE_SIZE; off < length; off += USB_PAGE_SIZE) {
254f9f848faSopenharmony_ci		pg++;
255f9f848faSopenharmony_ci		pg->physaddr = (phys + off) & ~(USB_PAGE_SIZE - 1);
256f9f848faSopenharmony_ci	}
257f9f848faSopenharmony_ci}
258f9f848faSopenharmony_ci
259f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
260f9f848faSopenharmony_ci *	usb_pc_alloc_mem - allocate DMA'able memory
261f9f848faSopenharmony_ci *
262f9f848faSopenharmony_ci * Returns:
263f9f848faSopenharmony_ci *	0: Success
264f9f848faSopenharmony_ci * Else: Failure
265f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
266f9f848faSopenharmony_ciuint8_t
267f9f848faSopenharmony_ciusb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg,
268f9f848faSopenharmony_ci			    usb_size_t size, usb_size_t align)
269f9f848faSopenharmony_ci{
270f9f848faSopenharmony_ci	void *ptr;
271f9f848faSopenharmony_ci	DMA_ADDR_T dma_handle;
272f9f848faSopenharmony_ci
273f9f848faSopenharmony_ci	/* allocate zeroed memory */
274f9f848faSopenharmony_ci	if (align < USB_CACHE_ALIGN_SIZE) {
275f9f848faSopenharmony_ci		ptr = LOS_DmaMemAlloc(&dma_handle, size, USB_CACHE_ALIGN_SIZE, DMA_NOCACHE);
276f9f848faSopenharmony_ci	} else {
277f9f848faSopenharmony_ci		ptr = LOS_DmaMemAlloc(&dma_handle, size, align, DMA_NOCACHE);
278f9f848faSopenharmony_ci	}
279f9f848faSopenharmony_ci	if (ptr == NULL)
280f9f848faSopenharmony_ci		goto error;
281f9f848faSopenharmony_ci
282f9f848faSopenharmony_ci	(void)memset_s(ptr, size, 0, size);
283f9f848faSopenharmony_ci	/* setup page cache */
284f9f848faSopenharmony_ci	pc->buffer = (uint8_t *)ptr;
285f9f848faSopenharmony_ci	pc->page_start = pg;
286f9f848faSopenharmony_ci	pc->page_offset_buf = 0;
287f9f848faSopenharmony_ci	pc->page_offset_end = size;
288f9f848faSopenharmony_ci	pc->map = NULL;
289f9f848faSopenharmony_ci	pc->tag = (bus_dma_tag_t)ptr;
290f9f848faSopenharmony_ci	pc->ismultiseg = (align == 1);
291f9f848faSopenharmony_ci
292f9f848faSopenharmony_ci	/* compute physical address */
293f9f848faSopenharmony_ci	usb_pc_common_mem_cb(pc, (void *)(UINTPTR)dma_handle, size);
294f9f848faSopenharmony_ci
295f9f848faSopenharmony_ci	usb_pc_cpu_flush(pc);
296f9f848faSopenharmony_ci	return (0);
297f9f848faSopenharmony_ci
298f9f848faSopenharmony_cierror:
299f9f848faSopenharmony_ci	/* reset most of the page cache */
300f9f848faSopenharmony_ci	pc->buffer = NULL;
301f9f848faSopenharmony_ci	pc->page_start = NULL;
302f9f848faSopenharmony_ci	pc->page_offset_buf = 0;
303f9f848faSopenharmony_ci	pc->page_offset_end = 0;
304f9f848faSopenharmony_ci	pc->map = NULL;
305f9f848faSopenharmony_ci	pc->tag = NULL;
306f9f848faSopenharmony_ci	return (1);
307f9f848faSopenharmony_ci}
308f9f848faSopenharmony_ci
309f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
310f9f848faSopenharmony_ci *	usb_pc_free_mem - free DMA memory
311f9f848faSopenharmony_ci *
312f9f848faSopenharmony_ci * This function is NULL safe.
313f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
314f9f848faSopenharmony_civoid
315f9f848faSopenharmony_ciusb_pc_free_mem(struct usb_page_cache *pc)
316f9f848faSopenharmony_ci{
317f9f848faSopenharmony_ci	if ((pc != NULL) && (pc->buffer != NULL)) {
318f9f848faSopenharmony_ci		LOS_DmaMemFree(pc->tag);
319f9f848faSopenharmony_ci		pc->buffer = NULL;
320f9f848faSopenharmony_ci	}
321f9f848faSopenharmony_ci}
322f9f848faSopenharmony_ci
323f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
324f9f848faSopenharmony_ci *	usb_pc_load_mem - load virtual memory into DMA
325f9f848faSopenharmony_ci *
326f9f848faSopenharmony_ci * Return values:
327f9f848faSopenharmony_ci * 0: Success
328f9f848faSopenharmony_ci * Else: Error
329f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
330f9f848faSopenharmony_ciuint8_t
331f9f848faSopenharmony_ciusb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t data_sync)
332f9f848faSopenharmony_ci{
333f9f848faSopenharmony_ci	/* setup page cache */
334f9f848faSopenharmony_ci	pc->page_offset_buf = 0;
335f9f848faSopenharmony_ci	pc->page_offset_end = size;
336f9f848faSopenharmony_ci	pc->ismultiseg = 1;
337f9f848faSopenharmony_ci
338f9f848faSopenharmony_ci	mtx_assert(pc->tag_parent->mtx, MA_OWNED);
339f9f848faSopenharmony_ci
340f9f848faSopenharmony_ci	if (size > 0) {
341f9f848faSopenharmony_ci		/* compute physical address */
342f9f848faSopenharmony_ci#if defined (LOSCFG_DRIVERS_HDF_USB_DDK_HOST) || defined (LOSCFG_DRIVERS_HDF_USB_DDK_DEVICE)
343f9f848faSopenharmony_ci		usb_pc_common_mem_cb(pc, (void *)VMM_TO_UNCACHED_ADDR((unsigned long)pc->buffer), size);
344f9f848faSopenharmony_ci#else
345f9f848faSopenharmony_ci		usb_pc_common_mem_cb(pc, (void *)(UINTPTR)LOS_DmaVaddrToPaddr(pc->buffer), size);
346f9f848faSopenharmony_ci#endif
347f9f848faSopenharmony_ci	}
348f9f848faSopenharmony_ci	if (data_sync == 0) {
349f9f848faSopenharmony_ci		/*
350f9f848faSopenharmony_ci		 * Call callback so that refcount is decremented
351f9f848faSopenharmony_ci		 * properly:
352f9f848faSopenharmony_ci		 */
353f9f848faSopenharmony_ci		pc->tag_parent->dma_error = 0;
354f9f848faSopenharmony_ci		(pc->tag_parent->func) (pc->tag_parent);
355f9f848faSopenharmony_ci	}
356f9f848faSopenharmony_ci	return (0);
357f9f848faSopenharmony_ci}
358f9f848faSopenharmony_ci
359f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
360f9f848faSopenharmony_ci *	usb_pc_cpu_invalidate - invalidate CPU cache
361f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
362f9f848faSopenharmony_civoid
363f9f848faSopenharmony_ciusb_pc_cpu_invalidate(struct usb_page_cache *pc)
364f9f848faSopenharmony_ci{
365f9f848faSopenharmony_ci	if (pc->page_offset_end == pc->page_offset_buf) {
366f9f848faSopenharmony_ci		/* nothing has been loaded into this page cache! */
367f9f848faSopenharmony_ci		return;
368f9f848faSopenharmony_ci	}
369f9f848faSopenharmony_ci	usb_dma_cache_invalid(pc->buffer, pc->page_offset_end - pc->page_offset_buf);
370f9f848faSopenharmony_ci}
371f9f848faSopenharmony_ci
372f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
373f9f848faSopenharmony_ci *	usb_pc_cpu_flush - flush CPU cache
374f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
375f9f848faSopenharmony_civoid
376f9f848faSopenharmony_ciusb_pc_cpu_flush(struct usb_page_cache *pc)
377f9f848faSopenharmony_ci{
378f9f848faSopenharmony_ci	if (pc->page_offset_end == pc->page_offset_buf) {
379f9f848faSopenharmony_ci		/* nothing has been loaded into this page cache! */
380f9f848faSopenharmony_ci		return;
381f9f848faSopenharmony_ci	}
382f9f848faSopenharmony_ci	usb_dma_cache_flush(pc->buffer, pc->page_offset_end - pc->page_offset_buf);
383f9f848faSopenharmony_ci}
384f9f848faSopenharmony_ci
385f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
386f9f848faSopenharmony_ci *	usb_pc_dmamap_create - create a DMA map
387f9f848faSopenharmony_ci *
388f9f848faSopenharmony_ci * Returns:
389f9f848faSopenharmony_ci *	0: Success
390f9f848faSopenharmony_ci * Else: Failure
391f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
392f9f848faSopenharmony_ciuint8_t
393f9f848faSopenharmony_ciusb_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size)
394f9f848faSopenharmony_ci{
395f9f848faSopenharmony_ci	return (0);	/* NOP, success */
396f9f848faSopenharmony_ci}
397f9f848faSopenharmony_ci
398f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
399f9f848faSopenharmony_ci *	usb_pc_dmamap_destroy
400f9f848faSopenharmony_ci *
401f9f848faSopenharmony_ci * This function is NULL safe.
402f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
403f9f848faSopenharmony_civoid
404f9f848faSopenharmony_ciusb_pc_dmamap_destroy(struct usb_page_cache *pc)
405f9f848faSopenharmony_ci{
406f9f848faSopenharmony_ci	/* NOP */
407f9f848faSopenharmony_ci}
408f9f848faSopenharmony_ci
409f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
410f9f848faSopenharmony_ci *	usb_dma_tag_setup - initialise USB DMA tags
411f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
412f9f848faSopenharmony_civoid
413f9f848faSopenharmony_ciusb_dma_tag_setup(struct usb_dma_parent_tag *udpt,
414f9f848faSopenharmony_ci			    struct usb_dma_tag *udt, bus_dma_tag_t dmat,
415f9f848faSopenharmony_ci			    struct mtx *mtx, usb_dma_callback_t *func,
416f9f848faSopenharmony_ci			    uint8_t ndmabits, uint8_t nudt)
417f9f848faSopenharmony_ci{
418f9f848faSopenharmony_ci	(void)memset_s(udpt, sizeof(*udpt), 0, sizeof(*udpt));
419f9f848faSopenharmony_ci
420f9f848faSopenharmony_ci	/* sanity checking */
421f9f848faSopenharmony_ci	if ((nudt == 0) ||
422f9f848faSopenharmony_ci		(ndmabits == 0) ||
423f9f848faSopenharmony_ci		(mtx == NULL)) {
424f9f848faSopenharmony_ci		/* something is corrupt */
425f9f848faSopenharmony_ci		return;
426f9f848faSopenharmony_ci	}
427f9f848faSopenharmony_ci	/* initialise condition variable */
428f9f848faSopenharmony_ci	cv_init(udpt->cv, "USB DMA CV");
429f9f848faSopenharmony_ci
430f9f848faSopenharmony_ci	/* store some information */
431f9f848faSopenharmony_ci	udpt->mtx = mtx;
432f9f848faSopenharmony_ci	udpt->func = func;
433f9f848faSopenharmony_ci	udpt->tag = dmat;
434f9f848faSopenharmony_ci	udpt->utag_first = udt;
435f9f848faSopenharmony_ci	udpt->utag_max = nudt;
436f9f848faSopenharmony_ci	udpt->dma_bits = ndmabits;
437f9f848faSopenharmony_ci
438f9f848faSopenharmony_ci	while (nudt--) {
439f9f848faSopenharmony_ci		(void)memset_s(udt, sizeof(*udt), 0, sizeof(*udt));
440f9f848faSopenharmony_ci		udt->tag_parent = udpt;
441f9f848faSopenharmony_ci		udt++;
442f9f848faSopenharmony_ci	}
443f9f848faSopenharmony_ci}
444f9f848faSopenharmony_ci
445f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
446f9f848faSopenharmony_ci *	usb_bus_tag_unsetup - factored out code
447f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
448f9f848faSopenharmony_civoid
449f9f848faSopenharmony_ciusb_dma_tag_unsetup(struct usb_dma_parent_tag *udpt)
450f9f848faSopenharmony_ci{
451f9f848faSopenharmony_ci	struct usb_dma_tag *udt;
452f9f848faSopenharmony_ci	uint8_t nudt;
453f9f848faSopenharmony_ci
454f9f848faSopenharmony_ci	udt = udpt->utag_first;
455f9f848faSopenharmony_ci	nudt = udpt->utag_max;
456f9f848faSopenharmony_ci
457f9f848faSopenharmony_ci	while (nudt--) {
458f9f848faSopenharmony_ci		udt->align = 0;
459f9f848faSopenharmony_ci		udt++;
460f9f848faSopenharmony_ci	}
461f9f848faSopenharmony_ci
462f9f848faSopenharmony_ci	if (udpt->utag_max) {
463f9f848faSopenharmony_ci		/* destroy the condition variable */
464f9f848faSopenharmony_ci		cv_destroy(udpt->cv);
465f9f848faSopenharmony_ci	}
466f9f848faSopenharmony_ci}
467f9f848faSopenharmony_ci
468f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
469f9f848faSopenharmony_ci *	usb_bdma_work_loop
470f9f848faSopenharmony_ci *
471f9f848faSopenharmony_ci * This function handles loading of virtual buffers into DMA and is
472f9f848faSopenharmony_ci * only called when "dma_refcount" is zero.
473f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
474f9f848faSopenharmony_civoid
475f9f848faSopenharmony_ciusb_bdma_work_loop(struct usb_xfer_queue *pq)
476f9f848faSopenharmony_ci{
477f9f848faSopenharmony_ci	struct usb_xfer_root *info;
478f9f848faSopenharmony_ci	struct usb_xfer *xfer;
479f9f848faSopenharmony_ci	usb_frcount_t nframes;
480f9f848faSopenharmony_ci
481f9f848faSopenharmony_ci	xfer = pq->curr;
482f9f848faSopenharmony_ci	info = xfer->xroot;
483f9f848faSopenharmony_ci
484f9f848faSopenharmony_ci	mtx_assert(info->xfer_mtx, MA_OWNED);
485f9f848faSopenharmony_ci
486f9f848faSopenharmony_ci	if (xfer->error) {
487f9f848faSopenharmony_ci		/* some error happened */
488f9f848faSopenharmony_ci		USB_BUS_LOCK(info->bus);
489f9f848faSopenharmony_ci		usbd_transfer_done(xfer, USB_ERR_NORMAL_COMPLETION);
490f9f848faSopenharmony_ci		USB_BUS_UNLOCK(info->bus);
491f9f848faSopenharmony_ci		return;
492f9f848faSopenharmony_ci	}
493f9f848faSopenharmony_ci	if (!xfer->flags_int.bdma_setup) {
494f9f848faSopenharmony_ci		struct usb_page *pg;
495f9f848faSopenharmony_ci		usb_frlength_t frlength_0;
496f9f848faSopenharmony_ci		uint8_t isread;
497f9f848faSopenharmony_ci
498f9f848faSopenharmony_ci		xfer->flags_int.bdma_setup = 1;
499f9f848faSopenharmony_ci
500f9f848faSopenharmony_ci		/* reset BUS-DMA load state */
501f9f848faSopenharmony_ci
502f9f848faSopenharmony_ci		info->dma_error = 0;
503f9f848faSopenharmony_ci
504f9f848faSopenharmony_ci		if (xfer->flags_int.isochronous_xfr) {
505f9f848faSopenharmony_ci			/* only one frame buffer */
506f9f848faSopenharmony_ci			nframes = 1;
507f9f848faSopenharmony_ci			frlength_0 = xfer->sumlen;
508f9f848faSopenharmony_ci		} else {
509f9f848faSopenharmony_ci			/* can be multiple frame buffers */
510f9f848faSopenharmony_ci			nframes = xfer->nframes;
511f9f848faSopenharmony_ci			frlength_0 = xfer->frlengths[0];
512f9f848faSopenharmony_ci		}
513f9f848faSopenharmony_ci
514f9f848faSopenharmony_ci		/*
515f9f848faSopenharmony_ci		 * Set DMA direction first. This is needed to
516f9f848faSopenharmony_ci		 * select the correct cache invalidate and cache
517f9f848faSopenharmony_ci		 * flush operations.
518f9f848faSopenharmony_ci		 */
519f9f848faSopenharmony_ci		isread = USB_GET_DATA_ISREAD(xfer);
520f9f848faSopenharmony_ci		pg = xfer->dma_page_ptr;
521f9f848faSopenharmony_ci
522f9f848faSopenharmony_ci		if (xfer->flags_int.control_xfr &&
523f9f848faSopenharmony_ci		    xfer->flags_int.control_hdr) {
524f9f848faSopenharmony_ci			/* special case */
525f9f848faSopenharmony_ci			if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
526f9f848faSopenharmony_ci				/* The device controller writes to memory */
527f9f848faSopenharmony_ci				xfer->frbuffers[0].isread = 1;
528f9f848faSopenharmony_ci			} else {
529f9f848faSopenharmony_ci				/* The host controller reads from memory */
530f9f848faSopenharmony_ci				xfer->frbuffers[0].isread = 0;
531f9f848faSopenharmony_ci			}
532f9f848faSopenharmony_ci		} else {
533f9f848faSopenharmony_ci			/* default case */
534f9f848faSopenharmony_ci			xfer->frbuffers[0].isread = isread;
535f9f848faSopenharmony_ci		}
536f9f848faSopenharmony_ci
537f9f848faSopenharmony_ci		/*
538f9f848faSopenharmony_ci		 * Setup the "page_start" pointer which points to an array of
539f9f848faSopenharmony_ci		 * USB pages where information about the physical address of a
540f9f848faSopenharmony_ci		 * page will be stored. Also initialise the "isread" field of
541f9f848faSopenharmony_ci		 * the USB page caches.
542f9f848faSopenharmony_ci		 */
543f9f848faSopenharmony_ci		xfer->frbuffers[0].page_start = pg;
544f9f848faSopenharmony_ci
545f9f848faSopenharmony_ci		info->dma_nframes = nframes;
546f9f848faSopenharmony_ci		info->dma_currframe = 0;
547f9f848faSopenharmony_ci		info->dma_frlength_0 = frlength_0;
548f9f848faSopenharmony_ci
549f9f848faSopenharmony_ci		pg += (frlength_0 / USB_PAGE_SIZE);
550f9f848faSopenharmony_ci		pg += 2;
551f9f848faSopenharmony_ci
552f9f848faSopenharmony_ci		while (--nframes > 0) {
553f9f848faSopenharmony_ci			xfer->frbuffers[nframes].isread = isread;
554f9f848faSopenharmony_ci			xfer->frbuffers[nframes].page_start = pg;
555f9f848faSopenharmony_ci
556f9f848faSopenharmony_ci			pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE);
557f9f848faSopenharmony_ci			pg += 2;
558f9f848faSopenharmony_ci		}
559f9f848faSopenharmony_ci
560f9f848faSopenharmony_ci	}
561f9f848faSopenharmony_ci	if (info->dma_error) {
562f9f848faSopenharmony_ci		USB_BUS_LOCK(info->bus);
563f9f848faSopenharmony_ci		usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED);
564f9f848faSopenharmony_ci		USB_BUS_UNLOCK(info->bus);
565f9f848faSopenharmony_ci		return;
566f9f848faSopenharmony_ci	}
567f9f848faSopenharmony_ci	if (info->dma_currframe != info->dma_nframes) {
568f9f848faSopenharmony_ci
569f9f848faSopenharmony_ci		if (info->dma_currframe == 0) {
570f9f848faSopenharmony_ci			/* special case */
571f9f848faSopenharmony_ci			(void)usb_pc_load_mem(xfer->frbuffers,
572f9f848faSopenharmony_ci			    info->dma_frlength_0, 0);
573f9f848faSopenharmony_ci		} else {
574f9f848faSopenharmony_ci			/* default case */
575f9f848faSopenharmony_ci			nframes = info->dma_currframe;
576f9f848faSopenharmony_ci			(void)usb_pc_load_mem(xfer->frbuffers + nframes,
577f9f848faSopenharmony_ci			    xfer->frlengths[nframes], 0);
578f9f848faSopenharmony_ci		}
579f9f848faSopenharmony_ci
580f9f848faSopenharmony_ci		/* advance frame index */
581f9f848faSopenharmony_ci		info->dma_currframe++;
582f9f848faSopenharmony_ci
583f9f848faSopenharmony_ci		return;
584f9f848faSopenharmony_ci	}
585f9f848faSopenharmony_ci	/* go ahead */
586f9f848faSopenharmony_ci	usb_bdma_pre_sync(xfer);
587f9f848faSopenharmony_ci
588f9f848faSopenharmony_ci	/* start loading next USB transfer, if any */
589f9f848faSopenharmony_ci	usb_command_wrapper(pq, NULL);
590f9f848faSopenharmony_ci
591f9f848faSopenharmony_ci	/* finally start the hardware */
592f9f848faSopenharmony_ci	usbd_pipe_enter(xfer);
593f9f848faSopenharmony_ci}
594f9f848faSopenharmony_ci
595f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
596f9f848faSopenharmony_ci *	usb_bdma_done_event
597f9f848faSopenharmony_ci *
598f9f848faSopenharmony_ci * This function is called when the BUS-DMA has loaded virtual memory
599f9f848faSopenharmony_ci * into DMA, if any.
600f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
601f9f848faSopenharmony_civoid
602f9f848faSopenharmony_ciusb_bdma_done_event(struct usb_dma_parent_tag *udpt)
603f9f848faSopenharmony_ci{
604f9f848faSopenharmony_ci	struct usb_xfer_root *info;
605f9f848faSopenharmony_ci
606f9f848faSopenharmony_ci	info = USB_DMATAG_TO_XROOT(udpt);
607f9f848faSopenharmony_ci
608f9f848faSopenharmony_ci	mtx_assert(info->xfer_mtx, MA_OWNED);
609f9f848faSopenharmony_ci
610f9f848faSopenharmony_ci	/* copy error */
611f9f848faSopenharmony_ci	info->dma_error = udpt->dma_error;
612f9f848faSopenharmony_ci
613f9f848faSopenharmony_ci	/* enter workloop again */
614f9f848faSopenharmony_ci	usb_command_wrapper(&info->dma_q,
615f9f848faSopenharmony_ci	    info->dma_q.curr);
616f9f848faSopenharmony_ci}
617f9f848faSopenharmony_ci
618f9f848faSopenharmony_cistatic usb_frcount_t
619f9f848faSopenharmony_ciusb_bdma_frame_num(struct usb_xfer *xfer)
620f9f848faSopenharmony_ci{
621f9f848faSopenharmony_ci	if (xfer->flags_int.isochronous_xfr) {
622f9f848faSopenharmony_ci		/* only one frame buffer */
623f9f848faSopenharmony_ci		return (1);
624f9f848faSopenharmony_ci	} else {
625f9f848faSopenharmony_ci		/* can be multiple frame buffers */
626f9f848faSopenharmony_ci		return (xfer->nframes);
627f9f848faSopenharmony_ci	}
628f9f848faSopenharmony_ci}
629f9f848faSopenharmony_ci
630f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
631f9f848faSopenharmony_ci *	usb_bdma_pre_sync
632f9f848faSopenharmony_ci *
633f9f848faSopenharmony_ci * This function handles DMA synchronisation that must be done before
634f9f848faSopenharmony_ci * an USB transfer is started.
635f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
636f9f848faSopenharmony_civoid
637f9f848faSopenharmony_ciusb_bdma_pre_sync(struct usb_xfer *xfer)
638f9f848faSopenharmony_ci{
639f9f848faSopenharmony_ci	struct usb_page_cache *pc;
640f9f848faSopenharmony_ci	usb_frcount_t nframes;
641f9f848faSopenharmony_ci
642f9f848faSopenharmony_ci	nframes = usb_bdma_frame_num(xfer);
643f9f848faSopenharmony_ci	pc = xfer->frbuffers;
644f9f848faSopenharmony_ci
645f9f848faSopenharmony_ci	while (nframes--) {
646f9f848faSopenharmony_ci
647f9f848faSopenharmony_ci		if (pc->isread) {
648f9f848faSopenharmony_ci			usb_pc_cpu_invalidate(pc);
649f9f848faSopenharmony_ci		} else {
650f9f848faSopenharmony_ci			usb_pc_cpu_flush(pc);
651f9f848faSopenharmony_ci		}
652f9f848faSopenharmony_ci		pc++;
653f9f848faSopenharmony_ci	}
654f9f848faSopenharmony_ci}
655f9f848faSopenharmony_ci
656f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
657f9f848faSopenharmony_ci *	usb_bdma_post_sync
658f9f848faSopenharmony_ci *
659f9f848faSopenharmony_ci * This function handles DMA synchronisation that must be done after
660f9f848faSopenharmony_ci * an USB transfer is complete.
661f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
662f9f848faSopenharmony_civoid
663f9f848faSopenharmony_ciusb_bdma_post_sync(struct usb_xfer *xfer)
664f9f848faSopenharmony_ci{
665f9f848faSopenharmony_ci	struct usb_page_cache *pc;
666f9f848faSopenharmony_ci	usb_frcount_t nframes;
667f9f848faSopenharmony_ci
668f9f848faSopenharmony_ci	nframes = usb_bdma_frame_num(xfer);
669f9f848faSopenharmony_ci	pc = xfer->frbuffers;
670f9f848faSopenharmony_ci
671f9f848faSopenharmony_ci	while (nframes--) {
672f9f848faSopenharmony_ci		if (pc->isread) {
673f9f848faSopenharmony_ci			usb_pc_cpu_invalidate(pc);
674f9f848faSopenharmony_ci		}
675f9f848faSopenharmony_ci		pc++;
676f9f848faSopenharmony_ci	}
677f9f848faSopenharmony_ci}
678f9f848faSopenharmony_ci#endif
679