xref: /freebsd/sys/dev/oce/oce_util.c (revision ec994981447e8a974426660b5071bc405280af73)
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 
42 #include "oce_if.h"
43 
44 static void oce_dma_map_ring(void *arg,
45 			     bus_dma_segment_t *segs,
46 			     int nseg,
47 			     int error);
48 
49 /**
50  * @brief		Allocate DMA memory
51  * @param sc		software handle to the device
52  * @param size		bus size
53  * @param dma		dma memory area
54  * @param flags		creation flags
55  * @returns		0 on success, error otherwize
56  */
57 int
58 oce_dma_alloc(POCE_SOFTC sc, bus_size_t size, POCE_DMA_MEM dma, int flags)
59 {
60 	int rc;
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 					BUS_DMA_ZERO,
76 				      &dma->map);
77 	}
78 
79 	dma->paddr = 0;
80 	if (rc == 0) {
81 		rc = bus_dmamap_load(dma->tag,
82 				     dma->map,
83 				     dma->ptr,
84 				     size,
85 				     oce_dma_map_addr,
86 				     &dma->paddr, flags | BUS_DMA_NOWAIT);
87 		if (dma->paddr == 0)
88 			rc = ENXIO;
89 	}
90 
91 	if (rc != 0)
92 		oce_dma_free(sc, dma);
93 
94 	return rc;
95 }
96 
97 /**
98  * @brief		Free DMA memory
99  * @param sc		software handle to the device
100  * @param dma		dma area to free
101  */
102 void
103 oce_dma_free(POCE_SOFTC sc, POCE_DMA_MEM dma)
104 {
105 	if (dma->tag == NULL)
106 		return;
107 
108 	if (dma->paddr != 0) {
109 		bus_dmamap_sync(dma->tag, dma->map,
110 				BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
111 		bus_dmamap_unload(dma->tag, dma->map);
112 		dma->paddr = 0;
113 	}
114 
115 	if (dma->ptr != NULL) {
116 		bus_dmamem_free(dma->tag, dma->ptr, dma->map);
117 		dma->ptr = NULL;
118 	}
119 
120 	bus_dma_tag_destroy(dma->tag);
121 	dma->tag = NULL;
122 
123 	return;
124 }
125 
126 /**
127  * @brief		Map DMA memory segment addresses
128  * @param arg		physical address pointer
129  * @param segs		dma memory segments
130  * @param nseg		number of dma memory segments
131  * @param error		if error, zeroes the physical address
132  */
133 void
134 oce_dma_map_addr(void *arg, bus_dma_segment_t * segs, int nseg, int error)
135 {
136 	bus_addr_t *paddr = arg;
137 
138 	if (error)
139 		*paddr = 0;
140 	else
141 		*paddr = segs->ds_addr;
142 }
143 
144 /**
145  * @brief		Destroy a ring buffer
146  * @param sc		software handle to the device
147  * @param ring		ring buffer
148  */
149 
150 void
151 oce_destroy_ring_buffer(POCE_SOFTC sc, oce_ring_buffer_t *ring)
152 {
153 	oce_dma_free(sc, &ring->dma);
154 	free(ring, M_DEVBUF);
155 }
156 
157 oce_ring_buffer_t *
158 oce_create_ring_buffer(POCE_SOFTC sc,
159 		uint32_t q_len, uint32_t item_size)
160 {
161 	uint32_t size = q_len * item_size;
162 	int rc;
163 	oce_ring_buffer_t *ring;
164 
165 	ring = malloc(sizeof(oce_ring_buffer_t), M_DEVBUF, M_NOWAIT | M_ZERO);
166 	if (ring == NULL)
167 		return NULL;
168 
169 	ring->item_size = item_size;
170 	ring->num_items = q_len;
171 
172 	rc = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
173 				4096, 0,
174 				BUS_SPACE_MAXADDR,
175 				BUS_SPACE_MAXADDR,
176 				NULL, NULL,
177 				size, 8, 4096, 0, NULL, NULL, &ring->dma.tag);
178 	if (rc)
179 		goto fail;
180 
181 	rc = bus_dmamem_alloc(ring->dma.tag,
182 				&ring->dma.ptr,
183 				BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
184 				&ring->dma.map);
185 	if (rc)
186 		goto fail;
187 
188 	bzero(ring->dma.ptr, size);
189 	bus_dmamap_sync(ring->dma.tag, ring->dma.map,
190 			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
191 	ring->dma.paddr = 0;
192 
193 	return ring;
194 
195 fail:
196 	oce_dma_free(sc, &ring->dma);
197 	free(ring, M_DEVBUF);
198 	ring = NULL;
199 	return NULL;
200 }
201 
202 struct _oce_dmamap_paddr_table {
203 	uint32_t max_entries;
204 	uint32_t num_entries;
205 	struct phys_addr *paddrs;
206 };
207 
208 /**
209  * @brief		Map ring buffer
210  * @param arg		dma map phyical address table pointer
211  * @param segs		dma memory segments
212  * @param nseg		number of dma memory segments
213  * @param error		maps only if error is 0
214  */
215 static void
216 oce_dma_map_ring(void *arg, bus_dma_segment_t * segs, int nseg, int error)
217 {
218 	int i;
219 	struct _oce_dmamap_paddr_table *dpt =
220 	    (struct _oce_dmamap_paddr_table *)arg;
221 
222 	if (error == 0) {
223 		if (nseg <= dpt->max_entries) {
224 			for (i = 0; i < nseg; i++) {
225 				dpt->paddrs[i].lo = ADDR_LO(segs[i].ds_addr);
226 				dpt->paddrs[i].hi = ADDR_HI(segs[i].ds_addr);
227 			}
228 			dpt->num_entries = nseg;
229 		}
230 	}
231 }
232 
233 /**
234  * @brief		Load bus dma map for a ring buffer
235  * @param ring		ring buffer pointer
236  * @param pa_list	physical address list
237  * @returns		number entries
238  */
239 uint32_t
240 oce_page_list(oce_ring_buffer_t *ring, struct phys_addr *pa_list)
241 {
242 	struct _oce_dmamap_paddr_table dpt;
243 
244 	dpt.max_entries = 8;
245 	dpt.num_entries = 0;
246 	dpt.paddrs = pa_list;
247 
248 	bus_dmamap_load(ring->dma.tag,
249 			ring->dma.map,
250 			ring->dma.ptr,
251 			ring->item_size * ring->num_items,
252 			oce_dma_map_ring, &dpt, BUS_DMA_NOWAIT);
253 
254 	return dpt.num_entries;
255 }
256