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