xref: /freebsd/sys/dev/oce/oce_util.c (revision 7750ad47a9a7dbc83f87158464170c8640723293)
1 /*-
2  * Copyright (C) 2012 Emulex
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the Emulex Corporation nor the names of its
16  *    contributors may be used to endorse or promote products derived from
17  *    this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * Contact Information:
32  * freebsd-drivers@emulex.com
33  *
34  * Emulex
35  * 3333 Susan Street
36  * Costa Mesa, CA 92626
37  */
38 
39 /* $FreeBSD$ */
40 
41 #include "oce_if.h"
42 
43 static void oce_dma_map_ring(void *arg,
44 			     bus_dma_segment_t *segs,
45 			     int nseg,
46 			     int error);
47 
48 /**
49  * @brief		Allocate DMA memory
50  * @param sc		software handle to the device
51  * @param size		bus size
52  * @param dma		dma memory area
53  * @param flags		creation flags
54  * @returns		0 on success, error otherwize
55  */
56 int
57 oce_dma_alloc(POCE_SOFTC sc, bus_size_t size, POCE_DMA_MEM dma, int flags)
58 {
59 	int rc;
60 
61 
62 	memset(dma, 0, sizeof(OCE_DMA_MEM));
63 
64 	rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
65 				8, 0,
66 				BUS_SPACE_MAXADDR,
67 				BUS_SPACE_MAXADDR,
68 				NULL, NULL,
69 				size, 1, size, 0, NULL, NULL, &dma->tag);
70 
71 	if (rc == 0) {
72 		rc = bus_dmamem_alloc(dma->tag,
73 				      &dma->ptr,
74 				      BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
75 				      &dma->map);
76 	}
77 
78 	dma->paddr = 0;
79 	if (rc == 0) {
80 		rc = bus_dmamap_load(dma->tag,
81 				     dma->map,
82 				     dma->ptr,
83 				     size,
84 				     oce_dma_map_addr,
85 				     &dma->paddr, flags | BUS_DMA_NOWAIT);
86 		if (dma->paddr == 0)
87 			rc = ENXIO;
88 	}
89 
90 	if (rc != 0)
91 		oce_dma_free(sc, dma);
92 
93 	return rc;
94 }
95 
96 /**
97  * @brief		Free DMA memory
98  * @param sc		software handle to the device
99  * @param dma		dma area to free
100  */
101 void
102 oce_dma_free(POCE_SOFTC sc, POCE_DMA_MEM dma)
103 {
104 	if (dma->tag == NULL)
105 		return;
106 
107 	if (dma->map != NULL) {
108 		bus_dmamap_sync(dma->tag, dma->map,
109 				BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
110 		bus_dmamap_unload(dma->tag, dma->map);
111 	}
112 
113 	if (dma->ptr != NULL) {
114 		bus_dmamem_free(dma->tag, dma->ptr, dma->map);
115 		dma->map = NULL;
116 		dma->ptr = NULL;
117 	}
118 
119 	bus_dma_tag_destroy(dma->tag);
120 	dma->tag = NULL;
121 
122 	return;
123 }
124 
125 
126 
127 /**
128  * @brief		Map DMA memory segment addresses
129  * @param arg		physical address pointer
130  * @param segs		dma memory segments
131  * @param nseg		number of dma memory segments
132  * @param error		if error, zeroes the physical address
133  */
134 void
135 oce_dma_map_addr(void *arg, bus_dma_segment_t * segs, int nseg, int error)
136 {
137 	bus_addr_t *paddr = arg;
138 
139 	if (error)
140 		*paddr = 0;
141 	else
142 		*paddr = segs->ds_addr;
143 }
144 
145 
146 
147 /**
148  * @brief		Destroy a ring buffer
149  * @param sc		software handle to the device
150  * @param ring		ring buffer
151  */
152 
153 void
154 oce_destroy_ring_buffer(POCE_SOFTC sc, oce_ring_buffer_t *ring)
155 {
156 	oce_dma_free(sc, &ring->dma);
157 	free(ring, M_DEVBUF);
158 }
159 
160 
161 
162 oce_ring_buffer_t *
163 oce_create_ring_buffer(POCE_SOFTC sc,
164 		uint32_t q_len, uint32_t item_size)
165 {
166 	uint32_t size = q_len * item_size;
167 	int rc;
168 	oce_ring_buffer_t *ring;
169 
170 
171 	ring = malloc(sizeof(oce_ring_buffer_t), M_DEVBUF, M_NOWAIT | M_ZERO);
172 	if (ring == NULL)
173 		return NULL;
174 
175 	ring->item_size = item_size;
176 	ring->num_items = q_len;
177 
178 	rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
179 				4096, 0,
180 				BUS_SPACE_MAXADDR,
181 				BUS_SPACE_MAXADDR,
182 				NULL, NULL,
183 				size, 8, 4096, 0, NULL, NULL, &ring->dma.tag);
184 	if (rc)
185 		goto fail;
186 
187 
188 	rc = bus_dmamem_alloc(ring->dma.tag,
189 				&ring->dma.ptr,
190 				BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
191 				&ring->dma.map);
192 	if (rc)
193 		goto fail;
194 
195 	bzero(ring->dma.ptr, size);
196 	bus_dmamap_sync(ring->dma.tag, ring->dma.map,
197 			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
198 	ring->dma.paddr = 0;
199 
200 	return ring;
201 
202 fail:
203 	oce_dma_free(sc, &ring->dma);
204 	free(ring, M_DEVBUF);
205 	ring = NULL;
206 	return NULL;
207 }
208 
209 
210 
211 struct _oce_dmamap_paddr_table {
212 	uint32_t max_entries;
213 	uint32_t num_entries;
214 	struct phys_addr *paddrs;
215 };
216 
217 
218 
219 /**
220  * @brief		Map ring buffer
221  * @param arg		dma map phyical address table pointer
222  * @param segs		dma memory segments
223  * @param nseg		number of dma memory segments
224  * @param error		maps only if error is 0
225  */
226 static void
227 oce_dma_map_ring(void *arg, bus_dma_segment_t * segs, int nseg, int error)
228 {
229 	int i;
230 	struct _oce_dmamap_paddr_table *dpt =
231 	    (struct _oce_dmamap_paddr_table *)arg;
232 
233 	if (error == 0) {
234 		if (nseg <= dpt->max_entries) {
235 			for (i = 0; i < nseg; i++) {
236 				dpt->paddrs[i].lo = ADDR_LO(segs[i].ds_addr);
237 				dpt->paddrs[i].hi = ADDR_HI(segs[i].ds_addr);
238 			}
239 			dpt->num_entries = nseg;
240 		}
241 	}
242 }
243 
244 
245 
246 /**
247  * @brief		Load bus dma map for a ring buffer
248  * @param ring		ring buffer pointer
249  * @param pa_list	physical address list
250  * @returns		number entries
251  */
252 uint32_t
253 oce_page_list(oce_ring_buffer_t *ring, struct phys_addr *pa_list)
254 {
255 	struct _oce_dmamap_paddr_table dpt;
256 
257 	dpt.max_entries = 8;
258 	dpt.num_entries = 0;
259 	dpt.paddrs = pa_list;
260 
261 	bus_dmamap_load(ring->dma.tag,
262 			ring->dma.map,
263 			ring->dma.ptr,
264 			ring->item_size * ring->num_items,
265 			oce_dma_map_ring, &dpt, BUS_DMA_NOWAIT);
266 
267 	return dpt.num_entries;
268 }
269