1231437Sluigi/*-
2252869Sdelphij * Copyright (C) 2013 Emulex
3231437Sluigi * All rights reserved.
4231437Sluigi *
5231437Sluigi * Redistribution and use in source and binary forms, with or without
6231437Sluigi * modification, are permitted provided that the following conditions are met:
7231437Sluigi *
8231437Sluigi * 1. Redistributions of source code must retain the above copyright notice,
9231437Sluigi *    this list of conditions and the following disclaimer.
10231437Sluigi *
11231437Sluigi * 2. Redistributions in binary form must reproduce the above copyright
12231437Sluigi *    notice, this list of conditions and the following disclaimer in the
13231437Sluigi *    documentation and/or other materials provided with the distribution.
14231437Sluigi *
15231437Sluigi * 3. Neither the name of the Emulex Corporation nor the names of its
16231437Sluigi *    contributors may be used to endorse or promote products derived from
17231437Sluigi *    this software without specific prior written permission.
18231437Sluigi *
19231437Sluigi * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20231437Sluigi * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21231437Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22231437Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23231437Sluigi * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24231437Sluigi * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25231437Sluigi * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26231437Sluigi * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27231437Sluigi * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28231437Sluigi * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29231437Sluigi * POSSIBILITY OF SUCH DAMAGE.
30231437Sluigi *
31231437Sluigi * Contact Information:
32231437Sluigi * freebsd-drivers@emulex.com
33231437Sluigi *
34231437Sluigi * Emulex
35231437Sluigi * 3333 Susan Street
36231437Sluigi * Costa Mesa, CA 92626
37231437Sluigi */
38231437Sluigi
39231437Sluigi/* $FreeBSD$ */
40231437Sluigi
41231437Sluigi#include "oce_if.h"
42231437Sluigi
43231437Sluigistatic void oce_dma_map_ring(void *arg,
44231437Sluigi			     bus_dma_segment_t *segs,
45231437Sluigi			     int nseg,
46231437Sluigi			     int error);
47231437Sluigi
48231437Sluigi/**
49231437Sluigi * @brief		Allocate DMA memory
50231437Sluigi * @param sc		software handle to the device
51231437Sluigi * @param size		bus size
52231437Sluigi * @param dma		dma memory area
53231437Sluigi * @param flags		creation flags
54231437Sluigi * @returns		0 on success, error otherwize
55231437Sluigi */
56231437Sluigiint
57231437Sluigioce_dma_alloc(POCE_SOFTC sc, bus_size_t size, POCE_DMA_MEM dma, int flags)
58231437Sluigi{
59231437Sluigi	int rc;
60231437Sluigi
61231437Sluigi
62231437Sluigi	memset(dma, 0, sizeof(OCE_DMA_MEM));
63231437Sluigi
64231437Sluigi	rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
65231437Sluigi				8, 0,
66231437Sluigi				BUS_SPACE_MAXADDR,
67231437Sluigi				BUS_SPACE_MAXADDR,
68231437Sluigi				NULL, NULL,
69231437Sluigi				size, 1, size, 0, NULL, NULL, &dma->tag);
70231437Sluigi
71231437Sluigi	if (rc == 0) {
72231437Sluigi		rc = bus_dmamem_alloc(dma->tag,
73231437Sluigi				      &dma->ptr,
74247880Sdelphij				      BUS_DMA_NOWAIT | BUS_DMA_COHERENT |
75247880Sdelphij					BUS_DMA_ZERO,
76231437Sluigi				      &dma->map);
77231437Sluigi	}
78231437Sluigi
79231437Sluigi	dma->paddr = 0;
80231437Sluigi	if (rc == 0) {
81231437Sluigi		rc = bus_dmamap_load(dma->tag,
82231437Sluigi				     dma->map,
83231437Sluigi				     dma->ptr,
84231437Sluigi				     size,
85231437Sluigi				     oce_dma_map_addr,
86231437Sluigi				     &dma->paddr, flags | BUS_DMA_NOWAIT);
87231437Sluigi		if (dma->paddr == 0)
88231437Sluigi			rc = ENXIO;
89231437Sluigi	}
90231437Sluigi
91231437Sluigi	if (rc != 0)
92231437Sluigi		oce_dma_free(sc, dma);
93231437Sluigi
94231437Sluigi	return rc;
95231437Sluigi}
96231437Sluigi
97231437Sluigi/**
98231437Sluigi * @brief		Free DMA memory
99231437Sluigi * @param sc		software handle to the device
100231437Sluigi * @param dma		dma area to free
101231437Sluigi */
102231437Sluigivoid
103231437Sluigioce_dma_free(POCE_SOFTC sc, POCE_DMA_MEM dma)
104231437Sluigi{
105231437Sluigi	if (dma->tag == NULL)
106231437Sluigi		return;
107231437Sluigi
108267580Sjhb	if (dma->paddr != 0) {
109231437Sluigi		bus_dmamap_sync(dma->tag, dma->map,
110231437Sluigi				BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
111231437Sluigi		bus_dmamap_unload(dma->tag, dma->map);
112267580Sjhb		dma->paddr = 0;
113231437Sluigi	}
114231437Sluigi
115231437Sluigi	if (dma->ptr != NULL) {
116231437Sluigi		bus_dmamem_free(dma->tag, dma->ptr, dma->map);
117231437Sluigi		dma->ptr = NULL;
118231437Sluigi	}
119231437Sluigi
120231437Sluigi	bus_dma_tag_destroy(dma->tag);
121231437Sluigi	dma->tag = NULL;
122231437Sluigi
123231437Sluigi	return;
124231437Sluigi}
125231437Sluigi
126231437Sluigi
127231437Sluigi
128231437Sluigi/**
129231437Sluigi * @brief		Map DMA memory segment addresses
130231437Sluigi * @param arg		physical address pointer
131231437Sluigi * @param segs		dma memory segments
132231437Sluigi * @param nseg		number of dma memory segments
133231437Sluigi * @param error		if error, zeroes the physical address
134231437Sluigi */
135231437Sluigivoid
136231437Sluigioce_dma_map_addr(void *arg, bus_dma_segment_t * segs, int nseg, int error)
137231437Sluigi{
138231437Sluigi	bus_addr_t *paddr = arg;
139231437Sluigi
140231437Sluigi	if (error)
141231437Sluigi		*paddr = 0;
142231437Sluigi	else
143231437Sluigi		*paddr = segs->ds_addr;
144231437Sluigi}
145231437Sluigi
146231437Sluigi
147231437Sluigi
148231437Sluigi/**
149231437Sluigi * @brief		Destroy a ring buffer
150231437Sluigi * @param sc		software handle to the device
151231437Sluigi * @param ring		ring buffer
152231437Sluigi */
153231437Sluigi
154231437Sluigivoid
155231437Sluigioce_destroy_ring_buffer(POCE_SOFTC sc, oce_ring_buffer_t *ring)
156231437Sluigi{
157231437Sluigi	oce_dma_free(sc, &ring->dma);
158231437Sluigi	free(ring, M_DEVBUF);
159231437Sluigi}
160231437Sluigi
161231437Sluigi
162231437Sluigi
163231437Sluigioce_ring_buffer_t *
164231437Sluigioce_create_ring_buffer(POCE_SOFTC sc,
165231437Sluigi		uint32_t q_len, uint32_t item_size)
166231437Sluigi{
167231437Sluigi	uint32_t size = q_len * item_size;
168231437Sluigi	int rc;
169231437Sluigi	oce_ring_buffer_t *ring;
170231437Sluigi
171231437Sluigi
172231437Sluigi	ring = malloc(sizeof(oce_ring_buffer_t), M_DEVBUF, M_NOWAIT | M_ZERO);
173231437Sluigi	if (ring == NULL)
174231437Sluigi		return NULL;
175231437Sluigi
176231437Sluigi	ring->item_size = item_size;
177231437Sluigi	ring->num_items = q_len;
178231437Sluigi
179231437Sluigi	rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
180231437Sluigi				4096, 0,
181231437Sluigi				BUS_SPACE_MAXADDR,
182231437Sluigi				BUS_SPACE_MAXADDR,
183231437Sluigi				NULL, NULL,
184231437Sluigi				size, 8, 4096, 0, NULL, NULL, &ring->dma.tag);
185231437Sluigi	if (rc)
186231437Sluigi		goto fail;
187231437Sluigi
188231437Sluigi
189231437Sluigi	rc = bus_dmamem_alloc(ring->dma.tag,
190231437Sluigi				&ring->dma.ptr,
191231437Sluigi				BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
192231437Sluigi				&ring->dma.map);
193231437Sluigi	if (rc)
194231437Sluigi		goto fail;
195231437Sluigi
196231437Sluigi	bzero(ring->dma.ptr, size);
197231437Sluigi	bus_dmamap_sync(ring->dma.tag, ring->dma.map,
198231437Sluigi			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
199231437Sluigi	ring->dma.paddr = 0;
200231437Sluigi
201231437Sluigi	return ring;
202231437Sluigi
203231437Sluigifail:
204231437Sluigi	oce_dma_free(sc, &ring->dma);
205231437Sluigi	free(ring, M_DEVBUF);
206231437Sluigi	ring = NULL;
207231437Sluigi	return NULL;
208231437Sluigi}
209231437Sluigi
210231437Sluigi
211231437Sluigi
212231437Sluigistruct _oce_dmamap_paddr_table {
213231437Sluigi	uint32_t max_entries;
214231437Sluigi	uint32_t num_entries;
215231437Sluigi	struct phys_addr *paddrs;
216231437Sluigi};
217231437Sluigi
218231437Sluigi
219231437Sluigi
220231437Sluigi/**
221231437Sluigi * @brief		Map ring buffer
222231437Sluigi * @param arg		dma map phyical address table pointer
223231437Sluigi * @param segs		dma memory segments
224231437Sluigi * @param nseg		number of dma memory segments
225231437Sluigi * @param error		maps only if error is 0
226231437Sluigi */
227231437Sluigistatic void
228231437Sluigioce_dma_map_ring(void *arg, bus_dma_segment_t * segs, int nseg, int error)
229231437Sluigi{
230231437Sluigi	int i;
231231437Sluigi	struct _oce_dmamap_paddr_table *dpt =
232231437Sluigi	    (struct _oce_dmamap_paddr_table *)arg;
233231437Sluigi
234231437Sluigi	if (error == 0) {
235231437Sluigi		if (nseg <= dpt->max_entries) {
236231437Sluigi			for (i = 0; i < nseg; i++) {
237231437Sluigi				dpt->paddrs[i].lo = ADDR_LO(segs[i].ds_addr);
238231437Sluigi				dpt->paddrs[i].hi = ADDR_HI(segs[i].ds_addr);
239231437Sluigi			}
240231437Sluigi			dpt->num_entries = nseg;
241231437Sluigi		}
242231437Sluigi	}
243231437Sluigi}
244231437Sluigi
245231437Sluigi
246231437Sluigi
247231437Sluigi/**
248231437Sluigi * @brief		Load bus dma map for a ring buffer
249231437Sluigi * @param ring		ring buffer pointer
250231437Sluigi * @param pa_list	physical address list
251231437Sluigi * @returns		number entries
252231437Sluigi */
253231437Sluigiuint32_t
254231437Sluigioce_page_list(oce_ring_buffer_t *ring, struct phys_addr *pa_list)
255231437Sluigi{
256231437Sluigi	struct _oce_dmamap_paddr_table dpt;
257231437Sluigi
258231437Sluigi	dpt.max_entries = 8;
259231437Sluigi	dpt.num_entries = 0;
260231437Sluigi	dpt.paddrs = pa_list;
261231437Sluigi
262231437Sluigi	bus_dmamap_load(ring->dma.tag,
263231437Sluigi			ring->dma.map,
264231437Sluigi			ring->dma.ptr,
265231437Sluigi			ring->item_size * ring->num_items,
266231437Sluigi			oce_dma_map_ring, &dpt, BUS_DMA_NOWAIT);
267231437Sluigi
268231437Sluigi	return dpt.num_entries;
269231437Sluigi}
270